From aeee75143baae771110ce078df3d19e74c386ac7 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 31 May 2008 15:04:32 +0000 Subject: si470x: move global lock to device structure From: Tobias Lorenz this patch brings the following changes: - move the global disconnect lock into the device structure - code cleanup (spaces to tabs, long line splits, ...) Signed-off-by: Tobias Lorenz Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/radio/radio-si470x.c | 84 ++++++++++++++++---------------- 1 file changed, 41 insertions(+), 43 deletions(-) (limited to 'linux/drivers/media/radio/radio-si470x.c') diff --git a/linux/drivers/media/radio/radio-si470x.c b/linux/drivers/media/radio/radio-si470x.c index 9f4207edc..8af0eb520 100644 --- a/linux/drivers/media/radio/radio-si470x.c +++ b/linux/drivers/media/radio/radio-si470x.c @@ -85,7 +85,7 @@ * Oliver Neukum * Version 1.0.7 * - usb autosuspend support - * - unplugging fixed + * - unplugging fixed * * ToDo: * - add seeking support @@ -428,7 +428,8 @@ struct si470x_device { /* driver management */ unsigned int users; - unsigned char disconnected; + unsigned char disconnected; + struct mutex disconnect_lock; /* Silabs internal registers (0..15) */ unsigned short registers[RADIO_REGISTER_NUM]; @@ -452,12 +453,6 @@ struct si470x_device { }; -/* - * Lock to prevent kfree of data before all users have releases the device. - */ -static DEFINE_MUTEX(open_close_lock); - - /* * The frequency is set in units of 62.5 Hz when using V4L2_TUNER_CAP_LOW, * 62.5 kHz otherwise. @@ -593,7 +588,7 @@ static int si470x_get_rds_registers(struct si470x_device *radio) usb_rcvintpipe(radio->usbdev, 1), (void *) &buf, sizeof(buf), &size, usb_timeout); if (size != sizeof(buf)) - printk(KERN_WARNING DRIVER_NAME ": si470x_get_rds_registers: " + printk(KERN_WARNING DRIVER_NAME ": si470x_get_rds_registers: " "return size differs: %d != %zu\n", size, sizeof(buf)); if (retval < 0) printk(KERN_WARNING DRIVER_NAME ": si470x_get_rds_registers: " @@ -602,7 +597,8 @@ static int si470x_get_rds_registers(struct si470x_device *radio) if (retval >= 0) for (regnr = 0; regnr < RDS_REGISTER_NUM; regnr++) radio->registers[STATUSRSSI + regnr] = - get_unaligned_be16(&buf[regnr * RADIO_REGISTER_SIZE + 1]); + get_unaligned_be16( + &buf[regnr * RADIO_REGISTER_SIZE + 1]); return (retval < 0) ? -EINVAL : 0; } @@ -890,8 +886,8 @@ static void si470x_work(struct work_struct *work) struct si470x_device *radio = container_of(work, struct si470x_device, work.work); - if (radio->disconnected) - return; + if (radio->disconnected) + return; if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) return; @@ -1018,20 +1014,20 @@ static int si470x_fops_open(struct inode *inode, struct file *file) static int si470x_fops_release(struct inode *inode, struct file *file) { struct si470x_device *radio = video_get_drvdata(video_devdata(file)); - int retval = 0; + int retval = 0; if (!radio) return -ENODEV; - mutex_lock(&open_close_lock); + mutex_lock(&radio->disconnect_lock); radio->users--; if (radio->users == 0) { - if (radio->disconnected) { - video_unregister_device(radio->videodev); - kfree(radio->buffer); - kfree(radio); - goto done; - } + if (radio->disconnected) { + video_unregister_device(radio->videodev); + kfree(radio->buffer); + kfree(radio); + goto unlock; + } /* stop rds reception */ cancel_delayed_work_sync(&radio->work); @@ -1043,9 +1039,9 @@ static int si470x_fops_release(struct inode *inode, struct file *file) usb_autopm_put_interface(radio->intf); } -done: - mutex_unlock(&open_close_lock); - return retval; +unlock: + mutex_unlock(&radio->disconnect_lock); + return retval; } @@ -1185,8 +1181,8 @@ static int si470x_vidioc_g_ctrl(struct file *file, void *priv, { struct si470x_device *radio = video_get_drvdata(video_devdata(file)); - if (radio->disconnected) - return -EIO; + if (radio->disconnected) + return -EIO; switch (ctrl->id) { case V4L2_CID_AUDIO_VOLUME: @@ -1212,8 +1208,8 @@ static int si470x_vidioc_s_ctrl(struct file *file, void *priv, struct si470x_device *radio = video_get_drvdata(video_devdata(file)); int retval; - if (radio->disconnected) - return -EIO; + if (radio->disconnected) + return -EIO; switch (ctrl->id) { case V4L2_CID_AUDIO_VOLUME: @@ -1277,8 +1273,8 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv, struct si470x_device *radio = video_get_drvdata(video_devdata(file)); int retval; - if (radio->disconnected) - return -EIO; + if (radio->disconnected) + return -EIO; if (tuner->index > 0) return -EINVAL; @@ -1335,8 +1331,8 @@ static int si470x_vidioc_s_tuner(struct file *file, void *priv, struct si470x_device *radio = video_get_drvdata(video_devdata(file)); int retval; - if (radio->disconnected) - return -EIO; + if (radio->disconnected) + return -EIO; if (tuner->index > 0) return -EINVAL; @@ -1362,8 +1358,8 @@ static int si470x_vidioc_g_frequency(struct file *file, void *priv, { struct si470x_device *radio = video_get_drvdata(video_devdata(file)); - if (radio->disconnected) - return -EIO; + if (radio->disconnected) + return -EIO; freq->type = V4L2_TUNER_RADIO; freq->frequency = si470x_get_freq(radio); @@ -1381,8 +1377,8 @@ static int si470x_vidioc_s_frequency(struct file *file, void *priv, struct si470x_device *radio = video_get_drvdata(video_devdata(file)); int retval; - if (radio->disconnected) - return -EIO; + if (radio->disconnected) + return -EIO; if (freq->type != V4L2_TUNER_RADIO) return -EINVAL; @@ -1447,8 +1443,10 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, memcpy(radio->videodev, &si470x_viddev_template, sizeof(si470x_viddev_template)); radio->users = 0; + radio->disconnected = 0; radio->usbdev = interface_to_usbdev(intf); radio->intf = intf; + mutex_init(&radio->disconnect_lock); mutex_init(&radio->lock); video_set_drvdata(radio->videodev, radio); @@ -1553,16 +1551,16 @@ static void si470x_usb_driver_disconnect(struct usb_interface *intf) { struct si470x_device *radio = usb_get_intfdata(intf); - mutex_lock(&open_close_lock); - radio->disconnected = 1; + mutex_lock(&radio->disconnect_lock); + radio->disconnected = 1; cancel_delayed_work_sync(&radio->work); usb_set_intfdata(intf, NULL); - if (radio->users == 0) { - video_unregister_device(radio->videodev); - kfree(radio->buffer); - kfree(radio); - } - mutex_unlock(&open_close_lock); + if (radio->users == 0) { + video_unregister_device(radio->videodev); + kfree(radio->buffer); + kfree(radio); + } + mutex_unlock(&radio->disconnect_lock); } -- cgit v1.2.3 From e1a2c4fcdec396c193e595da7b5327c1b1fda0c3 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 31 May 2008 15:06:50 +0000 Subject: si470x: let si470x_get_freq return errno From: Tobias Lorenz Hi Mauro, this patch brings the following changes: - version bumped to 1.0.8 for all the following patches - si470x_get_freq now returns errno Signed-off-by: Tobias Lorenz Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/radio/radio-si470x.c | 34 +++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) (limited to 'linux/drivers/media/radio/radio-si470x.c') diff --git a/linux/drivers/media/radio/radio-si470x.c b/linux/drivers/media/radio/radio-si470x.c index 8af0eb520..890207c6d 100644 --- a/linux/drivers/media/radio/radio-si470x.c +++ b/linux/drivers/media/radio/radio-si470x.c @@ -23,6 +23,19 @@ */ +/* + * User Notes: + * - USB Audio is provided by the alsa snd_usb_audio module. + * For listing you have to redirect the sound, for example using: + * arecord -D hw:1,0 -r96000 -c2 -f S16_LE | artsdsp aplay -B - + * - regarding module parameters in /sys/module/radio_si470x/parameters: + * the contents of read-only files (0444) are not updated, even if + * space, band and de are changed using private video controls + * - increase tune_timeout, if you often get -EIO errors + * - hw_freq_seek returns -EAGAIN, when timed out or band limit is reached + */ + + /* * History: * 2008-01-12 Tobias Lorenz @@ -86,6 +99,9 @@ * Version 1.0.7 * - usb autosuspend support * - unplugging fixed + * 2008-05-07 Tobias Lorenz + * Version 1.0.8 + * - let si470x_get_freq return errno * * ToDo: * - add seeking support @@ -98,10 +114,10 @@ /* driver definitions */ #define DRIVER_AUTHOR "Tobias Lorenz " #define DRIVER_NAME "radio-si470x" -#define DRIVER_KERNEL_VERSION KERNEL_VERSION(1, 0, 7) +#define DRIVER_KERNEL_VERSION KERNEL_VERSION(1, 0, 8) #define DRIVER_CARD "Silicon Labs Si470x FM Radio Receiver" #define DRIVER_DESC "USB radio driver for Si470x FM Radio Receivers" -#define DRIVER_VERSION "1.0.7" +#define DRIVER_VERSION "1.0.8" /* kernel includes */ @@ -642,9 +658,9 @@ static int si470x_set_chan(struct si470x_device *radio, unsigned short chan) /* * si470x_get_freq - get the frequency */ -static unsigned int si470x_get_freq(struct si470x_device *radio) +static int si470x_get_freq(struct si470x_device *radio, unsigned int *freq) { - unsigned int spacing, band_bottom, freq; + unsigned int spacing, band_bottom; unsigned short chan; int retval; @@ -670,14 +686,12 @@ static unsigned int si470x_get_freq(struct si470x_device *radio) /* read channel */ retval = si470x_get_register(radio, READCHAN); - if (retval < 0) - return retval; chan = radio->registers[READCHAN] & READCHAN_READCHAN; /* Frequency (MHz) = Spacing (kHz) x Channel + Bottom of Band (MHz) */ - freq = chan * spacing + band_bottom; + *freq = chan * spacing + band_bottom; - return freq; + return retval; } @@ -1362,9 +1376,7 @@ static int si470x_vidioc_g_frequency(struct file *file, void *priv, return -EIO; freq->type = V4L2_TUNER_RADIO; - freq->frequency = si470x_get_freq(radio); - - return 0; + return si470x_get_freq(radio, &radio->frequency); } -- cgit v1.2.3 From a331cce0876566ae8a7144ce1a56fe8c71e0194b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 31 May 2008 15:07:52 +0000 Subject: si470x: a lot of small code cleanups MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From: Tobias Lorenz this patch brings the following changes: - comment on how to listen to an usb audio device   (i get so many questions about that...) - code cleanup (error handling, more warnings, spacing, ...) Signed-off-by: Tobias Lorenz Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/radio/radio-si470x.c | 286 +++++++++++++++++++++---------- 1 file changed, 196 insertions(+), 90 deletions(-) (limited to 'linux/drivers/media/radio/radio-si470x.c') diff --git a/linux/drivers/media/radio/radio-si470x.c b/linux/drivers/media/radio/radio-si470x.c index 890207c6d..e56ce15aa 100644 --- a/linux/drivers/media/radio/radio-si470x.c +++ b/linux/drivers/media/radio/radio-si470x.c @@ -101,7 +101,7 @@ * - unplugging fixed * 2008-05-07 Tobias Lorenz * Version 1.0.8 - * - let si470x_get_freq return errno + * - more safety checks, let si470x_get_freq return errno * * ToDo: * - add seeking support @@ -498,11 +498,11 @@ static int si470x_get_report(struct si470x_device *radio, void *buf, int size) USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, report[0], 2, buf, size, usb_timeout); + if (retval < 0) printk(KERN_WARNING DRIVER_NAME ": si470x_get_report: usb_control_msg returned %d\n", retval); - return retval; } @@ -521,11 +521,11 @@ static int si470x_set_report(struct si470x_device *radio, void *buf, int size) USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, report[0], 2, buf, size, usb_timeout); + if (retval < 0) printk(KERN_WARNING DRIVER_NAME ": si470x_set_report: usb_control_msg returned %d\n", retval); - return retval; } @@ -634,24 +634,30 @@ static int si470x_set_chan(struct si470x_device *radio, unsigned short chan) radio->registers[CHANNEL] |= CHANNEL_TUNE | chan; retval = si470x_set_register(radio, CHANNEL); if (retval < 0) - return retval; + goto done; - /* wait till seek operation has completed */ + /* wait till tune operation has completed */ timeout = jiffies + msecs_to_jiffies(tune_timeout); do { retval = si470x_get_register(radio, STATUSRSSI); if (retval < 0) - return retval; + goto stop; timed_out = time_after(jiffies, timeout); } while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) && (!timed_out)); + if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) + printk(KERN_WARNING DRIVER_NAME ": tune does not complete\n"); if (timed_out) printk(KERN_WARNING DRIVER_NAME - ": seek does not finish after %u ms\n", tune_timeout); + ": tune timed out after %u ms\n", tune_timeout); +stop: /* stop tuning */ radio->registers[CHANNEL] &= ~CHANNEL_TUNE; - return si470x_set_register(radio, CHANNEL); + retval = si470x_set_register(radio, CHANNEL); + +done: + return retval; } @@ -742,27 +748,30 @@ static int si470x_start(struct si470x_device *radio) POWERCFG_DMUTE | POWERCFG_ENABLE | POWERCFG_RDSM; retval = si470x_set_register(radio, POWERCFG); if (retval < 0) - return retval; + goto done; /* sysconfig 1 */ radio->registers[SYSCONFIG1] = SYSCONFIG1_DE; retval = si470x_set_register(radio, SYSCONFIG1); if (retval < 0) - return retval; + goto done; /* sysconfig 2 */ radio->registers[SYSCONFIG2] = - (0x3f << 8) | /* SEEKTH */ - (band << 6) | /* BAND */ - (space << 4) | /* SPACE */ - 15; /* VOLUME (max) */ + (0x3f << 8) | /* SEEKTH */ + ((band << 6) & SYSCONFIG2_BAND) | /* BAND */ + ((space << 4) & SYSCONFIG2_SPACE) | /* SPACE */ + 15; /* VOLUME (max) */ retval = si470x_set_register(radio, SYSCONFIG2); if (retval < 0) - return retval; + goto done; /* reset last channel */ - return si470x_set_chan(radio, + retval = si470x_set_chan(radio, radio->registers[CHANNEL] & CHANNEL_CHAN); + +done: + return retval; } @@ -777,13 +786,16 @@ static int si470x_stop(struct si470x_device *radio) radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_RDS; retval = si470x_set_register(radio, SYSCONFIG1); if (retval < 0) - return retval; + goto done; /* powercfg */ radio->registers[POWERCFG] &= ~POWERCFG_DMUTE; /* POWERCFG_ENABLE has to automatically go low */ radio->registers[POWERCFG] |= POWERCFG_ENABLE | POWERCFG_DISABLE; - return si470x_set_register(radio, POWERCFG); + retval = si470x_set_register(radio, POWERCFG); + +done: + return retval; } @@ -900,6 +912,7 @@ static void si470x_work(struct work_struct *work) struct si470x_device *radio = container_of(work, struct si470x_device, work.work); + /* safety checks */ if (radio->disconnected) return; if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) @@ -934,11 +947,15 @@ static ssize_t si470x_fops_read(struct file *file, char __user *buf, /* block if no new data available */ while (radio->wr_index == radio->rd_index) { - if (file->f_flags & O_NONBLOCK) - return -EWOULDBLOCK; + if (file->f_flags & O_NONBLOCK) { + retval = -EWOULDBLOCK; + goto done; + } if (wait_event_interruptible(radio->read_queue, - radio->wr_index != radio->rd_index) < 0) - return -EINTR; + radio->wr_index != radio->rd_index) < 0) { + retval = -EINTR; + goto done; + } } /* calculate block count from byte count */ @@ -967,6 +984,7 @@ static ssize_t si470x_fops_read(struct file *file, char __user *buf, } mutex_unlock(&radio->lock); +done: return retval; } @@ -978,6 +996,7 @@ static unsigned int si470x_fops_poll(struct file *file, struct poll_table_struct *pts) { struct si470x_device *radio = video_get_drvdata(video_devdata(file)); + int retval = 0; /* switch on rds reception */ if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) { @@ -989,9 +1008,9 @@ static unsigned int si470x_fops_poll(struct file *file, poll_wait(file, &radio->read_queue, pts); if (radio->rd_index != radio->wr_index) - return POLLIN | POLLRDNORM; + retval = POLLIN | POLLRDNORM; - return 0; + return retval; } @@ -1008,17 +1027,18 @@ static int si470x_fops_open(struct inode *inode, struct file *file) retval = usb_autopm_get_interface(radio->intf); if (retval < 0) { radio->users--; - return -EIO; + retval = -EIO; + goto done; } if (radio->users == 1) { retval = si470x_start(radio); if (retval < 0) usb_autopm_put_interface(radio->intf); - return retval; } - return 0; +done: + return retval; } @@ -1030,8 +1050,11 @@ static int si470x_fops_release(struct inode *inode, struct file *file) struct si470x_device *radio = video_get_drvdata(video_devdata(file)); int retval = 0; - if (!radio) - return -ENODEV; + /* safety check */ + if (!radio) { + retval = -ENODEV; + goto done; + } mutex_lock(&radio->disconnect_lock); radio->users--; @@ -1055,6 +1078,8 @@ static int si470x_fops_release(struct inode *inode, struct file *file) unlock: mutex_unlock(&radio->disconnect_lock); + +done: return retval; } @@ -1142,7 +1167,7 @@ static int si470x_vidioc_querycap(struct file *file, void *priv, /* * si470x_vidioc_g_input - get input */ -static int si470x_vidioc_g_input(struct file *filp, void *priv, +static int si470x_vidioc_g_input(struct file *file, void *priv, unsigned int *i) { *i = 0; @@ -1154,12 +1179,18 @@ static int si470x_vidioc_g_input(struct file *filp, void *priv, /* * si470x_vidioc_s_input - set input */ -static int si470x_vidioc_s_input(struct file *filp, void *priv, unsigned int i) +static int si470x_vidioc_s_input(struct file *file, void *priv, unsigned int i) { + int retval = 0; + + /* safety checks */ if (i != 0) - return -EINVAL; + retval = -EINVAL; - return 0; + if (retval < 0) + printk(KERN_WARNING DRIVER_NAME + ": set input failed with %d\n", retval); + return retval; } @@ -1172,17 +1203,22 @@ static int si470x_vidioc_queryctrl(struct file *file, void *priv, unsigned char i; int retval = -EINVAL; + /* safety checks */ + if (!qc->id) + goto done; + for (i = 0; i < ARRAY_SIZE(si470x_v4l2_queryctrl); i++) { - if (qc->id && qc->id == si470x_v4l2_queryctrl[i].id) { + if (qc->id == si470x_v4l2_queryctrl[i].id) { memcpy(qc, &(si470x_v4l2_queryctrl[i]), sizeof(*qc)); retval = 0; break; } } + +done: if (retval < 0) printk(KERN_WARNING DRIVER_NAME - ": query control failed with %d\n", retval); - + ": query controls failed with %d\n", retval); return retval; } @@ -1194,9 +1230,13 @@ static int si470x_vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { struct si470x_device *radio = video_get_drvdata(video_devdata(file)); + int retval = 0; - if (radio->disconnected) - return -EIO; + /* safety checks */ + if (radio->disconnected) { + retval = -EIO; + goto done; + } switch (ctrl->id) { case V4L2_CID_AUDIO_VOLUME: @@ -1207,9 +1247,15 @@ static int si470x_vidioc_g_ctrl(struct file *file, void *priv, ctrl->value = ((radio->registers[POWERCFG] & POWERCFG_DMUTE) == 0) ? 1 : 0; break; + default: + retval = -EINVAL; } - return 0; +done: + if (retval < 0) + printk(KERN_WARNING DRIVER_NAME + ": get control failed with %d\n", retval); + return retval; } @@ -1220,10 +1266,13 @@ static int si470x_vidioc_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { struct si470x_device *radio = video_get_drvdata(video_devdata(file)); - int retval; + int retval = 0; - if (radio->disconnected) - return -EIO; + /* safety checks */ + if (radio->disconnected) { + retval = -EIO; + goto done; + } switch (ctrl->id) { case V4L2_CID_AUDIO_VOLUME: @@ -1241,10 +1290,11 @@ static int si470x_vidioc_s_ctrl(struct file *file, void *priv, default: retval = -EINVAL; } + +done: if (retval < 0) printk(KERN_WARNING DRIVER_NAME ": set control failed with %d\n", retval); - return retval; } @@ -1255,13 +1305,22 @@ static int si470x_vidioc_s_ctrl(struct file *file, void *priv, static int si470x_vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *audio) { - if (audio->index > 1) - return -EINVAL; + int retval = 0; + + /* safety checks */ + if (audio->index != 0) { + retval = -EINVAL; + goto done; + } strcpy(audio->name, "Radio"); audio->capability = V4L2_AUDCAP_STEREO; - return 0; +done: + if (retval < 0) + printk(KERN_WARNING DRIVER_NAME + ": get audio failed with %d\n", retval); + return retval; } @@ -1271,10 +1330,19 @@ static int si470x_vidioc_g_audio(struct file *file, void *priv, static int si470x_vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *audio) { - if (audio->index != 0) - return -EINVAL; + int retval = 0; - return 0; + /* safety checks */ + if (audio->index != 0) { + retval = -EINVAL; + goto done; + } + +done: + if (retval < 0) + printk(KERN_WARNING DRIVER_NAME + ": set audio failed with %d\n", retval); + return retval; } @@ -1285,20 +1353,23 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *tuner) { struct si470x_device *radio = video_get_drvdata(video_devdata(file)); - int retval; + int retval = 0; - if (radio->disconnected) - return -EIO; - if (tuner->index > 0) - return -EINVAL; + /* safety checks */ + if (radio->disconnected) { + retval = -EIO; + goto done; + } + if ((tuner->index != 0) && (tuner->type != V4L2_TUNER_RADIO)) { + retval = -EINVAL; + goto done; + } - /* read status rssi */ retval = si470x_get_register(radio, STATUSRSSI); if (retval < 0) - return retval; + goto done; strcpy(tuner->name, "FM"); - tuner->type = V4L2_TUNER_RADIO; switch (band) { /* 0: 87.5 - 108 MHz (USA, Europe, default) */ default: @@ -1332,7 +1403,11 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv, /* automatic frequency control: -1: freq to low, 1 freq to high */ tuner->afc = 0; - return 0; +done: + if (retval < 0) + printk(KERN_WARNING DRIVER_NAME + ": get tuner failed with %d\n", retval); + return retval; } @@ -1343,12 +1418,17 @@ static int si470x_vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *tuner) { struct si470x_device *radio = video_get_drvdata(video_devdata(file)); - int retval; + int retval = 0; - if (radio->disconnected) - return -EIO; - if (tuner->index > 0) - return -EINVAL; + /* safety checks */ + if (radio->disconnected) { + retval = -EIO; + goto done; + } + if ((tuner->index != 0) && (tuner->type != V4L2_TUNER_RADIO)) { + retval = -EINVAL; + goto done; + } if (tuner->audmode == V4L2_TUNER_MODE_MONO) radio->registers[POWERCFG] |= POWERCFG_MONO; /* force mono */ @@ -1356,10 +1436,11 @@ static int si470x_vidioc_s_tuner(struct file *file, void *priv, radio->registers[POWERCFG] &= ~POWERCFG_MONO; /* try stereo */ retval = si470x_set_register(radio, POWERCFG); + +done: if (retval < 0) printk(KERN_WARNING DRIVER_NAME ": set tuner failed with %d\n", retval); - return retval; } @@ -1371,12 +1452,25 @@ static int si470x_vidioc_g_frequency(struct file *file, void *priv, struct v4l2_frequency *freq) { struct si470x_device *radio = video_get_drvdata(video_devdata(file)); + int retval = 0; - if (radio->disconnected) - return -EIO; + /* safety checks */ + if (radio->disconnected) { + retval = -EIO; + goto done; + } + if ((freq->tuner != 0) && (freq->type != V4L2_TUNER_RADIO)) { + retval = -EINVAL; + goto done; + } + + retval = si470x_get_freq(radio, &freq->frequency); - freq->type = V4L2_TUNER_RADIO; - return si470x_get_freq(radio, &radio->frequency); +done: + if (retval < 0) + printk(KERN_WARNING DRIVER_NAME + ": get frequency failed with %d\n", retval); + return retval; } @@ -1387,19 +1481,25 @@ static int si470x_vidioc_s_frequency(struct file *file, void *priv, struct v4l2_frequency *freq) { struct si470x_device *radio = video_get_drvdata(video_devdata(file)); - int retval; + int retval = 0; - if (radio->disconnected) - return -EIO; - if (freq->type != V4L2_TUNER_RADIO) - return -EINVAL; + /* safety checks */ + if (radio->disconnected) { + retval = -EIO; + goto done; + } + if ((freq->tuner != 0) && (freq->type != V4L2_TUNER_RADIO)) { + retval = -EINVAL; + goto done; + } retval = si470x_set_freq(radio, freq->frequency); + +done: if (retval < 0) printk(KERN_WARNING DRIVER_NAME ": set frequency failed with %d\n", retval); - - return 0; + return retval; } @@ -1439,33 +1539,36 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct si470x_device *radio; - int retval = -ENOMEM; + int retval = 0; - /* private data allocation */ + /* private data allocation and initialization */ radio = kzalloc(sizeof(struct si470x_device), GFP_KERNEL); - if (!radio) + if (!radio) { + retval = -ENOMEM; goto err_initial; - - /* video device allocation */ - radio->videodev = video_device_alloc(); - if (!radio->videodev) - goto err_radio; - - /* initial configuration */ - memcpy(radio->videodev, &si470x_viddev_template, - sizeof(si470x_viddev_template)); + } radio->users = 0; radio->disconnected = 0; radio->usbdev = interface_to_usbdev(intf); radio->intf = intf; mutex_init(&radio->disconnect_lock); mutex_init(&radio->lock); + + /* video device allocation and initialization */ + radio->videodev = video_device_alloc(); + if (!radio->videodev) { + retval = -ENOMEM; + goto err_radio; + } + memcpy(radio->videodev, &si470x_viddev_template, + sizeof(si470x_viddev_template)); video_set_drvdata(radio->videodev, radio); /* show some infos about the specific device */ - retval = -EIO; - if (si470x_get_all_registers(radio) < 0) + if (si470x_get_all_registers(radio) < 0) { + retval = -EIO; goto err_all; + } printk(KERN_INFO DRIVER_NAME ": DeviceID=0x%4.4hx ChipID=0x%4.4hx\n", radio->registers[DEVICEID], radio->registers[CHIPID]); @@ -1491,8 +1594,10 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, /* rds buffer allocation */ radio->buf_size = rds_buf * 3; radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL); - if (!radio->buffer) + if (!radio->buffer) { + retval = -EIO; goto err_all; + } /* rds buffer configuration */ radio->wr_index = 0; @@ -1504,6 +1609,7 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, /* register video device */ if (video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr)) { + retval = -EIO; printk(KERN_WARNING DRIVER_NAME ": Could not register video device\n"); goto err_all; -- cgit v1.2.3 From 1614e38161dbbe2cb0a03ca75e4d4c4663f7bc2e Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 31 May 2008 15:09:07 +0000 Subject: si470x: afc indication From: Tobias Lorenz this patch brings the following changes: - afc indication: device has no indication whether freq is too low or too high therefore afc always return 1, when freq is wrong Signed-off-by: Tobias Lorenz Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/radio/radio-si470x.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'linux/drivers/media/radio/radio-si470x.c') diff --git a/linux/drivers/media/radio/radio-si470x.c b/linux/drivers/media/radio/radio-si470x.c index e56ce15aa..5cebcc11c 100644 --- a/linux/drivers/media/radio/radio-si470x.c +++ b/linux/drivers/media/radio/radio-si470x.c @@ -101,6 +101,7 @@ * - unplugging fixed * 2008-05-07 Tobias Lorenz * Version 1.0.8 + * - afc indication * - more safety checks, let si470x_get_freq return errno * * ToDo: @@ -1401,7 +1402,8 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv, * 0x0101; /* automatic frequency control: -1: freq to low, 1 freq to high */ - tuner->afc = 0; + /* AFCRL does only indicate that freq. differs, not if too low/high */ + tuner->afc = (radio->registers[STATUSRSSI] & STATUSRSSI_AFCRL) ? 1 : 0; done: if (retval < 0) -- cgit v1.2.3 From 0f339fedba49b4571e77a6e9eada52f4101750a2 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 31 May 2008 15:11:32 +0000 Subject: si470x: hardware frequency seek support From: Tobias Lorenz this patch brings the following changes: - this now finally adds hardware frequency seek support Signed-off-by: Tobias Lorenz Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/radio/radio-si470x.c | 97 +++++++++++++++++++++++++++++++- 1 file changed, 95 insertions(+), 2 deletions(-) (limited to 'linux/drivers/media/radio/radio-si470x.c') diff --git a/linux/drivers/media/radio/radio-si470x.c b/linux/drivers/media/radio/radio-si470x.c index 5cebcc11c..b41eddd4f 100644 --- a/linux/drivers/media/radio/radio-si470x.c +++ b/linux/drivers/media/radio/radio-si470x.c @@ -101,11 +101,11 @@ * - unplugging fixed * 2008-05-07 Tobias Lorenz * Version 1.0.8 + * - hardware frequency seek support * - afc indication * - more safety checks, let si470x_get_freq return errno * * ToDo: - * - add seeking support * - add firmware download/update support * - RDS support: interrupt mode, instead of polling * - add LED status output (check if that's not already done in firmware) @@ -195,6 +195,11 @@ static unsigned int tune_timeout = 3000; module_param(tune_timeout, uint, 0); MODULE_PARM_DESC(tune_timeout, "Tune timeout: *3000*"); +/* Seek timeout */ +static unsigned int seek_timeout = 5000; +module_param(seek_timeout, uint, 0); +MODULE_PARM_DESC(seek_timeout, "Seek timeout: *5000*"); + /* RDS buffer blocks */ static unsigned int rds_buf = 100; module_param(rds_buf, uint, 0); @@ -737,6 +742,62 @@ static int si470x_set_freq(struct si470x_device *radio, unsigned int freq) } +/* + * si470x_set_seek - set seek + */ +static int si470x_set_seek(struct si470x_device *radio, + unsigned int wrap_around, unsigned int seek_upward) +{ + int retval = 0; + unsigned long timeout; + bool timed_out = 0; + + /* start seeking */ + radio->registers[POWERCFG] |= POWERCFG_SEEK; + if (wrap_around == 1) + radio->registers[POWERCFG] &= ~POWERCFG_SKMODE; + else + radio->registers[POWERCFG] |= POWERCFG_SKMODE; + if (seek_upward == 1) + radio->registers[POWERCFG] |= POWERCFG_SEEKUP; + else + radio->registers[POWERCFG] &= ~POWERCFG_SEEKUP; + retval = si470x_set_register(radio, POWERCFG); + if (retval < 0) + goto done; + + /* wait till seek operation has completed */ + timeout = jiffies + msecs_to_jiffies(seek_timeout); + do { + retval = si470x_get_register(radio, STATUSRSSI); + if (retval < 0) + goto stop; + timed_out = time_after(jiffies, timeout); + } while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) && + (!timed_out)); + if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) + printk(KERN_WARNING DRIVER_NAME ": seek does not complete\n"); + if (radio->registers[STATUSRSSI] & STATUSRSSI_SF) + printk(KERN_WARNING DRIVER_NAME + ": seek failed / band limit reached\n"); + if (timed_out) + printk(KERN_WARNING DRIVER_NAME + ": seek timed out after %u ms\n", seek_timeout); + +stop: + /* stop seeking */ + radio->registers[POWERCFG] &= ~POWERCFG_SEEK; + retval = si470x_set_register(radio, POWERCFG); + +done: + /* try again, if timed out */ + if ((retval == 0) && timed_out) + retval = -EAGAIN; + + return retval; +} + + /* * si470x_start - switch on radio */ @@ -1159,7 +1220,8 @@ static int si470x_vidioc_querycap(struct file *file, void *priv, strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card)); sprintf(capability->bus_info, "USB"); capability->version = DRIVER_KERNEL_VERSION; - capability->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; + capability->capabilities = V4L2_CAP_HW_FREQ_SEEK | + V4L2_CAP_TUNER | V4L2_CAP_RADIO; return 0; } @@ -1505,6 +1567,36 @@ done: } +/* + * si470x_vidioc_s_hw_freq_seek - set hardware frequency seek + */ +static int si470x_vidioc_s_hw_freq_seek(struct file *file, void *priv, + struct v4l2_hw_freq_seek *seek) +{ + struct si470x_device *radio = video_get_drvdata(video_devdata(file)); + int retval = 0; + + /* safety checks */ + if (radio->disconnected) { + retval = -EIO; + goto done; + } + if ((seek->tuner != 0) && (seek->type != V4L2_TUNER_RADIO)) { + retval = -EINVAL; + goto done; + } + + retval = si470x_set_seek(radio, seek->wrap_around, seek->seek_upward); + +done: + if (retval < 0) + printk(KERN_WARNING DRIVER_NAME + ": set hardware frequency seek failed with %d\n", + retval); + return retval; +} + + /* * si470x_viddev_tamples - video device interface */ @@ -1525,6 +1617,7 @@ static struct video_device si470x_viddev_template = { .vidioc_s_tuner = si470x_vidioc_s_tuner, .vidioc_g_frequency = si470x_vidioc_g_frequency, .vidioc_s_frequency = si470x_vidioc_s_frequency, + .vidioc_s_hw_freq_seek = si470x_vidioc_s_hw_freq_seek, .owner = THIS_MODULE, }; -- cgit v1.2.3