From 5ad6b816e2227a7daf9569f1dbb8d7af90b387b2 Mon Sep 17 00:00:00 2001 From: Harvey Harrison Date: Tue, 15 Apr 2008 22:22:11 -0300 Subject: V4L/DVB (7586): radio: use get/put_unaligned_* helpers Signed-off-by: Harvey Harrison Signed-off-by: Andrew Morton Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/radio-si470x.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) (limited to 'drivers/media/radio/radio-si470x.c') diff --git a/drivers/media/radio/radio-si470x.c b/drivers/media/radio/radio-si470x.c index 77354ca6e8e..5dd33197bbd 100644 --- a/drivers/media/radio/radio-si470x.c +++ b/drivers/media/radio/radio-si470x.c @@ -521,8 +521,7 @@ static int si470x_get_register(struct si470x_device *radio, int regnr) retval = si470x_get_report(radio, (void *) &buf, sizeof(buf)); if (retval >= 0) - radio->registers[regnr] = be16_to_cpu(get_unaligned( - (unsigned short *) &buf[1])); + radio->registers[regnr] = get_unaligned_be16(&buf[1]); return (retval < 0) ? -EINVAL : 0; } @@ -537,8 +536,7 @@ static int si470x_set_register(struct si470x_device *radio, int regnr) int retval; buf[0] = REGISTER_REPORT(regnr); - put_unaligned(cpu_to_be16(radio->registers[regnr]), - (unsigned short *) &buf[1]); + put_unaligned_be16(radio->registers[regnr], &buf[1]); retval = si470x_set_report(radio, (void *) &buf, sizeof(buf)); @@ -561,9 +559,8 @@ static int si470x_get_all_registers(struct si470x_device *radio) if (retval >= 0) for (regnr = 0; regnr < RADIO_REGISTER_NUM; regnr++) - radio->registers[regnr] = be16_to_cpu(get_unaligned( - (unsigned short *) - &buf[regnr * RADIO_REGISTER_SIZE + 1])); + radio->registers[regnr] = get_unaligned_be16( + &buf[regnr * RADIO_REGISTER_SIZE + 1]); return (retval < 0) ? -EINVAL : 0; } @@ -594,8 +591,7 @@ 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] = - be16_to_cpu(get_unaligned((unsigned short *) - &buf[regnr * RADIO_REGISTER_SIZE + 1])); + get_unaligned_be16(&buf[regnr * RADIO_REGISTER_SIZE + 1]); return (retval < 0) ? -EINVAL : 0; } @@ -843,7 +839,7 @@ static void si470x_rds(struct si470x_device *radio) }; /* Fill the V4L2 RDS buffer */ - put_unaligned(cpu_to_le16(rds), (unsigned short *) &tmpbuf); + put_unaligned_le16(rds, &tmpbuf); tmpbuf[2] = blocknum; /* offset name */ tmpbuf[2] |= blocknum << 3; /* received offset */ if (bler > max_rds_errors) -- cgit v1.2.3-70-g09d2 From ce5829e5fc8204af09db5b226a3dce9824e7d596 Mon Sep 17 00:00:00 2001 From: Tobias Lorenz Date: Sat, 31 May 2008 15:04:32 -0300 Subject: V4L/DVB (7993): si470x: move global lock to device structure 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 --- drivers/media/radio/radio-si470x.c | 84 +++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 43 deletions(-) (limited to 'drivers/media/radio/radio-si470x.c') diff --git a/drivers/media/radio/radio-si470x.c b/drivers/media/radio/radio-si470x.c index 5dd33197bbd..707988edc1b 100644 --- a/drivers/media/radio/radio-si470x.c +++ b/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 @@ -425,7 +425,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]; @@ -441,12 +442,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. @@ -582,7 +577,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: " @@ -591,7 +586,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; } @@ -879,8 +875,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; @@ -1007,20 +1003,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); @@ -1032,9 +1028,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; } @@ -1174,8 +1170,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: @@ -1201,8 +1197,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: @@ -1266,8 +1262,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; @@ -1324,8 +1320,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; @@ -1351,8 +1347,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); @@ -1370,8 +1366,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; @@ -1436,8 +1432,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); @@ -1542,16 +1540,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-70-g09d2 From 6cc72658897ee970e4ecfefaae58f043a98a8e65 Mon Sep 17 00:00:00 2001 From: Tobias Lorenz Date: Sat, 31 May 2008 15:06:50 -0300 Subject: V4L/DVB (7994): si470x: let si470x_get_freq return errno 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 --- drivers/media/radio/radio-si470x.c | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) (limited to 'drivers/media/radio/radio-si470x.c') diff --git a/drivers/media/radio/radio-si470x.c b/drivers/media/radio/radio-si470x.c index 707988edc1b..7df1163d709 100644 --- a/drivers/media/radio/radio-si470x.c +++ b/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 */ @@ -631,9 +647,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; @@ -659,14 +675,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; } @@ -1351,9 +1365,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-70-g09d2 From a7c850a07eddf0e0f515d57b2ce696a9779c6ed1 Mon Sep 17 00:00:00 2001 From: Tobias Lorenz Date: Sat, 31 May 2008 15:07:52 -0300 Subject: V4L/DVB (7995): si470x: a lot of small code cleanups MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- drivers/media/radio/radio-si470x.c | 286 +++++++++++++++++++++++++------------ 1 file changed, 196 insertions(+), 90 deletions(-) (limited to 'drivers/media/radio/radio-si470x.c') diff --git a/drivers/media/radio/radio-si470x.c b/drivers/media/radio/radio-si470x.c index 7df1163d709..954ba997895 100644 --- a/drivers/media/radio/radio-si470x.c +++ b/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 @@ -487,11 +487,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; } @@ -510,11 +510,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; } @@ -623,24 +623,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; } @@ -731,27 +737,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; } @@ -766,13 +775,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; } @@ -889,6 +901,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) @@ -923,11 +936,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 */ @@ -956,6 +973,7 @@ static ssize_t si470x_fops_read(struct file *file, char __user *buf, } mutex_unlock(&radio->lock); +done: return retval; } @@ -967,6 +985,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) { @@ -978,9 +997,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; } @@ -997,17 +1016,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; } @@ -1019,8 +1039,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--; @@ -1044,6 +1067,8 @@ static int si470x_fops_release(struct inode *inode, struct file *file) unlock: mutex_unlock(&radio->disconnect_lock); + +done: return retval; } @@ -1131,7 +1156,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; @@ -1143,12 +1168,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; } @@ -1161,17 +1192,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; } @@ -1183,9 +1219,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: @@ -1196,9 +1236,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; } @@ -1209,10 +1255,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: @@ -1230,10 +1279,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; } @@ -1244,13 +1294,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; } @@ -1260,10 +1319,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; } @@ -1274,20 +1342,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: @@ -1321,7 +1392,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; } @@ -1332,12 +1407,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 */ @@ -1345,10 +1425,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; } @@ -1360,12 +1441,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; } @@ -1376,19 +1470,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; } @@ -1428,33 +1528,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]); @@ -1480,8 +1583,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; @@ -1493,6 +1598,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-70-g09d2 From ace7d4bbe6170ef9b72bfa82c3e5769d250fffae Mon Sep 17 00:00:00 2001 From: Tobias Lorenz Date: Sat, 31 May 2008 15:09:07 -0300 Subject: V4L/DVB (7996): si470x: afc indication 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 --- drivers/media/radio/radio-si470x.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/media/radio/radio-si470x.c') diff --git a/drivers/media/radio/radio-si470x.c b/drivers/media/radio/radio-si470x.c index 954ba997895..db9d64a228b 100644 --- a/drivers/media/radio/radio-si470x.c +++ b/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: @@ -1390,7 +1391,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-70-g09d2 From bbc8995f9fa3f9cdd77aeb7c9bbf5bf526feca3c Mon Sep 17 00:00:00 2001 From: Tobias Lorenz Date: Sat, 31 May 2008 15:11:32 -0300 Subject: V4L/DVB (7997): si470x: hardware frequency seek support 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 --- drivers/media/radio/radio-si470x.c | 97 +++++++++++++++++++++++++++++++++++++- 1 file changed, 95 insertions(+), 2 deletions(-) (limited to 'drivers/media/radio/radio-si470x.c') diff --git a/drivers/media/radio/radio-si470x.c b/drivers/media/radio/radio-si470x.c index db9d64a228b..dc93a882b38 100644 --- a/drivers/media/radio/radio-si470x.c +++ b/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) @@ -192,6 +192,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); @@ -726,6 +731,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 */ @@ -1148,7 +1209,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; } @@ -1494,6 +1556,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 */ @@ -1514,6 +1606,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-70-g09d2