xref: /openbmc/linux/drivers/media/radio/dsbr100.c (revision fc55bcb0)
1fc55bcb0SAlexey Klimov /* A driver for the D-Link DSB-R100 USB radio and Gemtek USB Radio 21.
2fc55bcb0SAlexey Klimov  The device plugs into both the USB and an analog audio input, so this thing
37fb65297SMauro Carvalho Chehab  only deals with initialisation and frequency setting, the
47fb65297SMauro Carvalho Chehab  audio data has to be handled by a sound driver.
57fb65297SMauro Carvalho Chehab 
67fb65297SMauro Carvalho Chehab  Major issue: I can't find out where the device reports the signal
77fb65297SMauro Carvalho Chehab  strength, and indeed the windows software appearantly just looks
87fb65297SMauro Carvalho Chehab  at the stereo indicator as well.  So, scanning will only find
97fb65297SMauro Carvalho Chehab  stereo stations.  Sad, but I can't help it.
107fb65297SMauro Carvalho Chehab 
117fb65297SMauro Carvalho Chehab  Also, the windows program sends oodles of messages over to the
127fb65297SMauro Carvalho Chehab  device, and I couldn't figure out their meaning.  My suspicion
137fb65297SMauro Carvalho Chehab  is that they don't have any:-)
147fb65297SMauro Carvalho Chehab 
157fb65297SMauro Carvalho Chehab  You might find some interesting stuff about this module at
167fb65297SMauro Carvalho Chehab  http://unimut.fsk.uni-heidelberg.de/unimut/demi/dsbr
177fb65297SMauro Carvalho Chehab 
187fb65297SMauro Carvalho Chehab  Copyright (c) 2000 Markus Demleitner <msdemlei@cl.uni-heidelberg.de>
197fb65297SMauro Carvalho Chehab 
207fb65297SMauro Carvalho Chehab  This program is free software; you can redistribute it and/or modify
217fb65297SMauro Carvalho Chehab  it under the terms of the GNU General Public License as published by
227fb65297SMauro Carvalho Chehab  the Free Software Foundation; either version 2 of the License, or
237fb65297SMauro Carvalho Chehab  (at your option) any later version.
247fb65297SMauro Carvalho Chehab 
257fb65297SMauro Carvalho Chehab  This program is distributed in the hope that it will be useful,
267fb65297SMauro Carvalho Chehab  but WITHOUT ANY WARRANTY; without even the implied warranty of
277fb65297SMauro Carvalho Chehab  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
287fb65297SMauro Carvalho Chehab  GNU General Public License for more details.
297fb65297SMauro Carvalho Chehab 
307fb65297SMauro Carvalho Chehab  You should have received a copy of the GNU General Public License
317fb65297SMauro Carvalho Chehab  along with this program; if not, write to the Free Software
327fb65297SMauro Carvalho Chehab  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
337fb65297SMauro Carvalho Chehab 
347fb65297SMauro Carvalho Chehab  History:
357fb65297SMauro Carvalho Chehab 
36863c86ddSOliver Neukum  Version 0.43:
37863c86ddSOliver Neukum 	Oliver Neukum: avoided DMA coherency issue
38863c86ddSOliver Neukum 
397002a4f3SDouglas Landgraf  Version 0.42:
407002a4f3SDouglas Landgraf 	Converted dsbr100 to use video_ioctl2
417002a4f3SDouglas Landgraf 	by Douglas Landgraf <dougsland@gmail.com>
427002a4f3SDouglas Landgraf 
435aff308cSAlan Cox  Version 0.41-ac1:
445aff308cSAlan Cox 	Alan Cox: Some cleanups and fixes
455aff308cSAlan Cox 
465aff308cSAlan Cox  Version 0.41:
475aff308cSAlan Cox 	Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
485aff308cSAlan Cox 
497fb65297SMauro Carvalho Chehab  Version 0.40:
507fb65297SMauro Carvalho Chehab 	Markus: Updates for 2.6.x kernels, code layout changes, name sanitizing
517fb65297SMauro Carvalho Chehab 
527fb65297SMauro Carvalho Chehab  Version 0.30:
537fb65297SMauro Carvalho Chehab 	Markus: Updates for 2.5.x kernel and more ISO compliant source
547fb65297SMauro Carvalho Chehab 
557fb65297SMauro Carvalho Chehab  Version 0.25:
567fb65297SMauro Carvalho Chehab 	PSL and Markus: Cleanup, radio now doesn't stop on device close
577fb65297SMauro Carvalho Chehab 
587fb65297SMauro Carvalho Chehab  Version 0.24:
597fb65297SMauro Carvalho Chehab 	Markus: Hope I got these silly VIDEO_TUNER_LOW issues finally
607fb65297SMauro Carvalho Chehab 	right.  Some minor cleanup, improved standalone compilation
617fb65297SMauro Carvalho Chehab 
627fb65297SMauro Carvalho Chehab  Version 0.23:
637fb65297SMauro Carvalho Chehab 	Markus: Sign extension bug fixed by declaring transfer_buffer unsigned
647fb65297SMauro Carvalho Chehab 
657fb65297SMauro Carvalho Chehab  Version 0.22:
667fb65297SMauro Carvalho Chehab 	Markus: Some (brown bag) cleanup in what VIDIOCSTUNER returns,
677fb65297SMauro Carvalho Chehab 	thanks to Mike Cox for pointing the problem out.
687fb65297SMauro Carvalho Chehab 
697fb65297SMauro Carvalho Chehab  Version 0.21:
707fb65297SMauro Carvalho Chehab 	Markus: Minor cleanup, warnings if something goes wrong, lame attempt
717fb65297SMauro Carvalho Chehab 	to adhere to Documentation/CodingStyle
727fb65297SMauro Carvalho Chehab 
737fb65297SMauro Carvalho Chehab  Version 0.2:
747fb65297SMauro Carvalho Chehab 	Brad Hards <bradh@dynamite.com.au>: Fixes to make it work as non-module
757fb65297SMauro Carvalho Chehab 	Markus: Copyright clarification
767fb65297SMauro Carvalho Chehab 
777fb65297SMauro Carvalho Chehab  Version 0.01: Markus: initial release
787fb65297SMauro Carvalho Chehab 
797fb65297SMauro Carvalho Chehab */
807fb65297SMauro Carvalho Chehab 
817fb65297SMauro Carvalho Chehab #include <linux/kernel.h>
827fb65297SMauro Carvalho Chehab #include <linux/module.h>
837fb65297SMauro Carvalho Chehab #include <linux/init.h>
847fb65297SMauro Carvalho Chehab #include <linux/slab.h>
857fb65297SMauro Carvalho Chehab #include <linux/input.h>
865aff308cSAlan Cox #include <linux/videodev2.h>
877fb65297SMauro Carvalho Chehab #include <media/v4l2-common.h>
8835ea11ffSHans Verkuil #include <media/v4l2-ioctl.h>
897fb65297SMauro Carvalho Chehab #include <linux/usb.h>
907fb65297SMauro Carvalho Chehab 
917fb65297SMauro Carvalho Chehab /*
927fb65297SMauro Carvalho Chehab  * Version Information
937fb65297SMauro Carvalho Chehab  */
945aff308cSAlan Cox #include <linux/version.h>	/* for KERNEL_VERSION MACRO	*/
955aff308cSAlan Cox 
9654b7b0deSAlexey Klimov #define DRIVER_VERSION "v0.43"
9754b7b0deSAlexey Klimov #define RADIO_VERSION KERNEL_VERSION(0, 4, 3)
985aff308cSAlan Cox 
995aff308cSAlan Cox static struct v4l2_queryctrl radio_qctrl[] = {
1005aff308cSAlan Cox 	{
1015aff308cSAlan Cox 		.id            = V4L2_CID_AUDIO_MUTE,
1025aff308cSAlan Cox 		.name          = "Mute",
1035aff308cSAlan Cox 		.minimum       = 0,
1045aff308cSAlan Cox 		.maximum       = 1,
1055aff308cSAlan Cox 		.default_value = 1,
1065aff308cSAlan Cox 		.type          = V4L2_CTRL_TYPE_BOOLEAN,
10754b7b0deSAlexey Klimov 	},
10854b7b0deSAlexey Klimov /* HINT: the disabled controls are only here to satify kradio and such apps */
10954b7b0deSAlexey Klimov 	{       .id             = V4L2_CID_AUDIO_VOLUME,
11054b7b0deSAlexey Klimov 		.flags          = V4L2_CTRL_FLAG_DISABLED,
11154b7b0deSAlexey Klimov 	},
11254b7b0deSAlexey Klimov 	{
11354b7b0deSAlexey Klimov 		.id             = V4L2_CID_AUDIO_BALANCE,
11454b7b0deSAlexey Klimov 		.flags          = V4L2_CTRL_FLAG_DISABLED,
11554b7b0deSAlexey Klimov 	},
11654b7b0deSAlexey Klimov 	{
11754b7b0deSAlexey Klimov 		.id             = V4L2_CID_AUDIO_BASS,
11854b7b0deSAlexey Klimov 		.flags          = V4L2_CTRL_FLAG_DISABLED,
11954b7b0deSAlexey Klimov 	},
12054b7b0deSAlexey Klimov 	{
12154b7b0deSAlexey Klimov 		.id             = V4L2_CID_AUDIO_TREBLE,
12254b7b0deSAlexey Klimov 		.flags          = V4L2_CTRL_FLAG_DISABLED,
12354b7b0deSAlexey Klimov 	},
12454b7b0deSAlexey Klimov 	{
12554b7b0deSAlexey Klimov 		.id             = V4L2_CID_AUDIO_LOUDNESS,
12654b7b0deSAlexey Klimov 		.flags          = V4L2_CTRL_FLAG_DISABLED,
12754b7b0deSAlexey Klimov 	},
1285aff308cSAlan Cox };
1295aff308cSAlan Cox 
1307fb65297SMauro Carvalho Chehab #define DRIVER_AUTHOR "Markus Demleitner <msdemlei@tucana.harvard.edu>"
1317fb65297SMauro Carvalho Chehab #define DRIVER_DESC "D-Link DSB-R100 USB FM radio driver"
1327fb65297SMauro Carvalho Chehab 
1337fb65297SMauro Carvalho Chehab #define DSB100_VENDOR 0x04b4
1347fb65297SMauro Carvalho Chehab #define DSB100_PRODUCT 0x1002
1357fb65297SMauro Carvalho Chehab 
1367fb65297SMauro Carvalho Chehab /* Commands the device appears to understand */
1377fb65297SMauro Carvalho Chehab #define DSB100_TUNE 1
1387fb65297SMauro Carvalho Chehab #define DSB100_ONOFF 2
1397fb65297SMauro Carvalho Chehab 
1407fb65297SMauro Carvalho Chehab #define TB_LEN 16
1417fb65297SMauro Carvalho Chehab 
1427fb65297SMauro Carvalho Chehab /* Frequency limits in MHz -- these are European values.  For Japanese
1437fb65297SMauro Carvalho Chehab devices, that would be 76 and 91.  */
1447fb65297SMauro Carvalho Chehab #define FREQ_MIN  87.5
1457fb65297SMauro Carvalho Chehab #define FREQ_MAX 108.0
1467fb65297SMauro Carvalho Chehab #define FREQ_MUL 16000
1477fb65297SMauro Carvalho Chehab 
1483a0efc32SAlexey Klimov #define videodev_to_radio(d) container_of(d, struct dsbr100_device, videodev)
1497fb65297SMauro Carvalho Chehab 
1507fb65297SMauro Carvalho Chehab static int usb_dsbr100_probe(struct usb_interface *intf,
1517fb65297SMauro Carvalho Chehab 			     const struct usb_device_id *id);
1527fb65297SMauro Carvalho Chehab static void usb_dsbr100_disconnect(struct usb_interface *intf);
1537fb65297SMauro Carvalho Chehab static int usb_dsbr100_open(struct inode *inode, struct file *file);
1547fb65297SMauro Carvalho Chehab static int usb_dsbr100_close(struct inode *inode, struct file *file);
15504e0ffbbSAlexey Klimov static int usb_dsbr100_suspend(struct usb_interface *intf,
15604e0ffbbSAlexey Klimov 						pm_message_t message);
15704e0ffbbSAlexey Klimov static int usb_dsbr100_resume(struct usb_interface *intf);
1587fb65297SMauro Carvalho Chehab 
1597fb65297SMauro Carvalho Chehab static int radio_nr = -1;
1607fb65297SMauro Carvalho Chehab module_param(radio_nr, int, 0);
1617fb65297SMauro Carvalho Chehab 
1627fb65297SMauro Carvalho Chehab /* Data for one (physical) device */
1635aff308cSAlan Cox struct dsbr100_device {
1647fb65297SMauro Carvalho Chehab 	struct usb_device *usbdev;
1653a0efc32SAlexey Klimov 	struct video_device videodev;
166863c86ddSOliver Neukum 	u8 *transfer_buffer;
1673a0efc32SAlexey Klimov 	struct mutex lock;	/* buffer locking */
1687fb65297SMauro Carvalho Chehab 	int curfreq;
1697fb65297SMauro Carvalho Chehab 	int stereo;
1707fb65297SMauro Carvalho Chehab 	int users;
1717fb65297SMauro Carvalho Chehab 	int removed;
1725aff308cSAlan Cox 	int muted;
1735aff308cSAlan Cox };
1747fb65297SMauro Carvalho Chehab 
1757fb65297SMauro Carvalho Chehab static struct usb_device_id usb_dsbr100_device_table [] = {
1767fb65297SMauro Carvalho Chehab 	{ USB_DEVICE(DSB100_VENDOR, DSB100_PRODUCT) },
1777fb65297SMauro Carvalho Chehab 	{ }						/* Terminating entry */
1787fb65297SMauro Carvalho Chehab };
1797fb65297SMauro Carvalho Chehab 
1807fb65297SMauro Carvalho Chehab MODULE_DEVICE_TABLE (usb, usb_dsbr100_device_table);
1817fb65297SMauro Carvalho Chehab 
1827fb65297SMauro Carvalho Chehab /* USB subsystem interface */
1837fb65297SMauro Carvalho Chehab static struct usb_driver usb_dsbr100_driver = {
1847fb65297SMauro Carvalho Chehab 	.name			= "dsbr100",
1857fb65297SMauro Carvalho Chehab 	.probe			= usb_dsbr100_probe,
1867fb65297SMauro Carvalho Chehab 	.disconnect		= usb_dsbr100_disconnect,
1877fb65297SMauro Carvalho Chehab 	.id_table		= usb_dsbr100_device_table,
18804e0ffbbSAlexey Klimov 	.suspend		= usb_dsbr100_suspend,
18904e0ffbbSAlexey Klimov 	.resume			= usb_dsbr100_resume,
19004e0ffbbSAlexey Klimov 	.reset_resume		= usb_dsbr100_resume,
19104e0ffbbSAlexey Klimov 	.supports_autosuspend	= 0,
1927fb65297SMauro Carvalho Chehab };
1937fb65297SMauro Carvalho Chehab 
1947fb65297SMauro Carvalho Chehab /* Low-level device interface begins here */
1957fb65297SMauro Carvalho Chehab 
1967fb65297SMauro Carvalho Chehab /* switch on radio */
1975aff308cSAlan Cox static int dsbr100_start(struct dsbr100_device *radio)
1987fb65297SMauro Carvalho Chehab {
199417b7953SAlexey Klimov 	int retval;
200417b7953SAlexey Klimov 	int request;
201417b7953SAlexey Klimov 
2023a0efc32SAlexey Klimov 	mutex_lock(&radio->lock);
203417b7953SAlexey Klimov 
204417b7953SAlexey Klimov 	retval = usb_control_msg(radio->usbdev,
205417b7953SAlexey Klimov 		usb_rcvctrlpipe(radio->usbdev, 0),
2067fb65297SMauro Carvalho Chehab 		USB_REQ_GET_STATUS,
2077fb65297SMauro Carvalho Chehab 		USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
208417b7953SAlexey Klimov 		0x00, 0xC7, radio->transfer_buffer, 8, 300);
209417b7953SAlexey Klimov 
210417b7953SAlexey Klimov 	if (retval < 0) {
211417b7953SAlexey Klimov 		request = USB_REQ_GET_STATUS;
212417b7953SAlexey Klimov 		goto usb_control_msg_failed;
213417b7953SAlexey Klimov 	}
214417b7953SAlexey Klimov 
215417b7953SAlexey Klimov 	retval = usb_control_msg(radio->usbdev,
216417b7953SAlexey Klimov 		usb_rcvctrlpipe(radio->usbdev, 0),
2177fb65297SMauro Carvalho Chehab 		DSB100_ONOFF,
2187fb65297SMauro Carvalho Chehab 		USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
219417b7953SAlexey Klimov 		0x01, 0x00, radio->transfer_buffer, 8, 300);
220417b7953SAlexey Klimov 
221417b7953SAlexey Klimov 	if (retval < 0) {
222417b7953SAlexey Klimov 		request = DSB100_ONOFF;
223417b7953SAlexey Klimov 		goto usb_control_msg_failed;
2243a0efc32SAlexey Klimov 	}
2253a0efc32SAlexey Klimov 
2265aff308cSAlan Cox 	radio->muted = 0;
2273a0efc32SAlexey Klimov 	mutex_unlock(&radio->lock);
2287fb65297SMauro Carvalho Chehab 	return (radio->transfer_buffer)[0];
229417b7953SAlexey Klimov 
230417b7953SAlexey Klimov usb_control_msg_failed:
231417b7953SAlexey Klimov 	mutex_unlock(&radio->lock);
232417b7953SAlexey Klimov 	dev_err(&radio->usbdev->dev,
233417b7953SAlexey Klimov 		"%s - usb_control_msg returned %i, request %i\n",
234417b7953SAlexey Klimov 			__func__, retval, request);
235417b7953SAlexey Klimov 	return -1;
236417b7953SAlexey Klimov 
2377fb65297SMauro Carvalho Chehab }
2387fb65297SMauro Carvalho Chehab 
2397fb65297SMauro Carvalho Chehab /* switch off radio */
2405aff308cSAlan Cox static int dsbr100_stop(struct dsbr100_device *radio)
2417fb65297SMauro Carvalho Chehab {
242417b7953SAlexey Klimov 	int retval;
243417b7953SAlexey Klimov 	int request;
244417b7953SAlexey Klimov 
2453a0efc32SAlexey Klimov 	mutex_lock(&radio->lock);
246417b7953SAlexey Klimov 
247417b7953SAlexey Klimov 	retval = usb_control_msg(radio->usbdev,
248417b7953SAlexey Klimov 		usb_rcvctrlpipe(radio->usbdev, 0),
2497fb65297SMauro Carvalho Chehab 		USB_REQ_GET_STATUS,
2507fb65297SMauro Carvalho Chehab 		USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
251417b7953SAlexey Klimov 		0x16, 0x1C, radio->transfer_buffer, 8, 300);
252417b7953SAlexey Klimov 
253417b7953SAlexey Klimov 	if (retval < 0) {
254417b7953SAlexey Klimov 		request = USB_REQ_GET_STATUS;
255417b7953SAlexey Klimov 		goto usb_control_msg_failed;
256417b7953SAlexey Klimov 	}
257417b7953SAlexey Klimov 
258417b7953SAlexey Klimov 	retval = usb_control_msg(radio->usbdev,
259417b7953SAlexey Klimov 		usb_rcvctrlpipe(radio->usbdev, 0),
2607fb65297SMauro Carvalho Chehab 		DSB100_ONOFF,
2617fb65297SMauro Carvalho Chehab 		USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
262417b7953SAlexey Klimov 		0x00, 0x00, radio->transfer_buffer, 8, 300);
263417b7953SAlexey Klimov 
264417b7953SAlexey Klimov 	if (retval < 0) {
265417b7953SAlexey Klimov 		request = DSB100_ONOFF;
266417b7953SAlexey Klimov 		goto usb_control_msg_failed;
2673a0efc32SAlexey Klimov 	}
2683a0efc32SAlexey Klimov 
2695aff308cSAlan Cox 	radio->muted = 1;
2703a0efc32SAlexey Klimov 	mutex_unlock(&radio->lock);
2717fb65297SMauro Carvalho Chehab 	return (radio->transfer_buffer)[0];
272417b7953SAlexey Klimov 
273417b7953SAlexey Klimov usb_control_msg_failed:
274417b7953SAlexey Klimov 	mutex_unlock(&radio->lock);
275417b7953SAlexey Klimov 	dev_err(&radio->usbdev->dev,
276417b7953SAlexey Klimov 		"%s - usb_control_msg returned %i, request %i\n",
277417b7953SAlexey Klimov 			__func__, retval, request);
278417b7953SAlexey Klimov 	return -1;
279417b7953SAlexey Klimov 
2807fb65297SMauro Carvalho Chehab }
2817fb65297SMauro Carvalho Chehab 
2827fb65297SMauro Carvalho Chehab /* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */
2835aff308cSAlan Cox static int dsbr100_setfreq(struct dsbr100_device *radio, int freq)
2847fb65297SMauro Carvalho Chehab {
285417b7953SAlexey Klimov 	int retval;
286417b7953SAlexey Klimov 	int request;
287417b7953SAlexey Klimov 
2887fb65297SMauro Carvalho Chehab 	freq = (freq / 16 * 80) / 1000 + 856;
2893a0efc32SAlexey Klimov 	mutex_lock(&radio->lock);
290417b7953SAlexey Klimov 
291417b7953SAlexey Klimov 	retval = usb_control_msg(radio->usbdev,
292417b7953SAlexey Klimov 		usb_rcvctrlpipe(radio->usbdev, 0),
2937fb65297SMauro Carvalho Chehab 		DSB100_TUNE,
2947fb65297SMauro Carvalho Chehab 		USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
2957fb65297SMauro Carvalho Chehab 		(freq >> 8) & 0x00ff, freq & 0xff,
296417b7953SAlexey Klimov 		radio->transfer_buffer, 8, 300);
297417b7953SAlexey Klimov 
298417b7953SAlexey Klimov 	if (retval < 0) {
299417b7953SAlexey Klimov 		request = DSB100_TUNE;
300417b7953SAlexey Klimov 		goto usb_control_msg_failed;
301417b7953SAlexey Klimov 	}
302417b7953SAlexey Klimov 
303417b7953SAlexey Klimov 	retval = usb_control_msg(radio->usbdev,
304417b7953SAlexey Klimov 		usb_rcvctrlpipe(radio->usbdev, 0),
3057fb65297SMauro Carvalho Chehab 		USB_REQ_GET_STATUS,
3067fb65297SMauro Carvalho Chehab 		USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
307417b7953SAlexey Klimov 		0x96, 0xB7, radio->transfer_buffer, 8, 300);
308417b7953SAlexey Klimov 
309417b7953SAlexey Klimov 	if (retval < 0) {
310417b7953SAlexey Klimov 		request = USB_REQ_GET_STATUS;
311417b7953SAlexey Klimov 		goto usb_control_msg_failed;
312417b7953SAlexey Klimov 	}
313417b7953SAlexey Klimov 
314417b7953SAlexey Klimov 	retval = usb_control_msg(radio->usbdev,
315417b7953SAlexey Klimov 		usb_rcvctrlpipe(radio->usbdev, 0),
3167fb65297SMauro Carvalho Chehab 		USB_REQ_GET_STATUS,
3177fb65297SMauro Carvalho Chehab 		USB_TYPE_VENDOR | USB_RECIP_DEVICE |  USB_DIR_IN,
318417b7953SAlexey Klimov 		0x00, 0x24, radio->transfer_buffer, 8, 300);
319417b7953SAlexey Klimov 
320417b7953SAlexey Klimov 	if (retval < 0) {
321417b7953SAlexey Klimov 		request = USB_REQ_GET_STATUS;
322417b7953SAlexey Klimov 		goto usb_control_msg_failed;
3237fb65297SMauro Carvalho Chehab 	}
3243a0efc32SAlexey Klimov 
3257fb65297SMauro Carvalho Chehab 	radio->stereo = !((radio->transfer_buffer)[0] & 0x01);
3263a0efc32SAlexey Klimov 	mutex_unlock(&radio->lock);
3277fb65297SMauro Carvalho Chehab 	return (radio->transfer_buffer)[0];
328417b7953SAlexey Klimov 
329417b7953SAlexey Klimov usb_control_msg_failed:
330417b7953SAlexey Klimov 	radio->stereo = -1;
331417b7953SAlexey Klimov 	mutex_unlock(&radio->lock);
332417b7953SAlexey Klimov 	dev_err(&radio->usbdev->dev,
333417b7953SAlexey Klimov 		"%s - usb_control_msg returned %i, request %i\n",
334417b7953SAlexey Klimov 			__func__, retval, request);
335417b7953SAlexey Klimov 	return -1;
3367fb65297SMauro Carvalho Chehab }
3377fb65297SMauro Carvalho Chehab 
3387fb65297SMauro Carvalho Chehab /* return the device status.  This is, in effect, just whether it
3397fb65297SMauro Carvalho Chehab sees a stereo signal or not.  Pity. */
3405aff308cSAlan Cox static void dsbr100_getstat(struct dsbr100_device *radio)
3417fb65297SMauro Carvalho Chehab {
342417b7953SAlexey Klimov 	int retval;
343417b7953SAlexey Klimov 
3443a0efc32SAlexey Klimov 	mutex_lock(&radio->lock);
345417b7953SAlexey Klimov 
346417b7953SAlexey Klimov 	retval = usb_control_msg(radio->usbdev,
347417b7953SAlexey Klimov 		usb_rcvctrlpipe(radio->usbdev, 0),
3487fb65297SMauro Carvalho Chehab 		USB_REQ_GET_STATUS,
3497fb65297SMauro Carvalho Chehab 		USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
350417b7953SAlexey Klimov 		0x00 , 0x24, radio->transfer_buffer, 8, 300);
351417b7953SAlexey Klimov 
352417b7953SAlexey Klimov 	if (retval < 0) {
3537fb65297SMauro Carvalho Chehab 		radio->stereo = -1;
354417b7953SAlexey Klimov 		dev_err(&radio->usbdev->dev,
355417b7953SAlexey Klimov 			"%s - usb_control_msg returned %i, request %i\n",
356417b7953SAlexey Klimov 				__func__, retval, USB_REQ_GET_STATUS);
357417b7953SAlexey Klimov 	} else {
3587fb65297SMauro Carvalho Chehab 		radio->stereo = !(radio->transfer_buffer[0] & 0x01);
359417b7953SAlexey Klimov 	}
360417b7953SAlexey Klimov 
3613a0efc32SAlexey Klimov 	mutex_unlock(&radio->lock);
3627fb65297SMauro Carvalho Chehab }
3637fb65297SMauro Carvalho Chehab 
3647fb65297SMauro Carvalho Chehab /* USB subsystem interface begins here */
3657fb65297SMauro Carvalho Chehab 
366fc55bcb0SAlexey Klimov /*
367fc55bcb0SAlexey Klimov  * Handle unplugging of the device.
368fc55bcb0SAlexey Klimov  * We call video_unregister_device in any case.
369fc55bcb0SAlexey Klimov  * The last function called in this procedure is
370fc55bcb0SAlexey Klimov  * usb_dsbr100_video_device_release
371fc55bcb0SAlexey Klimov  */
3727fb65297SMauro Carvalho Chehab static void usb_dsbr100_disconnect(struct usb_interface *intf)
3737fb65297SMauro Carvalho Chehab {
3745aff308cSAlan Cox 	struct dsbr100_device *radio = usb_get_intfdata(intf);
3757fb65297SMauro Carvalho Chehab 
3767fb65297SMauro Carvalho Chehab 	usb_set_intfdata (intf, NULL);
3773a0efc32SAlexey Klimov 
3783a0efc32SAlexey Klimov 	mutex_lock(&radio->lock);
3797fb65297SMauro Carvalho Chehab 	radio->removed = 1;
3803a0efc32SAlexey Klimov 	mutex_unlock(&radio->lock);
3813a0efc32SAlexey Klimov 
3823a0efc32SAlexey Klimov 	video_unregister_device(&radio->videodev);
3837fb65297SMauro Carvalho Chehab }
3847fb65297SMauro Carvalho Chehab 
3857fb65297SMauro Carvalho Chehab 
3867002a4f3SDouglas Landgraf static int vidioc_querycap(struct file *file, void *priv,
3877002a4f3SDouglas Landgraf 					struct v4l2_capability *v)
3887fb65297SMauro Carvalho Chehab {
3895aff308cSAlan Cox 	strlcpy(v->driver, "dsbr100", sizeof(v->driver));
3905aff308cSAlan Cox 	strlcpy(v->card, "D-Link R-100 USB FM Radio", sizeof(v->card));
391448441cfSAlexey Klimov 	sprintf(v->bus_info, "USB");
3925aff308cSAlan Cox 	v->version = RADIO_VERSION;
3935aff308cSAlan Cox 	v->capabilities = V4L2_CAP_TUNER;
3947fb65297SMauro Carvalho Chehab 	return 0;
3957fb65297SMauro Carvalho Chehab }
3967002a4f3SDouglas Landgraf 
3977002a4f3SDouglas Landgraf static int vidioc_g_tuner(struct file *file, void *priv,
3987002a4f3SDouglas Landgraf 				struct v4l2_tuner *v)
3995aff308cSAlan Cox {
400c170ecf4SHans Verkuil 	struct dsbr100_device *radio = video_drvdata(file);
4015aff308cSAlan Cox 
4023a0efc32SAlexey Klimov 	/* safety check */
4033a0efc32SAlexey Klimov 	if (radio->removed)
4043a0efc32SAlexey Klimov 		return -EIO;
4053a0efc32SAlexey Klimov 
4065aff308cSAlan Cox 	if (v->index > 0)
4075aff308cSAlan Cox 		return -EINVAL;
4087fb65297SMauro Carvalho Chehab 
4097fb65297SMauro Carvalho Chehab 	dsbr100_getstat(radio);
4105aff308cSAlan Cox 	strcpy(v->name, "FM");
4115aff308cSAlan Cox 	v->type = V4L2_TUNER_RADIO;
4127fb65297SMauro Carvalho Chehab 	v->rangelow = FREQ_MIN * FREQ_MUL;
4137fb65297SMauro Carvalho Chehab 	v->rangehigh = FREQ_MAX * FREQ_MUL;
4145aff308cSAlan Cox 	v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
4155aff308cSAlan Cox 	v->capability = V4L2_TUNER_CAP_LOW;
4165aff308cSAlan Cox 	if(radio->stereo)
4175aff308cSAlan Cox 		v->audmode = V4L2_TUNER_MODE_STEREO;
4185aff308cSAlan Cox 	else
4195aff308cSAlan Cox 		v->audmode = V4L2_TUNER_MODE_MONO;
4207002a4f3SDouglas Landgraf 	v->signal = 0xffff;     /* We can't get the signal strength */
4217fb65297SMauro Carvalho Chehab 	return 0;
4227fb65297SMauro Carvalho Chehab }
4237fb65297SMauro Carvalho Chehab 
4247002a4f3SDouglas Landgraf static int vidioc_s_tuner(struct file *file, void *priv,
4257002a4f3SDouglas Landgraf 				struct v4l2_tuner *v)
4267002a4f3SDouglas Landgraf {
4273a0efc32SAlexey Klimov 	struct dsbr100_device *radio = video_drvdata(file);
4283a0efc32SAlexey Klimov 
4293a0efc32SAlexey Klimov 	/* safety check */
4303a0efc32SAlexey Klimov 	if (radio->removed)
4313a0efc32SAlexey Klimov 		return -EIO;
4323a0efc32SAlexey Klimov 
4335aff308cSAlan Cox 	if (v->index > 0)
4347fb65297SMauro Carvalho Chehab 		return -EINVAL;
4355aff308cSAlan Cox 
4367fb65297SMauro Carvalho Chehab 	return 0;
4377fb65297SMauro Carvalho Chehab }
4387002a4f3SDouglas Landgraf 
4397002a4f3SDouglas Landgraf static int vidioc_s_frequency(struct file *file, void *priv,
4407002a4f3SDouglas Landgraf 				struct v4l2_frequency *f)
4415aff308cSAlan Cox {
442c170ecf4SHans Verkuil 	struct dsbr100_device *radio = video_drvdata(file);
443417b7953SAlexey Klimov 	int retval;
4447fb65297SMauro Carvalho Chehab 
4453a0efc32SAlexey Klimov 	/* safety check */
4463a0efc32SAlexey Klimov 	if (radio->removed)
4473a0efc32SAlexey Klimov 		return -EIO;
4483a0efc32SAlexey Klimov 
4495aff308cSAlan Cox 	radio->curfreq = f->frequency;
450417b7953SAlexey Klimov 	retval = dsbr100_setfreq(radio, radio->curfreq);
451417b7953SAlexey Klimov 	if (retval == -1)
452aa82661bSGreg Kroah-Hartman 		dev_warn(&radio->usbdev->dev, "Set frequency failed\n");
4537fb65297SMauro Carvalho Chehab 	return 0;
4547fb65297SMauro Carvalho Chehab }
4557002a4f3SDouglas Landgraf 
4567002a4f3SDouglas Landgraf static int vidioc_g_frequency(struct file *file, void *priv,
4577002a4f3SDouglas Landgraf 				struct v4l2_frequency *f)
4585aff308cSAlan Cox {
459c170ecf4SHans Verkuil 	struct dsbr100_device *radio = video_drvdata(file);
4607fb65297SMauro Carvalho Chehab 
4613a0efc32SAlexey Klimov 	/* safety check */
4623a0efc32SAlexey Klimov 	if (radio->removed)
4633a0efc32SAlexey Klimov 		return -EIO;
4643a0efc32SAlexey Klimov 
4655aff308cSAlan Cox 	f->type = V4L2_TUNER_RADIO;
4665aff308cSAlan Cox 	f->frequency = radio->curfreq;
4677fb65297SMauro Carvalho Chehab 	return 0;
4687fb65297SMauro Carvalho Chehab }
4697002a4f3SDouglas Landgraf 
4707002a4f3SDouglas Landgraf static int vidioc_queryctrl(struct file *file, void *priv,
4717002a4f3SDouglas Landgraf 				struct v4l2_queryctrl *qc)
4725aff308cSAlan Cox {
4735aff308cSAlan Cox 	int i;
4747fb65297SMauro Carvalho Chehab 
4755aff308cSAlan Cox 	for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
4765aff308cSAlan Cox 		if (qc->id && qc->id == radio_qctrl[i].id) {
477223377e7SAlexey Klimov 			memcpy(qc, &(radio_qctrl[i]), sizeof(*qc));
4785aff308cSAlan Cox 			return 0;
4795aff308cSAlan Cox 		}
4805aff308cSAlan Cox 	}
4817fb65297SMauro Carvalho Chehab 	return -EINVAL;
4825aff308cSAlan Cox }
4837002a4f3SDouglas Landgraf 
4847002a4f3SDouglas Landgraf static int vidioc_g_ctrl(struct file *file, void *priv,
4857002a4f3SDouglas Landgraf 				struct v4l2_control *ctrl)
4865aff308cSAlan Cox {
487c170ecf4SHans Verkuil 	struct dsbr100_device *radio = video_drvdata(file);
4885aff308cSAlan Cox 
4893a0efc32SAlexey Klimov 	/* safety check */
4903a0efc32SAlexey Klimov 	if (radio->removed)
4913a0efc32SAlexey Klimov 		return -EIO;
4923a0efc32SAlexey Klimov 
4935aff308cSAlan Cox 	switch (ctrl->id) {
4945aff308cSAlan Cox 	case V4L2_CID_AUDIO_MUTE:
4955aff308cSAlan Cox 		ctrl->value = radio->muted;
4965aff308cSAlan Cox 		return 0;
4975aff308cSAlan Cox 	}
4985aff308cSAlan Cox 	return -EINVAL;
4995aff308cSAlan Cox }
5007002a4f3SDouglas Landgraf 
5017002a4f3SDouglas Landgraf static int vidioc_s_ctrl(struct file *file, void *priv,
5027002a4f3SDouglas Landgraf 				struct v4l2_control *ctrl)
5035aff308cSAlan Cox {
504c170ecf4SHans Verkuil 	struct dsbr100_device *radio = video_drvdata(file);
505417b7953SAlexey Klimov 	int retval;
5065aff308cSAlan Cox 
5073a0efc32SAlexey Klimov 	/* safety check */
5083a0efc32SAlexey Klimov 	if (radio->removed)
5093a0efc32SAlexey Klimov 		return -EIO;
5103a0efc32SAlexey Klimov 
5115aff308cSAlan Cox 	switch (ctrl->id) {
5125aff308cSAlan Cox 	case V4L2_CID_AUDIO_MUTE:
5135aff308cSAlan Cox 		if (ctrl->value) {
514417b7953SAlexey Klimov 			retval = dsbr100_stop(radio);
515417b7953SAlexey Klimov 			if (retval == -1) {
516aa82661bSGreg Kroah-Hartman 				dev_warn(&radio->usbdev->dev,
517aa82661bSGreg Kroah-Hartman 					 "Radio did not respond properly\n");
51890b698ddSAlexey Klimov 				return -EBUSY;
51990b698ddSAlexey Klimov 			}
5205aff308cSAlan Cox 		} else {
521417b7953SAlexey Klimov 			retval = dsbr100_start(radio);
522417b7953SAlexey Klimov 			if (retval == -1) {
523aa82661bSGreg Kroah-Hartman 				dev_warn(&radio->usbdev->dev,
524aa82661bSGreg Kroah-Hartman 					 "Radio did not respond properly\n");
52590b698ddSAlexey Klimov 				return -EBUSY;
52690b698ddSAlexey Klimov 			}
5275aff308cSAlan Cox 		}
5287fb65297SMauro Carvalho Chehab 		return 0;
5297fb65297SMauro Carvalho Chehab 	}
5305aff308cSAlan Cox 	return -EINVAL;
5315aff308cSAlan Cox }
5327002a4f3SDouglas Landgraf 
5337002a4f3SDouglas Landgraf static int vidioc_g_audio(struct file *file, void *priv,
5347002a4f3SDouglas Landgraf 				struct v4l2_audio *a)
5357002a4f3SDouglas Landgraf {
5367002a4f3SDouglas Landgraf 	if (a->index > 1)
5377002a4f3SDouglas Landgraf 		return -EINVAL;
5387002a4f3SDouglas Landgraf 
5397002a4f3SDouglas Landgraf 	strcpy(a->name, "Radio");
5407002a4f3SDouglas Landgraf 	a->capability = V4L2_AUDCAP_STEREO;
5417002a4f3SDouglas Landgraf 	return 0;
5427fb65297SMauro Carvalho Chehab }
5437fb65297SMauro Carvalho Chehab 
5447002a4f3SDouglas Landgraf static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
5457fb65297SMauro Carvalho Chehab {
5467002a4f3SDouglas Landgraf 	*i = 0;
5477002a4f3SDouglas Landgraf 	return 0;
5487002a4f3SDouglas Landgraf }
5497002a4f3SDouglas Landgraf 
5507002a4f3SDouglas Landgraf static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
5517002a4f3SDouglas Landgraf {
5527002a4f3SDouglas Landgraf 	if (i != 0)
5537002a4f3SDouglas Landgraf 		return -EINVAL;
5547002a4f3SDouglas Landgraf 	return 0;
5557002a4f3SDouglas Landgraf }
5567002a4f3SDouglas Landgraf 
5577002a4f3SDouglas Landgraf static int vidioc_s_audio(struct file *file, void *priv,
5587002a4f3SDouglas Landgraf 					struct v4l2_audio *a)
5597002a4f3SDouglas Landgraf {
5607002a4f3SDouglas Landgraf 	if (a->index != 0)
5617002a4f3SDouglas Landgraf 		return -EINVAL;
5627002a4f3SDouglas Landgraf 	return 0;
5637fb65297SMauro Carvalho Chehab }
5647fb65297SMauro Carvalho Chehab 
5657fb65297SMauro Carvalho Chehab static int usb_dsbr100_open(struct inode *inode, struct file *file)
5667fb65297SMauro Carvalho Chehab {
567c170ecf4SHans Verkuil 	struct dsbr100_device *radio = video_drvdata(file);
568b9f35737SAlexey Klimov 	int retval;
5697fb65297SMauro Carvalho Chehab 
570d56dc612SHans Verkuil 	lock_kernel();
5717fb65297SMauro Carvalho Chehab 	radio->users = 1;
5725aff308cSAlan Cox 	radio->muted = 1;
5735aff308cSAlan Cox 
574417b7953SAlexey Klimov 	retval = dsbr100_start(radio);
575417b7953SAlexey Klimov 	if (retval < 0) {
576aa82661bSGreg Kroah-Hartman 		dev_warn(&radio->usbdev->dev,
577aa82661bSGreg Kroah-Hartman 			 "Radio did not start up properly\n");
5787fb65297SMauro Carvalho Chehab 		radio->users = 0;
579d56dc612SHans Verkuil 		unlock_kernel();
5807fb65297SMauro Carvalho Chehab 		return -EIO;
5817fb65297SMauro Carvalho Chehab 	}
582b9f35737SAlexey Klimov 
583b9f35737SAlexey Klimov 	retval = dsbr100_setfreq(radio, radio->curfreq);
584b9f35737SAlexey Klimov 	if (retval == -1)
585290588e0SAlexey Klimov 		dev_warn(&radio->usbdev->dev,
586290588e0SAlexey Klimov 			"set frequency failed\n");
587b9f35737SAlexey Klimov 
588d56dc612SHans Verkuil 	unlock_kernel();
5897fb65297SMauro Carvalho Chehab 	return 0;
5907fb65297SMauro Carvalho Chehab }
5917fb65297SMauro Carvalho Chehab 
5927fb65297SMauro Carvalho Chehab static int usb_dsbr100_close(struct inode *inode, struct file *file)
5937fb65297SMauro Carvalho Chehab {
594c170ecf4SHans Verkuil 	struct dsbr100_device *radio = video_drvdata(file);
5953a0efc32SAlexey Klimov 	int retval;
5967fb65297SMauro Carvalho Chehab 
5977fb65297SMauro Carvalho Chehab 	if (!radio)
5987fb65297SMauro Carvalho Chehab 		return -ENODEV;
5993a0efc32SAlexey Klimov 
6007fb65297SMauro Carvalho Chehab 	radio->users = 0;
6013a0efc32SAlexey Klimov 	if (!radio->removed) {
6023a0efc32SAlexey Klimov 		retval = dsbr100_stop(radio);
6033a0efc32SAlexey Klimov 		if (retval == -1) {
6043a0efc32SAlexey Klimov 			dev_warn(&radio->usbdev->dev,
6053a0efc32SAlexey Klimov 				"dsbr100_stop failed\n");
6063a0efc32SAlexey Klimov 		}
6073a0efc32SAlexey Klimov 
6087fb65297SMauro Carvalho Chehab 	}
6097fb65297SMauro Carvalho Chehab 	return 0;
6107fb65297SMauro Carvalho Chehab }
6117fb65297SMauro Carvalho Chehab 
61204e0ffbbSAlexey Klimov /* Suspend device - stop device. */
61304e0ffbbSAlexey Klimov static int usb_dsbr100_suspend(struct usb_interface *intf, pm_message_t message)
61404e0ffbbSAlexey Klimov {
61504e0ffbbSAlexey Klimov 	struct dsbr100_device *radio = usb_get_intfdata(intf);
61604e0ffbbSAlexey Klimov 	int retval;
61704e0ffbbSAlexey Klimov 
61804e0ffbbSAlexey Klimov 	retval = dsbr100_stop(radio);
61904e0ffbbSAlexey Klimov 	if (retval == -1)
62004e0ffbbSAlexey Klimov 		dev_warn(&intf->dev, "dsbr100_stop failed\n");
62104e0ffbbSAlexey Klimov 
62204e0ffbbSAlexey Klimov 	dev_info(&intf->dev, "going into suspend..\n");
62304e0ffbbSAlexey Klimov 
62404e0ffbbSAlexey Klimov 	return 0;
62504e0ffbbSAlexey Klimov }
62604e0ffbbSAlexey Klimov 
62704e0ffbbSAlexey Klimov /* Resume device - start device. */
62804e0ffbbSAlexey Klimov static int usb_dsbr100_resume(struct usb_interface *intf)
62904e0ffbbSAlexey Klimov {
63004e0ffbbSAlexey Klimov 	struct dsbr100_device *radio = usb_get_intfdata(intf);
63104e0ffbbSAlexey Klimov 	int retval;
63204e0ffbbSAlexey Klimov 
63304e0ffbbSAlexey Klimov 	retval = dsbr100_start(radio);
63404e0ffbbSAlexey Klimov 	if (retval == -1)
63504e0ffbbSAlexey Klimov 		dev_warn(&intf->dev, "dsbr100_start failed\n");
63604e0ffbbSAlexey Klimov 
63704e0ffbbSAlexey Klimov 	dev_info(&intf->dev, "coming out of suspend..\n");
63804e0ffbbSAlexey Klimov 
63904e0ffbbSAlexey Klimov 	return 0;
64004e0ffbbSAlexey Klimov }
64104e0ffbbSAlexey Klimov 
642fc55bcb0SAlexey Klimov /* free data structures */
6433a0efc32SAlexey Klimov static void usb_dsbr100_video_device_release(struct video_device *videodev)
6443a0efc32SAlexey Klimov {
6453a0efc32SAlexey Klimov 	struct dsbr100_device *radio = videodev_to_radio(videodev);
6463a0efc32SAlexey Klimov 
6473a0efc32SAlexey Klimov 	kfree(radio->transfer_buffer);
6483a0efc32SAlexey Klimov 	kfree(radio);
6493a0efc32SAlexey Klimov }
6503a0efc32SAlexey Klimov 
6517002a4f3SDouglas Landgraf /* File system interface */
6527002a4f3SDouglas Landgraf static const struct file_operations usb_dsbr100_fops = {
6537002a4f3SDouglas Landgraf 	.owner		= THIS_MODULE,
6547002a4f3SDouglas Landgraf 	.open		= usb_dsbr100_open,
6557002a4f3SDouglas Landgraf 	.release	= usb_dsbr100_close,
6567002a4f3SDouglas Landgraf 	.ioctl		= video_ioctl2,
657078ff795SDouglas Schilling Landgraf #ifdef CONFIG_COMPAT
6587002a4f3SDouglas Landgraf 	.compat_ioctl	= v4l_compat_ioctl32,
659078ff795SDouglas Schilling Landgraf #endif
6607002a4f3SDouglas Landgraf 	.llseek		= no_llseek,
6617002a4f3SDouglas Landgraf };
6627002a4f3SDouglas Landgraf 
663a399810cSHans Verkuil static const struct v4l2_ioctl_ops usb_dsbr100_ioctl_ops = {
6647002a4f3SDouglas Landgraf 	.vidioc_querycap    = vidioc_querycap,
6657002a4f3SDouglas Landgraf 	.vidioc_g_tuner     = vidioc_g_tuner,
6667002a4f3SDouglas Landgraf 	.vidioc_s_tuner     = vidioc_s_tuner,
6677002a4f3SDouglas Landgraf 	.vidioc_g_frequency = vidioc_g_frequency,
6687002a4f3SDouglas Landgraf 	.vidioc_s_frequency = vidioc_s_frequency,
6697002a4f3SDouglas Landgraf 	.vidioc_queryctrl   = vidioc_queryctrl,
6707002a4f3SDouglas Landgraf 	.vidioc_g_ctrl      = vidioc_g_ctrl,
6717002a4f3SDouglas Landgraf 	.vidioc_s_ctrl      = vidioc_s_ctrl,
6727002a4f3SDouglas Landgraf 	.vidioc_g_audio     = vidioc_g_audio,
6737002a4f3SDouglas Landgraf 	.vidioc_s_audio     = vidioc_s_audio,
6747002a4f3SDouglas Landgraf 	.vidioc_g_input     = vidioc_g_input,
6757002a4f3SDouglas Landgraf 	.vidioc_s_input     = vidioc_s_input,
6767002a4f3SDouglas Landgraf };
6777002a4f3SDouglas Landgraf 
678a399810cSHans Verkuil /* V4L2 interface */
6793a0efc32SAlexey Klimov static struct video_device dsbr100_videodev_data = {
680a399810cSHans Verkuil 	.name		= "D-Link DSB-R 100",
681a399810cSHans Verkuil 	.fops		= &usb_dsbr100_fops,
682a399810cSHans Verkuil 	.ioctl_ops 	= &usb_dsbr100_ioctl_ops,
6833a0efc32SAlexey Klimov 	.release	= usb_dsbr100_video_device_release,
684a399810cSHans Verkuil };
685a399810cSHans Verkuil 
686fc55bcb0SAlexey Klimov /* check if the device is present and register with v4l and usb if it is */
6877002a4f3SDouglas Landgraf static int usb_dsbr100_probe(struct usb_interface *intf,
6887002a4f3SDouglas Landgraf 				const struct usb_device_id *id)
6897002a4f3SDouglas Landgraf {
6907002a4f3SDouglas Landgraf 	struct dsbr100_device *radio;
691417b7953SAlexey Klimov 	int retval;
6927002a4f3SDouglas Landgraf 
693223377e7SAlexey Klimov 	radio = kmalloc(sizeof(struct dsbr100_device), GFP_KERNEL);
694223377e7SAlexey Klimov 
695223377e7SAlexey Klimov 	if (!radio)
6967002a4f3SDouglas Landgraf 		return -ENOMEM;
697223377e7SAlexey Klimov 
698223377e7SAlexey Klimov 	radio->transfer_buffer = kmalloc(TB_LEN, GFP_KERNEL);
699223377e7SAlexey Klimov 
700223377e7SAlexey Klimov 	if (!(radio->transfer_buffer)) {
701863c86ddSOliver Neukum 		kfree(radio);
702863c86ddSOliver Neukum 		return -ENOMEM;
703863c86ddSOliver Neukum 	}
704223377e7SAlexey Klimov 
7053a0efc32SAlexey Klimov 	mutex_init(&radio->lock);
7063a0efc32SAlexey Klimov 	radio->videodev = dsbr100_videodev_data;
7073a0efc32SAlexey Klimov 
7087002a4f3SDouglas Landgraf 	radio->removed = 0;
7097002a4f3SDouglas Landgraf 	radio->users = 0;
7107002a4f3SDouglas Landgraf 	radio->usbdev = interface_to_usbdev(intf);
7117002a4f3SDouglas Landgraf 	radio->curfreq = FREQ_MIN * FREQ_MUL;
7123a0efc32SAlexey Klimov 	video_set_drvdata(&radio->videodev, radio);
713417b7953SAlexey Klimov 	retval = video_register_device(&radio->videodev, VFL_TYPE_RADIO, radio_nr);
714417b7953SAlexey Klimov 	if (retval < 0) {
7157e1ca849SAlexey Klimov 		dev_err(&intf->dev, "couldn't register video device\n");
716863c86ddSOliver Neukum 		kfree(radio->transfer_buffer);
7177002a4f3SDouglas Landgraf 		kfree(radio);
7187002a4f3SDouglas Landgraf 		return -EIO;
7197002a4f3SDouglas Landgraf 	}
7207002a4f3SDouglas Landgraf 	usb_set_intfdata(intf, radio);
7217002a4f3SDouglas Landgraf 	return 0;
7227002a4f3SDouglas Landgraf }
7237002a4f3SDouglas Landgraf 
7247fb65297SMauro Carvalho Chehab static int __init dsbr100_init(void)
7257fb65297SMauro Carvalho Chehab {
7267fb65297SMauro Carvalho Chehab 	int retval = usb_register(&usb_dsbr100_driver);
727a482f327SGreg Kroah-Hartman 	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
728a482f327SGreg Kroah-Hartman 	       DRIVER_DESC "\n");
7297fb65297SMauro Carvalho Chehab 	return retval;
7307fb65297SMauro Carvalho Chehab }
7317fb65297SMauro Carvalho Chehab 
7327fb65297SMauro Carvalho Chehab static void __exit dsbr100_exit(void)
7337fb65297SMauro Carvalho Chehab {
7347fb65297SMauro Carvalho Chehab 	usb_deregister(&usb_dsbr100_driver);
7357fb65297SMauro Carvalho Chehab }
7367fb65297SMauro Carvalho Chehab 
7377fb65297SMauro Carvalho Chehab module_init (dsbr100_init);
7387fb65297SMauro Carvalho Chehab module_exit (dsbr100_exit);
7397fb65297SMauro Carvalho Chehab 
7407fb65297SMauro Carvalho Chehab MODULE_AUTHOR( DRIVER_AUTHOR );
7417fb65297SMauro Carvalho Chehab MODULE_DESCRIPTION( DRIVER_DESC );
7427fb65297SMauro Carvalho Chehab MODULE_LICENSE("GPL");
743