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 1487fb65297SMauro Carvalho Chehab 1497fb65297SMauro Carvalho Chehab static int usb_dsbr100_probe(struct usb_interface *intf, 1507fb65297SMauro Carvalho Chehab const struct usb_device_id *id); 1517fb65297SMauro Carvalho Chehab static void usb_dsbr100_disconnect(struct usb_interface *intf); 1527fb65297SMauro Carvalho Chehab static int usb_dsbr100_open(struct inode *inode, struct file *file); 1537fb65297SMauro Carvalho Chehab static int usb_dsbr100_close(struct inode *inode, struct file *file); 15404e0ffbbSAlexey Klimov static int usb_dsbr100_suspend(struct usb_interface *intf, 15504e0ffbbSAlexey Klimov pm_message_t message); 15604e0ffbbSAlexey Klimov static int usb_dsbr100_resume(struct usb_interface *intf); 1577fb65297SMauro Carvalho Chehab 1587fb65297SMauro Carvalho Chehab static int radio_nr = -1; 1597fb65297SMauro Carvalho Chehab module_param(radio_nr, int, 0); 1607fb65297SMauro Carvalho Chehab 1617fb65297SMauro Carvalho Chehab /* Data for one (physical) device */ 1625aff308cSAlan Cox struct dsbr100_device { 1637fb65297SMauro Carvalho Chehab struct usb_device *usbdev; 1647fb65297SMauro Carvalho Chehab struct video_device *videodev; 165863c86ddSOliver Neukum u8 *transfer_buffer; 1667fb65297SMauro Carvalho Chehab int curfreq; 1677fb65297SMauro Carvalho Chehab int stereo; 1687fb65297SMauro Carvalho Chehab int users; 1697fb65297SMauro Carvalho Chehab int removed; 1705aff308cSAlan Cox int muted; 1715aff308cSAlan Cox }; 1727fb65297SMauro Carvalho Chehab 1737fb65297SMauro Carvalho Chehab 1747fb65297SMauro Carvalho Chehab static struct usb_device_id usb_dsbr100_device_table [] = { 1757fb65297SMauro Carvalho Chehab { USB_DEVICE(DSB100_VENDOR, DSB100_PRODUCT) }, 1767fb65297SMauro Carvalho Chehab { } /* Terminating entry */ 1777fb65297SMauro Carvalho Chehab }; 1787fb65297SMauro Carvalho Chehab 1797fb65297SMauro Carvalho Chehab MODULE_DEVICE_TABLE (usb, usb_dsbr100_device_table); 1807fb65297SMauro Carvalho Chehab 1817fb65297SMauro Carvalho Chehab /* USB subsystem interface */ 1827fb65297SMauro Carvalho Chehab static struct usb_driver usb_dsbr100_driver = { 1837fb65297SMauro Carvalho Chehab .name = "dsbr100", 1847fb65297SMauro Carvalho Chehab .probe = usb_dsbr100_probe, 1857fb65297SMauro Carvalho Chehab .disconnect = usb_dsbr100_disconnect, 1867fb65297SMauro Carvalho Chehab .id_table = usb_dsbr100_device_table, 18704e0ffbbSAlexey Klimov .suspend = usb_dsbr100_suspend, 18804e0ffbbSAlexey Klimov .resume = usb_dsbr100_resume, 18904e0ffbbSAlexey Klimov .reset_resume = usb_dsbr100_resume, 19004e0ffbbSAlexey Klimov .supports_autosuspend = 0, 1917fb65297SMauro Carvalho Chehab }; 1927fb65297SMauro Carvalho Chehab 1937fb65297SMauro Carvalho Chehab /* Low-level device interface begins here */ 1947fb65297SMauro Carvalho Chehab 1957fb65297SMauro Carvalho Chehab /* switch on radio */ 1965aff308cSAlan Cox static int dsbr100_start(struct dsbr100_device *radio) 1977fb65297SMauro Carvalho Chehab { 1987fb65297SMauro Carvalho Chehab if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), 1997fb65297SMauro Carvalho Chehab USB_REQ_GET_STATUS, 2007fb65297SMauro Carvalho Chehab USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 2017fb65297SMauro Carvalho Chehab 0x00, 0xC7, radio->transfer_buffer, 8, 300) < 0 || 2027fb65297SMauro Carvalho Chehab usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), 2037fb65297SMauro Carvalho Chehab DSB100_ONOFF, 2047fb65297SMauro Carvalho Chehab USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 2057fb65297SMauro Carvalho Chehab 0x01, 0x00, radio->transfer_buffer, 8, 300) < 0) 2067fb65297SMauro Carvalho Chehab return -1; 2075aff308cSAlan Cox radio->muted=0; 2087fb65297SMauro Carvalho Chehab return (radio->transfer_buffer)[0]; 2097fb65297SMauro Carvalho Chehab } 2107fb65297SMauro Carvalho Chehab 2117fb65297SMauro Carvalho Chehab 2127fb65297SMauro Carvalho Chehab /* switch off radio */ 2135aff308cSAlan Cox static int dsbr100_stop(struct dsbr100_device *radio) 2147fb65297SMauro Carvalho Chehab { 2157fb65297SMauro Carvalho Chehab if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), 2167fb65297SMauro Carvalho Chehab USB_REQ_GET_STATUS, 2177fb65297SMauro Carvalho Chehab USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 2187fb65297SMauro Carvalho Chehab 0x16, 0x1C, radio->transfer_buffer, 8, 300) < 0 || 2197fb65297SMauro Carvalho Chehab usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), 2207fb65297SMauro Carvalho Chehab DSB100_ONOFF, 2217fb65297SMauro Carvalho Chehab USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 2227fb65297SMauro Carvalho Chehab 0x00, 0x00, radio->transfer_buffer, 8, 300) < 0) 2237fb65297SMauro Carvalho Chehab return -1; 2245aff308cSAlan Cox radio->muted=1; 2257fb65297SMauro Carvalho Chehab return (radio->transfer_buffer)[0]; 2267fb65297SMauro Carvalho Chehab } 2277fb65297SMauro Carvalho Chehab 2287fb65297SMauro Carvalho Chehab /* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */ 2295aff308cSAlan Cox static int dsbr100_setfreq(struct dsbr100_device *radio, int freq) 2307fb65297SMauro Carvalho Chehab { 2317fb65297SMauro Carvalho Chehab freq = (freq / 16 * 80) / 1000 + 856; 2327fb65297SMauro Carvalho Chehab if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), 2337fb65297SMauro Carvalho Chehab DSB100_TUNE, 2347fb65297SMauro Carvalho Chehab USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 2357fb65297SMauro Carvalho Chehab (freq >> 8) & 0x00ff, freq & 0xff, 2367fb65297SMauro Carvalho Chehab radio->transfer_buffer, 8, 300) < 0 || 2377fb65297SMauro Carvalho Chehab usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), 2387fb65297SMauro Carvalho Chehab USB_REQ_GET_STATUS, 2397fb65297SMauro Carvalho Chehab USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 2407fb65297SMauro Carvalho Chehab 0x96, 0xB7, radio->transfer_buffer, 8, 300) < 0 || 2417fb65297SMauro Carvalho Chehab usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), 2427fb65297SMauro Carvalho Chehab USB_REQ_GET_STATUS, 2437fb65297SMauro Carvalho Chehab USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 2447fb65297SMauro Carvalho Chehab 0x00, 0x24, radio->transfer_buffer, 8, 300) < 0) { 2457fb65297SMauro Carvalho Chehab radio->stereo = -1; 2467fb65297SMauro Carvalho Chehab return -1; 2477fb65297SMauro Carvalho Chehab } 2487fb65297SMauro Carvalho Chehab radio->stereo = !((radio->transfer_buffer)[0] & 0x01); 2497fb65297SMauro Carvalho Chehab return (radio->transfer_buffer)[0]; 2507fb65297SMauro Carvalho Chehab } 2517fb65297SMauro Carvalho Chehab 2527fb65297SMauro Carvalho Chehab /* return the device status. This is, in effect, just whether it 2537fb65297SMauro Carvalho Chehab sees a stereo signal or not. Pity. */ 2545aff308cSAlan Cox static void dsbr100_getstat(struct dsbr100_device *radio) 2557fb65297SMauro Carvalho Chehab { 2567fb65297SMauro Carvalho Chehab if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), 2577fb65297SMauro Carvalho Chehab USB_REQ_GET_STATUS, 2587fb65297SMauro Carvalho Chehab USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 2597fb65297SMauro Carvalho Chehab 0x00 , 0x24, radio->transfer_buffer, 8, 300) < 0) 2607fb65297SMauro Carvalho Chehab radio->stereo = -1; 2617fb65297SMauro Carvalho Chehab else 2627fb65297SMauro Carvalho Chehab radio->stereo = !(radio->transfer_buffer[0] & 0x01); 2637fb65297SMauro Carvalho Chehab } 2647fb65297SMauro Carvalho Chehab 2657fb65297SMauro Carvalho Chehab 2667fb65297SMauro Carvalho Chehab /* USB subsystem interface begins here */ 2677fb65297SMauro Carvalho Chehab 2687fb65297SMauro Carvalho Chehab /* handle unplugging of the device, release data structures 2697fb65297SMauro Carvalho Chehab if nothing keeps us from doing it. If something is still 2707fb65297SMauro Carvalho Chehab keeping us busy, the release callback of v4l will take care 271863c86ddSOliver Neukum of releasing it. */ 2727fb65297SMauro Carvalho Chehab static void usb_dsbr100_disconnect(struct usb_interface *intf) 2737fb65297SMauro Carvalho Chehab { 2745aff308cSAlan Cox struct dsbr100_device *radio = usb_get_intfdata(intf); 2757fb65297SMauro Carvalho Chehab 2767fb65297SMauro Carvalho Chehab usb_set_intfdata (intf, NULL); 2777fb65297SMauro Carvalho Chehab if (radio) { 2787fb65297SMauro Carvalho Chehab video_unregister_device(radio->videodev); 2797fb65297SMauro Carvalho Chehab radio->videodev = NULL; 2807fb65297SMauro Carvalho Chehab if (radio->users) { 281863c86ddSOliver Neukum kfree(radio->transfer_buffer); 2827fb65297SMauro Carvalho Chehab kfree(radio); 2837fb65297SMauro Carvalho Chehab } else { 2847fb65297SMauro Carvalho Chehab radio->removed = 1; 2857fb65297SMauro Carvalho Chehab } 2867fb65297SMauro Carvalho Chehab } 2877fb65297SMauro Carvalho Chehab } 2887fb65297SMauro Carvalho Chehab 2897fb65297SMauro Carvalho Chehab 2907002a4f3SDouglas Landgraf static int vidioc_querycap(struct file *file, void *priv, 2917002a4f3SDouglas Landgraf struct v4l2_capability *v) 2927fb65297SMauro Carvalho Chehab { 2935aff308cSAlan Cox strlcpy(v->driver, "dsbr100", sizeof(v->driver)); 2945aff308cSAlan Cox strlcpy(v->card, "D-Link R-100 USB FM Radio", sizeof(v->card)); 295448441cfSAlexey Klimov sprintf(v->bus_info, "USB"); 2965aff308cSAlan Cox v->version = RADIO_VERSION; 2975aff308cSAlan Cox v->capabilities = V4L2_CAP_TUNER; 2987fb65297SMauro Carvalho Chehab return 0; 2997fb65297SMauro Carvalho Chehab } 3007002a4f3SDouglas Landgraf 3017002a4f3SDouglas Landgraf static int vidioc_g_tuner(struct file *file, void *priv, 3027002a4f3SDouglas Landgraf struct v4l2_tuner *v) 3035aff308cSAlan Cox { 304c170ecf4SHans Verkuil struct dsbr100_device *radio = video_drvdata(file); 3055aff308cSAlan Cox 3065aff308cSAlan Cox if (v->index > 0) 3075aff308cSAlan Cox return -EINVAL; 3087fb65297SMauro Carvalho Chehab 3097fb65297SMauro Carvalho Chehab dsbr100_getstat(radio); 3105aff308cSAlan Cox strcpy(v->name, "FM"); 3115aff308cSAlan Cox v->type = V4L2_TUNER_RADIO; 3127fb65297SMauro Carvalho Chehab v->rangelow = FREQ_MIN * FREQ_MUL; 3137fb65297SMauro Carvalho Chehab v->rangehigh = FREQ_MAX * FREQ_MUL; 3145aff308cSAlan Cox v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; 3155aff308cSAlan Cox v->capability = V4L2_TUNER_CAP_LOW; 3165aff308cSAlan Cox if(radio->stereo) 3175aff308cSAlan Cox v->audmode = V4L2_TUNER_MODE_STEREO; 3185aff308cSAlan Cox else 3195aff308cSAlan Cox v->audmode = V4L2_TUNER_MODE_MONO; 3207002a4f3SDouglas Landgraf v->signal = 0xffff; /* We can't get the signal strength */ 3217fb65297SMauro Carvalho Chehab return 0; 3227fb65297SMauro Carvalho Chehab } 3237fb65297SMauro Carvalho Chehab 3247002a4f3SDouglas Landgraf static int vidioc_s_tuner(struct file *file, void *priv, 3257002a4f3SDouglas Landgraf struct v4l2_tuner *v) 3267002a4f3SDouglas Landgraf { 3275aff308cSAlan Cox if (v->index > 0) 3287fb65297SMauro Carvalho Chehab return -EINVAL; 3295aff308cSAlan Cox 3307fb65297SMauro Carvalho Chehab return 0; 3317fb65297SMauro Carvalho Chehab } 3327002a4f3SDouglas Landgraf 3337002a4f3SDouglas Landgraf static int vidioc_s_frequency(struct file *file, void *priv, 3347002a4f3SDouglas Landgraf struct v4l2_frequency *f) 3355aff308cSAlan Cox { 336c170ecf4SHans Verkuil struct dsbr100_device *radio = video_drvdata(file); 3377fb65297SMauro Carvalho Chehab 3385aff308cSAlan Cox radio->curfreq = f->frequency; 3397fb65297SMauro Carvalho Chehab if (dsbr100_setfreq(radio, radio->curfreq) == -1) 340aa82661bSGreg Kroah-Hartman dev_warn(&radio->usbdev->dev, "Set frequency failed\n"); 3417fb65297SMauro Carvalho Chehab return 0; 3427fb65297SMauro Carvalho Chehab } 3437002a4f3SDouglas Landgraf 3447002a4f3SDouglas Landgraf static int vidioc_g_frequency(struct file *file, void *priv, 3457002a4f3SDouglas Landgraf struct v4l2_frequency *f) 3465aff308cSAlan Cox { 347c170ecf4SHans Verkuil struct dsbr100_device *radio = video_drvdata(file); 3487fb65297SMauro Carvalho Chehab 3495aff308cSAlan Cox f->type = V4L2_TUNER_RADIO; 3505aff308cSAlan Cox f->frequency = radio->curfreq; 3517fb65297SMauro Carvalho Chehab return 0; 3527fb65297SMauro Carvalho Chehab } 3537002a4f3SDouglas Landgraf 3547002a4f3SDouglas Landgraf static int vidioc_queryctrl(struct file *file, void *priv, 3557002a4f3SDouglas Landgraf struct v4l2_queryctrl *qc) 3565aff308cSAlan Cox { 3575aff308cSAlan Cox int i; 3587fb65297SMauro Carvalho Chehab 3595aff308cSAlan Cox for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { 3605aff308cSAlan Cox if (qc->id && qc->id == radio_qctrl[i].id) { 361223377e7SAlexey Klimov memcpy(qc, &(radio_qctrl[i]), sizeof(*qc)); 3625aff308cSAlan Cox return 0; 3635aff308cSAlan Cox } 3645aff308cSAlan Cox } 3657fb65297SMauro Carvalho Chehab return -EINVAL; 3665aff308cSAlan Cox } 3677002a4f3SDouglas Landgraf 3687002a4f3SDouglas Landgraf static int vidioc_g_ctrl(struct file *file, void *priv, 3697002a4f3SDouglas Landgraf struct v4l2_control *ctrl) 3705aff308cSAlan Cox { 371c170ecf4SHans Verkuil struct dsbr100_device *radio = video_drvdata(file); 3725aff308cSAlan Cox 3735aff308cSAlan Cox switch (ctrl->id) { 3745aff308cSAlan Cox case V4L2_CID_AUDIO_MUTE: 3755aff308cSAlan Cox ctrl->value = radio->muted; 3765aff308cSAlan Cox return 0; 3775aff308cSAlan Cox } 3785aff308cSAlan Cox return -EINVAL; 3795aff308cSAlan Cox } 3807002a4f3SDouglas Landgraf 3817002a4f3SDouglas Landgraf static int vidioc_s_ctrl(struct file *file, void *priv, 3827002a4f3SDouglas Landgraf struct v4l2_control *ctrl) 3835aff308cSAlan Cox { 384c170ecf4SHans Verkuil struct dsbr100_device *radio = video_drvdata(file); 3855aff308cSAlan Cox 3865aff308cSAlan Cox switch (ctrl->id) { 3875aff308cSAlan Cox case V4L2_CID_AUDIO_MUTE: 3885aff308cSAlan Cox if (ctrl->value) { 38990b698ddSAlexey Klimov if (dsbr100_stop(radio) == -1) { 390aa82661bSGreg Kroah-Hartman dev_warn(&radio->usbdev->dev, 391aa82661bSGreg Kroah-Hartman "Radio did not respond properly\n"); 39290b698ddSAlexey Klimov return -EBUSY; 39390b698ddSAlexey Klimov } 3945aff308cSAlan Cox } else { 39590b698ddSAlexey Klimov if (dsbr100_start(radio) == -1) { 396aa82661bSGreg Kroah-Hartman dev_warn(&radio->usbdev->dev, 397aa82661bSGreg Kroah-Hartman "Radio did not respond properly\n"); 39890b698ddSAlexey Klimov return -EBUSY; 39990b698ddSAlexey Klimov } 4005aff308cSAlan Cox } 4017fb65297SMauro Carvalho Chehab return 0; 4027fb65297SMauro Carvalho Chehab } 4035aff308cSAlan Cox return -EINVAL; 4045aff308cSAlan Cox } 4057002a4f3SDouglas Landgraf 4067002a4f3SDouglas Landgraf static int vidioc_g_audio(struct file *file, void *priv, 4077002a4f3SDouglas Landgraf struct v4l2_audio *a) 4087002a4f3SDouglas Landgraf { 4097002a4f3SDouglas Landgraf if (a->index > 1) 4107002a4f3SDouglas Landgraf return -EINVAL; 4117002a4f3SDouglas Landgraf 4127002a4f3SDouglas Landgraf strcpy(a->name, "Radio"); 4137002a4f3SDouglas Landgraf a->capability = V4L2_AUDCAP_STEREO; 4147002a4f3SDouglas Landgraf return 0; 4157fb65297SMauro Carvalho Chehab } 4167fb65297SMauro Carvalho Chehab 4177002a4f3SDouglas Landgraf static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) 4187fb65297SMauro Carvalho Chehab { 4197002a4f3SDouglas Landgraf *i = 0; 4207002a4f3SDouglas Landgraf return 0; 4217002a4f3SDouglas Landgraf } 4227002a4f3SDouglas Landgraf 4237002a4f3SDouglas Landgraf static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) 4247002a4f3SDouglas Landgraf { 4257002a4f3SDouglas Landgraf if (i != 0) 4267002a4f3SDouglas Landgraf return -EINVAL; 4277002a4f3SDouglas Landgraf return 0; 4287002a4f3SDouglas Landgraf } 4297002a4f3SDouglas Landgraf 4307002a4f3SDouglas Landgraf static int vidioc_s_audio(struct file *file, void *priv, 4317002a4f3SDouglas Landgraf struct v4l2_audio *a) 4327002a4f3SDouglas Landgraf { 4337002a4f3SDouglas Landgraf if (a->index != 0) 4347002a4f3SDouglas Landgraf return -EINVAL; 4357002a4f3SDouglas Landgraf return 0; 4367fb65297SMauro Carvalho Chehab } 4377fb65297SMauro Carvalho Chehab 4387fb65297SMauro Carvalho Chehab static int usb_dsbr100_open(struct inode *inode, struct file *file) 4397fb65297SMauro Carvalho Chehab { 440c170ecf4SHans Verkuil struct dsbr100_device *radio = video_drvdata(file); 441b9f35737SAlexey Klimov int retval; 4427fb65297SMauro Carvalho Chehab 443d56dc612SHans Verkuil lock_kernel(); 4447fb65297SMauro Carvalho Chehab radio->users = 1; 4455aff308cSAlan Cox radio->muted = 1; 4465aff308cSAlan Cox 4477fb65297SMauro Carvalho Chehab if (dsbr100_start(radio) < 0) { 448aa82661bSGreg Kroah-Hartman dev_warn(&radio->usbdev->dev, 449aa82661bSGreg Kroah-Hartman "Radio did not start up properly\n"); 4507fb65297SMauro Carvalho Chehab radio->users = 0; 451d56dc612SHans Verkuil unlock_kernel(); 4527fb65297SMauro Carvalho Chehab return -EIO; 4537fb65297SMauro Carvalho Chehab } 454b9f35737SAlexey Klimov 455b9f35737SAlexey Klimov retval = dsbr100_setfreq(radio, radio->curfreq); 456b9f35737SAlexey Klimov 457b9f35737SAlexey Klimov if (retval == -1) 458b9f35737SAlexey Klimov printk(KERN_WARNING KBUILD_MODNAME ": Set frequency failed\n"); 459b9f35737SAlexey Klimov 460d56dc612SHans Verkuil unlock_kernel(); 4617fb65297SMauro Carvalho Chehab return 0; 4627fb65297SMauro Carvalho Chehab } 4637fb65297SMauro Carvalho Chehab 4647fb65297SMauro Carvalho Chehab static int usb_dsbr100_close(struct inode *inode, struct file *file) 4657fb65297SMauro Carvalho Chehab { 466c170ecf4SHans Verkuil struct dsbr100_device *radio = video_drvdata(file); 4677fb65297SMauro Carvalho Chehab 4687fb65297SMauro Carvalho Chehab if (!radio) 4697fb65297SMauro Carvalho Chehab return -ENODEV; 4707fb65297SMauro Carvalho Chehab radio->users = 0; 4717fb65297SMauro Carvalho Chehab if (radio->removed) { 472863c86ddSOliver Neukum kfree(radio->transfer_buffer); 4737fb65297SMauro Carvalho Chehab kfree(radio); 4747fb65297SMauro Carvalho Chehab } 4757fb65297SMauro Carvalho Chehab return 0; 4767fb65297SMauro Carvalho Chehab } 4777fb65297SMauro Carvalho Chehab 47804e0ffbbSAlexey Klimov /* Suspend device - stop device. */ 47904e0ffbbSAlexey Klimov static int usb_dsbr100_suspend(struct usb_interface *intf, pm_message_t message) 48004e0ffbbSAlexey Klimov { 48104e0ffbbSAlexey Klimov struct dsbr100_device *radio = usb_get_intfdata(intf); 48204e0ffbbSAlexey Klimov int retval; 48304e0ffbbSAlexey Klimov 48404e0ffbbSAlexey Klimov retval = dsbr100_stop(radio); 48504e0ffbbSAlexey Klimov if (retval == -1) 48604e0ffbbSAlexey Klimov dev_warn(&intf->dev, "dsbr100_stop failed\n"); 48704e0ffbbSAlexey Klimov 48804e0ffbbSAlexey Klimov dev_info(&intf->dev, "going into suspend..\n"); 48904e0ffbbSAlexey Klimov 49004e0ffbbSAlexey Klimov return 0; 49104e0ffbbSAlexey Klimov } 49204e0ffbbSAlexey Klimov 49304e0ffbbSAlexey Klimov /* Resume device - start device. */ 49404e0ffbbSAlexey Klimov static int usb_dsbr100_resume(struct usb_interface *intf) 49504e0ffbbSAlexey Klimov { 49604e0ffbbSAlexey Klimov struct dsbr100_device *radio = usb_get_intfdata(intf); 49704e0ffbbSAlexey Klimov int retval; 49804e0ffbbSAlexey Klimov 49904e0ffbbSAlexey Klimov retval = dsbr100_start(radio); 50004e0ffbbSAlexey Klimov if (retval == -1) 50104e0ffbbSAlexey Klimov dev_warn(&intf->dev, "dsbr100_start failed\n"); 50204e0ffbbSAlexey Klimov 50304e0ffbbSAlexey Klimov dev_info(&intf->dev, "coming out of suspend..\n"); 50404e0ffbbSAlexey Klimov 50504e0ffbbSAlexey Klimov return 0; 50604e0ffbbSAlexey Klimov } 50704e0ffbbSAlexey Klimov 5087002a4f3SDouglas Landgraf /* File system interface */ 5097002a4f3SDouglas Landgraf static const struct file_operations usb_dsbr100_fops = { 5107002a4f3SDouglas Landgraf .owner = THIS_MODULE, 5117002a4f3SDouglas Landgraf .open = usb_dsbr100_open, 5127002a4f3SDouglas Landgraf .release = usb_dsbr100_close, 5137002a4f3SDouglas Landgraf .ioctl = video_ioctl2, 514078ff795SDouglas Schilling Landgraf #ifdef CONFIG_COMPAT 5157002a4f3SDouglas Landgraf .compat_ioctl = v4l_compat_ioctl32, 516078ff795SDouglas Schilling Landgraf #endif 5177002a4f3SDouglas Landgraf .llseek = no_llseek, 5187002a4f3SDouglas Landgraf }; 5197002a4f3SDouglas Landgraf 520a399810cSHans Verkuil static const struct v4l2_ioctl_ops usb_dsbr100_ioctl_ops = { 5217002a4f3SDouglas Landgraf .vidioc_querycap = vidioc_querycap, 5227002a4f3SDouglas Landgraf .vidioc_g_tuner = vidioc_g_tuner, 5237002a4f3SDouglas Landgraf .vidioc_s_tuner = vidioc_s_tuner, 5247002a4f3SDouglas Landgraf .vidioc_g_frequency = vidioc_g_frequency, 5257002a4f3SDouglas Landgraf .vidioc_s_frequency = vidioc_s_frequency, 5267002a4f3SDouglas Landgraf .vidioc_queryctrl = vidioc_queryctrl, 5277002a4f3SDouglas Landgraf .vidioc_g_ctrl = vidioc_g_ctrl, 5287002a4f3SDouglas Landgraf .vidioc_s_ctrl = vidioc_s_ctrl, 5297002a4f3SDouglas Landgraf .vidioc_g_audio = vidioc_g_audio, 5307002a4f3SDouglas Landgraf .vidioc_s_audio = vidioc_s_audio, 5317002a4f3SDouglas Landgraf .vidioc_g_input = vidioc_g_input, 5327002a4f3SDouglas Landgraf .vidioc_s_input = vidioc_s_input, 5337002a4f3SDouglas Landgraf }; 5347002a4f3SDouglas Landgraf 535a399810cSHans Verkuil /* V4L2 interface */ 536a399810cSHans Verkuil static struct video_device dsbr100_videodev_template = { 537a399810cSHans Verkuil .name = "D-Link DSB-R 100", 538a399810cSHans Verkuil .fops = &usb_dsbr100_fops, 539a399810cSHans Verkuil .ioctl_ops = &usb_dsbr100_ioctl_ops, 540a399810cSHans Verkuil .release = video_device_release, 541a399810cSHans Verkuil }; 542a399810cSHans Verkuil 5437002a4f3SDouglas Landgraf /* check if the device is present and register with v4l and 5447002a4f3SDouglas Landgraf usb if it is */ 5457002a4f3SDouglas Landgraf static int usb_dsbr100_probe(struct usb_interface *intf, 5467002a4f3SDouglas Landgraf const struct usb_device_id *id) 5477002a4f3SDouglas Landgraf { 5487002a4f3SDouglas Landgraf struct dsbr100_device *radio; 5497002a4f3SDouglas Landgraf 550223377e7SAlexey Klimov radio = kmalloc(sizeof(struct dsbr100_device), GFP_KERNEL); 551223377e7SAlexey Klimov 552223377e7SAlexey Klimov if (!radio) 5537002a4f3SDouglas Landgraf return -ENOMEM; 554223377e7SAlexey Klimov 555223377e7SAlexey Klimov radio->transfer_buffer = kmalloc(TB_LEN, GFP_KERNEL); 556223377e7SAlexey Klimov 557223377e7SAlexey Klimov if (!(radio->transfer_buffer)) { 558863c86ddSOliver Neukum kfree(radio); 559863c86ddSOliver Neukum return -ENOMEM; 560863c86ddSOliver Neukum } 561223377e7SAlexey Klimov radio->videodev = video_device_alloc(); 562223377e7SAlexey Klimov 563223377e7SAlexey Klimov if (!(radio->videodev)) { 564863c86ddSOliver Neukum kfree(radio->transfer_buffer); 5657002a4f3SDouglas Landgraf kfree(radio); 5667002a4f3SDouglas Landgraf return -ENOMEM; 5677002a4f3SDouglas Landgraf } 5687002a4f3SDouglas Landgraf memcpy(radio->videodev, &dsbr100_videodev_template, 5697002a4f3SDouglas Landgraf sizeof(dsbr100_videodev_template)); 5707002a4f3SDouglas Landgraf radio->removed = 0; 5717002a4f3SDouglas Landgraf radio->users = 0; 5727002a4f3SDouglas Landgraf radio->usbdev = interface_to_usbdev(intf); 5737002a4f3SDouglas Landgraf radio->curfreq = FREQ_MIN * FREQ_MUL; 5747002a4f3SDouglas Landgraf video_set_drvdata(radio->videodev, radio); 575cba99ae8SHans Verkuil if (video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr) < 0) { 576aa82661bSGreg Kroah-Hartman dev_warn(&intf->dev, "Could not register video device\n"); 5777002a4f3SDouglas Landgraf video_device_release(radio->videodev); 578863c86ddSOliver Neukum kfree(radio->transfer_buffer); 5797002a4f3SDouglas Landgraf kfree(radio); 5807002a4f3SDouglas Landgraf return -EIO; 5817002a4f3SDouglas Landgraf } 5827002a4f3SDouglas Landgraf usb_set_intfdata(intf, radio); 5837002a4f3SDouglas Landgraf return 0; 5847002a4f3SDouglas Landgraf } 5857002a4f3SDouglas Landgraf 5867fb65297SMauro Carvalho Chehab static int __init dsbr100_init(void) 5877fb65297SMauro Carvalho Chehab { 5887fb65297SMauro Carvalho Chehab int retval = usb_register(&usb_dsbr100_driver); 589a482f327SGreg Kroah-Hartman printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" 590a482f327SGreg Kroah-Hartman DRIVER_DESC "\n"); 5917fb65297SMauro Carvalho Chehab return retval; 5927fb65297SMauro Carvalho Chehab } 5937fb65297SMauro Carvalho Chehab 5947fb65297SMauro Carvalho Chehab static void __exit dsbr100_exit(void) 5957fb65297SMauro Carvalho Chehab { 5967fb65297SMauro Carvalho Chehab usb_deregister(&usb_dsbr100_driver); 5977fb65297SMauro Carvalho Chehab } 5987fb65297SMauro Carvalho Chehab 5997fb65297SMauro Carvalho Chehab module_init (dsbr100_init); 6007fb65297SMauro Carvalho Chehab module_exit (dsbr100_exit); 6017fb65297SMauro Carvalho Chehab 6027fb65297SMauro Carvalho Chehab MODULE_AUTHOR( DRIVER_AUTHOR ); 6037fb65297SMauro Carvalho Chehab MODULE_DESCRIPTION( DRIVER_DESC ); 6047fb65297SMauro Carvalho Chehab MODULE_LICENSE("GPL"); 605