17fb65297SMauro Carvalho Chehab /* A driver for the D-Link DSB-R100 USB radio. The R100 plugs 27fb65297SMauro Carvalho Chehab into both the USB and an analog audio input, so this thing 37fb65297SMauro Carvalho Chehab only deals with initialisation and frequency setting, the 47fb65297SMauro Carvalho Chehab audio data has to be handled by a sound driver. 57fb65297SMauro Carvalho Chehab 67fb65297SMauro Carvalho Chehab Major issue: I can't find out where the device reports the signal 77fb65297SMauro Carvalho Chehab strength, and indeed the windows software appearantly just looks 87fb65297SMauro Carvalho Chehab at the stereo indicator as well. So, scanning will only find 97fb65297SMauro Carvalho Chehab stereo stations. Sad, but I can't help it. 107fb65297SMauro Carvalho Chehab 117fb65297SMauro Carvalho Chehab Also, the windows program sends oodles of messages over to the 127fb65297SMauro Carvalho Chehab device, and I couldn't figure out their meaning. My suspicion 137fb65297SMauro Carvalho Chehab is that they don't have any:-) 147fb65297SMauro Carvalho Chehab 157fb65297SMauro Carvalho Chehab You might find some interesting stuff about this module at 167fb65297SMauro Carvalho Chehab http://unimut.fsk.uni-heidelberg.de/unimut/demi/dsbr 177fb65297SMauro Carvalho Chehab 187fb65297SMauro Carvalho Chehab Copyright (c) 2000 Markus Demleitner <msdemlei@cl.uni-heidelberg.de> 197fb65297SMauro Carvalho Chehab 207fb65297SMauro Carvalho Chehab This program is free software; you can redistribute it and/or modify 217fb65297SMauro Carvalho Chehab it under the terms of the GNU General Public License as published by 227fb65297SMauro Carvalho Chehab the Free Software Foundation; either version 2 of the License, or 237fb65297SMauro Carvalho Chehab (at your option) any later version. 247fb65297SMauro Carvalho Chehab 257fb65297SMauro Carvalho Chehab This program is distributed in the hope that it will be useful, 267fb65297SMauro Carvalho Chehab but WITHOUT ANY WARRANTY; without even the implied warranty of 277fb65297SMauro Carvalho Chehab MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 287fb65297SMauro Carvalho Chehab GNU General Public License for more details. 297fb65297SMauro Carvalho Chehab 307fb65297SMauro Carvalho Chehab You should have received a copy of the GNU General Public License 317fb65297SMauro Carvalho Chehab along with this program; if not, write to the Free Software 327fb65297SMauro Carvalho Chehab Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 337fb65297SMauro Carvalho Chehab 347fb65297SMauro Carvalho Chehab History: 357fb65297SMauro Carvalho Chehab 36863c86ddSOliver Neukum Version 0.43: 37863c86ddSOliver Neukum Oliver Neukum: avoided DMA coherency issue 38863c86ddSOliver Neukum 397002a4f3SDouglas Landgraf Version 0.42: 407002a4f3SDouglas Landgraf Converted dsbr100 to use video_ioctl2 417002a4f3SDouglas Landgraf by Douglas Landgraf <dougsland@gmail.com> 427002a4f3SDouglas Landgraf 435aff308cSAlan Cox Version 0.41-ac1: 445aff308cSAlan Cox Alan Cox: Some cleanups and fixes 455aff308cSAlan Cox 465aff308cSAlan Cox Version 0.41: 475aff308cSAlan Cox Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> 485aff308cSAlan Cox 497fb65297SMauro Carvalho Chehab Version 0.40: 507fb65297SMauro Carvalho Chehab Markus: Updates for 2.6.x kernels, code layout changes, name sanitizing 517fb65297SMauro Carvalho Chehab 527fb65297SMauro Carvalho Chehab Version 0.30: 537fb65297SMauro Carvalho Chehab Markus: Updates for 2.5.x kernel and more ISO compliant source 547fb65297SMauro Carvalho Chehab 557fb65297SMauro Carvalho Chehab Version 0.25: 567fb65297SMauro Carvalho Chehab PSL and Markus: Cleanup, radio now doesn't stop on device close 577fb65297SMauro Carvalho Chehab 587fb65297SMauro Carvalho Chehab Version 0.24: 597fb65297SMauro Carvalho Chehab Markus: Hope I got these silly VIDEO_TUNER_LOW issues finally 607fb65297SMauro Carvalho Chehab right. Some minor cleanup, improved standalone compilation 617fb65297SMauro Carvalho Chehab 627fb65297SMauro Carvalho Chehab Version 0.23: 637fb65297SMauro Carvalho Chehab Markus: Sign extension bug fixed by declaring transfer_buffer unsigned 647fb65297SMauro Carvalho Chehab 657fb65297SMauro Carvalho Chehab Version 0.22: 667fb65297SMauro Carvalho Chehab Markus: Some (brown bag) cleanup in what VIDIOCSTUNER returns, 677fb65297SMauro Carvalho Chehab thanks to Mike Cox for pointing the problem out. 687fb65297SMauro Carvalho Chehab 697fb65297SMauro Carvalho Chehab Version 0.21: 707fb65297SMauro Carvalho Chehab Markus: Minor cleanup, warnings if something goes wrong, lame attempt 717fb65297SMauro Carvalho Chehab to adhere to Documentation/CodingStyle 727fb65297SMauro Carvalho Chehab 737fb65297SMauro Carvalho Chehab Version 0.2: 747fb65297SMauro Carvalho Chehab Brad Hards <bradh@dynamite.com.au>: Fixes to make it work as non-module 757fb65297SMauro Carvalho Chehab Markus: Copyright clarification 767fb65297SMauro Carvalho Chehab 777fb65297SMauro Carvalho Chehab Version 0.01: Markus: initial release 787fb65297SMauro Carvalho Chehab 797fb65297SMauro Carvalho Chehab */ 807fb65297SMauro Carvalho Chehab 817fb65297SMauro Carvalho Chehab #include <linux/kernel.h> 827fb65297SMauro Carvalho Chehab #include <linux/module.h> 837fb65297SMauro Carvalho Chehab #include <linux/init.h> 847fb65297SMauro Carvalho Chehab #include <linux/slab.h> 857fb65297SMauro Carvalho Chehab #include <linux/input.h> 865aff308cSAlan Cox #include <linux/videodev2.h> 877fb65297SMauro Carvalho Chehab #include <media/v4l2-common.h> 8835ea11ffSHans Verkuil #include <media/v4l2-ioctl.h> 897fb65297SMauro Carvalho Chehab #include <linux/usb.h> 907fb65297SMauro Carvalho Chehab 917fb65297SMauro Carvalho Chehab /* 927fb65297SMauro Carvalho Chehab * Version Information 937fb65297SMauro Carvalho Chehab */ 945aff308cSAlan Cox #include <linux/version.h> /* for KERNEL_VERSION MACRO */ 955aff308cSAlan Cox 9654b7b0deSAlexey Klimov #define DRIVER_VERSION "v0.43" 9754b7b0deSAlexey Klimov #define RADIO_VERSION KERNEL_VERSION(0, 4, 3) 985aff308cSAlan Cox 995aff308cSAlan Cox static struct v4l2_queryctrl radio_qctrl[] = { 1005aff308cSAlan Cox { 1015aff308cSAlan Cox .id = V4L2_CID_AUDIO_MUTE, 1025aff308cSAlan Cox .name = "Mute", 1035aff308cSAlan Cox .minimum = 0, 1045aff308cSAlan Cox .maximum = 1, 1055aff308cSAlan Cox .default_value = 1, 1065aff308cSAlan Cox .type = V4L2_CTRL_TYPE_BOOLEAN, 10754b7b0deSAlexey Klimov }, 10854b7b0deSAlexey Klimov /* HINT: the disabled controls are only here to satify kradio and such apps */ 10954b7b0deSAlexey Klimov { .id = V4L2_CID_AUDIO_VOLUME, 11054b7b0deSAlexey Klimov .flags = V4L2_CTRL_FLAG_DISABLED, 11154b7b0deSAlexey Klimov }, 11254b7b0deSAlexey Klimov { 11354b7b0deSAlexey Klimov .id = V4L2_CID_AUDIO_BALANCE, 11454b7b0deSAlexey Klimov .flags = V4L2_CTRL_FLAG_DISABLED, 11554b7b0deSAlexey Klimov }, 11654b7b0deSAlexey Klimov { 11754b7b0deSAlexey Klimov .id = V4L2_CID_AUDIO_BASS, 11854b7b0deSAlexey Klimov .flags = V4L2_CTRL_FLAG_DISABLED, 11954b7b0deSAlexey Klimov }, 12054b7b0deSAlexey Klimov { 12154b7b0deSAlexey Klimov .id = V4L2_CID_AUDIO_TREBLE, 12254b7b0deSAlexey Klimov .flags = V4L2_CTRL_FLAG_DISABLED, 12354b7b0deSAlexey Klimov }, 12454b7b0deSAlexey Klimov { 12554b7b0deSAlexey Klimov .id = V4L2_CID_AUDIO_LOUDNESS, 12654b7b0deSAlexey Klimov .flags = V4L2_CTRL_FLAG_DISABLED, 12754b7b0deSAlexey Klimov }, 1285aff308cSAlan Cox }; 1295aff308cSAlan Cox 1307fb65297SMauro Carvalho Chehab #define DRIVER_AUTHOR "Markus Demleitner <msdemlei@tucana.harvard.edu>" 1317fb65297SMauro Carvalho Chehab #define DRIVER_DESC "D-Link DSB-R100 USB FM radio driver" 1327fb65297SMauro Carvalho Chehab 1337fb65297SMauro Carvalho Chehab #define DSB100_VENDOR 0x04b4 1347fb65297SMauro Carvalho Chehab #define DSB100_PRODUCT 0x1002 1357fb65297SMauro Carvalho Chehab 1367fb65297SMauro Carvalho Chehab /* Commands the device appears to understand */ 1377fb65297SMauro Carvalho Chehab #define DSB100_TUNE 1 1387fb65297SMauro Carvalho Chehab #define DSB100_ONOFF 2 1397fb65297SMauro Carvalho Chehab 1407fb65297SMauro Carvalho Chehab #define TB_LEN 16 1417fb65297SMauro Carvalho Chehab 1427fb65297SMauro Carvalho Chehab /* Frequency limits in MHz -- these are European values. For Japanese 1437fb65297SMauro Carvalho Chehab devices, that would be 76 and 91. */ 1447fb65297SMauro Carvalho Chehab #define FREQ_MIN 87.5 1457fb65297SMauro Carvalho Chehab #define FREQ_MAX 108.0 1467fb65297SMauro Carvalho Chehab #define FREQ_MUL 16000 1477fb65297SMauro Carvalho Chehab 1483a0efc32SAlexey Klimov #define videodev_to_radio(d) container_of(d, struct dsbr100_device, videodev) 1497fb65297SMauro Carvalho Chehab 1507fb65297SMauro Carvalho Chehab static int usb_dsbr100_probe(struct usb_interface *intf, 1517fb65297SMauro Carvalho Chehab const struct usb_device_id *id); 1527fb65297SMauro Carvalho Chehab static void usb_dsbr100_disconnect(struct usb_interface *intf); 1537fb65297SMauro Carvalho Chehab static int usb_dsbr100_open(struct inode *inode, struct file *file); 1547fb65297SMauro Carvalho Chehab static int usb_dsbr100_close(struct inode *inode, struct file *file); 15504e0ffbbSAlexey Klimov static int usb_dsbr100_suspend(struct usb_interface *intf, 15604e0ffbbSAlexey Klimov pm_message_t message); 15704e0ffbbSAlexey Klimov static int usb_dsbr100_resume(struct usb_interface *intf); 1587fb65297SMauro Carvalho Chehab 1597fb65297SMauro Carvalho Chehab static int radio_nr = -1; 1607fb65297SMauro Carvalho Chehab module_param(radio_nr, int, 0); 1617fb65297SMauro Carvalho Chehab 1627fb65297SMauro Carvalho Chehab /* Data for one (physical) device */ 1635aff308cSAlan Cox struct dsbr100_device { 1647fb65297SMauro Carvalho Chehab struct usb_device *usbdev; 1653a0efc32SAlexey Klimov struct video_device videodev; 166863c86ddSOliver Neukum u8 *transfer_buffer; 1673a0efc32SAlexey Klimov struct mutex lock; /* buffer locking */ 1687fb65297SMauro Carvalho Chehab int curfreq; 1697fb65297SMauro Carvalho Chehab int stereo; 1707fb65297SMauro Carvalho Chehab int users; 1717fb65297SMauro Carvalho Chehab int removed; 1725aff308cSAlan Cox int muted; 1735aff308cSAlan Cox }; 1747fb65297SMauro Carvalho Chehab 1757fb65297SMauro Carvalho Chehab 1767fb65297SMauro Carvalho Chehab static struct usb_device_id usb_dsbr100_device_table [] = { 1777fb65297SMauro Carvalho Chehab { USB_DEVICE(DSB100_VENDOR, DSB100_PRODUCT) }, 1787fb65297SMauro Carvalho Chehab { } /* Terminating entry */ 1797fb65297SMauro Carvalho Chehab }; 1807fb65297SMauro Carvalho Chehab 1817fb65297SMauro Carvalho Chehab MODULE_DEVICE_TABLE (usb, usb_dsbr100_device_table); 1827fb65297SMauro Carvalho Chehab 1837fb65297SMauro Carvalho Chehab /* USB subsystem interface */ 1847fb65297SMauro Carvalho Chehab static struct usb_driver usb_dsbr100_driver = { 1857fb65297SMauro Carvalho Chehab .name = "dsbr100", 1867fb65297SMauro Carvalho Chehab .probe = usb_dsbr100_probe, 1877fb65297SMauro Carvalho Chehab .disconnect = usb_dsbr100_disconnect, 1887fb65297SMauro Carvalho Chehab .id_table = usb_dsbr100_device_table, 18904e0ffbbSAlexey Klimov .suspend = usb_dsbr100_suspend, 19004e0ffbbSAlexey Klimov .resume = usb_dsbr100_resume, 19104e0ffbbSAlexey Klimov .reset_resume = usb_dsbr100_resume, 19204e0ffbbSAlexey Klimov .supports_autosuspend = 0, 1937fb65297SMauro Carvalho Chehab }; 1947fb65297SMauro Carvalho Chehab 1957fb65297SMauro Carvalho Chehab /* Low-level device interface begins here */ 1967fb65297SMauro Carvalho Chehab 1977fb65297SMauro Carvalho Chehab /* switch on radio */ 1985aff308cSAlan Cox static int dsbr100_start(struct dsbr100_device *radio) 1997fb65297SMauro Carvalho Chehab { 200417b7953SAlexey Klimov int retval; 201417b7953SAlexey Klimov int request; 202417b7953SAlexey Klimov 2033a0efc32SAlexey Klimov mutex_lock(&radio->lock); 204417b7953SAlexey Klimov 205417b7953SAlexey Klimov retval = usb_control_msg(radio->usbdev, 206417b7953SAlexey Klimov usb_rcvctrlpipe(radio->usbdev, 0), 2077fb65297SMauro Carvalho Chehab USB_REQ_GET_STATUS, 2087fb65297SMauro Carvalho Chehab USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 209417b7953SAlexey Klimov 0x00, 0xC7, radio->transfer_buffer, 8, 300); 210417b7953SAlexey Klimov 211417b7953SAlexey Klimov if (retval < 0) { 212417b7953SAlexey Klimov request = USB_REQ_GET_STATUS; 213417b7953SAlexey Klimov goto usb_control_msg_failed; 214417b7953SAlexey Klimov } 215417b7953SAlexey Klimov 216417b7953SAlexey Klimov retval = usb_control_msg(radio->usbdev, 217417b7953SAlexey Klimov usb_rcvctrlpipe(radio->usbdev, 0), 2187fb65297SMauro Carvalho Chehab DSB100_ONOFF, 2197fb65297SMauro Carvalho Chehab USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 220417b7953SAlexey Klimov 0x01, 0x00, radio->transfer_buffer, 8, 300); 221417b7953SAlexey Klimov 222417b7953SAlexey Klimov if (retval < 0) { 223417b7953SAlexey Klimov request = DSB100_ONOFF; 224417b7953SAlexey Klimov goto usb_control_msg_failed; 2253a0efc32SAlexey Klimov } 2263a0efc32SAlexey Klimov 2275aff308cSAlan Cox radio->muted = 0; 2283a0efc32SAlexey Klimov mutex_unlock(&radio->lock); 2297fb65297SMauro Carvalho Chehab return (radio->transfer_buffer)[0]; 230417b7953SAlexey Klimov 231417b7953SAlexey Klimov usb_control_msg_failed: 232417b7953SAlexey Klimov mutex_unlock(&radio->lock); 233417b7953SAlexey Klimov dev_err(&radio->usbdev->dev, 234417b7953SAlexey Klimov "%s - usb_control_msg returned %i, request %i\n", 235417b7953SAlexey Klimov __func__, retval, request); 236417b7953SAlexey Klimov return -1; 237417b7953SAlexey Klimov 2387fb65297SMauro Carvalho Chehab } 2397fb65297SMauro Carvalho Chehab 2407fb65297SMauro Carvalho Chehab 2417fb65297SMauro Carvalho Chehab /* switch off radio */ 2425aff308cSAlan Cox static int dsbr100_stop(struct dsbr100_device *radio) 2437fb65297SMauro Carvalho Chehab { 244417b7953SAlexey Klimov int retval; 245417b7953SAlexey Klimov int request; 246417b7953SAlexey Klimov 2473a0efc32SAlexey Klimov mutex_lock(&radio->lock); 248417b7953SAlexey Klimov 249417b7953SAlexey Klimov retval = usb_control_msg(radio->usbdev, 250417b7953SAlexey Klimov usb_rcvctrlpipe(radio->usbdev, 0), 2517fb65297SMauro Carvalho Chehab USB_REQ_GET_STATUS, 2527fb65297SMauro Carvalho Chehab USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 253417b7953SAlexey Klimov 0x16, 0x1C, radio->transfer_buffer, 8, 300); 254417b7953SAlexey Klimov 255417b7953SAlexey Klimov if (retval < 0) { 256417b7953SAlexey Klimov request = USB_REQ_GET_STATUS; 257417b7953SAlexey Klimov goto usb_control_msg_failed; 258417b7953SAlexey Klimov } 259417b7953SAlexey Klimov 260417b7953SAlexey Klimov retval = usb_control_msg(radio->usbdev, 261417b7953SAlexey Klimov usb_rcvctrlpipe(radio->usbdev, 0), 2627fb65297SMauro Carvalho Chehab DSB100_ONOFF, 2637fb65297SMauro Carvalho Chehab USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 264417b7953SAlexey Klimov 0x00, 0x00, radio->transfer_buffer, 8, 300); 265417b7953SAlexey Klimov 266417b7953SAlexey Klimov if (retval < 0) { 267417b7953SAlexey Klimov request = DSB100_ONOFF; 268417b7953SAlexey Klimov goto usb_control_msg_failed; 2693a0efc32SAlexey Klimov } 2703a0efc32SAlexey Klimov 2715aff308cSAlan Cox radio->muted = 1; 2723a0efc32SAlexey Klimov mutex_unlock(&radio->lock); 2737fb65297SMauro Carvalho Chehab return (radio->transfer_buffer)[0]; 274417b7953SAlexey Klimov 275417b7953SAlexey Klimov usb_control_msg_failed: 276417b7953SAlexey Klimov mutex_unlock(&radio->lock); 277417b7953SAlexey Klimov dev_err(&radio->usbdev->dev, 278417b7953SAlexey Klimov "%s - usb_control_msg returned %i, request %i\n", 279417b7953SAlexey Klimov __func__, retval, request); 280417b7953SAlexey Klimov return -1; 281417b7953SAlexey Klimov 2827fb65297SMauro Carvalho Chehab } 2837fb65297SMauro Carvalho Chehab 2847fb65297SMauro Carvalho Chehab /* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */ 2855aff308cSAlan Cox static int dsbr100_setfreq(struct dsbr100_device *radio, int freq) 2867fb65297SMauro Carvalho Chehab { 287417b7953SAlexey Klimov int retval; 288417b7953SAlexey Klimov int request; 289417b7953SAlexey Klimov 2907fb65297SMauro Carvalho Chehab freq = (freq / 16 * 80) / 1000 + 856; 2913a0efc32SAlexey Klimov mutex_lock(&radio->lock); 292417b7953SAlexey Klimov 293417b7953SAlexey Klimov retval = usb_control_msg(radio->usbdev, 294417b7953SAlexey Klimov usb_rcvctrlpipe(radio->usbdev, 0), 2957fb65297SMauro Carvalho Chehab DSB100_TUNE, 2967fb65297SMauro Carvalho Chehab USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 2977fb65297SMauro Carvalho Chehab (freq >> 8) & 0x00ff, freq & 0xff, 298417b7953SAlexey Klimov radio->transfer_buffer, 8, 300); 299417b7953SAlexey Klimov 300417b7953SAlexey Klimov if (retval < 0) { 301417b7953SAlexey Klimov request = DSB100_TUNE; 302417b7953SAlexey Klimov goto usb_control_msg_failed; 303417b7953SAlexey Klimov } 304417b7953SAlexey Klimov 305417b7953SAlexey Klimov retval = usb_control_msg(radio->usbdev, 306417b7953SAlexey Klimov usb_rcvctrlpipe(radio->usbdev, 0), 3077fb65297SMauro Carvalho Chehab USB_REQ_GET_STATUS, 3087fb65297SMauro Carvalho Chehab USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 309417b7953SAlexey Klimov 0x96, 0xB7, radio->transfer_buffer, 8, 300); 310417b7953SAlexey Klimov 311417b7953SAlexey Klimov if (retval < 0) { 312417b7953SAlexey Klimov request = USB_REQ_GET_STATUS; 313417b7953SAlexey Klimov goto usb_control_msg_failed; 314417b7953SAlexey Klimov } 315417b7953SAlexey Klimov 316417b7953SAlexey Klimov retval = usb_control_msg(radio->usbdev, 317417b7953SAlexey Klimov usb_rcvctrlpipe(radio->usbdev, 0), 3187fb65297SMauro Carvalho Chehab USB_REQ_GET_STATUS, 3197fb65297SMauro Carvalho Chehab USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 320417b7953SAlexey Klimov 0x00, 0x24, radio->transfer_buffer, 8, 300); 321417b7953SAlexey Klimov 322417b7953SAlexey Klimov if (retval < 0) { 323417b7953SAlexey Klimov request = USB_REQ_GET_STATUS; 324417b7953SAlexey Klimov goto usb_control_msg_failed; 3257fb65297SMauro Carvalho Chehab } 3263a0efc32SAlexey Klimov 3277fb65297SMauro Carvalho Chehab radio->stereo = !((radio->transfer_buffer)[0] & 0x01); 3283a0efc32SAlexey Klimov mutex_unlock(&radio->lock); 3297fb65297SMauro Carvalho Chehab return (radio->transfer_buffer)[0]; 330417b7953SAlexey Klimov 331417b7953SAlexey Klimov usb_control_msg_failed: 332417b7953SAlexey Klimov radio->stereo = -1; 333417b7953SAlexey Klimov mutex_unlock(&radio->lock); 334417b7953SAlexey Klimov dev_err(&radio->usbdev->dev, 335417b7953SAlexey Klimov "%s - usb_control_msg returned %i, request %i\n", 336417b7953SAlexey Klimov __func__, retval, request); 337417b7953SAlexey Klimov return -1; 3387fb65297SMauro Carvalho Chehab } 3397fb65297SMauro Carvalho Chehab 3407fb65297SMauro Carvalho Chehab /* return the device status. This is, in effect, just whether it 3417fb65297SMauro Carvalho Chehab sees a stereo signal or not. Pity. */ 3425aff308cSAlan Cox static void dsbr100_getstat(struct dsbr100_device *radio) 3437fb65297SMauro Carvalho Chehab { 344417b7953SAlexey Klimov int retval; 345417b7953SAlexey Klimov 3463a0efc32SAlexey Klimov mutex_lock(&radio->lock); 347417b7953SAlexey Klimov 348417b7953SAlexey Klimov retval = usb_control_msg(radio->usbdev, 349417b7953SAlexey Klimov usb_rcvctrlpipe(radio->usbdev, 0), 3507fb65297SMauro Carvalho Chehab USB_REQ_GET_STATUS, 3517fb65297SMauro Carvalho Chehab USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 352417b7953SAlexey Klimov 0x00 , 0x24, radio->transfer_buffer, 8, 300); 353417b7953SAlexey Klimov 354417b7953SAlexey Klimov if (retval < 0) { 3557fb65297SMauro Carvalho Chehab radio->stereo = -1; 356417b7953SAlexey Klimov dev_err(&radio->usbdev->dev, 357417b7953SAlexey Klimov "%s - usb_control_msg returned %i, request %i\n", 358417b7953SAlexey Klimov __func__, retval, USB_REQ_GET_STATUS); 359417b7953SAlexey Klimov } else { 3607fb65297SMauro Carvalho Chehab radio->stereo = !(radio->transfer_buffer[0] & 0x01); 361417b7953SAlexey Klimov } 362417b7953SAlexey Klimov 3633a0efc32SAlexey Klimov mutex_unlock(&radio->lock); 3647fb65297SMauro Carvalho Chehab } 3657fb65297SMauro Carvalho Chehab 3667fb65297SMauro Carvalho Chehab 3677fb65297SMauro Carvalho Chehab /* USB subsystem interface begins here */ 3687fb65297SMauro Carvalho Chehab 3697fb65297SMauro Carvalho Chehab /* handle unplugging of the device, release data structures 3707fb65297SMauro Carvalho Chehab if nothing keeps us from doing it. If something is still 3717fb65297SMauro Carvalho Chehab keeping us busy, the release callback of v4l will take care 372863c86ddSOliver Neukum of releasing it. */ 3737fb65297SMauro Carvalho Chehab static void usb_dsbr100_disconnect(struct usb_interface *intf) 3747fb65297SMauro Carvalho Chehab { 3755aff308cSAlan Cox struct dsbr100_device *radio = usb_get_intfdata(intf); 3767fb65297SMauro Carvalho Chehab 3777fb65297SMauro Carvalho Chehab usb_set_intfdata (intf, NULL); 3783a0efc32SAlexey Klimov 3793a0efc32SAlexey Klimov mutex_lock(&radio->lock); 3807fb65297SMauro Carvalho Chehab radio->removed = 1; 3813a0efc32SAlexey Klimov mutex_unlock(&radio->lock); 3823a0efc32SAlexey Klimov 3833a0efc32SAlexey Klimov video_unregister_device(&radio->videodev); 3847fb65297SMauro Carvalho Chehab } 3857fb65297SMauro Carvalho Chehab 3867fb65297SMauro Carvalho Chehab 3877002a4f3SDouglas Landgraf static int vidioc_querycap(struct file *file, void *priv, 3887002a4f3SDouglas Landgraf struct v4l2_capability *v) 3897fb65297SMauro Carvalho Chehab { 3905aff308cSAlan Cox strlcpy(v->driver, "dsbr100", sizeof(v->driver)); 3915aff308cSAlan Cox strlcpy(v->card, "D-Link R-100 USB FM Radio", sizeof(v->card)); 392448441cfSAlexey Klimov sprintf(v->bus_info, "USB"); 3935aff308cSAlan Cox v->version = RADIO_VERSION; 3945aff308cSAlan Cox v->capabilities = V4L2_CAP_TUNER; 3957fb65297SMauro Carvalho Chehab return 0; 3967fb65297SMauro Carvalho Chehab } 3977002a4f3SDouglas Landgraf 3987002a4f3SDouglas Landgraf static int vidioc_g_tuner(struct file *file, void *priv, 3997002a4f3SDouglas Landgraf struct v4l2_tuner *v) 4005aff308cSAlan Cox { 401c170ecf4SHans Verkuil struct dsbr100_device *radio = video_drvdata(file); 4025aff308cSAlan Cox 4033a0efc32SAlexey Klimov /* safety check */ 4043a0efc32SAlexey Klimov if (radio->removed) 4053a0efc32SAlexey Klimov return -EIO; 4063a0efc32SAlexey Klimov 4075aff308cSAlan Cox if (v->index > 0) 4085aff308cSAlan Cox return -EINVAL; 4097fb65297SMauro Carvalho Chehab 4107fb65297SMauro Carvalho Chehab dsbr100_getstat(radio); 4115aff308cSAlan Cox strcpy(v->name, "FM"); 4125aff308cSAlan Cox v->type = V4L2_TUNER_RADIO; 4137fb65297SMauro Carvalho Chehab v->rangelow = FREQ_MIN * FREQ_MUL; 4147fb65297SMauro Carvalho Chehab v->rangehigh = FREQ_MAX * FREQ_MUL; 4155aff308cSAlan Cox v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; 4165aff308cSAlan Cox v->capability = V4L2_TUNER_CAP_LOW; 4175aff308cSAlan Cox if(radio->stereo) 4185aff308cSAlan Cox v->audmode = V4L2_TUNER_MODE_STEREO; 4195aff308cSAlan Cox else 4205aff308cSAlan Cox v->audmode = V4L2_TUNER_MODE_MONO; 4217002a4f3SDouglas Landgraf v->signal = 0xffff; /* We can't get the signal strength */ 4227fb65297SMauro Carvalho Chehab return 0; 4237fb65297SMauro Carvalho Chehab } 4247fb65297SMauro Carvalho Chehab 4257002a4f3SDouglas Landgraf static int vidioc_s_tuner(struct file *file, void *priv, 4267002a4f3SDouglas Landgraf struct v4l2_tuner *v) 4277002a4f3SDouglas Landgraf { 4283a0efc32SAlexey Klimov struct dsbr100_device *radio = video_drvdata(file); 4293a0efc32SAlexey Klimov 4303a0efc32SAlexey Klimov /* safety check */ 4313a0efc32SAlexey Klimov if (radio->removed) 4323a0efc32SAlexey Klimov return -EIO; 4333a0efc32SAlexey Klimov 4345aff308cSAlan Cox if (v->index > 0) 4357fb65297SMauro Carvalho Chehab return -EINVAL; 4365aff308cSAlan Cox 4377fb65297SMauro Carvalho Chehab return 0; 4387fb65297SMauro Carvalho Chehab } 4397002a4f3SDouglas Landgraf 4407002a4f3SDouglas Landgraf static int vidioc_s_frequency(struct file *file, void *priv, 4417002a4f3SDouglas Landgraf struct v4l2_frequency *f) 4425aff308cSAlan Cox { 443c170ecf4SHans Verkuil struct dsbr100_device *radio = video_drvdata(file); 444417b7953SAlexey Klimov int retval; 4457fb65297SMauro Carvalho Chehab 4463a0efc32SAlexey Klimov /* safety check */ 4473a0efc32SAlexey Klimov if (radio->removed) 4483a0efc32SAlexey Klimov return -EIO; 4493a0efc32SAlexey Klimov 4505aff308cSAlan Cox radio->curfreq = f->frequency; 451417b7953SAlexey Klimov retval = dsbr100_setfreq(radio, radio->curfreq); 452417b7953SAlexey Klimov if (retval == -1) 453aa82661bSGreg Kroah-Hartman dev_warn(&radio->usbdev->dev, "Set frequency failed\n"); 4547fb65297SMauro Carvalho Chehab return 0; 4557fb65297SMauro Carvalho Chehab } 4567002a4f3SDouglas Landgraf 4577002a4f3SDouglas Landgraf static int vidioc_g_frequency(struct file *file, void *priv, 4587002a4f3SDouglas Landgraf struct v4l2_frequency *f) 4595aff308cSAlan Cox { 460c170ecf4SHans Verkuil struct dsbr100_device *radio = video_drvdata(file); 4617fb65297SMauro Carvalho Chehab 4623a0efc32SAlexey Klimov /* safety check */ 4633a0efc32SAlexey Klimov if (radio->removed) 4643a0efc32SAlexey Klimov return -EIO; 4653a0efc32SAlexey Klimov 4665aff308cSAlan Cox f->type = V4L2_TUNER_RADIO; 4675aff308cSAlan Cox f->frequency = radio->curfreq; 4687fb65297SMauro Carvalho Chehab return 0; 4697fb65297SMauro Carvalho Chehab } 4707002a4f3SDouglas Landgraf 4717002a4f3SDouglas Landgraf static int vidioc_queryctrl(struct file *file, void *priv, 4727002a4f3SDouglas Landgraf struct v4l2_queryctrl *qc) 4735aff308cSAlan Cox { 4745aff308cSAlan Cox int i; 4757fb65297SMauro Carvalho Chehab 4765aff308cSAlan Cox for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { 4775aff308cSAlan Cox if (qc->id && qc->id == radio_qctrl[i].id) { 478223377e7SAlexey Klimov memcpy(qc, &(radio_qctrl[i]), sizeof(*qc)); 4795aff308cSAlan Cox return 0; 4805aff308cSAlan Cox } 4815aff308cSAlan Cox } 4827fb65297SMauro Carvalho Chehab return -EINVAL; 4835aff308cSAlan Cox } 4847002a4f3SDouglas Landgraf 4857002a4f3SDouglas Landgraf static int vidioc_g_ctrl(struct file *file, void *priv, 4867002a4f3SDouglas Landgraf struct v4l2_control *ctrl) 4875aff308cSAlan Cox { 488c170ecf4SHans Verkuil struct dsbr100_device *radio = video_drvdata(file); 4895aff308cSAlan Cox 4903a0efc32SAlexey Klimov /* safety check */ 4913a0efc32SAlexey Klimov if (radio->removed) 4923a0efc32SAlexey Klimov return -EIO; 4933a0efc32SAlexey Klimov 4945aff308cSAlan Cox switch (ctrl->id) { 4955aff308cSAlan Cox case V4L2_CID_AUDIO_MUTE: 4965aff308cSAlan Cox ctrl->value = radio->muted; 4975aff308cSAlan Cox return 0; 4985aff308cSAlan Cox } 4995aff308cSAlan Cox return -EINVAL; 5005aff308cSAlan Cox } 5017002a4f3SDouglas Landgraf 5027002a4f3SDouglas Landgraf static int vidioc_s_ctrl(struct file *file, void *priv, 5037002a4f3SDouglas Landgraf struct v4l2_control *ctrl) 5045aff308cSAlan Cox { 505c170ecf4SHans Verkuil struct dsbr100_device *radio = video_drvdata(file); 506417b7953SAlexey Klimov int retval; 5075aff308cSAlan Cox 5083a0efc32SAlexey Klimov /* safety check */ 5093a0efc32SAlexey Klimov if (radio->removed) 5103a0efc32SAlexey Klimov return -EIO; 5113a0efc32SAlexey Klimov 5125aff308cSAlan Cox switch (ctrl->id) { 5135aff308cSAlan Cox case V4L2_CID_AUDIO_MUTE: 5145aff308cSAlan Cox if (ctrl->value) { 515417b7953SAlexey Klimov retval = dsbr100_stop(radio); 516417b7953SAlexey Klimov if (retval == -1) { 517aa82661bSGreg Kroah-Hartman dev_warn(&radio->usbdev->dev, 518aa82661bSGreg Kroah-Hartman "Radio did not respond properly\n"); 51990b698ddSAlexey Klimov return -EBUSY; 52090b698ddSAlexey Klimov } 5215aff308cSAlan Cox } else { 522417b7953SAlexey Klimov retval = dsbr100_start(radio); 523417b7953SAlexey Klimov if (retval == -1) { 524aa82661bSGreg Kroah-Hartman dev_warn(&radio->usbdev->dev, 525aa82661bSGreg Kroah-Hartman "Radio did not respond properly\n"); 52690b698ddSAlexey Klimov return -EBUSY; 52790b698ddSAlexey Klimov } 5285aff308cSAlan Cox } 5297fb65297SMauro Carvalho Chehab return 0; 5307fb65297SMauro Carvalho Chehab } 5315aff308cSAlan Cox return -EINVAL; 5325aff308cSAlan Cox } 5337002a4f3SDouglas Landgraf 5347002a4f3SDouglas Landgraf static int vidioc_g_audio(struct file *file, void *priv, 5357002a4f3SDouglas Landgraf struct v4l2_audio *a) 5367002a4f3SDouglas Landgraf { 5377002a4f3SDouglas Landgraf if (a->index > 1) 5387002a4f3SDouglas Landgraf return -EINVAL; 5397002a4f3SDouglas Landgraf 5407002a4f3SDouglas Landgraf strcpy(a->name, "Radio"); 5417002a4f3SDouglas Landgraf a->capability = V4L2_AUDCAP_STEREO; 5427002a4f3SDouglas Landgraf return 0; 5437fb65297SMauro Carvalho Chehab } 5447fb65297SMauro Carvalho Chehab 5457002a4f3SDouglas Landgraf static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) 5467fb65297SMauro Carvalho Chehab { 5477002a4f3SDouglas Landgraf *i = 0; 5487002a4f3SDouglas Landgraf return 0; 5497002a4f3SDouglas Landgraf } 5507002a4f3SDouglas Landgraf 5517002a4f3SDouglas Landgraf static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) 5527002a4f3SDouglas Landgraf { 5537002a4f3SDouglas Landgraf if (i != 0) 5547002a4f3SDouglas Landgraf return -EINVAL; 5557002a4f3SDouglas Landgraf return 0; 5567002a4f3SDouglas Landgraf } 5577002a4f3SDouglas Landgraf 5587002a4f3SDouglas Landgraf static int vidioc_s_audio(struct file *file, void *priv, 5597002a4f3SDouglas Landgraf struct v4l2_audio *a) 5607002a4f3SDouglas Landgraf { 5617002a4f3SDouglas Landgraf if (a->index != 0) 5627002a4f3SDouglas Landgraf return -EINVAL; 5637002a4f3SDouglas Landgraf return 0; 5647fb65297SMauro Carvalho Chehab } 5657fb65297SMauro Carvalho Chehab 5667fb65297SMauro Carvalho Chehab static int usb_dsbr100_open(struct inode *inode, struct file *file) 5677fb65297SMauro Carvalho Chehab { 568c170ecf4SHans Verkuil struct dsbr100_device *radio = video_drvdata(file); 569b9f35737SAlexey Klimov int retval; 5707fb65297SMauro Carvalho Chehab 571d56dc612SHans Verkuil lock_kernel(); 5727fb65297SMauro Carvalho Chehab radio->users = 1; 5735aff308cSAlan Cox radio->muted = 1; 5745aff308cSAlan Cox 575417b7953SAlexey Klimov retval = dsbr100_start(radio); 576417b7953SAlexey Klimov if (retval < 0) { 577aa82661bSGreg Kroah-Hartman dev_warn(&radio->usbdev->dev, 578aa82661bSGreg Kroah-Hartman "Radio did not start up properly\n"); 5797fb65297SMauro Carvalho Chehab radio->users = 0; 580d56dc612SHans Verkuil unlock_kernel(); 5817fb65297SMauro Carvalho Chehab return -EIO; 5827fb65297SMauro Carvalho Chehab } 583b9f35737SAlexey Klimov 584b9f35737SAlexey Klimov retval = dsbr100_setfreq(radio, radio->curfreq); 585b9f35737SAlexey Klimov if (retval == -1) 586290588e0SAlexey Klimov dev_warn(&radio->usbdev->dev, 587290588e0SAlexey Klimov "set frequency failed\n"); 588b9f35737SAlexey Klimov 589d56dc612SHans Verkuil unlock_kernel(); 5907fb65297SMauro Carvalho Chehab return 0; 5917fb65297SMauro Carvalho Chehab } 5927fb65297SMauro Carvalho Chehab 5937fb65297SMauro Carvalho Chehab static int usb_dsbr100_close(struct inode *inode, struct file *file) 5947fb65297SMauro Carvalho Chehab { 595c170ecf4SHans Verkuil struct dsbr100_device *radio = video_drvdata(file); 5963a0efc32SAlexey Klimov int retval; 5977fb65297SMauro Carvalho Chehab 5987fb65297SMauro Carvalho Chehab if (!radio) 5997fb65297SMauro Carvalho Chehab return -ENODEV; 6003a0efc32SAlexey Klimov 6017fb65297SMauro Carvalho Chehab radio->users = 0; 6023a0efc32SAlexey Klimov if (!radio->removed) { 6033a0efc32SAlexey Klimov retval = dsbr100_stop(radio); 6043a0efc32SAlexey Klimov if (retval == -1) { 6053a0efc32SAlexey Klimov dev_warn(&radio->usbdev->dev, 6063a0efc32SAlexey Klimov "dsbr100_stop failed\n"); 6073a0efc32SAlexey Klimov } 6083a0efc32SAlexey Klimov 6097fb65297SMauro Carvalho Chehab } 6107fb65297SMauro Carvalho Chehab return 0; 6117fb65297SMauro Carvalho Chehab } 6127fb65297SMauro Carvalho Chehab 61304e0ffbbSAlexey Klimov /* Suspend device - stop device. */ 61404e0ffbbSAlexey Klimov static int usb_dsbr100_suspend(struct usb_interface *intf, pm_message_t message) 61504e0ffbbSAlexey Klimov { 61604e0ffbbSAlexey Klimov struct dsbr100_device *radio = usb_get_intfdata(intf); 61704e0ffbbSAlexey Klimov int retval; 61804e0ffbbSAlexey Klimov 61904e0ffbbSAlexey Klimov retval = dsbr100_stop(radio); 62004e0ffbbSAlexey Klimov if (retval == -1) 62104e0ffbbSAlexey Klimov dev_warn(&intf->dev, "dsbr100_stop failed\n"); 62204e0ffbbSAlexey Klimov 62304e0ffbbSAlexey Klimov dev_info(&intf->dev, "going into suspend..\n"); 62404e0ffbbSAlexey Klimov 62504e0ffbbSAlexey Klimov return 0; 62604e0ffbbSAlexey Klimov } 62704e0ffbbSAlexey Klimov 62804e0ffbbSAlexey Klimov /* Resume device - start device. */ 62904e0ffbbSAlexey Klimov static int usb_dsbr100_resume(struct usb_interface *intf) 63004e0ffbbSAlexey Klimov { 63104e0ffbbSAlexey Klimov struct dsbr100_device *radio = usb_get_intfdata(intf); 63204e0ffbbSAlexey Klimov int retval; 63304e0ffbbSAlexey Klimov 63404e0ffbbSAlexey Klimov retval = dsbr100_start(radio); 63504e0ffbbSAlexey Klimov if (retval == -1) 63604e0ffbbSAlexey Klimov dev_warn(&intf->dev, "dsbr100_start failed\n"); 63704e0ffbbSAlexey Klimov 63804e0ffbbSAlexey Klimov dev_info(&intf->dev, "coming out of suspend..\n"); 63904e0ffbbSAlexey Klimov 64004e0ffbbSAlexey Klimov return 0; 64104e0ffbbSAlexey Klimov } 64204e0ffbbSAlexey Klimov 6433a0efc32SAlexey Klimov static void usb_dsbr100_video_device_release(struct video_device *videodev) 6443a0efc32SAlexey Klimov { 6453a0efc32SAlexey Klimov struct dsbr100_device *radio = videodev_to_radio(videodev); 6463a0efc32SAlexey Klimov 6473a0efc32SAlexey Klimov kfree(radio->transfer_buffer); 6483a0efc32SAlexey Klimov kfree(radio); 6493a0efc32SAlexey Klimov } 6503a0efc32SAlexey Klimov 6517002a4f3SDouglas Landgraf /* File system interface */ 6527002a4f3SDouglas Landgraf static const struct file_operations usb_dsbr100_fops = { 6537002a4f3SDouglas Landgraf .owner = THIS_MODULE, 6547002a4f3SDouglas Landgraf .open = usb_dsbr100_open, 6557002a4f3SDouglas Landgraf .release = usb_dsbr100_close, 6567002a4f3SDouglas Landgraf .ioctl = video_ioctl2, 657078ff795SDouglas Schilling Landgraf #ifdef CONFIG_COMPAT 6587002a4f3SDouglas Landgraf .compat_ioctl = v4l_compat_ioctl32, 659078ff795SDouglas Schilling Landgraf #endif 6607002a4f3SDouglas Landgraf .llseek = no_llseek, 6617002a4f3SDouglas Landgraf }; 6627002a4f3SDouglas Landgraf 663a399810cSHans Verkuil static const struct v4l2_ioctl_ops usb_dsbr100_ioctl_ops = { 6647002a4f3SDouglas Landgraf .vidioc_querycap = vidioc_querycap, 6657002a4f3SDouglas Landgraf .vidioc_g_tuner = vidioc_g_tuner, 6667002a4f3SDouglas Landgraf .vidioc_s_tuner = vidioc_s_tuner, 6677002a4f3SDouglas Landgraf .vidioc_g_frequency = vidioc_g_frequency, 6687002a4f3SDouglas Landgraf .vidioc_s_frequency = vidioc_s_frequency, 6697002a4f3SDouglas Landgraf .vidioc_queryctrl = vidioc_queryctrl, 6707002a4f3SDouglas Landgraf .vidioc_g_ctrl = vidioc_g_ctrl, 6717002a4f3SDouglas Landgraf .vidioc_s_ctrl = vidioc_s_ctrl, 6727002a4f3SDouglas Landgraf .vidioc_g_audio = vidioc_g_audio, 6737002a4f3SDouglas Landgraf .vidioc_s_audio = vidioc_s_audio, 6747002a4f3SDouglas Landgraf .vidioc_g_input = vidioc_g_input, 6757002a4f3SDouglas Landgraf .vidioc_s_input = vidioc_s_input, 6767002a4f3SDouglas Landgraf }; 6777002a4f3SDouglas Landgraf 678a399810cSHans Verkuil /* V4L2 interface */ 6793a0efc32SAlexey Klimov static struct video_device dsbr100_videodev_data = { 680a399810cSHans Verkuil .name = "D-Link DSB-R 100", 681a399810cSHans Verkuil .fops = &usb_dsbr100_fops, 682a399810cSHans Verkuil .ioctl_ops = &usb_dsbr100_ioctl_ops, 6833a0efc32SAlexey Klimov .release = usb_dsbr100_video_device_release, 684a399810cSHans Verkuil }; 685a399810cSHans Verkuil 6867002a4f3SDouglas Landgraf /* check if the device is present and register with v4l and 6877002a4f3SDouglas Landgraf usb if it is */ 6887002a4f3SDouglas Landgraf static int usb_dsbr100_probe(struct usb_interface *intf, 6897002a4f3SDouglas Landgraf const struct usb_device_id *id) 6907002a4f3SDouglas Landgraf { 6917002a4f3SDouglas Landgraf struct dsbr100_device *radio; 692417b7953SAlexey Klimov int retval; 6937002a4f3SDouglas Landgraf 694223377e7SAlexey Klimov radio = kmalloc(sizeof(struct dsbr100_device), GFP_KERNEL); 695223377e7SAlexey Klimov 696223377e7SAlexey Klimov if (!radio) 6977002a4f3SDouglas Landgraf return -ENOMEM; 698223377e7SAlexey Klimov 699223377e7SAlexey Klimov radio->transfer_buffer = kmalloc(TB_LEN, GFP_KERNEL); 700223377e7SAlexey Klimov 701223377e7SAlexey Klimov if (!(radio->transfer_buffer)) { 702863c86ddSOliver Neukum kfree(radio); 703863c86ddSOliver Neukum return -ENOMEM; 704863c86ddSOliver Neukum } 705223377e7SAlexey Klimov 7063a0efc32SAlexey Klimov mutex_init(&radio->lock); 7073a0efc32SAlexey Klimov radio->videodev = dsbr100_videodev_data; 7083a0efc32SAlexey Klimov 7097002a4f3SDouglas Landgraf radio->removed = 0; 7107002a4f3SDouglas Landgraf radio->users = 0; 7117002a4f3SDouglas Landgraf radio->usbdev = interface_to_usbdev(intf); 7127002a4f3SDouglas Landgraf radio->curfreq = FREQ_MIN * FREQ_MUL; 7133a0efc32SAlexey Klimov video_set_drvdata(&radio->videodev, radio); 714417b7953SAlexey Klimov retval = video_register_device(&radio->videodev, VFL_TYPE_RADIO, radio_nr); 715417b7953SAlexey Klimov if (retval < 0) { 7167e1ca849SAlexey Klimov dev_err(&intf->dev, "couldn't register video device\n"); 717863c86ddSOliver Neukum kfree(radio->transfer_buffer); 7187002a4f3SDouglas Landgraf kfree(radio); 7197002a4f3SDouglas Landgraf return -EIO; 7207002a4f3SDouglas Landgraf } 7217002a4f3SDouglas Landgraf usb_set_intfdata(intf, radio); 7227002a4f3SDouglas Landgraf return 0; 7237002a4f3SDouglas Landgraf } 7247002a4f3SDouglas Landgraf 7257fb65297SMauro Carvalho Chehab static int __init dsbr100_init(void) 7267fb65297SMauro Carvalho Chehab { 7277fb65297SMauro Carvalho Chehab int retval = usb_register(&usb_dsbr100_driver); 728a482f327SGreg Kroah-Hartman printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" 729a482f327SGreg Kroah-Hartman DRIVER_DESC "\n"); 7307fb65297SMauro Carvalho Chehab return retval; 7317fb65297SMauro Carvalho Chehab } 7327fb65297SMauro Carvalho Chehab 7337fb65297SMauro Carvalho Chehab static void __exit dsbr100_exit(void) 7347fb65297SMauro Carvalho Chehab { 7357fb65297SMauro Carvalho Chehab usb_deregister(&usb_dsbr100_driver); 7367fb65297SMauro Carvalho Chehab } 7377fb65297SMauro Carvalho Chehab 7387fb65297SMauro Carvalho Chehab module_init (dsbr100_init); 7397fb65297SMauro Carvalho Chehab module_exit (dsbr100_exit); 7407fb65297SMauro Carvalho Chehab 7417fb65297SMauro Carvalho Chehab MODULE_AUTHOR( DRIVER_AUTHOR ); 7427fb65297SMauro Carvalho Chehab MODULE_DESCRIPTION( DRIVER_DESC ); 7437fb65297SMauro Carvalho Chehab MODULE_LICENSE("GPL"); 744