Path: blob/master/drivers/media/radio/si470x/radio-si470x-usb.c
15112 views
/*1* drivers/media/radio/si470x/radio-si470x-usb.c2*3* USB driver for radios with Silicon Labs Si470x FM Radio Receivers4*5* Copyright (c) 2009 Tobias Lorenz <[email protected]>6*7* This program is free software; you can redistribute it and/or modify8* it under the terms of the GNU General Public License as published by9* the Free Software Foundation; either version 2 of the License, or10* (at your option) any later version.11*12* This program is distributed in the hope that it will be useful,13* but WITHOUT ANY WARRANTY; without even the implied warranty of14* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the15* GNU General Public License for more details.16*17* You should have received a copy of the GNU General Public License18* along with this program; if not, write to the Free Software19* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA20*/212223/*24* ToDo:25* - add firmware download/update support26*/272829/* driver definitions */30#define DRIVER_AUTHOR "Tobias Lorenz <[email protected]>"31#define DRIVER_KERNEL_VERSION KERNEL_VERSION(1, 0, 10)32#define DRIVER_CARD "Silicon Labs Si470x FM Radio Receiver"33#define DRIVER_DESC "USB radio driver for Si470x FM Radio Receivers"34#define DRIVER_VERSION "1.0.10"3536/* kernel includes */37#include <linux/usb.h>38#include <linux/hid.h>39#include <linux/slab.h>4041#include "radio-si470x.h"424344/* USB Device ID List */45static struct usb_device_id si470x_usb_driver_id_table[] = {46/* Silicon Labs USB FM Radio Reference Design */47{ USB_DEVICE_AND_INTERFACE_INFO(0x10c4, 0x818a, USB_CLASS_HID, 0, 0) },48/* ADS/Tech FM Radio Receiver (formerly Instant FM Music) */49{ USB_DEVICE_AND_INTERFACE_INFO(0x06e1, 0xa155, USB_CLASS_HID, 0, 0) },50/* KWorld USB FM Radio SnapMusic Mobile 700 (FM700) */51{ USB_DEVICE_AND_INTERFACE_INFO(0x1b80, 0xd700, USB_CLASS_HID, 0, 0) },52/* Sanei Electric, Inc. FM USB Radio (sold as DealExtreme.com PCear) */53{ USB_DEVICE_AND_INTERFACE_INFO(0x10c5, 0x819a, USB_CLASS_HID, 0, 0) },54/* Terminating entry */55{ }56};57MODULE_DEVICE_TABLE(usb, si470x_usb_driver_id_table);58596061/**************************************************************************62* Module Parameters63**************************************************************************/6465/* Radio Nr */66static int radio_nr = -1;67module_param(radio_nr, int, 0444);68MODULE_PARM_DESC(radio_nr, "Radio Nr");6970/* USB timeout */71static unsigned int usb_timeout = 500;72module_param(usb_timeout, uint, 0644);73MODULE_PARM_DESC(usb_timeout, "USB timeout (ms): *500*");7475/* RDS buffer blocks */76static unsigned int rds_buf = 100;77module_param(rds_buf, uint, 0444);78MODULE_PARM_DESC(rds_buf, "RDS buffer entries: *100*");7980/* RDS maximum block errors */81static unsigned short max_rds_errors = 1;82/* 0 means 0 errors requiring correction */83/* 1 means 1-2 errors requiring correction (used by original USBRadio.exe) */84/* 2 means 3-5 errors requiring correction */85/* 3 means 6+ errors or errors in checkword, correction not possible */86module_param(max_rds_errors, ushort, 0644);87MODULE_PARM_DESC(max_rds_errors, "RDS maximum block errors: *1*");88899091/**************************************************************************92* USB HID Reports93**************************************************************************/9495/* Reports 1-16 give direct read/write access to the 16 Si470x registers */96/* with the (REPORT_ID - 1) corresponding to the register address across USB */97/* endpoint 0 using GET_REPORT and SET_REPORT */98#define REGISTER_REPORT_SIZE (RADIO_REGISTER_SIZE + 1)99#define REGISTER_REPORT(reg) ((reg) + 1)100101/* Report 17 gives direct read/write access to the entire Si470x register */102/* map across endpoint 0 using GET_REPORT and SET_REPORT */103#define ENTIRE_REPORT_SIZE (RADIO_REGISTER_NUM * RADIO_REGISTER_SIZE + 1)104#define ENTIRE_REPORT 17105106/* Report 18 is used to send the lowest 6 Si470x registers up the HID */107/* interrupt endpoint 1 to Windows every 20 milliseconds for status */108#define RDS_REPORT_SIZE (RDS_REGISTER_NUM * RADIO_REGISTER_SIZE + 1)109#define RDS_REPORT 18110111/* Report 19: LED state */112#define LED_REPORT_SIZE 3113#define LED_REPORT 19114115/* Report 19: stream */116#define STREAM_REPORT_SIZE 3117#define STREAM_REPORT 19118119/* Report 20: scratch */120#define SCRATCH_PAGE_SIZE 63121#define SCRATCH_REPORT_SIZE (SCRATCH_PAGE_SIZE + 1)122#define SCRATCH_REPORT 20123124/* Reports 19-22: flash upgrade of the C8051F321 */125#define WRITE_REPORT_SIZE 4126#define WRITE_REPORT 19127#define FLASH_REPORT_SIZE 64128#define FLASH_REPORT 20129#define CRC_REPORT_SIZE 3130#define CRC_REPORT 21131#define RESPONSE_REPORT_SIZE 2132#define RESPONSE_REPORT 22133134/* Report 23: currently unused, but can accept 60 byte reports on the HID */135/* interrupt out endpoint 2 every 1 millisecond */136#define UNUSED_REPORT 23137138139140/**************************************************************************141* Software/Hardware Versions from Scratch Page142**************************************************************************/143#define RADIO_SW_VERSION_NOT_BOOTLOADABLE 6144#define RADIO_SW_VERSION 7145#define RADIO_HW_VERSION 1146147148149/**************************************************************************150* LED State Definitions151**************************************************************************/152#define LED_COMMAND 0x35153154#define NO_CHANGE_LED 0x00155#define ALL_COLOR_LED 0x01 /* streaming state */156#define BLINK_GREEN_LED 0x02 /* connect state */157#define BLINK_RED_LED 0x04158#define BLINK_ORANGE_LED 0x10 /* disconnect state */159#define SOLID_GREEN_LED 0x20 /* tuning/seeking state */160#define SOLID_RED_LED 0x40 /* bootload state */161#define SOLID_ORANGE_LED 0x80162163164165/**************************************************************************166* Stream State Definitions167**************************************************************************/168#define STREAM_COMMAND 0x36169#define STREAM_VIDPID 0x00170#define STREAM_AUDIO 0xff171172173174/**************************************************************************175* Bootloader / Flash Commands176**************************************************************************/177178/* unique id sent to bootloader and required to put into a bootload state */179#define UNIQUE_BL_ID 0x34180181/* mask for the flash data */182#define FLASH_DATA_MASK 0x55183184/* bootloader commands */185#define GET_SW_VERSION_COMMAND 0x00186#define SET_PAGE_COMMAND 0x01187#define ERASE_PAGE_COMMAND 0x02188#define WRITE_PAGE_COMMAND 0x03189#define CRC_ON_PAGE_COMMAND 0x04190#define READ_FLASH_BYTE_COMMAND 0x05191#define RESET_DEVICE_COMMAND 0x06192#define GET_HW_VERSION_COMMAND 0x07193#define BLANK 0xff194195/* bootloader command responses */196#define COMMAND_OK 0x01197#define COMMAND_FAILED 0x02198#define COMMAND_PENDING 0x03199200201202/**************************************************************************203* General Driver Functions - REGISTER_REPORTs204**************************************************************************/205206/*207* si470x_get_report - receive a HID report208*/209static int si470x_get_report(struct si470x_device *radio, void *buf, int size)210{211unsigned char *report = (unsigned char *) buf;212int retval;213214retval = usb_control_msg(radio->usbdev,215usb_rcvctrlpipe(radio->usbdev, 0),216HID_REQ_GET_REPORT,217USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,218report[0], 2,219buf, size, usb_timeout);220221if (retval < 0)222dev_warn(&radio->intf->dev,223"si470x_get_report: usb_control_msg returned %d\n",224retval);225return retval;226}227228229/*230* si470x_set_report - send a HID report231*/232static int si470x_set_report(struct si470x_device *radio, void *buf, int size)233{234unsigned char *report = (unsigned char *) buf;235int retval;236237retval = usb_control_msg(radio->usbdev,238usb_sndctrlpipe(radio->usbdev, 0),239HID_REQ_SET_REPORT,240USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,241report[0], 2,242buf, size, usb_timeout);243244if (retval < 0)245dev_warn(&radio->intf->dev,246"si470x_set_report: usb_control_msg returned %d\n",247retval);248return retval;249}250251252/*253* si470x_get_register - read register254*/255int si470x_get_register(struct si470x_device *radio, int regnr)256{257unsigned char buf[REGISTER_REPORT_SIZE];258int retval;259260buf[0] = REGISTER_REPORT(regnr);261262retval = si470x_get_report(radio, (void *) &buf, sizeof(buf));263264if (retval >= 0)265radio->registers[regnr] = get_unaligned_be16(&buf[1]);266267return (retval < 0) ? -EINVAL : 0;268}269270271/*272* si470x_set_register - write register273*/274int si470x_set_register(struct si470x_device *radio, int regnr)275{276unsigned char buf[REGISTER_REPORT_SIZE];277int retval;278279buf[0] = REGISTER_REPORT(regnr);280put_unaligned_be16(radio->registers[regnr], &buf[1]);281282retval = si470x_set_report(radio, (void *) &buf, sizeof(buf));283284return (retval < 0) ? -EINVAL : 0;285}286287288289/**************************************************************************290* General Driver Functions - ENTIRE_REPORT291**************************************************************************/292293/*294* si470x_get_all_registers - read entire registers295*/296static int si470x_get_all_registers(struct si470x_device *radio)297{298unsigned char buf[ENTIRE_REPORT_SIZE];299int retval;300unsigned char regnr;301302buf[0] = ENTIRE_REPORT;303304retval = si470x_get_report(radio, (void *) &buf, sizeof(buf));305306if (retval >= 0)307for (regnr = 0; regnr < RADIO_REGISTER_NUM; regnr++)308radio->registers[regnr] = get_unaligned_be16(309&buf[regnr * RADIO_REGISTER_SIZE + 1]);310311return (retval < 0) ? -EINVAL : 0;312}313314315316/**************************************************************************317* General Driver Functions - LED_REPORT318**************************************************************************/319320/*321* si470x_set_led_state - sets the led state322*/323static int si470x_set_led_state(struct si470x_device *radio,324unsigned char led_state)325{326unsigned char buf[LED_REPORT_SIZE];327int retval;328329buf[0] = LED_REPORT;330buf[1] = LED_COMMAND;331buf[2] = led_state;332333retval = si470x_set_report(radio, (void *) &buf, sizeof(buf));334335return (retval < 0) ? -EINVAL : 0;336}337338339340/**************************************************************************341* General Driver Functions - SCRATCH_REPORT342**************************************************************************/343344/*345* si470x_get_scratch_versions - gets the scratch page and version infos346*/347static int si470x_get_scratch_page_versions(struct si470x_device *radio)348{349unsigned char buf[SCRATCH_REPORT_SIZE];350int retval;351352buf[0] = SCRATCH_REPORT;353354retval = si470x_get_report(radio, (void *) &buf, sizeof(buf));355356if (retval < 0)357dev_warn(&radio->intf->dev, "si470x_get_scratch: "358"si470x_get_report returned %d\n", retval);359else {360radio->software_version = buf[1];361radio->hardware_version = buf[2];362}363364return (retval < 0) ? -EINVAL : 0;365}366367368369/**************************************************************************370* General Driver Functions - DISCONNECT_CHECK371**************************************************************************/372373/*374* si470x_disconnect_check - check whether radio disconnects375*/376int si470x_disconnect_check(struct si470x_device *radio)377{378if (radio->disconnected)379return -EIO;380else381return 0;382}383384385386/**************************************************************************387* RDS Driver Functions388**************************************************************************/389390/*391* si470x_int_in_callback - rds callback and processing function392*393* TODO: do we need to use mutex locks in some sections?394*/395static void si470x_int_in_callback(struct urb *urb)396{397struct si470x_device *radio = urb->context;398unsigned char buf[RDS_REPORT_SIZE];399int retval;400unsigned char regnr;401unsigned char blocknum;402unsigned short bler; /* rds block errors */403unsigned short rds;404unsigned char tmpbuf[3];405406if (urb->status) {407if (urb->status == -ENOENT ||408urb->status == -ECONNRESET ||409urb->status == -ESHUTDOWN) {410return;411} else {412dev_warn(&radio->intf->dev,413"non-zero urb status (%d)\n", urb->status);414goto resubmit; /* Maybe we can recover. */415}416}417418/* safety checks */419if (radio->disconnected)420return;421if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0)422goto resubmit;423424if (urb->actual_length > 0) {425/* Update RDS registers with URB data */426buf[0] = RDS_REPORT;427for (regnr = 0; regnr < RDS_REGISTER_NUM; regnr++)428radio->registers[STATUSRSSI + regnr] =429get_unaligned_be16(&radio->int_in_buffer[430regnr * RADIO_REGISTER_SIZE + 1]);431/* get rds blocks */432if ((radio->registers[STATUSRSSI] & STATUSRSSI_RDSR) == 0) {433/* No RDS group ready, better luck next time */434goto resubmit;435}436if ((radio->registers[STATUSRSSI] & STATUSRSSI_RDSS) == 0) {437/* RDS decoder not synchronized */438goto resubmit;439}440for (blocknum = 0; blocknum < 4; blocknum++) {441switch (blocknum) {442default:443bler = (radio->registers[STATUSRSSI] &444STATUSRSSI_BLERA) >> 9;445rds = radio->registers[RDSA];446break;447case 1:448bler = (radio->registers[READCHAN] &449READCHAN_BLERB) >> 14;450rds = radio->registers[RDSB];451break;452case 2:453bler = (radio->registers[READCHAN] &454READCHAN_BLERC) >> 12;455rds = radio->registers[RDSC];456break;457case 3:458bler = (radio->registers[READCHAN] &459READCHAN_BLERD) >> 10;460rds = radio->registers[RDSD];461break;462};463464/* Fill the V4L2 RDS buffer */465put_unaligned_le16(rds, &tmpbuf);466tmpbuf[2] = blocknum; /* offset name */467tmpbuf[2] |= blocknum << 3; /* received offset */468if (bler > max_rds_errors)469tmpbuf[2] |= 0x80; /* uncorrectable errors */470else if (bler > 0)471tmpbuf[2] |= 0x40; /* corrected error(s) */472473/* copy RDS block to internal buffer */474memcpy(&radio->buffer[radio->wr_index], &tmpbuf, 3);475radio->wr_index += 3;476477/* wrap write pointer */478if (radio->wr_index >= radio->buf_size)479radio->wr_index = 0;480481/* check for overflow */482if (radio->wr_index == radio->rd_index) {483/* increment and wrap read pointer */484radio->rd_index += 3;485if (radio->rd_index >= radio->buf_size)486radio->rd_index = 0;487}488}489if (radio->wr_index != radio->rd_index)490wake_up_interruptible(&radio->read_queue);491}492493resubmit:494/* Resubmit if we're still running. */495if (radio->int_in_running && radio->usbdev) {496retval = usb_submit_urb(radio->int_in_urb, GFP_ATOMIC);497if (retval) {498dev_warn(&radio->intf->dev,499"resubmitting urb failed (%d)", retval);500radio->int_in_running = 0;501}502}503}504505506507/**************************************************************************508* File Operations Interface509**************************************************************************/510511/*512* si470x_fops_open - file open513*/514int si470x_fops_open(struct file *file)515{516struct si470x_device *radio = video_drvdata(file);517int retval;518519mutex_lock(&radio->lock);520radio->users++;521522retval = usb_autopm_get_interface(radio->intf);523if (retval < 0) {524radio->users--;525retval = -EIO;526goto done;527}528529if (radio->users == 1) {530/* start radio */531retval = si470x_start(radio);532if (retval < 0) {533usb_autopm_put_interface(radio->intf);534goto done;535}536537/* initialize interrupt urb */538usb_fill_int_urb(radio->int_in_urb, radio->usbdev,539usb_rcvintpipe(radio->usbdev,540radio->int_in_endpoint->bEndpointAddress),541radio->int_in_buffer,542le16_to_cpu(radio->int_in_endpoint->wMaxPacketSize),543si470x_int_in_callback,544radio,545radio->int_in_endpoint->bInterval);546547radio->int_in_running = 1;548mb();549550retval = usb_submit_urb(radio->int_in_urb, GFP_KERNEL);551if (retval) {552dev_info(&radio->intf->dev,553"submitting int urb failed (%d)\n", retval);554radio->int_in_running = 0;555usb_autopm_put_interface(radio->intf);556}557}558559done:560mutex_unlock(&radio->lock);561return retval;562}563564565/*566* si470x_fops_release - file release567*/568int si470x_fops_release(struct file *file)569{570struct si470x_device *radio = video_drvdata(file);571int retval = 0;572573/* safety check */574if (!radio) {575retval = -ENODEV;576goto done;577}578579mutex_lock(&radio->lock);580radio->users--;581if (radio->users == 0) {582/* shutdown interrupt handler */583if (radio->int_in_running) {584radio->int_in_running = 0;585if (radio->int_in_urb)586usb_kill_urb(radio->int_in_urb);587}588589if (radio->disconnected) {590video_unregister_device(radio->videodev);591kfree(radio->int_in_buffer);592kfree(radio->buffer);593mutex_unlock(&radio->lock);594kfree(radio);595goto done;596}597598/* cancel read processes */599wake_up_interruptible(&radio->read_queue);600601/* stop radio */602retval = si470x_stop(radio);603usb_autopm_put_interface(radio->intf);604}605mutex_unlock(&radio->lock);606done:607return retval;608}609610611612/**************************************************************************613* Video4Linux Interface614**************************************************************************/615616/*617* si470x_vidioc_querycap - query device capabilities618*/619int si470x_vidioc_querycap(struct file *file, void *priv,620struct v4l2_capability *capability)621{622struct si470x_device *radio = video_drvdata(file);623624strlcpy(capability->driver, DRIVER_NAME, sizeof(capability->driver));625strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card));626usb_make_path(radio->usbdev, capability->bus_info,627sizeof(capability->bus_info));628capability->version = DRIVER_KERNEL_VERSION;629capability->capabilities = V4L2_CAP_HW_FREQ_SEEK |630V4L2_CAP_TUNER | V4L2_CAP_RADIO | V4L2_CAP_RDS_CAPTURE;631632return 0;633}634635636637/**************************************************************************638* USB Interface639**************************************************************************/640641/*642* si470x_usb_driver_probe - probe for the device643*/644static int si470x_usb_driver_probe(struct usb_interface *intf,645const struct usb_device_id *id)646{647struct si470x_device *radio;648struct usb_host_interface *iface_desc;649struct usb_endpoint_descriptor *endpoint;650int i, int_end_size, retval = 0;651unsigned char version_warning = 0;652653/* private data allocation and initialization */654radio = kzalloc(sizeof(struct si470x_device), GFP_KERNEL);655if (!radio) {656retval = -ENOMEM;657goto err_initial;658}659radio->users = 0;660radio->disconnected = 0;661radio->usbdev = interface_to_usbdev(intf);662radio->intf = intf;663mutex_init(&radio->lock);664665iface_desc = intf->cur_altsetting;666667/* Set up interrupt endpoint information. */668for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {669endpoint = &iface_desc->endpoint[i].desc;670if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ==671USB_DIR_IN) && ((endpoint->bmAttributes &672USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT))673radio->int_in_endpoint = endpoint;674}675if (!radio->int_in_endpoint) {676dev_info(&intf->dev, "could not find interrupt in endpoint\n");677retval = -EIO;678goto err_radio;679}680681int_end_size = le16_to_cpu(radio->int_in_endpoint->wMaxPacketSize);682683radio->int_in_buffer = kmalloc(int_end_size, GFP_KERNEL);684if (!radio->int_in_buffer) {685dev_info(&intf->dev, "could not allocate int_in_buffer");686retval = -ENOMEM;687goto err_radio;688}689690radio->int_in_urb = usb_alloc_urb(0, GFP_KERNEL);691if (!radio->int_in_urb) {692dev_info(&intf->dev, "could not allocate int_in_urb");693retval = -ENOMEM;694goto err_intbuffer;695}696697/* video device allocation and initialization */698radio->videodev = video_device_alloc();699if (!radio->videodev) {700retval = -ENOMEM;701goto err_intbuffer;702}703memcpy(radio->videodev, &si470x_viddev_template,704sizeof(si470x_viddev_template));705video_set_drvdata(radio->videodev, radio);706707/* get device and chip versions */708if (si470x_get_all_registers(radio) < 0) {709retval = -EIO;710goto err_video;711}712dev_info(&intf->dev, "DeviceID=0x%4.4hx ChipID=0x%4.4hx\n",713radio->registers[DEVICEID], radio->registers[CHIPID]);714if ((radio->registers[CHIPID] & CHIPID_FIRMWARE) < RADIO_FW_VERSION) {715dev_warn(&intf->dev,716"This driver is known to work with "717"firmware version %hu,\n", RADIO_FW_VERSION);718dev_warn(&intf->dev,719"but the device has firmware version %hu.\n",720radio->registers[CHIPID] & CHIPID_FIRMWARE);721version_warning = 1;722}723724/* get software and hardware versions */725if (si470x_get_scratch_page_versions(radio) < 0) {726retval = -EIO;727goto err_video;728}729dev_info(&intf->dev, "software version %d, hardware version %d\n",730radio->software_version, radio->hardware_version);731if (radio->software_version < RADIO_SW_VERSION) {732dev_warn(&intf->dev,733"This driver is known to work with "734"software version %hu,\n", RADIO_SW_VERSION);735dev_warn(&intf->dev,736"but the device has software version %hu.\n",737radio->software_version);738version_warning = 1;739}740if (radio->hardware_version < RADIO_HW_VERSION) {741dev_warn(&intf->dev,742"This driver is known to work with "743"hardware version %hu,\n", RADIO_HW_VERSION);744dev_warn(&intf->dev,745"but the device has hardware version %hu.\n",746radio->hardware_version);747version_warning = 1;748}749750/* give out version warning */751if (version_warning == 1) {752dev_warn(&intf->dev,753"If you have some trouble using this driver,\n");754dev_warn(&intf->dev,755"please report to V4L ML at "756"[email protected]\n");757}758759/* set initial frequency */760si470x_set_freq(radio, 87.5 * FREQ_MUL); /* available in all regions */761762/* set led to connect state */763si470x_set_led_state(radio, BLINK_GREEN_LED);764765/* rds buffer allocation */766radio->buf_size = rds_buf * 3;767radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL);768if (!radio->buffer) {769retval = -EIO;770goto err_video;771}772773/* rds buffer configuration */774radio->wr_index = 0;775radio->rd_index = 0;776init_waitqueue_head(&radio->read_queue);777778/* register video device */779retval = video_register_device(radio->videodev, VFL_TYPE_RADIO,780radio_nr);781if (retval) {782dev_warn(&intf->dev, "Could not register video device\n");783goto err_all;784}785usb_set_intfdata(intf, radio);786787return 0;788err_all:789kfree(radio->buffer);790err_video:791video_device_release(radio->videodev);792err_intbuffer:793kfree(radio->int_in_buffer);794err_radio:795kfree(radio);796err_initial:797return retval;798}799800801/*802* si470x_usb_driver_suspend - suspend the device803*/804static int si470x_usb_driver_suspend(struct usb_interface *intf,805pm_message_t message)806{807dev_info(&intf->dev, "suspending now...\n");808809return 0;810}811812813/*814* si470x_usb_driver_resume - resume the device815*/816static int si470x_usb_driver_resume(struct usb_interface *intf)817{818dev_info(&intf->dev, "resuming now...\n");819820return 0;821}822823824/*825* si470x_usb_driver_disconnect - disconnect the device826*/827static void si470x_usb_driver_disconnect(struct usb_interface *intf)828{829struct si470x_device *radio = usb_get_intfdata(intf);830831mutex_lock(&radio->lock);832radio->disconnected = 1;833usb_set_intfdata(intf, NULL);834if (radio->users == 0) {835/* set led to disconnect state */836si470x_set_led_state(radio, BLINK_ORANGE_LED);837838/* Free data structures. */839usb_free_urb(radio->int_in_urb);840841kfree(radio->int_in_buffer);842video_unregister_device(radio->videodev);843kfree(radio->buffer);844mutex_unlock(&radio->lock);845kfree(radio);846} else {847mutex_unlock(&radio->lock);848}849}850851852/*853* si470x_usb_driver - usb driver interface854*/855static struct usb_driver si470x_usb_driver = {856.name = DRIVER_NAME,857.probe = si470x_usb_driver_probe,858.disconnect = si470x_usb_driver_disconnect,859.suspend = si470x_usb_driver_suspend,860.resume = si470x_usb_driver_resume,861.id_table = si470x_usb_driver_id_table,862.supports_autosuspend = 1,863};864865866867/**************************************************************************868* Module Interface869**************************************************************************/870871/*872* si470x_module_init - module init873*/874static int __init si470x_module_init(void)875{876printk(KERN_INFO DRIVER_DESC ", Version " DRIVER_VERSION "\n");877return usb_register(&si470x_usb_driver);878}879880881/*882* si470x_module_exit - module exit883*/884static void __exit si470x_module_exit(void)885{886usb_deregister(&si470x_usb_driver);887}888889890module_init(si470x_module_init);891module_exit(si470x_module_exit);892893MODULE_LICENSE("GPL");894MODULE_AUTHOR(DRIVER_AUTHOR);895MODULE_DESCRIPTION(DRIVER_DESC);896MODULE_VERSION(DRIVER_VERSION);897898899