xref: /openbmc/linux/drivers/media/radio/dsbr100.c (revision 35ea11ff)
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