xref: /openbmc/linux/drivers/media/radio/dsbr100.c (revision e83ce300)
1fc55bcb0SAlexey Klimov // SPDX-License-Identifier: GPL-2.0-or-later
25d778648SHans Verkuil /* A driver for the D-Link DSB-R100 USB radio and Gemtek USB Radio 21.
35d778648SHans Verkuil  * The device plugs into both the USB and an analog audio input, so this thing
45d778648SHans Verkuil  * only deals with initialisation and frequency setting, the
55d778648SHans Verkuil  * audio data has to be handled by a sound driver.
65d778648SHans Verkuil  *
75d778648SHans Verkuil  * Major issue: I can't find out where the device reports the signal
85d778648SHans Verkuil  * strength, and indeed the windows software appearantly just looks
95d778648SHans Verkuil  * at the stereo indicator as well.  So, scanning will only find
105d778648SHans Verkuil  * stereo stations.  Sad, but I can't help it.
115d778648SHans Verkuil  *
125d778648SHans Verkuil  * Also, the windows program sends oodles of messages over to the
135d778648SHans Verkuil  * device, and I couldn't figure out their meaning.  My suspicion
145d778648SHans Verkuil  * is that they don't have any:-)
155d778648SHans Verkuil  *
165d778648SHans Verkuil  * You might find some interesting stuff about this module at
175d778648SHans Verkuil  * http://unimut.fsk.uni-heidelberg.de/unimut/demi/dsbr
185d778648SHans Verkuil  *
195d778648SHans Verkuil  * Fully tested with the Keene USB FM Transmitter and the v4l2-compliance tool.
205d778648SHans Verkuil  *
215d778648SHans Verkuil  * Copyright (c) 2000 Markus Demleitner <msdemlei@cl.uni-heidelberg.de>
225d778648SHans Verkuil */
235d778648SHans Verkuil 
245d778648SHans Verkuil #include <linux/kernel.h>
255d778648SHans Verkuil #include <linux/module.h>
265d778648SHans Verkuil #include <linux/init.h>
275d778648SHans Verkuil #include <linux/slab.h>
285d778648SHans Verkuil #include <linux/input.h>
295d778648SHans Verkuil #include <linux/videodev2.h>
305d778648SHans Verkuil #include <linux/usb.h>
317fb65297SMauro Carvalho Chehab #include <media/v4l2-device.h>
327fb65297SMauro Carvalho Chehab #include <media/v4l2-ioctl.h>
337fb65297SMauro Carvalho Chehab #include <media/v4l2-ctrls.h>
347fb65297SMauro Carvalho Chehab #include <media/v4l2-event.h>
357fb65297SMauro Carvalho Chehab 
367fb65297SMauro Carvalho Chehab /*
377fb65297SMauro Carvalho Chehab  * Version Information
385aff308cSAlan Cox  */
395d778648SHans Verkuil MODULE_AUTHOR("Markus Demleitner <msdemlei@tucana.harvard.edu>");
40406827ccSAlexey Klimov MODULE_DESCRIPTION("D-Link DSB-R100 USB FM radio driver");
4135ea11ffSHans Verkuil MODULE_LICENSE("GPL");
425d778648SHans Verkuil MODULE_VERSION("1.1.0");
435d778648SHans Verkuil 
447fb65297SMauro Carvalho Chehab #define DSB100_VENDOR 0x04b4
457fb65297SMauro Carvalho Chehab #define DSB100_PRODUCT 0x1002
467fb65297SMauro Carvalho Chehab 
477fb65297SMauro Carvalho Chehab /* Commands the device appears to understand */
485d778648SHans Verkuil #define DSB100_TUNE 1
495d778648SHans Verkuil #define DSB100_ONOFF 2
505d778648SHans Verkuil 
515d778648SHans Verkuil #define TB_LEN 16
527fb65297SMauro Carvalho Chehab 
537fb65297SMauro Carvalho Chehab /* Frequency limits in MHz -- these are European values.  For Japanese
547fb65297SMauro Carvalho Chehab devices, that would be 76 and 91.  */
557fb65297SMauro Carvalho Chehab #define FREQ_MIN  87.5
567fb65297SMauro Carvalho Chehab #define FREQ_MAX 108.0
577fb65297SMauro Carvalho Chehab #define FREQ_MUL 16000
587fb65297SMauro Carvalho Chehab 
597fb65297SMauro Carvalho Chehab #define v4l2_dev_to_radio(d) container_of(d, struct dsbr100_device, v4l2_dev)
607fb65297SMauro Carvalho Chehab 
617fb65297SMauro Carvalho Chehab static int radio_nr = -1;
627fb65297SMauro Carvalho Chehab module_param(radio_nr, int, 0);
637fb65297SMauro Carvalho Chehab 
647fb65297SMauro Carvalho Chehab /* Data for one (physical) device */
657fb65297SMauro Carvalho Chehab struct dsbr100_device {
667fb65297SMauro Carvalho Chehab 	struct usb_device *usbdev;
677fb65297SMauro Carvalho Chehab 	struct video_device videodev;
6813099294SHans Verkuil 	struct v4l2_device v4l2_dev;
697fb65297SMauro Carvalho Chehab 	struct v4l2_ctrl_handler hdl;
707fb65297SMauro Carvalho Chehab 
717fb65297SMauro Carvalho Chehab 	u8 *transfer_buffer;
727fb65297SMauro Carvalho Chehab 	struct mutex v4l2_lock;
737fb65297SMauro Carvalho Chehab 	int curfreq;
745aff308cSAlan Cox 	bool stereo;
757fb65297SMauro Carvalho Chehab 	bool muted;
763a0efc32SAlexey Klimov };
77406827ccSAlexey Klimov 
785d778648SHans Verkuil /* Low-level device interface begins here */
79406827ccSAlexey Klimov 
80863c86ddSOliver Neukum /* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */
dsbr100_setfreq(struct dsbr100_device * radio,unsigned freq)81e64d07c9SHans Verkuil static int dsbr100_setfreq(struct dsbr100_device *radio, unsigned freq)
827fb65297SMauro Carvalho Chehab {
835d778648SHans Verkuil 	unsigned f = (freq / 16 * 80) / 1000 + 856;
845d778648SHans Verkuil 	int retval = 0;
857fb65297SMauro Carvalho Chehab 
867fb65297SMauro Carvalho Chehab 	if (!radio->muted) {
877fb65297SMauro Carvalho Chehab 		retval = usb_control_msg(radio->usbdev,
887fb65297SMauro Carvalho Chehab 				usb_rcvctrlpipe(radio->usbdev, 0),
895d778648SHans Verkuil 				DSB100_TUNE,
905d778648SHans Verkuil 				USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
915d778648SHans Verkuil 				(f >> 8) & 0x00ff, f & 0xff,
925d778648SHans Verkuil 				radio->transfer_buffer, 8, 300);
935d778648SHans Verkuil 		if (retval >= 0)
945d778648SHans Verkuil 			mdelay(1);
955d778648SHans Verkuil 	}
965d778648SHans Verkuil 
975d778648SHans Verkuil 	if (retval >= 0) {
985d778648SHans Verkuil 		radio->curfreq = freq;
995d778648SHans Verkuil 		return 0;
1005d778648SHans Verkuil 	}
1015d778648SHans Verkuil 	dev_err(&radio->usbdev->dev,
1025d778648SHans Verkuil 		"%s - usb_control_msg returned %i, request %i\n",
1035d778648SHans Verkuil 			__func__, retval, DSB100_TUNE);
1045d778648SHans Verkuil 	return retval;
1055d778648SHans Verkuil }
1065d778648SHans Verkuil 
1075d778648SHans Verkuil /* switch on radio */
dsbr100_start(struct dsbr100_device * radio)1085d778648SHans Verkuil static int dsbr100_start(struct dsbr100_device *radio)
1095d778648SHans Verkuil {
1105d778648SHans Verkuil 	int retval = usb_control_msg(radio->usbdev,
1115d778648SHans Verkuil 		usb_rcvctrlpipe(radio->usbdev, 0),
1125d778648SHans Verkuil 		DSB100_ONOFF,
1135d778648SHans Verkuil 		USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
1145d778648SHans Verkuil 		0x01, 0x00, radio->transfer_buffer, 8, 300);
1155d778648SHans Verkuil 
1167fb65297SMauro Carvalho Chehab 	if (retval >= 0)
1175aff308cSAlan Cox 		return dsbr100_setfreq(radio, radio->curfreq);
1187fb65297SMauro Carvalho Chehab 	dev_err(&radio->usbdev->dev,
1195d778648SHans Verkuil 		"%s - usb_control_msg returned %i, request %i\n",
120417b7953SAlexey Klimov 			__func__, retval, DSB100_ONOFF);
1217fb65297SMauro Carvalho Chehab 	return retval;
1227fb65297SMauro Carvalho Chehab 
123417b7953SAlexey Klimov }
124417b7953SAlexey Klimov 
1255d778648SHans Verkuil /* switch off radio */
dsbr100_stop(struct dsbr100_device * radio)1265d778648SHans Verkuil static int dsbr100_stop(struct dsbr100_device *radio)
127417b7953SAlexey Klimov {
128417b7953SAlexey Klimov 	int retval = usb_control_msg(radio->usbdev,
1295d778648SHans Verkuil 		usb_rcvctrlpipe(radio->usbdev, 0),
130d25cb646SAlexey Klimov 		DSB100_ONOFF,
131417b7953SAlexey Klimov 		USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
1327fb65297SMauro Carvalho Chehab 		0x00, 0x00, radio->transfer_buffer, 8, 300);
1337fb65297SMauro Carvalho Chehab 
1347fb65297SMauro Carvalho Chehab 	if (retval >= 0)
1355aff308cSAlan Cox 		return 0;
1367fb65297SMauro Carvalho Chehab 	dev_err(&radio->usbdev->dev,
1375d778648SHans Verkuil 		"%s - usb_control_msg returned %i, request %i\n",
138417b7953SAlexey Klimov 			__func__, retval, DSB100_ONOFF);
1397fb65297SMauro Carvalho Chehab 	return retval;
1407fb65297SMauro Carvalho Chehab 
141417b7953SAlexey Klimov }
142417b7953SAlexey Klimov 
1435d778648SHans Verkuil /* return the device status.  This is, in effect, just whether it
1445d778648SHans Verkuil sees a stereo signal or not.  Pity. */
dsbr100_getstat(struct dsbr100_device * radio)145417b7953SAlexey Klimov static void dsbr100_getstat(struct dsbr100_device *radio)
146417b7953SAlexey Klimov {
1475d778648SHans Verkuil 	int retval = usb_control_msg(radio->usbdev,
148d25cb646SAlexey Klimov 		usb_rcvctrlpipe(radio->usbdev, 0),
149417b7953SAlexey Klimov 		USB_REQ_GET_STATUS,
1507fb65297SMauro Carvalho Chehab 		USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
1517fb65297SMauro Carvalho Chehab 		0x00, 0x24, radio->transfer_buffer, 8, 300);
1527fb65297SMauro Carvalho Chehab 
1537fb65297SMauro Carvalho Chehab 	if (retval < 0) {
1545aff308cSAlan Cox 		radio->stereo = false;
1557fb65297SMauro Carvalho Chehab 		dev_err(&radio->usbdev->dev,
1565d778648SHans Verkuil 			"%s - usb_control_msg returned %i, request %i\n",
157417b7953SAlexey Klimov 				__func__, retval, USB_REQ_GET_STATUS);
1587fb65297SMauro Carvalho Chehab 	} else {
1597fb65297SMauro Carvalho Chehab 		radio->stereo = !(radio->transfer_buffer[0] & 0x01);
160417b7953SAlexey Klimov 	}
161417b7953SAlexey Klimov }
162417b7953SAlexey Klimov 
vidioc_querycap(struct file * file,void * priv,struct v4l2_capability * v)1635d778648SHans Verkuil static int vidioc_querycap(struct file *file, void *priv,
164417b7953SAlexey Klimov 					struct v4l2_capability *v)
165417b7953SAlexey Klimov {
166417b7953SAlexey Klimov 	struct dsbr100_device *radio = video_drvdata(file);
167417b7953SAlexey Klimov 
1687fb65297SMauro Carvalho Chehab 	strscpy(v->driver, "dsbr100", sizeof(v->driver));
169417b7953SAlexey Klimov 	strscpy(v->card, "D-Link R-100 USB FM Radio", sizeof(v->card));
1707fb65297SMauro Carvalho Chehab 	usb_make_path(radio->usbdev, v->bus_info, sizeof(v->bus_info));
1717fb65297SMauro Carvalho Chehab 	return 0;
1727002a4f3SDouglas Landgraf }
1737002a4f3SDouglas Landgraf 
vidioc_g_tuner(struct file * file,void * priv,struct v4l2_tuner * v)1747fb65297SMauro Carvalho Chehab static int vidioc_g_tuner(struct file *file, void *priv,
175c7181cfaSAlexey Klimov 				struct v4l2_tuner *v)
176c7181cfaSAlexey Klimov {
177c0decac1SMauro Carvalho Chehab 	struct dsbr100_device *radio = video_drvdata(file);
178c0decac1SMauro Carvalho Chehab 
179c7181cfaSAlexey Klimov 	if (v->index > 0)
1807fb65297SMauro Carvalho Chehab 		return -EINVAL;
1817fb65297SMauro Carvalho Chehab 
1827002a4f3SDouglas Landgraf 	dsbr100_getstat(radio);
1837002a4f3SDouglas Landgraf 	strscpy(v->name, "FM", sizeof(v->name));
1847002a4f3SDouglas Landgraf 	v->type = V4L2_TUNER_RADIO;
1855aff308cSAlan Cox 	v->rangelow = FREQ_MIN * FREQ_MUL;
186c170ecf4SHans Verkuil 	v->rangehigh = FREQ_MAX * FREQ_MUL;
1875aff308cSAlan Cox 	v->rxsubchans = radio->stereo ? V4L2_TUNER_SUB_STEREO :
1885aff308cSAlan Cox 		V4L2_TUNER_SUB_MONO;
1895aff308cSAlan Cox 	v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
1907fb65297SMauro Carvalho Chehab 	v->audmode = V4L2_TUNER_MODE_STEREO;
1917fb65297SMauro Carvalho Chehab 	v->signal = radio->stereo ? 0xffff : 0;     /* We can't get the signal strength */
192cc1e6315SMauro Carvalho Chehab 	return 0;
1935aff308cSAlan Cox }
1947fb65297SMauro Carvalho Chehab 
vidioc_s_tuner(struct file * file,void * priv,const struct v4l2_tuner * v)1957fb65297SMauro Carvalho Chehab static int vidioc_s_tuner(struct file *file, void *priv,
1965d778648SHans Verkuil 				const struct v4l2_tuner *v)
1975d778648SHans Verkuil {
1985d778648SHans Verkuil 	return v->index ? -EINVAL : 0;
1995aff308cSAlan Cox }
2005d778648SHans Verkuil 
vidioc_s_frequency(struct file * file,void * priv,const struct v4l2_frequency * f)2017fb65297SMauro Carvalho Chehab static int vidioc_s_frequency(struct file *file, void *priv,
2027fb65297SMauro Carvalho Chehab 				const struct v4l2_frequency *f)
2037fb65297SMauro Carvalho Chehab {
2047002a4f3SDouglas Landgraf 	struct dsbr100_device *radio = video_drvdata(file);
2052f73c7c5SHans Verkuil 
2067002a4f3SDouglas Landgraf 	if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
20713099294SHans Verkuil 		return -EINVAL;
2087fb65297SMauro Carvalho Chehab 
2097002a4f3SDouglas Landgraf 	return dsbr100_setfreq(radio, clamp_t(unsigned, f->frequency,
2107002a4f3SDouglas Landgraf 			FREQ_MIN * FREQ_MUL, FREQ_MAX * FREQ_MUL));
211b530a447SHans Verkuil }
2125aff308cSAlan Cox 
vidioc_g_frequency(struct file * file,void * priv,struct v4l2_frequency * f)213c170ecf4SHans Verkuil static int vidioc_g_frequency(struct file *file, void *priv,
2147fb65297SMauro Carvalho Chehab 				struct v4l2_frequency *f)
2155d778648SHans Verkuil {
2165d778648SHans Verkuil 	struct dsbr100_device *radio = video_drvdata(file);
217fdf9c997SAlexey Klimov 
2185d778648SHans Verkuil 	if (f->tuner)
2195d778648SHans Verkuil 		return -EINVAL;
2207fb65297SMauro Carvalho Chehab 	f->type = V4L2_TUNER_RADIO;
2217002a4f3SDouglas Landgraf 	f->frequency = radio->curfreq;
2227002a4f3SDouglas Landgraf 	return 0;
2237002a4f3SDouglas Landgraf }
2245aff308cSAlan Cox 
usb_dsbr100_s_ctrl(struct v4l2_ctrl * ctrl)225c170ecf4SHans Verkuil static int usb_dsbr100_s_ctrl(struct v4l2_ctrl *ctrl)
2267fb65297SMauro Carvalho Chehab {
2275d778648SHans Verkuil 	struct dsbr100_device *radio =
2285d778648SHans Verkuil 		container_of(ctrl->handler, struct dsbr100_device, hdl);
2295aff308cSAlan Cox 
2305aff308cSAlan Cox 	switch (ctrl->id) {
2317fb65297SMauro Carvalho Chehab 	case V4L2_CID_AUDIO_MUTE:
2327fb65297SMauro Carvalho Chehab 		radio->muted = ctrl->val;
2337002a4f3SDouglas Landgraf 		return radio->muted ? dsbr100_stop(radio) : dsbr100_start(radio);
2345d778648SHans Verkuil 	}
2355aff308cSAlan Cox 	return -EINVAL;
2365d778648SHans Verkuil }
2375d778648SHans Verkuil 
2385aff308cSAlan Cox 
2395aff308cSAlan Cox /* USB subsystem interface begins here */
2405aff308cSAlan Cox 
2415d778648SHans Verkuil /*
2425d778648SHans Verkuil  * Handle unplugging of the device.
2435aff308cSAlan Cox  * We call video_unregister_device in any case.
2445aff308cSAlan Cox  * The last function called in this procedure is
2455aff308cSAlan Cox  * usb_dsbr100_video_device_release
2467002a4f3SDouglas Landgraf  */
usb_dsbr100_disconnect(struct usb_interface * intf)2477fb65297SMauro Carvalho Chehab static void usb_dsbr100_disconnect(struct usb_interface *intf)
248e64d07c9SHans Verkuil {
249e64d07c9SHans Verkuil 	struct dsbr100_device *radio = usb_get_intfdata(intf);
250e64d07c9SHans Verkuil 
251e64d07c9SHans Verkuil 	mutex_lock(&radio->v4l2_lock);
252e64d07c9SHans Verkuil 	/*
253e64d07c9SHans Verkuil 	 * Disconnect is also called on unload, and in that case we need to
254e64d07c9SHans Verkuil 	 * mute the device. This call will silently fail if it is called
255e64d07c9SHans Verkuil 	 * after a physical disconnect.
256e64d07c9SHans Verkuil 	 */
257e64d07c9SHans Verkuil 	usb_control_msg(radio->usbdev,
258e64d07c9SHans Verkuil 		usb_rcvctrlpipe(radio->usbdev, 0),
259e64d07c9SHans Verkuil 		DSB100_ONOFF,
260e64d07c9SHans Verkuil 		USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
2615d778648SHans Verkuil 		0x00, 0x00, radio->transfer_buffer, 8, 300);
2625d778648SHans Verkuil 	usb_set_intfdata(intf, NULL);
2635d778648SHans Verkuil 	video_unregister_device(&radio->videodev);
2645d778648SHans Verkuil 	v4l2_device_disconnect(&radio->v4l2_dev);
2655d778648SHans Verkuil 	mutex_unlock(&radio->v4l2_lock);
2665d778648SHans Verkuil 	v4l2_device_put(&radio->v4l2_dev);
2675d778648SHans Verkuil }
2685d778648SHans Verkuil 
2695d778648SHans Verkuil 
2705d778648SHans Verkuil /* Suspend device - stop device. */
usb_dsbr100_suspend(struct usb_interface * intf,pm_message_t message)27113099294SHans Verkuil static int usb_dsbr100_suspend(struct usb_interface *intf, pm_message_t message)
272e64d07c9SHans Verkuil {
273e64d07c9SHans Verkuil 	struct dsbr100_device *radio = usb_get_intfdata(intf);
27413099294SHans Verkuil 
27513099294SHans Verkuil 	mutex_lock(&radio->v4l2_lock);
276e64d07c9SHans Verkuil 	if (!radio->muted && dsbr100_stop(radio) < 0)
277e64d07c9SHans Verkuil 		dev_warn(&intf->dev, "dsbr100_stop failed\n");
278e64d07c9SHans Verkuil 	mutex_unlock(&radio->v4l2_lock);
27904e0ffbbSAlexey Klimov 
28004e0ffbbSAlexey Klimov 	dev_info(&intf->dev, "going into suspend..\n");
28104e0ffbbSAlexey Klimov 	return 0;
28204e0ffbbSAlexey Klimov }
28304e0ffbbSAlexey Klimov 
284e64d07c9SHans Verkuil /* Resume device - start device. */
usb_dsbr100_resume(struct usb_interface * intf)2855d778648SHans Verkuil static int usb_dsbr100_resume(struct usb_interface *intf)
28604e0ffbbSAlexey Klimov {
287e64d07c9SHans Verkuil 	struct dsbr100_device *radio = usb_get_intfdata(intf);
288f1ca0adfSAlexey Klimov 
28904e0ffbbSAlexey Klimov 	mutex_lock(&radio->v4l2_lock);
29004e0ffbbSAlexey Klimov 	if (!radio->muted && dsbr100_start(radio) < 0)
29104e0ffbbSAlexey Klimov 		dev_warn(&intf->dev, "dsbr100_start failed\n");
29204e0ffbbSAlexey Klimov 	mutex_unlock(&radio->v4l2_lock);
29304e0ffbbSAlexey Klimov 
29404e0ffbbSAlexey Klimov 	dev_info(&intf->dev, "coming out of suspend..\n");
29504e0ffbbSAlexey Klimov 	return 0;
29604e0ffbbSAlexey Klimov }
29704e0ffbbSAlexey Klimov 
298e64d07c9SHans Verkuil /* free data structures */
usb_dsbr100_release(struct v4l2_device * v4l2_dev)2995d778648SHans Verkuil static void usb_dsbr100_release(struct v4l2_device *v4l2_dev)
30004e0ffbbSAlexey Klimov {
301e64d07c9SHans Verkuil 	struct dsbr100_device *radio = v4l2_dev_to_radio(v4l2_dev);
30204e0ffbbSAlexey Klimov 
30304e0ffbbSAlexey Klimov 	v4l2_ctrl_handler_free(&radio->hdl);
30404e0ffbbSAlexey Klimov 	v4l2_device_unregister(&radio->v4l2_dev);
30504e0ffbbSAlexey Klimov 	kfree(radio->transfer_buffer);
30604e0ffbbSAlexey Klimov 	kfree(radio);
307fc55bcb0SAlexey Klimov }
30813099294SHans Verkuil 
3093a0efc32SAlexey Klimov static const struct v4l2_ctrl_ops usb_dsbr100_ctrl_ops = {
31013099294SHans Verkuil 	.s_ctrl = usb_dsbr100_s_ctrl,
3113a0efc32SAlexey Klimov };
3125d778648SHans Verkuil 
313406827ccSAlexey Klimov /* File system interface */
3143a0efc32SAlexey Klimov static const struct v4l2_file_operations usb_dsbr100_fops = {
3153a0efc32SAlexey Klimov 	.owner		= THIS_MODULE,
3163a0efc32SAlexey Klimov 	.unlocked_ioctl	= video_ioctl2,
3173a0efc32SAlexey Klimov 	.open           = v4l2_fh_open,
3185d778648SHans Verkuil 	.release        = v4l2_fh_release,
3195d778648SHans Verkuil 	.poll		= v4l2_ctrl_poll,
3205d778648SHans Verkuil };
3215d778648SHans Verkuil 
3227002a4f3SDouglas Landgraf static const struct v4l2_ioctl_ops usb_dsbr100_ioctl_ops = {
323bec43661SHans Verkuil 	.vidioc_querycap    = vidioc_querycap,
3247002a4f3SDouglas Landgraf 	.vidioc_g_tuner     = vidioc_g_tuner,
325e64d07c9SHans Verkuil 	.vidioc_s_tuner     = vidioc_s_tuner,
3265d778648SHans Verkuil 	.vidioc_g_frequency = vidioc_g_frequency,
3275d778648SHans Verkuil 	.vidioc_s_frequency = vidioc_s_frequency,
3285d778648SHans Verkuil 	.vidioc_log_status  = v4l2_ctrl_log_status,
3297002a4f3SDouglas Landgraf 	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
3307002a4f3SDouglas Landgraf 	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
331a399810cSHans Verkuil };
3327002a4f3SDouglas Landgraf 
3337002a4f3SDouglas Landgraf /* check if the device is present and register with v4l and usb if it is */
usb_dsbr100_probe(struct usb_interface * intf,const struct usb_device_id * id)3347002a4f3SDouglas Landgraf static int usb_dsbr100_probe(struct usb_interface *intf,
3357002a4f3SDouglas Landgraf 				const struct usb_device_id *id)
3367002a4f3SDouglas Landgraf {
3375d778648SHans Verkuil 	struct dsbr100_device *radio;
3385d778648SHans Verkuil 	struct v4l2_device *v4l2_dev;
3395d778648SHans Verkuil 	int retval;
3407002a4f3SDouglas Landgraf 
3417002a4f3SDouglas Landgraf 	radio = kzalloc(sizeof(struct dsbr100_device), GFP_KERNEL);
342fc55bcb0SAlexey Klimov 
3437002a4f3SDouglas Landgraf 	if (!radio)
3447002a4f3SDouglas Landgraf 		return -ENOMEM;
3457002a4f3SDouglas Landgraf 
3467002a4f3SDouglas Landgraf 	radio->transfer_buffer = kmalloc(TB_LEN, GFP_KERNEL);
347406827ccSAlexey Klimov 
348417b7953SAlexey Klimov 	if (!(radio->transfer_buffer)) {
3497002a4f3SDouglas Landgraf 		kfree(radio);
350406827ccSAlexey Klimov 		return -ENOMEM;
351223377e7SAlexey Klimov 	}
352223377e7SAlexey Klimov 
3537002a4f3SDouglas Landgraf 	v4l2_dev = &radio->v4l2_dev;
354223377e7SAlexey Klimov 	v4l2_dev->release = usb_dsbr100_release;
355223377e7SAlexey Klimov 
356223377e7SAlexey Klimov 	retval = v4l2_device_register(&intf->dev, v4l2_dev);
357223377e7SAlexey Klimov 	if (retval < 0) {
358863c86ddSOliver Neukum 		v4l2_err(v4l2_dev, "couldn't register v4l2_device\n");
359863c86ddSOliver Neukum 		goto err_reg_dev;
360863c86ddSOliver Neukum 	}
361223377e7SAlexey Klimov 
362406827ccSAlexey Klimov 	v4l2_ctrl_handler_init(&radio->hdl, 1);
36313099294SHans Verkuil 	v4l2_ctrl_new_std(&radio->hdl, &usb_dsbr100_ctrl_ops,
364406827ccSAlexey Klimov 			  V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
365406827ccSAlexey Klimov 	if (radio->hdl.error) {
366406827ccSAlexey Klimov 		retval = radio->hdl.error;
367406827ccSAlexey Klimov 		v4l2_err(v4l2_dev, "couldn't register control\n");
3685d778648SHans Verkuil 		goto err_reg_ctrl;
369406827ccSAlexey Klimov 	}
370406827ccSAlexey Klimov 	mutex_init(&radio->v4l2_lock);
3715d778648SHans Verkuil 	strscpy(radio->videodev.name, v4l2_dev->name,
3725d778648SHans Verkuil 		sizeof(radio->videodev.name));
3735d778648SHans Verkuil 	radio->videodev.v4l2_dev = v4l2_dev;
3745d778648SHans Verkuil 	radio->videodev.fops = &usb_dsbr100_fops;
3755d778648SHans Verkuil 	radio->videodev.ioctl_ops = &usb_dsbr100_ioctl_ops;
3765d778648SHans Verkuil 	radio->videodev.release = video_device_release_empty;
3775d778648SHans Verkuil 	radio->videodev.lock = &radio->v4l2_lock;
3785d778648SHans Verkuil 	radio->videodev.ctrl_handler = &radio->hdl;
379e64d07c9SHans Verkuil 	radio->videodev.device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER;
380c0decac1SMauro Carvalho Chehab 
381c0decac1SMauro Carvalho Chehab 	radio->usbdev = interface_to_usbdev(intf);
382406827ccSAlexey Klimov 	radio->curfreq = FREQ_MIN * FREQ_MUL;
383406827ccSAlexey Klimov 	radio->muted = true;
384406827ccSAlexey Klimov 
38513099294SHans Verkuil 	video_set_drvdata(&radio->videodev, radio);
386e64d07c9SHans Verkuil 	usb_set_intfdata(intf, radio);
3875d778648SHans Verkuil 
388e83ce300SHans Verkuil 	retval = video_register_device(&radio->videodev, VFL_TYPE_RADIO, radio_nr);
3893a0efc32SAlexey Klimov 	if (retval == 0)
3907002a4f3SDouglas Landgraf 		return 0;
3917002a4f3SDouglas Landgraf 	v4l2_err(v4l2_dev, "couldn't register video device\n");
3925d778648SHans Verkuil 
393406827ccSAlexey Klimov err_reg_ctrl:
3943a0efc32SAlexey Klimov 	v4l2_ctrl_handler_free(&radio->hdl);
3955d778648SHans Verkuil 	v4l2_device_unregister(v4l2_dev);
396406827ccSAlexey Klimov err_reg_dev:
397417b7953SAlexey Klimov 	kfree(radio->transfer_buffer);
3985d778648SHans Verkuil 	kfree(radio);
3995d778648SHans Verkuil 	return retval;
400406827ccSAlexey Klimov }
4015d778648SHans Verkuil 
4025d778648SHans Verkuil static const struct usb_device_id usb_dsbr100_device_table[] = {
4035d778648SHans Verkuil 	{ USB_DEVICE(DSB100_VENDOR, DSB100_PRODUCT) },
404406827ccSAlexey Klimov 	{ }						/* Terminating entry */
4055d778648SHans Verkuil };
406863c86ddSOliver Neukum 
4077002a4f3SDouglas Landgraf MODULE_DEVICE_TABLE(usb, usb_dsbr100_device_table);
4085d778648SHans Verkuil 
4097002a4f3SDouglas Landgraf /* USB subsystem interface */
4105d778648SHans Verkuil static struct usb_driver usb_dsbr100_driver = {
4111ab2234eSArvind Yadav 	.name			= "dsbr100",
4125d778648SHans Verkuil 	.probe			= usb_dsbr100_probe,
4135d778648SHans Verkuil 	.disconnect		= usb_dsbr100_disconnect,
4145d778648SHans Verkuil 	.id_table		= usb_dsbr100_device_table,
4155d778648SHans Verkuil 	.suspend		= usb_dsbr100_suspend,
4165d778648SHans Verkuil 	.resume			= usb_dsbr100_resume,
4175d778648SHans Verkuil 	.reset_resume		= usb_dsbr100_resume,
4185d778648SHans Verkuil };
4195d778648SHans Verkuil 
4205d778648SHans Verkuil module_usb_driver(usb_dsbr100_driver);
4215d778648SHans Verkuil