From ae85ab3edf003a61636030b72791577b41818bb0 Mon Sep 17 00:00:00 2001 From: Tobias Lorenz Date: Thu, 12 Feb 2009 18:55:45 +0100 Subject: Documentation and code cleanups From: Tobias Lorenz - "DealExtreme" sells the "PCear" radio and that comes from "Sanei Electric". - MPlayer is also usable as radio application. - Consistent usage of tabulators and blanks in the code. Priority: normal Signed-off-by: Tobias Lorenz --- linux/drivers/media/radio/radio-si470x.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 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 f77a65d2a..718481313 100644 --- a/linux/drivers/media/radio/radio-si470x.c +++ b/linux/drivers/media/radio/radio-si470x.c @@ -5,8 +5,9 @@ * - Silicon Labs USB FM Radio Reference Design * - ADS/Tech FM Radio Receiver (formerly Instant FM Music) (RDX-155-EF) * - KWorld USB FM Radio SnapMusic Mobile 700 (FM700) + * - Sanei Electric, Inc. FM USB Radio (sold as DealExtreme.com PCear) * - * Copyright (c) 2008 Tobias Lorenz + * Copyright (c) 2009 Tobias Lorenz * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -29,7 +30,7 @@ * 2008-01-12 Tobias Lorenz * Version 1.0.0 * - First working version - * 2008-01-13 Tobias Lorenz + * 2008-01-13 Tobias Lorenz * Version 1.0.1 * - Improved error handling, every function now returns errno * - Improved multi user access (start/mute/stop) @@ -141,7 +142,7 @@ static struct usb_device_id si470x_usb_driver_id_table[] = { { USB_DEVICE_AND_INTERFACE_INFO(0x06e1, 0xa155, USB_CLASS_HID, 0, 0) }, /* KWorld USB FM Radio SnapMusic Mobile 700 (FM700) */ { USB_DEVICE_AND_INTERFACE_INFO(0x1b80, 0xd700, USB_CLASS_HID, 0, 0) }, - /* DealExtreme USB Radio */ + /* Sanei Electric, Inc. FM USB Radio (sold as DealExtreme.com PCear) */ { USB_DEVICE_AND_INTERFACE_INFO(0x10c5, 0x819a, USB_CLASS_HID, 0, 0) }, /* Terminating entry */ { } @@ -341,7 +342,7 @@ MODULE_PARM_DESC(rds_poll_time, "RDS poll time (ms): *40*"); /* Report 19: stream */ #define STREAM_REPORT_SIZE 3 -#define STREAM_REPORT 19 +#define STREAM_REPORT 19 /* Report 20: scratch */ #define SCRATCH_PAGE_SIZE 63 @@ -410,7 +411,7 @@ MODULE_PARM_DESC(rds_poll_time, "RDS poll time (ms): *40*"); /* bootloader commands */ #define GET_SW_VERSION_COMMAND 0x00 -#define SET_PAGE_COMMAND 0x01 +#define SET_PAGE_COMMAND 0x01 #define ERASE_PAGE_COMMAND 0x02 #define WRITE_PAGE_COMMAND 0x03 #define CRC_ON_PAGE_COMMAND 0x04 -- cgit v1.2.3 From d0578265f7ee9cfe91ebb445d025f67987b7aaed Mon Sep 17 00:00:00 2001 From: Tobias Lorenz Date: Thu, 12 Feb 2009 18:55:56 +0100 Subject: Code rearrangements in preparation for other report types From: Tobias Lorenz LED_REPORT and all flash REPORTs are on it's way. This code rearrangement cleans up the code for proper integration later on. Priority: normal Signed-off-by: Tobias Lorenz --- linux/drivers/media/radio/radio-si470x.c | 122 +++++++++++++++++-------------- 1 file changed, 67 insertions(+), 55 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 718481313..49317f91f 100644 --- a/linux/drivers/media/radio/radio-si470x.c +++ b/linux/drivers/media/radio/radio-si470x.c @@ -481,7 +481,7 @@ struct si470x_device { /************************************************************************** - * General Driver Functions + * General Driver Functions - REGISTER_REPORTs **************************************************************************/ /* @@ -566,60 +566,6 @@ static int si470x_set_register(struct si470x_device *radio, int regnr) } -/* - * si470x_get_all_registers - read entire registers - */ -static int si470x_get_all_registers(struct si470x_device *radio) -{ - unsigned char buf[ENTIRE_REPORT_SIZE]; - int retval; - unsigned char regnr; - - buf[0] = ENTIRE_REPORT; - - retval = si470x_get_report(radio, (void *) &buf, sizeof(buf)); - - if (retval >= 0) - for (regnr = 0; regnr < RADIO_REGISTER_NUM; regnr++) - radio->registers[regnr] = get_unaligned_be16( - &buf[regnr * RADIO_REGISTER_SIZE + 1]); - - return (retval < 0) ? -EINVAL : 0; -} - - -/* - * si470x_get_rds_registers - read rds registers - */ -static int si470x_get_rds_registers(struct si470x_device *radio) -{ - unsigned char buf[RDS_REPORT_SIZE]; - int retval; - int size; - unsigned char regnr; - - buf[0] = RDS_REPORT; - - retval = usb_interrupt_msg(radio->usbdev, - 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: " - "return size differs: %d != %zu\n", size, sizeof(buf)); - if (retval < 0) - printk(KERN_WARNING DRIVER_NAME ": si470x_get_rds_registers: " - "usb_interrupt_msg returned %d\n", retval); - - if (retval >= 0) - for (regnr = 0; regnr < RDS_REGISTER_NUM; regnr++) - radio->registers[STATUSRSSI + regnr] = - get_unaligned_be16( - &buf[regnr * RADIO_REGISTER_SIZE + 1]); - - return (retval < 0) ? -EINVAL : 0; -} - - /* * si470x_set_chan - set the channel */ @@ -887,6 +833,70 @@ static int si470x_rds_on(struct si470x_device *radio) +/************************************************************************** + * General Driver Functions - ENTIRE_REPORT + **************************************************************************/ + +/* + * si470x_get_all_registers - read entire registers + */ +static int si470x_get_all_registers(struct si470x_device *radio) +{ + unsigned char buf[ENTIRE_REPORT_SIZE]; + int retval; + unsigned char regnr; + + buf[0] = ENTIRE_REPORT; + + retval = si470x_get_report(radio, (void *) &buf, sizeof(buf)); + + if (retval >= 0) + for (regnr = 0; regnr < RADIO_REGISTER_NUM; regnr++) + radio->registers[regnr] = get_unaligned_be16( + &buf[regnr * RADIO_REGISTER_SIZE + 1]); + + return (retval < 0) ? -EINVAL : 0; +} + + + +/************************************************************************** + * General Driver Functions - RDS_REPORT + **************************************************************************/ + +/* + * si470x_get_rds_registers - read rds registers + */ +static int si470x_get_rds_registers(struct si470x_device *radio) +{ + unsigned char buf[RDS_REPORT_SIZE]; + int retval; + int size; + unsigned char regnr; + + buf[0] = RDS_REPORT; + + retval = usb_interrupt_msg(radio->usbdev, + 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: " + "return size differs: %d != %zu\n", size, sizeof(buf)); + if (retval < 0) + printk(KERN_WARNING DRIVER_NAME ": si470x_get_rds_registers: " + "usb_interrupt_msg returned %d\n", retval); + + if (retval >= 0) + for (regnr = 0; regnr < RDS_REGISTER_NUM; regnr++) + radio->registers[STATUSRSSI + regnr] = + get_unaligned_be16( + &buf[regnr * RADIO_REGISTER_SIZE + 1]); + + return (retval < 0) ? -EINVAL : 0; +} + + + /************************************************************************** * RDS Driver Functions **************************************************************************/ @@ -1101,6 +1111,7 @@ static int si470x_fops_open(struct file *file) } if (radio->users == 1) { + /* start radio */ retval = si470x_start(radio); if (retval < 0) usb_autopm_put_interface(radio->intf); @@ -1142,6 +1153,7 @@ static int si470x_fops_release(struct file *file) /* cancel read processes */ wake_up_interruptible(&radio->read_queue); + /* stop radio */ retval = si470x_stop(radio); usb_autopm_put_interface(radio->intf); } -- cgit v1.2.3 From 30db0e876957f7d463bc717f76c73b9e34aa9e92 Mon Sep 17 00:00:00 2001 From: Tobias Lorenz Date: Thu, 12 Feb 2009 18:56:10 +0100 Subject: Correction of Stereo detection/setting and signal strength indication From: Tobias Lorenz Thanks to Bob Ross - correction of stereo detection/setting - correction of signal strength indicator scaling Priority: normal Signed-off-by: Tobias Lorenz --- linux/drivers/media/radio/radio-si470x.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 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 49317f91f..d211d5176 100644 --- a/linux/drivers/media/radio/radio-si470x.c +++ b/linux/drivers/media/radio/radio-si470x.c @@ -99,6 +99,9 @@ * - blacklisted KWorld radio in hid-core.c and hid-ids.h * 2008-12-03 Mark Lord * - add support for DealExtreme USB Radio + * 2009-01-31 Bob Ross + * - correction of stereo detection/setting + * - correction of signal strength indicator scaling * * ToDo: * - add firmware download/update support @@ -1405,20 +1408,22 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv, }; /* stereo indicator == stereo (instead of mono) */ - if ((radio->registers[STATUSRSSI] & STATUSRSSI_ST) == 1) - tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; - else + if ((radio->registers[STATUSRSSI] & STATUSRSSI_ST) == 0) tuner->rxsubchans = V4L2_TUNER_SUB_MONO; + else + tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; /* mono/stereo selector */ - if ((radio->registers[POWERCFG] & POWERCFG_MONO) == 1) - tuner->audmode = V4L2_TUNER_MODE_MONO; - else + if ((radio->registers[POWERCFG] & POWERCFG_MONO) == 0) tuner->audmode = V4L2_TUNER_MODE_STEREO; + else + tuner->audmode = V4L2_TUNER_MODE_MONO; /* min is worst, max is best; signal:0..0xffff; rssi: 0..0xff */ - tuner->signal = (radio->registers[STATUSRSSI] & STATUSRSSI_RSSI) - * 0x0101; + /* measured in units of dbµV in 1 db increments (max at ~75 dbµV) */ + tuner->signal = (radio->registers[STATUSRSSI] & STATUSRSSI_RSSI); + /* the ideal factor is 0xffff/75 = 873,8 */ + tuner->signal = (tuner->signal * 873) + (8 * tuner->signal / 10); /* automatic frequency control: -1: freq to low, 1 freq to high */ /* AFCRL does only indicate that freq. differs, not if too low/high */ -- cgit v1.2.3 From c29af88de22ef512cc331c823d9d334f1c4515fc Mon Sep 17 00:00:00 2001 From: Tobias Lorenz Date: Thu, 12 Feb 2009 18:56:19 +0100 Subject: LED status output From: Tobias Lorenz This patch closes one of my todos that was since long on my list. Some people reported clicks and glitches in the audio stream, correlated to the LED color changing cycle. Thanks to Rick Bronson . Priority: normal Signed-off-by: Tobias Lorenz --- linux/drivers/media/radio/radio-si470x.c | 34 +++++++++++++++++++++++++++++++- 1 file changed, 33 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 d211d5176..71c3228d8 100644 --- a/linux/drivers/media/radio/radio-si470x.c +++ b/linux/drivers/media/radio/radio-si470x.c @@ -102,11 +102,13 @@ * 2009-01-31 Bob Ross * - correction of stereo detection/setting * - correction of signal strength indicator scaling + * 2009-01-31 Rick Bronson + * Tobias Lorenz + * - add LED status output * * ToDo: * - 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) */ @@ -900,6 +902,30 @@ static int si470x_get_rds_registers(struct si470x_device *radio) +/************************************************************************** + * General Driver Functions - LED_REPORT + **************************************************************************/ + +/* + * si470x_set_led_state - sets the led state + */ +static int si470x_set_led_state(struct si470x_device *radio, + unsigned char led_state) +{ + unsigned char buf[LED_REPORT_SIZE]; + int retval; + + buf[0] = LED_REPORT; + buf[1] = LED_COMMAND; + buf[2] = led_state; + + retval = si470x_set_report(radio, (void *) &buf, sizeof(buf)); + + return (retval < 0) ? -EINVAL : 0; +} + + + /************************************************************************** * RDS Driver Functions **************************************************************************/ @@ -1657,6 +1683,9 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, /* set initial frequency */ si470x_set_freq(radio, 87.5 * FREQ_MUL); /* available in all regions */ + /* set led to connect state */ + si470x_set_led_state(radio, BLINK_GREEN_LED); + /* rds buffer allocation */ radio->buf_size = rds_buf * 3; radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL); @@ -1740,6 +1769,9 @@ static void si470x_usb_driver_disconnect(struct usb_interface *intf) cancel_delayed_work_sync(&radio->work); usb_set_intfdata(intf, NULL); if (radio->users == 0) { + /* set led to disconnect state */ + si470x_set_led_state(radio, BLINK_ORANGE_LED); + video_unregister_device(radio->videodev); kfree(radio->buffer); kfree(radio); -- cgit v1.2.3 From a7fd6975aa1ecb967ed5c0ed1186a4cb17b59a1a Mon Sep 17 00:00:00 2001 From: Tobias Lorenz Date: Thu, 12 Feb 2009 18:56:30 +0100 Subject: Output HW/SW version from scratchpad From: Tobias Lorenz This patch adds functions to access the scratchpad. For it is this good for? In the first two bytes, the developers stored the HW/PCB version and the software release of the firmware. This is now written to syslog, so debugging get's easier. Also knowing the versions is the key for flash upgrades later on. There are also some cleanups of the flash report sizes. Altogether this should justify the new version number 1.0.9. Thanks to Rick Bronson Priority: normal Signed-off-by: Tobias Lorenz --- linux/drivers/media/radio/radio-si470x.c | 61 ++++++++++++++++++++++++++------ 1 file changed, 51 insertions(+), 10 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 71c3228d8..b0a1e5832 100644 --- a/linux/drivers/media/radio/radio-si470x.c +++ b/linux/drivers/media/radio/radio-si470x.c @@ -105,6 +105,7 @@ * 2009-01-31 Rick Bronson * Tobias Lorenz * - add LED status output + * - get HW/SW version from scratchpad * * ToDo: * - add firmware download/update support @@ -115,10 +116,10 @@ /* driver definitions */ #define DRIVER_AUTHOR "Tobias Lorenz " #define DRIVER_NAME "radio-si470x" -#define DRIVER_KERNEL_VERSION KERNEL_VERSION(1, 0, 8) +#define DRIVER_KERNEL_VERSION KERNEL_VERSION(1, 0, 9) #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.8" +#define DRIVER_VERSION "1.0.9" /* kernel includes */ @@ -355,9 +356,13 @@ MODULE_PARM_DESC(rds_poll_time, "RDS poll time (ms): *40*"); #define SCRATCH_REPORT 20 /* Reports 19-22: flash upgrade of the C8051F321 */ +#define WRITE_REPORT_SIZE 4 #define WRITE_REPORT 19 +#define FLASH_REPORT_SIZE 64 #define FLASH_REPORT 20 +#define CRC_REPORT_SIZE 3 #define CRC_REPORT 21 +#define RESPONSE_REPORT_SIZE 2 #define RESPONSE_REPORT 22 /* Report 23: currently unused, but can accept 60 byte reports on the HID */ @@ -430,12 +435,6 @@ MODULE_PARM_DESC(rds_poll_time, "RDS poll time (ms): *40*"); #define COMMAND_FAILED 0x02 #define COMMAND_PENDING 0x03 -/* buffer sizes */ -#define COMMAND_BUFFER_SIZE 4 -#define RESPONSE_BUFFER_SIZE 2 -#define FLASH_BUFFER_SIZE 64 -#define CRC_BUFFER_SIZE 3 - /************************************************************************** @@ -471,6 +470,10 @@ struct si470x_device { unsigned int buf_size; unsigned int rd_index; unsigned int wr_index; + + /* scratch page */ + unsigned char software_version; + unsigned char hardware_version; }; @@ -926,6 +929,35 @@ static int si470x_set_led_state(struct si470x_device *radio, +/************************************************************************** + * General Driver Functions - SCRATCH_REPORT + **************************************************************************/ + +/* + * si470x_get_scratch_versions - gets the scratch page and version infos + */ +static int si470x_get_scratch_page_versions(struct si470x_device *radio) +{ + unsigned char buf[SCRATCH_REPORT_SIZE]; + int retval; + + buf[0] = SCRATCH_REPORT; + + retval = si470x_get_report(radio, (void *) &buf, sizeof(buf)); + + if (retval < 0) + printk(KERN_WARNING DRIVER_NAME ": si470x_get_scratch: " + "si470x_get_report returned %d\n", retval); + else { + radio->software_version = buf[1]; + radio->hardware_version = buf[2]; + } + + return (retval < 0) ? -EINVAL : 0; +} + + + /************************************************************************** * RDS Driver Functions **************************************************************************/ @@ -1656,7 +1688,7 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, sizeof(si470x_viddev_template)); video_set_drvdata(radio->videodev, radio); - /* show some infos about the specific device */ + /* show some infos about the specific si470x device */ if (si470x_get_all_registers(radio) < 0) { retval = -EIO; goto err_all; @@ -1664,7 +1696,16 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, printk(KERN_INFO DRIVER_NAME ": DeviceID=0x%4.4hx ChipID=0x%4.4hx\n", radio->registers[DEVICEID], radio->registers[CHIPID]); - /* check if firmware is current */ + /* get software and hardware versions */ + if (si470x_get_scratch_page_versions(radio) < 0) { + retval = -EIO; + goto err_all; + } + printk(KERN_INFO DRIVER_NAME + ": software version %d, hardware version %d\n", + radio->software_version, radio->hardware_version); + + /* check if device and firmware is current */ if ((radio->registers[CHIPID] & CHIPID_FIRMWARE) < RADIO_SW_VERSION_CURRENT) { printk(KERN_WARNING DRIVER_NAME -- cgit v1.2.3