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