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 965aff308cSAlan Cox #define DRIVER_VERSION "v0.41" 975aff308cSAlan Cox #define RADIO_VERSION KERNEL_VERSION(0,4,1) 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, 1075aff308cSAlan Cox } 1085aff308cSAlan Cox }; 1095aff308cSAlan Cox 1107fb65297SMauro Carvalho Chehab #define DRIVER_AUTHOR "Markus Demleitner <msdemlei@tucana.harvard.edu>" 1117fb65297SMauro Carvalho Chehab #define DRIVER_DESC "D-Link DSB-R100 USB FM radio driver" 1127fb65297SMauro Carvalho Chehab 1137fb65297SMauro Carvalho Chehab #define DSB100_VENDOR 0x04b4 1147fb65297SMauro Carvalho Chehab #define DSB100_PRODUCT 0x1002 1157fb65297SMauro Carvalho Chehab 1167fb65297SMauro Carvalho Chehab /* Commands the device appears to understand */ 1177fb65297SMauro Carvalho Chehab #define DSB100_TUNE 1 1187fb65297SMauro Carvalho Chehab #define DSB100_ONOFF 2 1197fb65297SMauro Carvalho Chehab 1207fb65297SMauro Carvalho Chehab #define TB_LEN 16 1217fb65297SMauro Carvalho Chehab 1227fb65297SMauro Carvalho Chehab /* Frequency limits in MHz -- these are European values. For Japanese 1237fb65297SMauro Carvalho Chehab devices, that would be 76 and 91. */ 1247fb65297SMauro Carvalho Chehab #define FREQ_MIN 87.5 1257fb65297SMauro Carvalho Chehab #define FREQ_MAX 108.0 1267fb65297SMauro Carvalho Chehab #define FREQ_MUL 16000 1277fb65297SMauro Carvalho Chehab 1287fb65297SMauro Carvalho Chehab 1297fb65297SMauro Carvalho Chehab static int usb_dsbr100_probe(struct usb_interface *intf, 1307fb65297SMauro Carvalho Chehab const struct usb_device_id *id); 1317fb65297SMauro Carvalho Chehab static void usb_dsbr100_disconnect(struct usb_interface *intf); 1327fb65297SMauro Carvalho Chehab static int usb_dsbr100_open(struct inode *inode, struct file *file); 1337fb65297SMauro Carvalho Chehab static int usb_dsbr100_close(struct inode *inode, struct file *file); 1347fb65297SMauro Carvalho Chehab 1357fb65297SMauro Carvalho Chehab static int radio_nr = -1; 1367fb65297SMauro Carvalho Chehab module_param(radio_nr, int, 0); 1377fb65297SMauro Carvalho Chehab 1387fb65297SMauro Carvalho Chehab /* Data for one (physical) device */ 1395aff308cSAlan Cox struct dsbr100_device { 1407fb65297SMauro Carvalho Chehab struct usb_device *usbdev; 1417fb65297SMauro Carvalho Chehab struct video_device *videodev; 142863c86ddSOliver Neukum u8 *transfer_buffer; 1437fb65297SMauro Carvalho Chehab int curfreq; 1447fb65297SMauro Carvalho Chehab int stereo; 1457fb65297SMauro Carvalho Chehab int users; 1467fb65297SMauro Carvalho Chehab int removed; 1475aff308cSAlan Cox int muted; 1485aff308cSAlan Cox }; 1497fb65297SMauro Carvalho Chehab 1507fb65297SMauro Carvalho Chehab 1517fb65297SMauro Carvalho Chehab static struct usb_device_id usb_dsbr100_device_table [] = { 1527fb65297SMauro Carvalho Chehab { USB_DEVICE(DSB100_VENDOR, DSB100_PRODUCT) }, 1537fb65297SMauro Carvalho Chehab { } /* Terminating entry */ 1547fb65297SMauro Carvalho Chehab }; 1557fb65297SMauro Carvalho Chehab 1567fb65297SMauro Carvalho Chehab MODULE_DEVICE_TABLE (usb, usb_dsbr100_device_table); 1577fb65297SMauro Carvalho Chehab 1587fb65297SMauro Carvalho Chehab /* USB subsystem interface */ 1597fb65297SMauro Carvalho Chehab static struct usb_driver usb_dsbr100_driver = { 1607fb65297SMauro Carvalho Chehab .name = "dsbr100", 1617fb65297SMauro Carvalho Chehab .probe = usb_dsbr100_probe, 1627fb65297SMauro Carvalho Chehab .disconnect = usb_dsbr100_disconnect, 1637fb65297SMauro Carvalho Chehab .id_table = usb_dsbr100_device_table, 1647fb65297SMauro Carvalho Chehab }; 1657fb65297SMauro Carvalho Chehab 1667fb65297SMauro Carvalho Chehab /* Low-level device interface begins here */ 1677fb65297SMauro Carvalho Chehab 1687fb65297SMauro Carvalho Chehab /* switch on radio */ 1695aff308cSAlan Cox static int dsbr100_start(struct dsbr100_device *radio) 1707fb65297SMauro Carvalho Chehab { 1717fb65297SMauro Carvalho Chehab if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), 1727fb65297SMauro Carvalho Chehab USB_REQ_GET_STATUS, 1737fb65297SMauro Carvalho Chehab USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 1747fb65297SMauro Carvalho Chehab 0x00, 0xC7, radio->transfer_buffer, 8, 300)<0 || 1757fb65297SMauro Carvalho Chehab usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), 1767fb65297SMauro Carvalho Chehab DSB100_ONOFF, 1777fb65297SMauro Carvalho Chehab USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 1787fb65297SMauro Carvalho Chehab 0x01, 0x00, radio->transfer_buffer, 8, 300)<0) 1797fb65297SMauro Carvalho Chehab return -1; 1805aff308cSAlan Cox radio->muted=0; 1817fb65297SMauro Carvalho Chehab return (radio->transfer_buffer)[0]; 1827fb65297SMauro Carvalho Chehab } 1837fb65297SMauro Carvalho Chehab 1847fb65297SMauro Carvalho Chehab 1857fb65297SMauro Carvalho Chehab /* switch off radio */ 1865aff308cSAlan Cox static int dsbr100_stop(struct dsbr100_device *radio) 1877fb65297SMauro Carvalho Chehab { 1887fb65297SMauro Carvalho Chehab if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), 1897fb65297SMauro Carvalho Chehab USB_REQ_GET_STATUS, 1907fb65297SMauro Carvalho Chehab USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 1917fb65297SMauro Carvalho Chehab 0x16, 0x1C, radio->transfer_buffer, 8, 300)<0 || 1927fb65297SMauro Carvalho Chehab usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), 1937fb65297SMauro Carvalho Chehab DSB100_ONOFF, 1947fb65297SMauro Carvalho Chehab USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 1957fb65297SMauro Carvalho Chehab 0x00, 0x00, radio->transfer_buffer, 8, 300)<0) 1967fb65297SMauro Carvalho Chehab return -1; 1975aff308cSAlan Cox radio->muted=1; 1987fb65297SMauro Carvalho Chehab return (radio->transfer_buffer)[0]; 1997fb65297SMauro Carvalho Chehab } 2007fb65297SMauro Carvalho Chehab 2017fb65297SMauro Carvalho Chehab /* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */ 2025aff308cSAlan Cox static int dsbr100_setfreq(struct dsbr100_device *radio, int freq) 2037fb65297SMauro Carvalho Chehab { 2047fb65297SMauro Carvalho Chehab freq = (freq/16*80)/1000+856; 2057fb65297SMauro Carvalho Chehab if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), 2067fb65297SMauro Carvalho Chehab DSB100_TUNE, 2077fb65297SMauro Carvalho Chehab USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 2087fb65297SMauro Carvalho Chehab (freq>>8)&0x00ff, freq&0xff, 2097fb65297SMauro Carvalho Chehab radio->transfer_buffer, 8, 300)<0 || 2107fb65297SMauro Carvalho Chehab usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), 2117fb65297SMauro Carvalho Chehab USB_REQ_GET_STATUS, 2127fb65297SMauro Carvalho Chehab USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 2137fb65297SMauro Carvalho Chehab 0x96, 0xB7, radio->transfer_buffer, 8, 300)<0 || 2147fb65297SMauro Carvalho Chehab usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), 2157fb65297SMauro Carvalho Chehab USB_REQ_GET_STATUS, 2167fb65297SMauro Carvalho Chehab USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 2177fb65297SMauro Carvalho Chehab 0x00, 0x24, radio->transfer_buffer, 8, 300)<0) { 2187fb65297SMauro Carvalho Chehab radio->stereo = -1; 2197fb65297SMauro Carvalho Chehab return -1; 2207fb65297SMauro Carvalho Chehab } 2217fb65297SMauro Carvalho Chehab radio->stereo = ! ((radio->transfer_buffer)[0]&0x01); 2227fb65297SMauro Carvalho Chehab return (radio->transfer_buffer)[0]; 2237fb65297SMauro Carvalho Chehab } 2247fb65297SMauro Carvalho Chehab 2257fb65297SMauro Carvalho Chehab /* return the device status. This is, in effect, just whether it 2267fb65297SMauro Carvalho Chehab sees a stereo signal or not. Pity. */ 2275aff308cSAlan Cox static void dsbr100_getstat(struct dsbr100_device *radio) 2287fb65297SMauro Carvalho Chehab { 2297fb65297SMauro Carvalho Chehab if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), 2307fb65297SMauro Carvalho Chehab USB_REQ_GET_STATUS, 2317fb65297SMauro Carvalho Chehab USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 2327fb65297SMauro Carvalho Chehab 0x00 , 0x24, radio->transfer_buffer, 8, 300)<0) 2337fb65297SMauro Carvalho Chehab radio->stereo = -1; 2347fb65297SMauro Carvalho Chehab else 2357fb65297SMauro Carvalho Chehab radio->stereo = ! (radio->transfer_buffer[0]&0x01); 2367fb65297SMauro Carvalho Chehab } 2377fb65297SMauro Carvalho Chehab 2387fb65297SMauro Carvalho Chehab 2397fb65297SMauro Carvalho Chehab /* USB subsystem interface begins here */ 2407fb65297SMauro Carvalho Chehab 2417fb65297SMauro Carvalho Chehab /* handle unplugging of the device, release data structures 2427fb65297SMauro Carvalho Chehab if nothing keeps us from doing it. If something is still 2437fb65297SMauro Carvalho Chehab keeping us busy, the release callback of v4l will take care 244863c86ddSOliver Neukum of releasing it. */ 2457fb65297SMauro Carvalho Chehab static void usb_dsbr100_disconnect(struct usb_interface *intf) 2467fb65297SMauro Carvalho Chehab { 2475aff308cSAlan Cox struct dsbr100_device *radio = usb_get_intfdata(intf); 2487fb65297SMauro Carvalho Chehab 2497fb65297SMauro Carvalho Chehab usb_set_intfdata (intf, NULL); 2507fb65297SMauro Carvalho Chehab if (radio) { 2517fb65297SMauro Carvalho Chehab video_unregister_device(radio->videodev); 2527fb65297SMauro Carvalho Chehab radio->videodev = NULL; 2537fb65297SMauro Carvalho Chehab if (radio->users) { 254863c86ddSOliver Neukum kfree(radio->transfer_buffer); 2557fb65297SMauro Carvalho Chehab kfree(radio); 2567fb65297SMauro Carvalho Chehab } else { 2577fb65297SMauro Carvalho Chehab radio->removed = 1; 2587fb65297SMauro Carvalho Chehab } 2597fb65297SMauro Carvalho Chehab } 2607fb65297SMauro Carvalho Chehab } 2617fb65297SMauro Carvalho Chehab 2627fb65297SMauro Carvalho Chehab 2637002a4f3SDouglas Landgraf static int vidioc_querycap(struct file *file, void *priv, 2647002a4f3SDouglas Landgraf struct v4l2_capability *v) 2657fb65297SMauro Carvalho Chehab { 2665aff308cSAlan Cox strlcpy(v->driver, "dsbr100", sizeof(v->driver)); 2675aff308cSAlan Cox strlcpy(v->card, "D-Link R-100 USB FM Radio", sizeof(v->card)); 2685aff308cSAlan Cox sprintf(v->bus_info, "ISA"); 2695aff308cSAlan Cox v->version = RADIO_VERSION; 2705aff308cSAlan Cox v->capabilities = V4L2_CAP_TUNER; 2717fb65297SMauro Carvalho Chehab return 0; 2727fb65297SMauro Carvalho Chehab } 2737002a4f3SDouglas Landgraf 2747002a4f3SDouglas Landgraf static int vidioc_g_tuner(struct file *file, void *priv, 2757002a4f3SDouglas Landgraf struct v4l2_tuner *v) 2765aff308cSAlan Cox { 2777002a4f3SDouglas Landgraf struct dsbr100_device *radio = video_get_drvdata(video_devdata(file)); 2785aff308cSAlan Cox 2795aff308cSAlan Cox if (v->index > 0) 2805aff308cSAlan Cox return -EINVAL; 2817fb65297SMauro Carvalho Chehab 2827fb65297SMauro Carvalho Chehab dsbr100_getstat(radio); 2835aff308cSAlan Cox strcpy(v->name, "FM"); 2845aff308cSAlan Cox v->type = V4L2_TUNER_RADIO; 2857fb65297SMauro Carvalho Chehab v->rangelow = FREQ_MIN*FREQ_MUL; 2867fb65297SMauro Carvalho Chehab v->rangehigh = FREQ_MAX*FREQ_MUL; 2875aff308cSAlan Cox v->rxsubchans = V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO; 2885aff308cSAlan Cox v->capability = V4L2_TUNER_CAP_LOW; 2895aff308cSAlan Cox if(radio->stereo) 2905aff308cSAlan Cox v->audmode = V4L2_TUNER_MODE_STEREO; 2915aff308cSAlan Cox else 2925aff308cSAlan Cox v->audmode = V4L2_TUNER_MODE_MONO; 2937002a4f3SDouglas Landgraf v->signal = 0xffff; /* We can't get the signal strength */ 2947fb65297SMauro Carvalho Chehab return 0; 2957fb65297SMauro Carvalho Chehab } 2967fb65297SMauro Carvalho Chehab 2977002a4f3SDouglas Landgraf static int vidioc_s_tuner(struct file *file, void *priv, 2987002a4f3SDouglas Landgraf struct v4l2_tuner *v) 2997002a4f3SDouglas Landgraf { 3005aff308cSAlan Cox if (v->index > 0) 3017fb65297SMauro Carvalho Chehab return -EINVAL; 3025aff308cSAlan Cox 3037fb65297SMauro Carvalho Chehab return 0; 3047fb65297SMauro Carvalho Chehab } 3057002a4f3SDouglas Landgraf 3067002a4f3SDouglas Landgraf static int vidioc_s_frequency(struct file *file, void *priv, 3077002a4f3SDouglas Landgraf struct v4l2_frequency *f) 3085aff308cSAlan Cox { 3097002a4f3SDouglas Landgraf struct dsbr100_device *radio = video_get_drvdata(video_devdata(file)); 3107fb65297SMauro Carvalho Chehab 3115aff308cSAlan Cox radio->curfreq = f->frequency; 3127fb65297SMauro Carvalho Chehab if (dsbr100_setfreq(radio, radio->curfreq)==-1) 3137fb65297SMauro Carvalho Chehab warn("Set frequency failed"); 3147fb65297SMauro Carvalho Chehab return 0; 3157fb65297SMauro Carvalho Chehab } 3167002a4f3SDouglas Landgraf 3177002a4f3SDouglas Landgraf static int vidioc_g_frequency(struct file *file, void *priv, 3187002a4f3SDouglas Landgraf struct v4l2_frequency *f) 3195aff308cSAlan Cox { 3207002a4f3SDouglas Landgraf struct dsbr100_device *radio = video_get_drvdata(video_devdata(file)); 3217fb65297SMauro Carvalho Chehab 3225aff308cSAlan Cox f->type = V4L2_TUNER_RADIO; 3235aff308cSAlan Cox f->frequency = radio->curfreq; 3247fb65297SMauro Carvalho Chehab return 0; 3257fb65297SMauro Carvalho Chehab } 3267002a4f3SDouglas Landgraf 3277002a4f3SDouglas Landgraf static int vidioc_queryctrl(struct file *file, void *priv, 3287002a4f3SDouglas Landgraf struct v4l2_queryctrl *qc) 3295aff308cSAlan Cox { 3305aff308cSAlan Cox int i; 3317fb65297SMauro Carvalho Chehab 3325aff308cSAlan Cox for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { 3335aff308cSAlan Cox if (qc->id && qc->id == radio_qctrl[i].id) { 3345aff308cSAlan Cox memcpy(qc, &(radio_qctrl[i]), 3355aff308cSAlan Cox sizeof(*qc)); 3365aff308cSAlan Cox return 0; 3375aff308cSAlan Cox } 3385aff308cSAlan Cox } 3397fb65297SMauro Carvalho Chehab return -EINVAL; 3405aff308cSAlan Cox } 3417002a4f3SDouglas Landgraf 3427002a4f3SDouglas Landgraf static int vidioc_g_ctrl(struct file *file, void *priv, 3437002a4f3SDouglas Landgraf struct v4l2_control *ctrl) 3445aff308cSAlan Cox { 3457002a4f3SDouglas Landgraf struct dsbr100_device *radio = video_get_drvdata(video_devdata(file)); 3465aff308cSAlan Cox 3475aff308cSAlan Cox switch (ctrl->id) { 3485aff308cSAlan Cox case V4L2_CID_AUDIO_MUTE: 3495aff308cSAlan Cox ctrl->value = radio->muted; 3505aff308cSAlan Cox return 0; 3515aff308cSAlan Cox } 3525aff308cSAlan Cox return -EINVAL; 3535aff308cSAlan Cox } 3547002a4f3SDouglas Landgraf 3557002a4f3SDouglas Landgraf static int vidioc_s_ctrl(struct file *file, void *priv, 3567002a4f3SDouglas Landgraf struct v4l2_control *ctrl) 3575aff308cSAlan Cox { 3587002a4f3SDouglas Landgraf struct dsbr100_device *radio = video_get_drvdata(video_devdata(file)); 3595aff308cSAlan Cox 3605aff308cSAlan Cox switch (ctrl->id) { 3615aff308cSAlan Cox case V4L2_CID_AUDIO_MUTE: 3625aff308cSAlan Cox if (ctrl->value) { 3637fb65297SMauro Carvalho Chehab if (dsbr100_stop(radio)==-1) 3647fb65297SMauro Carvalho Chehab warn("Radio did not respond properly"); 3655aff308cSAlan Cox } else { 3667fb65297SMauro Carvalho Chehab if (dsbr100_start(radio)==-1) 3677fb65297SMauro Carvalho Chehab warn("Radio did not respond properly"); 3685aff308cSAlan Cox } 3697fb65297SMauro Carvalho Chehab return 0; 3707fb65297SMauro Carvalho Chehab } 3715aff308cSAlan Cox return -EINVAL; 3725aff308cSAlan Cox } 3737002a4f3SDouglas Landgraf 3747002a4f3SDouglas Landgraf static int vidioc_g_audio(struct file *file, void *priv, 3757002a4f3SDouglas Landgraf struct v4l2_audio *a) 3767002a4f3SDouglas Landgraf { 3777002a4f3SDouglas Landgraf if (a->index > 1) 3787002a4f3SDouglas Landgraf return -EINVAL; 3797002a4f3SDouglas Landgraf 3807002a4f3SDouglas Landgraf strcpy(a->name, "Radio"); 3817002a4f3SDouglas Landgraf a->capability = V4L2_AUDCAP_STEREO; 3827002a4f3SDouglas Landgraf return 0; 3837fb65297SMauro Carvalho Chehab } 3847fb65297SMauro Carvalho Chehab 3857002a4f3SDouglas Landgraf static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) 3867fb65297SMauro Carvalho Chehab { 3877002a4f3SDouglas Landgraf *i = 0; 3887002a4f3SDouglas Landgraf return 0; 3897002a4f3SDouglas Landgraf } 3907002a4f3SDouglas Landgraf 3917002a4f3SDouglas Landgraf static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) 3927002a4f3SDouglas Landgraf { 3937002a4f3SDouglas Landgraf if (i != 0) 3947002a4f3SDouglas Landgraf return -EINVAL; 3957002a4f3SDouglas Landgraf return 0; 3967002a4f3SDouglas Landgraf } 3977002a4f3SDouglas Landgraf 3987002a4f3SDouglas Landgraf static int vidioc_s_audio(struct file *file, void *priv, 3997002a4f3SDouglas Landgraf struct v4l2_audio *a) 4007002a4f3SDouglas Landgraf { 4017002a4f3SDouglas Landgraf if (a->index != 0) 4027002a4f3SDouglas Landgraf return -EINVAL; 4037002a4f3SDouglas Landgraf return 0; 4047fb65297SMauro Carvalho Chehab } 4057fb65297SMauro Carvalho Chehab 4067fb65297SMauro Carvalho Chehab static int usb_dsbr100_open(struct inode *inode, struct file *file) 4077fb65297SMauro Carvalho Chehab { 4085aff308cSAlan Cox struct dsbr100_device *radio=video_get_drvdata(video_devdata(file)); 4097fb65297SMauro Carvalho Chehab 4107fb65297SMauro Carvalho Chehab radio->users = 1; 4115aff308cSAlan Cox radio->muted = 1; 4125aff308cSAlan Cox 4137fb65297SMauro Carvalho Chehab if (dsbr100_start(radio)<0) { 4147fb65297SMauro Carvalho Chehab warn("Radio did not start up properly"); 4157fb65297SMauro Carvalho Chehab radio->users = 0; 4167fb65297SMauro Carvalho Chehab return -EIO; 4177fb65297SMauro Carvalho Chehab } 4187fb65297SMauro Carvalho Chehab dsbr100_setfreq(radio, radio->curfreq); 4197fb65297SMauro Carvalho Chehab return 0; 4207fb65297SMauro Carvalho Chehab } 4217fb65297SMauro Carvalho Chehab 4227fb65297SMauro Carvalho Chehab static int usb_dsbr100_close(struct inode *inode, struct file *file) 4237fb65297SMauro Carvalho Chehab { 4245aff308cSAlan Cox struct dsbr100_device *radio=video_get_drvdata(video_devdata(file)); 4257fb65297SMauro Carvalho Chehab 4267fb65297SMauro Carvalho Chehab if (!radio) 4277fb65297SMauro Carvalho Chehab return -ENODEV; 4287fb65297SMauro Carvalho Chehab radio->users = 0; 4297fb65297SMauro Carvalho Chehab if (radio->removed) { 430863c86ddSOliver Neukum kfree(radio->transfer_buffer); 4317fb65297SMauro Carvalho Chehab kfree(radio); 4327fb65297SMauro Carvalho Chehab } 4337fb65297SMauro Carvalho Chehab return 0; 4347fb65297SMauro Carvalho Chehab } 4357fb65297SMauro Carvalho Chehab 4367002a4f3SDouglas Landgraf /* File system interface */ 4377002a4f3SDouglas Landgraf static const struct file_operations usb_dsbr100_fops = { 4387002a4f3SDouglas Landgraf .owner = THIS_MODULE, 4397002a4f3SDouglas Landgraf .open = usb_dsbr100_open, 4407002a4f3SDouglas Landgraf .release = usb_dsbr100_close, 4417002a4f3SDouglas Landgraf .ioctl = video_ioctl2, 442078ff795SDouglas Schilling Landgraf #ifdef CONFIG_COMPAT 4437002a4f3SDouglas Landgraf .compat_ioctl = v4l_compat_ioctl32, 444078ff795SDouglas Schilling Landgraf #endif 4457002a4f3SDouglas Landgraf .llseek = no_llseek, 4467002a4f3SDouglas Landgraf }; 4477002a4f3SDouglas Landgraf 4487002a4f3SDouglas Landgraf /* V4L2 interface */ 4497002a4f3SDouglas Landgraf static struct video_device dsbr100_videodev_template = 4507002a4f3SDouglas Landgraf { 4517002a4f3SDouglas Landgraf .owner = THIS_MODULE, 4527002a4f3SDouglas Landgraf .name = "D-Link DSB-R 100", 4537002a4f3SDouglas Landgraf .type = VID_TYPE_TUNER, 4547002a4f3SDouglas Landgraf .fops = &usb_dsbr100_fops, 4557002a4f3SDouglas Landgraf .release = video_device_release, 4567002a4f3SDouglas Landgraf .vidioc_querycap = vidioc_querycap, 4577002a4f3SDouglas Landgraf .vidioc_g_tuner = vidioc_g_tuner, 4587002a4f3SDouglas Landgraf .vidioc_s_tuner = vidioc_s_tuner, 4597002a4f3SDouglas Landgraf .vidioc_g_frequency = vidioc_g_frequency, 4607002a4f3SDouglas Landgraf .vidioc_s_frequency = vidioc_s_frequency, 4617002a4f3SDouglas Landgraf .vidioc_queryctrl = vidioc_queryctrl, 4627002a4f3SDouglas Landgraf .vidioc_g_ctrl = vidioc_g_ctrl, 4637002a4f3SDouglas Landgraf .vidioc_s_ctrl = vidioc_s_ctrl, 4647002a4f3SDouglas Landgraf .vidioc_g_audio = vidioc_g_audio, 4657002a4f3SDouglas Landgraf .vidioc_s_audio = vidioc_s_audio, 4667002a4f3SDouglas Landgraf .vidioc_g_input = vidioc_g_input, 4677002a4f3SDouglas Landgraf .vidioc_s_input = vidioc_s_input, 4687002a4f3SDouglas Landgraf }; 4697002a4f3SDouglas Landgraf 4707002a4f3SDouglas Landgraf /* check if the device is present and register with v4l and 4717002a4f3SDouglas Landgraf usb if it is */ 4727002a4f3SDouglas Landgraf static int usb_dsbr100_probe(struct usb_interface *intf, 4737002a4f3SDouglas Landgraf const struct usb_device_id *id) 4747002a4f3SDouglas Landgraf { 4757002a4f3SDouglas Landgraf struct dsbr100_device *radio; 4767002a4f3SDouglas Landgraf 4777002a4f3SDouglas Landgraf if (!(radio = kmalloc(sizeof(struct dsbr100_device), GFP_KERNEL))) 4787002a4f3SDouglas Landgraf return -ENOMEM; 479863c86ddSOliver Neukum if (!(radio->transfer_buffer = kmalloc(TB_LEN, GFP_KERNEL))) { 480863c86ddSOliver Neukum kfree(radio); 481863c86ddSOliver Neukum return -ENOMEM; 482863c86ddSOliver Neukum } 4837002a4f3SDouglas Landgraf if (!(radio->videodev = video_device_alloc())) { 484863c86ddSOliver Neukum kfree(radio->transfer_buffer); 4857002a4f3SDouglas Landgraf kfree(radio); 4867002a4f3SDouglas Landgraf return -ENOMEM; 4877002a4f3SDouglas Landgraf } 4887002a4f3SDouglas Landgraf memcpy(radio->videodev, &dsbr100_videodev_template, 4897002a4f3SDouglas Landgraf sizeof(dsbr100_videodev_template)); 4907002a4f3SDouglas Landgraf radio->removed = 0; 4917002a4f3SDouglas Landgraf radio->users = 0; 4927002a4f3SDouglas Landgraf radio->usbdev = interface_to_usbdev(intf); 4937002a4f3SDouglas Landgraf radio->curfreq = FREQ_MIN*FREQ_MUL; 4947002a4f3SDouglas Landgraf video_set_drvdata(radio->videodev, radio); 4957002a4f3SDouglas Landgraf if (video_register_device(radio->videodev, VFL_TYPE_RADIO,radio_nr)) { 4967002a4f3SDouglas Landgraf warn("Could not register video device"); 4977002a4f3SDouglas Landgraf video_device_release(radio->videodev); 498863c86ddSOliver Neukum kfree(radio->transfer_buffer); 4997002a4f3SDouglas Landgraf kfree(radio); 5007002a4f3SDouglas Landgraf return -EIO; 5017002a4f3SDouglas Landgraf } 5027002a4f3SDouglas Landgraf usb_set_intfdata(intf, radio); 5037002a4f3SDouglas Landgraf return 0; 5047002a4f3SDouglas Landgraf } 5057002a4f3SDouglas Landgraf 5067fb65297SMauro Carvalho Chehab static int __init dsbr100_init(void) 5077fb65297SMauro Carvalho Chehab { 5087fb65297SMauro Carvalho Chehab int retval = usb_register(&usb_dsbr100_driver); 5097fb65297SMauro Carvalho Chehab info(DRIVER_VERSION ":" DRIVER_DESC); 5107fb65297SMauro Carvalho Chehab return retval; 5117fb65297SMauro Carvalho Chehab } 5127fb65297SMauro Carvalho Chehab 5137fb65297SMauro Carvalho Chehab static void __exit dsbr100_exit(void) 5147fb65297SMauro Carvalho Chehab { 5157fb65297SMauro Carvalho Chehab usb_deregister(&usb_dsbr100_driver); 5167fb65297SMauro Carvalho Chehab } 5177fb65297SMauro Carvalho Chehab 5187fb65297SMauro Carvalho Chehab module_init (dsbr100_init); 5197fb65297SMauro Carvalho Chehab module_exit (dsbr100_exit); 5207fb65297SMauro Carvalho Chehab 5217fb65297SMauro Carvalho Chehab MODULE_AUTHOR( DRIVER_AUTHOR ); 5227fb65297SMauro Carvalho Chehab MODULE_DESCRIPTION( DRIVER_DESC ); 5237fb65297SMauro Carvalho Chehab MODULE_LICENSE("GPL"); 524