1fc55bcb0SAlexey Klimov /* A driver for the D-Link DSB-R100 USB radio and Gemtek USB Radio 21. 2fc55bcb0SAlexey Klimov The device plugs 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 static struct usb_device_id usb_dsbr100_device_table [] = { 1767fb65297SMauro Carvalho Chehab { USB_DEVICE(DSB100_VENDOR, DSB100_PRODUCT) }, 1777fb65297SMauro Carvalho Chehab { } /* Terminating entry */ 1787fb65297SMauro Carvalho Chehab }; 1797fb65297SMauro Carvalho Chehab 1807fb65297SMauro Carvalho Chehab MODULE_DEVICE_TABLE (usb, usb_dsbr100_device_table); 1817fb65297SMauro Carvalho Chehab 1827fb65297SMauro Carvalho Chehab /* USB subsystem interface */ 1837fb65297SMauro Carvalho Chehab static struct usb_driver usb_dsbr100_driver = { 1847fb65297SMauro Carvalho Chehab .name = "dsbr100", 1857fb65297SMauro Carvalho Chehab .probe = usb_dsbr100_probe, 1867fb65297SMauro Carvalho Chehab .disconnect = usb_dsbr100_disconnect, 1877fb65297SMauro Carvalho Chehab .id_table = usb_dsbr100_device_table, 18804e0ffbbSAlexey Klimov .suspend = usb_dsbr100_suspend, 18904e0ffbbSAlexey Klimov .resume = usb_dsbr100_resume, 19004e0ffbbSAlexey Klimov .reset_resume = usb_dsbr100_resume, 19104e0ffbbSAlexey Klimov .supports_autosuspend = 0, 1927fb65297SMauro Carvalho Chehab }; 1937fb65297SMauro Carvalho Chehab 1947fb65297SMauro Carvalho Chehab /* Low-level device interface begins here */ 1957fb65297SMauro Carvalho Chehab 1967fb65297SMauro Carvalho Chehab /* switch on radio */ 1975aff308cSAlan Cox static int dsbr100_start(struct dsbr100_device *radio) 1987fb65297SMauro Carvalho Chehab { 199417b7953SAlexey Klimov int retval; 200417b7953SAlexey Klimov int request; 201417b7953SAlexey Klimov 2023a0efc32SAlexey Klimov mutex_lock(&radio->lock); 203417b7953SAlexey Klimov 204417b7953SAlexey Klimov retval = usb_control_msg(radio->usbdev, 205417b7953SAlexey Klimov usb_rcvctrlpipe(radio->usbdev, 0), 2067fb65297SMauro Carvalho Chehab USB_REQ_GET_STATUS, 2077fb65297SMauro Carvalho Chehab USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 208417b7953SAlexey Klimov 0x00, 0xC7, radio->transfer_buffer, 8, 300); 209417b7953SAlexey Klimov 210417b7953SAlexey Klimov if (retval < 0) { 211417b7953SAlexey Klimov request = USB_REQ_GET_STATUS; 212417b7953SAlexey Klimov goto usb_control_msg_failed; 213417b7953SAlexey Klimov } 214417b7953SAlexey Klimov 215417b7953SAlexey Klimov retval = usb_control_msg(radio->usbdev, 216417b7953SAlexey Klimov usb_rcvctrlpipe(radio->usbdev, 0), 2177fb65297SMauro Carvalho Chehab DSB100_ONOFF, 2187fb65297SMauro Carvalho Chehab USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 219417b7953SAlexey Klimov 0x01, 0x00, radio->transfer_buffer, 8, 300); 220417b7953SAlexey Klimov 221417b7953SAlexey Klimov if (retval < 0) { 222417b7953SAlexey Klimov request = DSB100_ONOFF; 223417b7953SAlexey Klimov goto usb_control_msg_failed; 2243a0efc32SAlexey Klimov } 2253a0efc32SAlexey Klimov 2265aff308cSAlan Cox radio->muted = 0; 2273a0efc32SAlexey Klimov mutex_unlock(&radio->lock); 2287fb65297SMauro Carvalho Chehab return (radio->transfer_buffer)[0]; 229417b7953SAlexey Klimov 230417b7953SAlexey Klimov usb_control_msg_failed: 231417b7953SAlexey Klimov mutex_unlock(&radio->lock); 232417b7953SAlexey Klimov dev_err(&radio->usbdev->dev, 233417b7953SAlexey Klimov "%s - usb_control_msg returned %i, request %i\n", 234417b7953SAlexey Klimov __func__, retval, request); 235417b7953SAlexey Klimov return -1; 236417b7953SAlexey Klimov 2377fb65297SMauro Carvalho Chehab } 2387fb65297SMauro Carvalho Chehab 2397fb65297SMauro Carvalho Chehab /* switch off radio */ 2405aff308cSAlan Cox static int dsbr100_stop(struct dsbr100_device *radio) 2417fb65297SMauro Carvalho Chehab { 242417b7953SAlexey Klimov int retval; 243417b7953SAlexey Klimov int request; 244417b7953SAlexey Klimov 2453a0efc32SAlexey Klimov mutex_lock(&radio->lock); 246417b7953SAlexey Klimov 247417b7953SAlexey Klimov retval = usb_control_msg(radio->usbdev, 248417b7953SAlexey Klimov usb_rcvctrlpipe(radio->usbdev, 0), 2497fb65297SMauro Carvalho Chehab USB_REQ_GET_STATUS, 2507fb65297SMauro Carvalho Chehab USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 251417b7953SAlexey Klimov 0x16, 0x1C, radio->transfer_buffer, 8, 300); 252417b7953SAlexey Klimov 253417b7953SAlexey Klimov if (retval < 0) { 254417b7953SAlexey Klimov request = USB_REQ_GET_STATUS; 255417b7953SAlexey Klimov goto usb_control_msg_failed; 256417b7953SAlexey Klimov } 257417b7953SAlexey Klimov 258417b7953SAlexey Klimov retval = usb_control_msg(radio->usbdev, 259417b7953SAlexey Klimov usb_rcvctrlpipe(radio->usbdev, 0), 2607fb65297SMauro Carvalho Chehab DSB100_ONOFF, 2617fb65297SMauro Carvalho Chehab USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 262417b7953SAlexey Klimov 0x00, 0x00, radio->transfer_buffer, 8, 300); 263417b7953SAlexey Klimov 264417b7953SAlexey Klimov if (retval < 0) { 265417b7953SAlexey Klimov request = DSB100_ONOFF; 266417b7953SAlexey Klimov goto usb_control_msg_failed; 2673a0efc32SAlexey Klimov } 2683a0efc32SAlexey Klimov 2695aff308cSAlan Cox radio->muted = 1; 2703a0efc32SAlexey Klimov mutex_unlock(&radio->lock); 2717fb65297SMauro Carvalho Chehab return (radio->transfer_buffer)[0]; 272417b7953SAlexey Klimov 273417b7953SAlexey Klimov usb_control_msg_failed: 274417b7953SAlexey Klimov mutex_unlock(&radio->lock); 275417b7953SAlexey Klimov dev_err(&radio->usbdev->dev, 276417b7953SAlexey Klimov "%s - usb_control_msg returned %i, request %i\n", 277417b7953SAlexey Klimov __func__, retval, request); 278417b7953SAlexey Klimov return -1; 279417b7953SAlexey Klimov 2807fb65297SMauro Carvalho Chehab } 2817fb65297SMauro Carvalho Chehab 2827fb65297SMauro Carvalho Chehab /* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */ 2835aff308cSAlan Cox static int dsbr100_setfreq(struct dsbr100_device *radio, int freq) 2847fb65297SMauro Carvalho Chehab { 285417b7953SAlexey Klimov int retval; 286417b7953SAlexey Klimov int request; 287417b7953SAlexey Klimov 2887fb65297SMauro Carvalho Chehab freq = (freq / 16 * 80) / 1000 + 856; 2893a0efc32SAlexey Klimov mutex_lock(&radio->lock); 290417b7953SAlexey Klimov 291417b7953SAlexey Klimov retval = usb_control_msg(radio->usbdev, 292417b7953SAlexey Klimov usb_rcvctrlpipe(radio->usbdev, 0), 2937fb65297SMauro Carvalho Chehab DSB100_TUNE, 2947fb65297SMauro Carvalho Chehab USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 2957fb65297SMauro Carvalho Chehab (freq >> 8) & 0x00ff, freq & 0xff, 296417b7953SAlexey Klimov radio->transfer_buffer, 8, 300); 297417b7953SAlexey Klimov 298417b7953SAlexey Klimov if (retval < 0) { 299417b7953SAlexey Klimov request = DSB100_TUNE; 300417b7953SAlexey Klimov goto usb_control_msg_failed; 301417b7953SAlexey Klimov } 302417b7953SAlexey Klimov 303417b7953SAlexey Klimov retval = usb_control_msg(radio->usbdev, 304417b7953SAlexey Klimov usb_rcvctrlpipe(radio->usbdev, 0), 3057fb65297SMauro Carvalho Chehab USB_REQ_GET_STATUS, 3067fb65297SMauro Carvalho Chehab USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 307417b7953SAlexey Klimov 0x96, 0xB7, radio->transfer_buffer, 8, 300); 308417b7953SAlexey Klimov 309417b7953SAlexey Klimov if (retval < 0) { 310417b7953SAlexey Klimov request = USB_REQ_GET_STATUS; 311417b7953SAlexey Klimov goto usb_control_msg_failed; 312417b7953SAlexey Klimov } 313417b7953SAlexey Klimov 314417b7953SAlexey Klimov retval = usb_control_msg(radio->usbdev, 315417b7953SAlexey Klimov usb_rcvctrlpipe(radio->usbdev, 0), 3167fb65297SMauro Carvalho Chehab USB_REQ_GET_STATUS, 3177fb65297SMauro Carvalho Chehab USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 318417b7953SAlexey Klimov 0x00, 0x24, radio->transfer_buffer, 8, 300); 319417b7953SAlexey Klimov 320417b7953SAlexey Klimov if (retval < 0) { 321417b7953SAlexey Klimov request = USB_REQ_GET_STATUS; 322417b7953SAlexey Klimov goto usb_control_msg_failed; 3237fb65297SMauro Carvalho Chehab } 3243a0efc32SAlexey Klimov 3257fb65297SMauro Carvalho Chehab radio->stereo = !((radio->transfer_buffer)[0] & 0x01); 3263a0efc32SAlexey Klimov mutex_unlock(&radio->lock); 3277fb65297SMauro Carvalho Chehab return (radio->transfer_buffer)[0]; 328417b7953SAlexey Klimov 329417b7953SAlexey Klimov usb_control_msg_failed: 330417b7953SAlexey Klimov radio->stereo = -1; 331417b7953SAlexey Klimov mutex_unlock(&radio->lock); 332417b7953SAlexey Klimov dev_err(&radio->usbdev->dev, 333417b7953SAlexey Klimov "%s - usb_control_msg returned %i, request %i\n", 334417b7953SAlexey Klimov __func__, retval, request); 335417b7953SAlexey Klimov return -1; 3367fb65297SMauro Carvalho Chehab } 3377fb65297SMauro Carvalho Chehab 3387fb65297SMauro Carvalho Chehab /* return the device status. This is, in effect, just whether it 3397fb65297SMauro Carvalho Chehab sees a stereo signal or not. Pity. */ 3405aff308cSAlan Cox static void dsbr100_getstat(struct dsbr100_device *radio) 3417fb65297SMauro Carvalho Chehab { 342417b7953SAlexey Klimov int retval; 343417b7953SAlexey Klimov 3443a0efc32SAlexey Klimov mutex_lock(&radio->lock); 345417b7953SAlexey Klimov 346417b7953SAlexey Klimov retval = usb_control_msg(radio->usbdev, 347417b7953SAlexey Klimov usb_rcvctrlpipe(radio->usbdev, 0), 3487fb65297SMauro Carvalho Chehab USB_REQ_GET_STATUS, 3497fb65297SMauro Carvalho Chehab USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 350417b7953SAlexey Klimov 0x00 , 0x24, radio->transfer_buffer, 8, 300); 351417b7953SAlexey Klimov 352417b7953SAlexey Klimov if (retval < 0) { 3537fb65297SMauro Carvalho Chehab radio->stereo = -1; 354417b7953SAlexey Klimov dev_err(&radio->usbdev->dev, 355417b7953SAlexey Klimov "%s - usb_control_msg returned %i, request %i\n", 356417b7953SAlexey Klimov __func__, retval, USB_REQ_GET_STATUS); 357417b7953SAlexey Klimov } else { 3587fb65297SMauro Carvalho Chehab radio->stereo = !(radio->transfer_buffer[0] & 0x01); 359417b7953SAlexey Klimov } 360417b7953SAlexey Klimov 3613a0efc32SAlexey Klimov mutex_unlock(&radio->lock); 3627fb65297SMauro Carvalho Chehab } 3637fb65297SMauro Carvalho Chehab 3647fb65297SMauro Carvalho Chehab /* USB subsystem interface begins here */ 3657fb65297SMauro Carvalho Chehab 366fc55bcb0SAlexey Klimov /* 367fc55bcb0SAlexey Klimov * Handle unplugging of the device. 368fc55bcb0SAlexey Klimov * We call video_unregister_device in any case. 369fc55bcb0SAlexey Klimov * The last function called in this procedure is 370fc55bcb0SAlexey Klimov * usb_dsbr100_video_device_release 371fc55bcb0SAlexey Klimov */ 3727fb65297SMauro Carvalho Chehab static void usb_dsbr100_disconnect(struct usb_interface *intf) 3737fb65297SMauro Carvalho Chehab { 3745aff308cSAlan Cox struct dsbr100_device *radio = usb_get_intfdata(intf); 3757fb65297SMauro Carvalho Chehab 3767fb65297SMauro Carvalho Chehab usb_set_intfdata (intf, NULL); 3773a0efc32SAlexey Klimov 3783a0efc32SAlexey Klimov mutex_lock(&radio->lock); 3797fb65297SMauro Carvalho Chehab radio->removed = 1; 3803a0efc32SAlexey Klimov mutex_unlock(&radio->lock); 3813a0efc32SAlexey Klimov 3823a0efc32SAlexey Klimov video_unregister_device(&radio->videodev); 3837fb65297SMauro Carvalho Chehab } 3847fb65297SMauro Carvalho Chehab 3857fb65297SMauro Carvalho Chehab 3867002a4f3SDouglas Landgraf static int vidioc_querycap(struct file *file, void *priv, 3877002a4f3SDouglas Landgraf struct v4l2_capability *v) 3887fb65297SMauro Carvalho Chehab { 3895aff308cSAlan Cox strlcpy(v->driver, "dsbr100", sizeof(v->driver)); 3905aff308cSAlan Cox strlcpy(v->card, "D-Link R-100 USB FM Radio", sizeof(v->card)); 391448441cfSAlexey Klimov sprintf(v->bus_info, "USB"); 3925aff308cSAlan Cox v->version = RADIO_VERSION; 3935aff308cSAlan Cox v->capabilities = V4L2_CAP_TUNER; 3947fb65297SMauro Carvalho Chehab return 0; 3957fb65297SMauro Carvalho Chehab } 3967002a4f3SDouglas Landgraf 3977002a4f3SDouglas Landgraf static int vidioc_g_tuner(struct file *file, void *priv, 3987002a4f3SDouglas Landgraf struct v4l2_tuner *v) 3995aff308cSAlan Cox { 400c170ecf4SHans Verkuil struct dsbr100_device *radio = video_drvdata(file); 4015aff308cSAlan Cox 4023a0efc32SAlexey Klimov /* safety check */ 4033a0efc32SAlexey Klimov if (radio->removed) 4043a0efc32SAlexey Klimov return -EIO; 4053a0efc32SAlexey Klimov 4065aff308cSAlan Cox if (v->index > 0) 4075aff308cSAlan Cox return -EINVAL; 4087fb65297SMauro Carvalho Chehab 4097fb65297SMauro Carvalho Chehab dsbr100_getstat(radio); 4105aff308cSAlan Cox strcpy(v->name, "FM"); 4115aff308cSAlan Cox v->type = V4L2_TUNER_RADIO; 4127fb65297SMauro Carvalho Chehab v->rangelow = FREQ_MIN * FREQ_MUL; 4137fb65297SMauro Carvalho Chehab v->rangehigh = FREQ_MAX * FREQ_MUL; 4145aff308cSAlan Cox v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; 4155aff308cSAlan Cox v->capability = V4L2_TUNER_CAP_LOW; 4165aff308cSAlan Cox if(radio->stereo) 4175aff308cSAlan Cox v->audmode = V4L2_TUNER_MODE_STEREO; 4185aff308cSAlan Cox else 4195aff308cSAlan Cox v->audmode = V4L2_TUNER_MODE_MONO; 4207002a4f3SDouglas Landgraf v->signal = 0xffff; /* We can't get the signal strength */ 4217fb65297SMauro Carvalho Chehab return 0; 4227fb65297SMauro Carvalho Chehab } 4237fb65297SMauro Carvalho Chehab 4247002a4f3SDouglas Landgraf static int vidioc_s_tuner(struct file *file, void *priv, 4257002a4f3SDouglas Landgraf struct v4l2_tuner *v) 4267002a4f3SDouglas Landgraf { 4273a0efc32SAlexey Klimov struct dsbr100_device *radio = video_drvdata(file); 4283a0efc32SAlexey Klimov 4293a0efc32SAlexey Klimov /* safety check */ 4303a0efc32SAlexey Klimov if (radio->removed) 4313a0efc32SAlexey Klimov return -EIO; 4323a0efc32SAlexey Klimov 4335aff308cSAlan Cox if (v->index > 0) 4347fb65297SMauro Carvalho Chehab return -EINVAL; 4355aff308cSAlan Cox 4367fb65297SMauro Carvalho Chehab return 0; 4377fb65297SMauro Carvalho Chehab } 4387002a4f3SDouglas Landgraf 4397002a4f3SDouglas Landgraf static int vidioc_s_frequency(struct file *file, void *priv, 4407002a4f3SDouglas Landgraf struct v4l2_frequency *f) 4415aff308cSAlan Cox { 442c170ecf4SHans Verkuil struct dsbr100_device *radio = video_drvdata(file); 443417b7953SAlexey Klimov int retval; 4447fb65297SMauro Carvalho Chehab 4453a0efc32SAlexey Klimov /* safety check */ 4463a0efc32SAlexey Klimov if (radio->removed) 4473a0efc32SAlexey Klimov return -EIO; 4483a0efc32SAlexey Klimov 4495aff308cSAlan Cox radio->curfreq = f->frequency; 450417b7953SAlexey Klimov retval = dsbr100_setfreq(radio, radio->curfreq); 451417b7953SAlexey Klimov if (retval == -1) 452aa82661bSGreg Kroah-Hartman dev_warn(&radio->usbdev->dev, "Set frequency failed\n"); 4537fb65297SMauro Carvalho Chehab return 0; 4547fb65297SMauro Carvalho Chehab } 4557002a4f3SDouglas Landgraf 4567002a4f3SDouglas Landgraf static int vidioc_g_frequency(struct file *file, void *priv, 4577002a4f3SDouglas Landgraf struct v4l2_frequency *f) 4585aff308cSAlan Cox { 459c170ecf4SHans Verkuil struct dsbr100_device *radio = video_drvdata(file); 4607fb65297SMauro Carvalho Chehab 4613a0efc32SAlexey Klimov /* safety check */ 4623a0efc32SAlexey Klimov if (radio->removed) 4633a0efc32SAlexey Klimov return -EIO; 4643a0efc32SAlexey Klimov 4655aff308cSAlan Cox f->type = V4L2_TUNER_RADIO; 4665aff308cSAlan Cox f->frequency = radio->curfreq; 4677fb65297SMauro Carvalho Chehab return 0; 4687fb65297SMauro Carvalho Chehab } 4697002a4f3SDouglas Landgraf 4707002a4f3SDouglas Landgraf static int vidioc_queryctrl(struct file *file, void *priv, 4717002a4f3SDouglas Landgraf struct v4l2_queryctrl *qc) 4725aff308cSAlan Cox { 4735aff308cSAlan Cox int i; 4747fb65297SMauro Carvalho Chehab 4755aff308cSAlan Cox for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { 4765aff308cSAlan Cox if (qc->id && qc->id == radio_qctrl[i].id) { 477223377e7SAlexey Klimov memcpy(qc, &(radio_qctrl[i]), sizeof(*qc)); 4785aff308cSAlan Cox return 0; 4795aff308cSAlan Cox } 4805aff308cSAlan Cox } 4817fb65297SMauro Carvalho Chehab return -EINVAL; 4825aff308cSAlan Cox } 4837002a4f3SDouglas Landgraf 4847002a4f3SDouglas Landgraf static int vidioc_g_ctrl(struct file *file, void *priv, 4857002a4f3SDouglas Landgraf struct v4l2_control *ctrl) 4865aff308cSAlan Cox { 487c170ecf4SHans Verkuil struct dsbr100_device *radio = video_drvdata(file); 4885aff308cSAlan Cox 4893a0efc32SAlexey Klimov /* safety check */ 4903a0efc32SAlexey Klimov if (radio->removed) 4913a0efc32SAlexey Klimov return -EIO; 4923a0efc32SAlexey Klimov 4935aff308cSAlan Cox switch (ctrl->id) { 4945aff308cSAlan Cox case V4L2_CID_AUDIO_MUTE: 4955aff308cSAlan Cox ctrl->value = radio->muted; 4965aff308cSAlan Cox return 0; 4975aff308cSAlan Cox } 4985aff308cSAlan Cox return -EINVAL; 4995aff308cSAlan Cox } 5007002a4f3SDouglas Landgraf 5017002a4f3SDouglas Landgraf static int vidioc_s_ctrl(struct file *file, void *priv, 5027002a4f3SDouglas Landgraf struct v4l2_control *ctrl) 5035aff308cSAlan Cox { 504c170ecf4SHans Verkuil struct dsbr100_device *radio = video_drvdata(file); 505417b7953SAlexey Klimov int retval; 5065aff308cSAlan Cox 5073a0efc32SAlexey Klimov /* safety check */ 5083a0efc32SAlexey Klimov if (radio->removed) 5093a0efc32SAlexey Klimov return -EIO; 5103a0efc32SAlexey Klimov 5115aff308cSAlan Cox switch (ctrl->id) { 5125aff308cSAlan Cox case V4L2_CID_AUDIO_MUTE: 5135aff308cSAlan Cox if (ctrl->value) { 514417b7953SAlexey Klimov retval = dsbr100_stop(radio); 515417b7953SAlexey Klimov if (retval == -1) { 516aa82661bSGreg Kroah-Hartman dev_warn(&radio->usbdev->dev, 517aa82661bSGreg Kroah-Hartman "Radio did not respond properly\n"); 51890b698ddSAlexey Klimov return -EBUSY; 51990b698ddSAlexey Klimov } 5205aff308cSAlan Cox } else { 521417b7953SAlexey Klimov retval = dsbr100_start(radio); 522417b7953SAlexey Klimov if (retval == -1) { 523aa82661bSGreg Kroah-Hartman dev_warn(&radio->usbdev->dev, 524aa82661bSGreg Kroah-Hartman "Radio did not respond properly\n"); 52590b698ddSAlexey Klimov return -EBUSY; 52690b698ddSAlexey Klimov } 5275aff308cSAlan Cox } 5287fb65297SMauro Carvalho Chehab return 0; 5297fb65297SMauro Carvalho Chehab } 5305aff308cSAlan Cox return -EINVAL; 5315aff308cSAlan Cox } 5327002a4f3SDouglas Landgraf 5337002a4f3SDouglas Landgraf static int vidioc_g_audio(struct file *file, void *priv, 5347002a4f3SDouglas Landgraf struct v4l2_audio *a) 5357002a4f3SDouglas Landgraf { 5367002a4f3SDouglas Landgraf if (a->index > 1) 5377002a4f3SDouglas Landgraf return -EINVAL; 5387002a4f3SDouglas Landgraf 5397002a4f3SDouglas Landgraf strcpy(a->name, "Radio"); 5407002a4f3SDouglas Landgraf a->capability = V4L2_AUDCAP_STEREO; 5417002a4f3SDouglas Landgraf return 0; 5427fb65297SMauro Carvalho Chehab } 5437fb65297SMauro Carvalho Chehab 5447002a4f3SDouglas Landgraf static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) 5457fb65297SMauro Carvalho Chehab { 5467002a4f3SDouglas Landgraf *i = 0; 5477002a4f3SDouglas Landgraf return 0; 5487002a4f3SDouglas Landgraf } 5497002a4f3SDouglas Landgraf 5507002a4f3SDouglas Landgraf static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) 5517002a4f3SDouglas Landgraf { 5527002a4f3SDouglas Landgraf if (i != 0) 5537002a4f3SDouglas Landgraf return -EINVAL; 5547002a4f3SDouglas Landgraf return 0; 5557002a4f3SDouglas Landgraf } 5567002a4f3SDouglas Landgraf 5577002a4f3SDouglas Landgraf static int vidioc_s_audio(struct file *file, void *priv, 5587002a4f3SDouglas Landgraf struct v4l2_audio *a) 5597002a4f3SDouglas Landgraf { 5607002a4f3SDouglas Landgraf if (a->index != 0) 5617002a4f3SDouglas Landgraf return -EINVAL; 5627002a4f3SDouglas Landgraf return 0; 5637fb65297SMauro Carvalho Chehab } 5647fb65297SMauro Carvalho Chehab 5657fb65297SMauro Carvalho Chehab static int usb_dsbr100_open(struct inode *inode, struct file *file) 5667fb65297SMauro Carvalho Chehab { 567c170ecf4SHans Verkuil struct dsbr100_device *radio = video_drvdata(file); 568b9f35737SAlexey Klimov int retval; 5697fb65297SMauro Carvalho Chehab 570d56dc612SHans Verkuil lock_kernel(); 5717fb65297SMauro Carvalho Chehab radio->users = 1; 5725aff308cSAlan Cox radio->muted = 1; 5735aff308cSAlan Cox 574417b7953SAlexey Klimov retval = dsbr100_start(radio); 575417b7953SAlexey Klimov if (retval < 0) { 576aa82661bSGreg Kroah-Hartman dev_warn(&radio->usbdev->dev, 577aa82661bSGreg Kroah-Hartman "Radio did not start up properly\n"); 5787fb65297SMauro Carvalho Chehab radio->users = 0; 579d56dc612SHans Verkuil unlock_kernel(); 5807fb65297SMauro Carvalho Chehab return -EIO; 5817fb65297SMauro Carvalho Chehab } 582b9f35737SAlexey Klimov 583b9f35737SAlexey Klimov retval = dsbr100_setfreq(radio, radio->curfreq); 584b9f35737SAlexey Klimov if (retval == -1) 585290588e0SAlexey Klimov dev_warn(&radio->usbdev->dev, 586290588e0SAlexey Klimov "set frequency failed\n"); 587b9f35737SAlexey Klimov 588d56dc612SHans Verkuil unlock_kernel(); 5897fb65297SMauro Carvalho Chehab return 0; 5907fb65297SMauro Carvalho Chehab } 5917fb65297SMauro Carvalho Chehab 5927fb65297SMauro Carvalho Chehab static int usb_dsbr100_close(struct inode *inode, struct file *file) 5937fb65297SMauro Carvalho Chehab { 594c170ecf4SHans Verkuil struct dsbr100_device *radio = video_drvdata(file); 5953a0efc32SAlexey Klimov int retval; 5967fb65297SMauro Carvalho Chehab 5977fb65297SMauro Carvalho Chehab if (!radio) 5987fb65297SMauro Carvalho Chehab return -ENODEV; 5993a0efc32SAlexey Klimov 6007fb65297SMauro Carvalho Chehab radio->users = 0; 6013a0efc32SAlexey Klimov if (!radio->removed) { 6023a0efc32SAlexey Klimov retval = dsbr100_stop(radio); 6033a0efc32SAlexey Klimov if (retval == -1) { 6043a0efc32SAlexey Klimov dev_warn(&radio->usbdev->dev, 6053a0efc32SAlexey Klimov "dsbr100_stop failed\n"); 6063a0efc32SAlexey Klimov } 6073a0efc32SAlexey Klimov 6087fb65297SMauro Carvalho Chehab } 6097fb65297SMauro Carvalho Chehab return 0; 6107fb65297SMauro Carvalho Chehab } 6117fb65297SMauro Carvalho Chehab 61204e0ffbbSAlexey Klimov /* Suspend device - stop device. */ 61304e0ffbbSAlexey Klimov static int usb_dsbr100_suspend(struct usb_interface *intf, pm_message_t message) 61404e0ffbbSAlexey Klimov { 61504e0ffbbSAlexey Klimov struct dsbr100_device *radio = usb_get_intfdata(intf); 61604e0ffbbSAlexey Klimov int retval; 61704e0ffbbSAlexey Klimov 61804e0ffbbSAlexey Klimov retval = dsbr100_stop(radio); 61904e0ffbbSAlexey Klimov if (retval == -1) 62004e0ffbbSAlexey Klimov dev_warn(&intf->dev, "dsbr100_stop failed\n"); 62104e0ffbbSAlexey Klimov 62204e0ffbbSAlexey Klimov dev_info(&intf->dev, "going into suspend..\n"); 62304e0ffbbSAlexey Klimov 62404e0ffbbSAlexey Klimov return 0; 62504e0ffbbSAlexey Klimov } 62604e0ffbbSAlexey Klimov 62704e0ffbbSAlexey Klimov /* Resume device - start device. */ 62804e0ffbbSAlexey Klimov static int usb_dsbr100_resume(struct usb_interface *intf) 62904e0ffbbSAlexey Klimov { 63004e0ffbbSAlexey Klimov struct dsbr100_device *radio = usb_get_intfdata(intf); 63104e0ffbbSAlexey Klimov int retval; 63204e0ffbbSAlexey Klimov 63304e0ffbbSAlexey Klimov retval = dsbr100_start(radio); 63404e0ffbbSAlexey Klimov if (retval == -1) 63504e0ffbbSAlexey Klimov dev_warn(&intf->dev, "dsbr100_start failed\n"); 63604e0ffbbSAlexey Klimov 63704e0ffbbSAlexey Klimov dev_info(&intf->dev, "coming out of suspend..\n"); 63804e0ffbbSAlexey Klimov 63904e0ffbbSAlexey Klimov return 0; 64004e0ffbbSAlexey Klimov } 64104e0ffbbSAlexey Klimov 642fc55bcb0SAlexey Klimov /* free data structures */ 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 686fc55bcb0SAlexey Klimov /* check if the device is present and register with v4l and usb if it is */ 6877002a4f3SDouglas Landgraf static int usb_dsbr100_probe(struct usb_interface *intf, 6887002a4f3SDouglas Landgraf const struct usb_device_id *id) 6897002a4f3SDouglas Landgraf { 6907002a4f3SDouglas Landgraf struct dsbr100_device *radio; 691417b7953SAlexey Klimov int retval; 6927002a4f3SDouglas Landgraf 693223377e7SAlexey Klimov radio = kmalloc(sizeof(struct dsbr100_device), GFP_KERNEL); 694223377e7SAlexey Klimov 695223377e7SAlexey Klimov if (!radio) 6967002a4f3SDouglas Landgraf return -ENOMEM; 697223377e7SAlexey Klimov 698223377e7SAlexey Klimov radio->transfer_buffer = kmalloc(TB_LEN, GFP_KERNEL); 699223377e7SAlexey Klimov 700223377e7SAlexey Klimov if (!(radio->transfer_buffer)) { 701863c86ddSOliver Neukum kfree(radio); 702863c86ddSOliver Neukum return -ENOMEM; 703863c86ddSOliver Neukum } 704223377e7SAlexey Klimov 7053a0efc32SAlexey Klimov mutex_init(&radio->lock); 7063a0efc32SAlexey Klimov radio->videodev = dsbr100_videodev_data; 7073a0efc32SAlexey Klimov 7087002a4f3SDouglas Landgraf radio->removed = 0; 7097002a4f3SDouglas Landgraf radio->users = 0; 7107002a4f3SDouglas Landgraf radio->usbdev = interface_to_usbdev(intf); 7117002a4f3SDouglas Landgraf radio->curfreq = FREQ_MIN * FREQ_MUL; 7123a0efc32SAlexey Klimov video_set_drvdata(&radio->videodev, radio); 713417b7953SAlexey Klimov retval = video_register_device(&radio->videodev, VFL_TYPE_RADIO, radio_nr); 714417b7953SAlexey Klimov if (retval < 0) { 7157e1ca849SAlexey Klimov dev_err(&intf->dev, "couldn't register video device\n"); 716863c86ddSOliver Neukum kfree(radio->transfer_buffer); 7177002a4f3SDouglas Landgraf kfree(radio); 7187002a4f3SDouglas Landgraf return -EIO; 7197002a4f3SDouglas Landgraf } 7207002a4f3SDouglas Landgraf usb_set_intfdata(intf, radio); 7217002a4f3SDouglas Landgraf return 0; 7227002a4f3SDouglas Landgraf } 7237002a4f3SDouglas Landgraf 7247fb65297SMauro Carvalho Chehab static int __init dsbr100_init(void) 7257fb65297SMauro Carvalho Chehab { 7267fb65297SMauro Carvalho Chehab int retval = usb_register(&usb_dsbr100_driver); 727a482f327SGreg Kroah-Hartman printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" 728a482f327SGreg Kroah-Hartman DRIVER_DESC "\n"); 7297fb65297SMauro Carvalho Chehab return retval; 7307fb65297SMauro Carvalho Chehab } 7317fb65297SMauro Carvalho Chehab 7327fb65297SMauro Carvalho Chehab static void __exit dsbr100_exit(void) 7337fb65297SMauro Carvalho Chehab { 7347fb65297SMauro Carvalho Chehab usb_deregister(&usb_dsbr100_driver); 7357fb65297SMauro Carvalho Chehab } 7367fb65297SMauro Carvalho Chehab 7377fb65297SMauro Carvalho Chehab module_init (dsbr100_init); 7387fb65297SMauro Carvalho Chehab module_exit (dsbr100_exit); 7397fb65297SMauro Carvalho Chehab 7407fb65297SMauro Carvalho Chehab MODULE_AUTHOR( DRIVER_AUTHOR ); 7417fb65297SMauro Carvalho Chehab MODULE_DESCRIPTION( DRIVER_DESC ); 7427fb65297SMauro Carvalho Chehab MODULE_LICENSE("GPL"); 743