xref: /linux/drivers/media/radio/radio-mr800.c (revision e57458dc7086bac9e05b6fc6d5ab336de1647c12)
12aa72f3bSAlexey Klimov /*
22aa72f3bSAlexey Klimov  * A driver for the AverMedia MR 800 USB FM radio. This device plugs
32aa72f3bSAlexey Klimov  * into both the USB and an analog audio input, so this thing
42aa72f3bSAlexey Klimov  * only deals with initialization and frequency setting, the
52aa72f3bSAlexey Klimov  * audio data has to be handled by a sound driver.
62aa72f3bSAlexey Klimov  *
72aa72f3bSAlexey Klimov  * Copyright (c) 2008 Alexey Klimov <klimov.linux@gmail.com>
82aa72f3bSAlexey Klimov  *
92aa72f3bSAlexey Klimov  * This program is free software; you can redistribute it and/or modify
102aa72f3bSAlexey Klimov  * it under the terms of the GNU General Public License as published by
112aa72f3bSAlexey Klimov  * the Free Software Foundation; either version 2 of the License, or
122aa72f3bSAlexey Klimov  * (at your option) any later version.
132aa72f3bSAlexey Klimov  *
142aa72f3bSAlexey Klimov  * This program is distributed in the hope that it will be useful,
152aa72f3bSAlexey Klimov  * but WITHOUT ANY WARRANTY; without even the implied warranty of
162aa72f3bSAlexey Klimov  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
172aa72f3bSAlexey Klimov  * GNU General Public License for more details.
182aa72f3bSAlexey Klimov  *
192aa72f3bSAlexey Klimov  * You should have received a copy of the GNU General Public License
202aa72f3bSAlexey Klimov  * along with this program; if not, write to the Free Software
212aa72f3bSAlexey Klimov  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
222aa72f3bSAlexey Klimov  */
232aa72f3bSAlexey Klimov 
242aa72f3bSAlexey Klimov /*
2571f07d94SAlexey Klimov  * Big thanks to authors and contributors of dsbr100.c and radio-si470x.c
262aa72f3bSAlexey Klimov  *
272aa72f3bSAlexey Klimov  * When work was looked pretty good, i discover this:
282aa72f3bSAlexey Klimov  * http://av-usbradio.sourceforge.net/index.php
292aa72f3bSAlexey Klimov  * http://sourceforge.net/projects/av-usbradio/
302aa72f3bSAlexey Klimov  * Latest release of theirs project was in 2005.
312aa72f3bSAlexey Klimov  * Probably, this driver could be improved trough using their
322aa72f3bSAlexey Klimov  * achievements (specifications given).
3371f07d94SAlexey Klimov  * Also, Faidon Liambotis <paravoid@debian.org> wrote nice driver for this radio
3471f07d94SAlexey Klimov  * in 2007. He allowed to use his driver to improve current mr800 radio driver.
3571f07d94SAlexey Klimov  * http://kerneltrap.org/mailarchive/linux-usb-devel/2007/10/11/342492
362aa72f3bSAlexey Klimov  *
372aa72f3bSAlexey Klimov  * Version 0.01:	First working version.
382aa72f3bSAlexey Klimov  * 			It's required to blacklist AverMedia USB Radio
392aa72f3bSAlexey Klimov  * 			in usbhid/hid-quirks.c
4071f07d94SAlexey Klimov  * Version 0.10:	A lot of cleanups and fixes: unpluging the device,
4171f07d94SAlexey Klimov  * 			few mutex locks were added, codinstyle issues, etc.
4271f07d94SAlexey Klimov  * 			Added stereo support. Thanks to
4371f07d94SAlexey Klimov  * 			Douglas Schilling Landgraf <dougsland@gmail.com> and
4471f07d94SAlexey Klimov  * 			David Ellingsworth <david@identd.dyndns.org>
4571f07d94SAlexey Klimov  * 			for discussion, help and support.
462aa72f3bSAlexey Klimov  *
472aa72f3bSAlexey Klimov  * Many things to do:
482aa72f3bSAlexey Klimov  * 	- Correct power managment of device (suspend & resume)
492aa72f3bSAlexey Klimov  * 	- Add code for scanning and smooth tuning
502aa72f3bSAlexey Klimov  * 	- Add code for sensitivity value
512aa72f3bSAlexey Klimov  * 	- Correct mistakes
522aa72f3bSAlexey Klimov  * 	- In Japan another FREQ_MIN and FREQ_MAX
532aa72f3bSAlexey Klimov  */
542aa72f3bSAlexey Klimov 
552aa72f3bSAlexey Klimov /* kernel includes */
562aa72f3bSAlexey Klimov #include <linux/kernel.h>
572aa72f3bSAlexey Klimov #include <linux/module.h>
582aa72f3bSAlexey Klimov #include <linux/init.h>
592aa72f3bSAlexey Klimov #include <linux/slab.h>
602aa72f3bSAlexey Klimov #include <linux/input.h>
612aa72f3bSAlexey Klimov #include <linux/videodev2.h>
622aa72f3bSAlexey Klimov #include <media/v4l2-common.h>
632aa72f3bSAlexey Klimov #include <media/v4l2-ioctl.h>
642aa72f3bSAlexey Klimov #include <linux/usb.h>
652aa72f3bSAlexey Klimov #include <linux/version.h>	/* for KERNEL_VERSION MACRO */
662aa72f3bSAlexey Klimov 
672aa72f3bSAlexey Klimov /* driver and module definitions */
682aa72f3bSAlexey Klimov #define DRIVER_AUTHOR "Alexey Klimov <klimov.linux@gmail.com>"
692aa72f3bSAlexey Klimov #define DRIVER_DESC "AverMedia MR 800 USB FM radio driver"
7071f07d94SAlexey Klimov #define DRIVER_VERSION "0.10"
7171f07d94SAlexey Klimov #define RADIO_VERSION KERNEL_VERSION(0, 1, 0)
722aa72f3bSAlexey Klimov 
732aa72f3bSAlexey Klimov MODULE_AUTHOR(DRIVER_AUTHOR);
742aa72f3bSAlexey Klimov MODULE_DESCRIPTION(DRIVER_DESC);
752aa72f3bSAlexey Klimov MODULE_LICENSE("GPL");
762aa72f3bSAlexey Klimov 
772aa72f3bSAlexey Klimov #define USB_AMRADIO_VENDOR 0x07ca
782aa72f3bSAlexey Klimov #define USB_AMRADIO_PRODUCT 0xb800
792aa72f3bSAlexey Klimov 
80e60b022eSAlexey Klimov /* dev_warn macro with driver name */
81e60b022eSAlexey Klimov #define MR800_DRIVER_NAME "radio-mr800"
82e60b022eSAlexey Klimov #define amradio_dev_warn(dev, fmt, arg...)				\
83e60b022eSAlexey Klimov 		dev_warn(dev, MR800_DRIVER_NAME " - " fmt, ##arg)
84e60b022eSAlexey Klimov 
852aa72f3bSAlexey Klimov /* Probably USB_TIMEOUT should be modified in module parameter */
862aa72f3bSAlexey Klimov #define BUFFER_LENGTH 8
872aa72f3bSAlexey Klimov #define USB_TIMEOUT 500
882aa72f3bSAlexey Klimov 
892aa72f3bSAlexey Klimov /* Frequency limits in MHz -- these are European values.  For Japanese
902aa72f3bSAlexey Klimov devices, that would be 76 and 91.  */
912aa72f3bSAlexey Klimov #define FREQ_MIN  87.5
922aa72f3bSAlexey Klimov #define FREQ_MAX 108.0
932aa72f3bSAlexey Klimov #define FREQ_MUL 16000
942aa72f3bSAlexey Klimov 
95db821804SAlexey Klimov /*
96db821804SAlexey Klimov  * Commands that device should understand
97db821804SAlexey Klimov  * List isnt full and will be updated with implementation of new functions
98db821804SAlexey Klimov  */
99f7c1a380SAlexey Klimov #define AMRADIO_SET_FREQ	0xa4
100db821804SAlexey Klimov #define AMRADIO_SET_MUTE	0xab
1011bb16d71SAlexey Klimov #define AMRADIO_SET_MONO	0xae
102db821804SAlexey Klimov 
103db821804SAlexey Klimov /* Comfortable defines for amradio_set_mute */
104db821804SAlexey Klimov #define AMRADIO_START		0x00
105db821804SAlexey Klimov #define AMRADIO_STOP		0x01
106db821804SAlexey Klimov 
1071bb16d71SAlexey Klimov /* Comfortable defines for amradio_set_stereo */
1081bb16d71SAlexey Klimov #define WANT_STEREO		0x00
1091bb16d71SAlexey Klimov #define WANT_MONO		0x01
1101bb16d71SAlexey Klimov 
1112aa72f3bSAlexey Klimov /* module parameter */
1122aa72f3bSAlexey Klimov static int radio_nr = -1;
1132aa72f3bSAlexey Klimov module_param(radio_nr, int, 0);
1142aa72f3bSAlexey Klimov MODULE_PARM_DESC(radio_nr, "Radio Nr");
1152aa72f3bSAlexey Klimov 
1162aa72f3bSAlexey Klimov static struct v4l2_queryctrl radio_qctrl[] = {
1172aa72f3bSAlexey Klimov 	{
1182aa72f3bSAlexey Klimov 		.id            = V4L2_CID_AUDIO_MUTE,
1192aa72f3bSAlexey Klimov 		.name          = "Mute",
1202aa72f3bSAlexey Klimov 		.minimum       = 0,
1212aa72f3bSAlexey Klimov 		.maximum       = 1,
1222aa72f3bSAlexey Klimov 		.step	       = 1,
1232aa72f3bSAlexey Klimov 		.default_value = 1,
1242aa72f3bSAlexey Klimov 		.type          = V4L2_CTRL_TYPE_BOOLEAN,
1252aa72f3bSAlexey Klimov 	},
1262aa72f3bSAlexey Klimov /* HINT: the disabled controls are only here to satify kradio and such apps */
1272aa72f3bSAlexey Klimov 	{	.id		= V4L2_CID_AUDIO_VOLUME,
1282aa72f3bSAlexey Klimov 		.flags		= V4L2_CTRL_FLAG_DISABLED,
1292aa72f3bSAlexey Klimov 	},
1302aa72f3bSAlexey Klimov 	{
1312aa72f3bSAlexey Klimov 		.id		= V4L2_CID_AUDIO_BALANCE,
1322aa72f3bSAlexey Klimov 		.flags		= V4L2_CTRL_FLAG_DISABLED,
1332aa72f3bSAlexey Klimov 	},
1342aa72f3bSAlexey Klimov 	{
1352aa72f3bSAlexey Klimov 		.id		= V4L2_CID_AUDIO_BASS,
1362aa72f3bSAlexey Klimov 		.flags		= V4L2_CTRL_FLAG_DISABLED,
1372aa72f3bSAlexey Klimov 	},
1382aa72f3bSAlexey Klimov 	{
1392aa72f3bSAlexey Klimov 		.id		= V4L2_CID_AUDIO_TREBLE,
1402aa72f3bSAlexey Klimov 		.flags		= V4L2_CTRL_FLAG_DISABLED,
1412aa72f3bSAlexey Klimov 	},
1422aa72f3bSAlexey Klimov 	{
1432aa72f3bSAlexey Klimov 		.id		= V4L2_CID_AUDIO_LOUDNESS,
1442aa72f3bSAlexey Klimov 		.flags		= V4L2_CTRL_FLAG_DISABLED,
1452aa72f3bSAlexey Klimov 	},
1462aa72f3bSAlexey Klimov };
1472aa72f3bSAlexey Klimov 
1482aa72f3bSAlexey Klimov static int usb_amradio_probe(struct usb_interface *intf,
1492aa72f3bSAlexey Klimov 			     const struct usb_device_id *id);
1502aa72f3bSAlexey Klimov static void usb_amradio_disconnect(struct usb_interface *intf);
151bec43661SHans Verkuil static int usb_amradio_open(struct file *file);
152bec43661SHans Verkuil static int usb_amradio_close(struct file *file);
1532aa72f3bSAlexey Klimov static int usb_amradio_suspend(struct usb_interface *intf,
1542aa72f3bSAlexey Klimov 				pm_message_t message);
1552aa72f3bSAlexey Klimov static int usb_amradio_resume(struct usb_interface *intf);
1562aa72f3bSAlexey Klimov 
1572aa72f3bSAlexey Klimov /* Data for one (physical) device */
1582aa72f3bSAlexey Klimov struct amradio_device {
1592aa72f3bSAlexey Klimov 	/* reference to USB and video device */
1602aa72f3bSAlexey Klimov 	struct usb_device *usbdev;
1612aa72f3bSAlexey Klimov 	struct video_device *videodev;
1622aa72f3bSAlexey Klimov 
1632aa72f3bSAlexey Klimov 	unsigned char *buffer;
1642aa72f3bSAlexey Klimov 	struct mutex lock;	/* buffer locking */
1652aa72f3bSAlexey Klimov 	int curfreq;
1662aa72f3bSAlexey Klimov 	int stereo;
1672aa72f3bSAlexey Klimov 	int users;
1682aa72f3bSAlexey Klimov 	int removed;
1692aa72f3bSAlexey Klimov 	int muted;
1702aa72f3bSAlexey Klimov };
1712aa72f3bSAlexey Klimov 
1722aa72f3bSAlexey Klimov /* USB Device ID List */
1732aa72f3bSAlexey Klimov static struct usb_device_id usb_amradio_device_table[] = {
1742aa72f3bSAlexey Klimov 	{USB_DEVICE_AND_INTERFACE_INFO(USB_AMRADIO_VENDOR, USB_AMRADIO_PRODUCT,
1752aa72f3bSAlexey Klimov 							USB_CLASS_HID, 0, 0) },
1762aa72f3bSAlexey Klimov 	{ }						/* Terminating entry */
1772aa72f3bSAlexey Klimov };
1782aa72f3bSAlexey Klimov 
1792aa72f3bSAlexey Klimov MODULE_DEVICE_TABLE(usb, usb_amradio_device_table);
1802aa72f3bSAlexey Klimov 
1812aa72f3bSAlexey Klimov /* USB subsystem interface */
1822aa72f3bSAlexey Klimov static struct usb_driver usb_amradio_driver = {
183e60b022eSAlexey Klimov 	.name			= MR800_DRIVER_NAME,
1842aa72f3bSAlexey Klimov 	.probe			= usb_amradio_probe,
1852aa72f3bSAlexey Klimov 	.disconnect		= usb_amradio_disconnect,
1862aa72f3bSAlexey Klimov 	.suspend		= usb_amradio_suspend,
1872aa72f3bSAlexey Klimov 	.resume			= usb_amradio_resume,
1882aa72f3bSAlexey Klimov 	.reset_resume		= usb_amradio_resume,
1892aa72f3bSAlexey Klimov 	.id_table		= usb_amradio_device_table,
190f2ce9179SAlexey Klimov 	.supports_autosuspend	= 0,
1912aa72f3bSAlexey Klimov };
1922aa72f3bSAlexey Klimov 
193db821804SAlexey Klimov /* switch on/off the radio. Send 8 bytes to device */
194db821804SAlexey Klimov static int amradio_set_mute(struct amradio_device *radio, char argument)
1952aa72f3bSAlexey Klimov {
1962aa72f3bSAlexey Klimov 	int retval;
1972aa72f3bSAlexey Klimov 	int size;
1982aa72f3bSAlexey Klimov 
1993480130aSAlexey Klimov 	/* safety check */
2003480130aSAlexey Klimov 	if (radio->removed)
2013480130aSAlexey Klimov 		return -EIO;
2023480130aSAlexey Klimov 
2032aa72f3bSAlexey Klimov 	mutex_lock(&radio->lock);
2042aa72f3bSAlexey Klimov 
2052aa72f3bSAlexey Klimov 	radio->buffer[0] = 0x00;
2062aa72f3bSAlexey Klimov 	radio->buffer[1] = 0x55;
2072aa72f3bSAlexey Klimov 	radio->buffer[2] = 0xaa;
2082aa72f3bSAlexey Klimov 	radio->buffer[3] = 0x00;
209db821804SAlexey Klimov 	radio->buffer[4] = AMRADIO_SET_MUTE;
210db821804SAlexey Klimov 	radio->buffer[5] = argument;
2112aa72f3bSAlexey Klimov 	radio->buffer[6] = 0x00;
2122aa72f3bSAlexey Klimov 	radio->buffer[7] = 0x00;
2132aa72f3bSAlexey Klimov 
2142aa72f3bSAlexey Klimov 	retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2),
2152aa72f3bSAlexey Klimov 		(void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT);
2162aa72f3bSAlexey Klimov 
217*e57458dcSAlexey Klimov 	if (retval < 0 || size != BUFFER_LENGTH) {
2182aa72f3bSAlexey Klimov 		mutex_unlock(&radio->lock);
2192aa72f3bSAlexey Klimov 		return retval;
2202aa72f3bSAlexey Klimov 	}
2212aa72f3bSAlexey Klimov 
222db821804SAlexey Klimov 	radio->muted = argument;
2232aa72f3bSAlexey Klimov 
2247f03a585SAlexey Klimov 	mutex_unlock(&radio->lock);
2257f03a585SAlexey Klimov 
2262aa72f3bSAlexey Klimov 	return retval;
2272aa72f3bSAlexey Klimov }
2282aa72f3bSAlexey Klimov 
2292aa72f3bSAlexey Klimov /* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */
2302aa72f3bSAlexey Klimov static int amradio_setfreq(struct amradio_device *radio, int freq)
2312aa72f3bSAlexey Klimov {
2322aa72f3bSAlexey Klimov 	int retval;
2332aa72f3bSAlexey Klimov 	int size;
234f7c1a380SAlexey Klimov 	unsigned short freq_send = 0x10 + (freq >> 3) / 25;
2352aa72f3bSAlexey Klimov 
2363480130aSAlexey Klimov 	/* safety check */
2373480130aSAlexey Klimov 	if (radio->removed)
2383480130aSAlexey Klimov 		return -EIO;
2393480130aSAlexey Klimov 
2402aa72f3bSAlexey Klimov 	mutex_lock(&radio->lock);
2412aa72f3bSAlexey Klimov 
2422aa72f3bSAlexey Klimov 	radio->buffer[0] = 0x00;
2432aa72f3bSAlexey Klimov 	radio->buffer[1] = 0x55;
2442aa72f3bSAlexey Klimov 	radio->buffer[2] = 0xaa;
2452aa72f3bSAlexey Klimov 	radio->buffer[3] = 0x03;
246f7c1a380SAlexey Klimov 	radio->buffer[4] = AMRADIO_SET_FREQ;
2472aa72f3bSAlexey Klimov 	radio->buffer[5] = 0x00;
2482aa72f3bSAlexey Klimov 	radio->buffer[6] = 0x00;
2492aa72f3bSAlexey Klimov 	radio->buffer[7] = 0x08;
2502aa72f3bSAlexey Klimov 
2512aa72f3bSAlexey Klimov 	retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2),
2522aa72f3bSAlexey Klimov 		(void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT);
2532aa72f3bSAlexey Klimov 
254*e57458dcSAlexey Klimov 	if (retval < 0 || size != BUFFER_LENGTH) {
2552aa72f3bSAlexey Klimov 		mutex_unlock(&radio->lock);
2562aa72f3bSAlexey Klimov 		return retval;
2572aa72f3bSAlexey Klimov 	}
2582aa72f3bSAlexey Klimov 
2592aa72f3bSAlexey Klimov 	/* frequency is calculated from freq_send and placed in first 2 bytes */
2602aa72f3bSAlexey Klimov 	radio->buffer[0] = (freq_send >> 8) & 0xff;
2612aa72f3bSAlexey Klimov 	radio->buffer[1] = freq_send & 0xff;
2622aa72f3bSAlexey Klimov 	radio->buffer[2] = 0x01;
2632aa72f3bSAlexey Klimov 	radio->buffer[3] = 0x00;
2642aa72f3bSAlexey Klimov 	radio->buffer[4] = 0x00;
2652aa72f3bSAlexey Klimov 	/* 5 and 6 bytes of buffer already = 0x00 */
2662aa72f3bSAlexey Klimov 	radio->buffer[7] = 0x00;
2672aa72f3bSAlexey Klimov 
2682aa72f3bSAlexey Klimov 	retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2),
2692aa72f3bSAlexey Klimov 		(void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT);
2702aa72f3bSAlexey Klimov 
271*e57458dcSAlexey Klimov 	if (retval < 0 || size != BUFFER_LENGTH) {
2722aa72f3bSAlexey Klimov 		mutex_unlock(&radio->lock);
2732aa72f3bSAlexey Klimov 		return retval;
2742aa72f3bSAlexey Klimov 	}
2752aa72f3bSAlexey Klimov 
2761bb16d71SAlexey Klimov 	mutex_unlock(&radio->lock);
2771bb16d71SAlexey Klimov 
2781bb16d71SAlexey Klimov 	return retval;
2791bb16d71SAlexey Klimov }
2801bb16d71SAlexey Klimov 
2811bb16d71SAlexey Klimov static int amradio_set_stereo(struct amradio_device *radio, char argument)
2821bb16d71SAlexey Klimov {
2831bb16d71SAlexey Klimov 	int retval;
2841bb16d71SAlexey Klimov 	int size;
2851bb16d71SAlexey Klimov 
2861bb16d71SAlexey Klimov 	/* safety check */
2871bb16d71SAlexey Klimov 	if (radio->removed)
2881bb16d71SAlexey Klimov 		return -EIO;
2891bb16d71SAlexey Klimov 
2901bb16d71SAlexey Klimov 	mutex_lock(&radio->lock);
2911bb16d71SAlexey Klimov 
2921bb16d71SAlexey Klimov 	radio->buffer[0] = 0x00;
2931bb16d71SAlexey Klimov 	radio->buffer[1] = 0x55;
2941bb16d71SAlexey Klimov 	radio->buffer[2] = 0xaa;
2951bb16d71SAlexey Klimov 	radio->buffer[3] = 0x00;
2961bb16d71SAlexey Klimov 	radio->buffer[4] = AMRADIO_SET_MONO;
2971bb16d71SAlexey Klimov 	radio->buffer[5] = argument;
2981bb16d71SAlexey Klimov 	radio->buffer[6] = 0x00;
2991bb16d71SAlexey Klimov 	radio->buffer[7] = 0x00;
3001bb16d71SAlexey Klimov 
3011bb16d71SAlexey Klimov 	retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2),
3021bb16d71SAlexey Klimov 		(void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT);
3031bb16d71SAlexey Klimov 
3041bb16d71SAlexey Klimov 	if (retval < 0 || size != BUFFER_LENGTH) {
3051bb16d71SAlexey Klimov 		radio->stereo = -1;
3061bb16d71SAlexey Klimov 		mutex_unlock(&radio->lock);
3071bb16d71SAlexey Klimov 		return retval;
3081bb16d71SAlexey Klimov 	}
3091bb16d71SAlexey Klimov 
3101bb16d71SAlexey Klimov 	radio->stereo = 1;
3112aa72f3bSAlexey Klimov 
3127f03a585SAlexey Klimov 	mutex_unlock(&radio->lock);
3137f03a585SAlexey Klimov 
3142aa72f3bSAlexey Klimov 	return retval;
3152aa72f3bSAlexey Klimov }
3162aa72f3bSAlexey Klimov 
31771f07d94SAlexey Klimov /* Handle unplugging the device.
31871f07d94SAlexey Klimov  * We call video_unregister_device in any case.
31971f07d94SAlexey Klimov  * The last function called in this procedure is
32071f07d94SAlexey Klimov  * usb_amradio_device_release.
32171f07d94SAlexey Klimov  */
3222aa72f3bSAlexey Klimov static void usb_amradio_disconnect(struct usb_interface *intf)
3232aa72f3bSAlexey Klimov {
3242aa72f3bSAlexey Klimov 	struct amradio_device *radio = usb_get_intfdata(intf);
3252aa72f3bSAlexey Klimov 
326f4e9043eSAlexey Klimov 	mutex_lock(&radio->lock);
3273480130aSAlexey Klimov 	radio->removed = 1;
328f4e9043eSAlexey Klimov 	mutex_unlock(&radio->lock);
3292aa72f3bSAlexey Klimov 
330f4e9043eSAlexey Klimov 	usb_set_intfdata(intf, NULL);
3312aa72f3bSAlexey Klimov 	video_unregister_device(radio->videodev);
3322aa72f3bSAlexey Klimov }
3332aa72f3bSAlexey Klimov 
3342aa72f3bSAlexey Klimov /* vidioc_querycap - query device capabilities */
3352aa72f3bSAlexey Klimov static int vidioc_querycap(struct file *file, void *priv,
3362aa72f3bSAlexey Klimov 					struct v4l2_capability *v)
3372aa72f3bSAlexey Klimov {
338c7181cfaSAlexey Klimov 	struct amradio_device *radio = video_drvdata(file);
339c7181cfaSAlexey Klimov 
3402aa72f3bSAlexey Klimov 	strlcpy(v->driver, "radio-mr800", sizeof(v->driver));
3412aa72f3bSAlexey Klimov 	strlcpy(v->card, "AverMedia MR 800 USB FM Radio", sizeof(v->card));
342c7181cfaSAlexey Klimov 	usb_make_path(radio->usbdev, v->bus_info, sizeof(v->bus_info));
3432aa72f3bSAlexey Klimov 	v->version = RADIO_VERSION;
3442aa72f3bSAlexey Klimov 	v->capabilities = V4L2_CAP_TUNER;
3452aa72f3bSAlexey Klimov 	return 0;
3462aa72f3bSAlexey Klimov }
3472aa72f3bSAlexey Klimov 
3482aa72f3bSAlexey Klimov /* vidioc_g_tuner - get tuner attributes */
3492aa72f3bSAlexey Klimov static int vidioc_g_tuner(struct file *file, void *priv,
3502aa72f3bSAlexey Klimov 				struct v4l2_tuner *v)
3512aa72f3bSAlexey Klimov {
3522aa72f3bSAlexey Klimov 	struct amradio_device *radio = video_get_drvdata(video_devdata(file));
3531bb16d71SAlexey Klimov 	int retval;
3542aa72f3bSAlexey Klimov 
3553480130aSAlexey Klimov 	/* safety check */
3563480130aSAlexey Klimov 	if (radio->removed)
3573480130aSAlexey Klimov 		return -EIO;
3583480130aSAlexey Klimov 
3592aa72f3bSAlexey Klimov 	if (v->index > 0)
3602aa72f3bSAlexey Klimov 		return -EINVAL;
3612aa72f3bSAlexey Klimov 
3622aa72f3bSAlexey Klimov /* TODO: Add function which look is signal stereo or not
3632aa72f3bSAlexey Klimov  * 	amradio_getstat(radio);
3642aa72f3bSAlexey Klimov  */
3651bb16d71SAlexey Klimov 
3661bb16d71SAlexey Klimov /* we call amradio_set_stereo to set radio->stereo
3671bb16d71SAlexey Klimov  * Honestly, amradio_getstat should cover this in future and
3681bb16d71SAlexey Klimov  * amradio_set_stereo shouldn't be here
3691bb16d71SAlexey Klimov  */
3701bb16d71SAlexey Klimov 	retval = amradio_set_stereo(radio, WANT_STEREO);
3711bb16d71SAlexey Klimov 	if (retval < 0)
3721bb16d71SAlexey Klimov 		amradio_dev_warn(&radio->videodev->dev,
3731bb16d71SAlexey Klimov 			"set stereo failed\n");
3741bb16d71SAlexey Klimov 
3752aa72f3bSAlexey Klimov 	strcpy(v->name, "FM");
3762aa72f3bSAlexey Klimov 	v->type = V4L2_TUNER_RADIO;
3772aa72f3bSAlexey Klimov 	v->rangelow = FREQ_MIN * FREQ_MUL;
3782aa72f3bSAlexey Klimov 	v->rangehigh = FREQ_MAX * FREQ_MUL;
3792aa72f3bSAlexey Klimov 	v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
3802aa72f3bSAlexey Klimov 	v->capability = V4L2_TUNER_CAP_LOW;
3812aa72f3bSAlexey Klimov 	if (radio->stereo)
3822aa72f3bSAlexey Klimov 		v->audmode = V4L2_TUNER_MODE_STEREO;
3832aa72f3bSAlexey Klimov 	else
3842aa72f3bSAlexey Klimov 		v->audmode = V4L2_TUNER_MODE_MONO;
3852aa72f3bSAlexey Klimov 	v->signal = 0xffff;     /* Can't get the signal strength, sad.. */
3862aa72f3bSAlexey Klimov 	v->afc = 0; /* Don't know what is this */
3872aa72f3bSAlexey Klimov 	return 0;
3882aa72f3bSAlexey Klimov }
3892aa72f3bSAlexey Klimov 
3902aa72f3bSAlexey Klimov /* vidioc_s_tuner - set tuner attributes */
3912aa72f3bSAlexey Klimov static int vidioc_s_tuner(struct file *file, void *priv,
3922aa72f3bSAlexey Klimov 				struct v4l2_tuner *v)
3932aa72f3bSAlexey Klimov {
3943480130aSAlexey Klimov 	struct amradio_device *radio = video_get_drvdata(video_devdata(file));
3951bb16d71SAlexey Klimov 	int retval;
3963480130aSAlexey Klimov 
3973480130aSAlexey Klimov 	/* safety check */
3983480130aSAlexey Klimov 	if (radio->removed)
3993480130aSAlexey Klimov 		return -EIO;
4003480130aSAlexey Klimov 
4012aa72f3bSAlexey Klimov 	if (v->index > 0)
4022aa72f3bSAlexey Klimov 		return -EINVAL;
4031bb16d71SAlexey Klimov 
4041bb16d71SAlexey Klimov 	/* mono/stereo selector */
4051bb16d71SAlexey Klimov 	switch (v->audmode) {
4061bb16d71SAlexey Klimov 	case V4L2_TUNER_MODE_MONO:
4071bb16d71SAlexey Klimov 		retval = amradio_set_stereo(radio, WANT_MONO);
4081bb16d71SAlexey Klimov 		if (retval < 0)
4091bb16d71SAlexey Klimov 			amradio_dev_warn(&radio->videodev->dev,
4101bb16d71SAlexey Klimov 				"set mono failed\n");
4111bb16d71SAlexey Klimov 		break;
4121bb16d71SAlexey Klimov 	case V4L2_TUNER_MODE_STEREO:
4131bb16d71SAlexey Klimov 		retval = amradio_set_stereo(radio, WANT_STEREO);
4141bb16d71SAlexey Klimov 		if (retval < 0)
4151bb16d71SAlexey Klimov 			amradio_dev_warn(&radio->videodev->dev,
4161bb16d71SAlexey Klimov 				"set stereo failed\n");
4171bb16d71SAlexey Klimov 		break;
4181bb16d71SAlexey Klimov 	default:
4191bb16d71SAlexey Klimov 		return -EINVAL;
4201bb16d71SAlexey Klimov 	}
4211bb16d71SAlexey Klimov 
4222aa72f3bSAlexey Klimov 	return 0;
4232aa72f3bSAlexey Klimov }
4242aa72f3bSAlexey Klimov 
4252aa72f3bSAlexey Klimov /* vidioc_s_frequency - set tuner radio frequency */
4262aa72f3bSAlexey Klimov static int vidioc_s_frequency(struct file *file, void *priv,
4272aa72f3bSAlexey Klimov 				struct v4l2_frequency *f)
4282aa72f3bSAlexey Klimov {
4292aa72f3bSAlexey Klimov 	struct amradio_device *radio = video_get_drvdata(video_devdata(file));
430a5d69475SAlexey Klimov 	int retval;
4312aa72f3bSAlexey Klimov 
4323480130aSAlexey Klimov 	/* safety check */
4333480130aSAlexey Klimov 	if (radio->removed)
4343480130aSAlexey Klimov 		return -EIO;
4353480130aSAlexey Klimov 
43652433bbbSAlexey Klimov 	mutex_lock(&radio->lock);
4372aa72f3bSAlexey Klimov 	radio->curfreq = f->frequency;
43852433bbbSAlexey Klimov 	mutex_unlock(&radio->lock);
43952433bbbSAlexey Klimov 
440a5d69475SAlexey Klimov 	retval = amradio_setfreq(radio, radio->curfreq);
441a5d69475SAlexey Klimov 	if (retval < 0)
442e60b022eSAlexey Klimov 		amradio_dev_warn(&radio->videodev->dev,
443e60b022eSAlexey Klimov 			"set frequency failed\n");
4442aa72f3bSAlexey Klimov 	return 0;
4452aa72f3bSAlexey Klimov }
4462aa72f3bSAlexey Klimov 
4472aa72f3bSAlexey Klimov /* vidioc_g_frequency - get tuner radio frequency */
4482aa72f3bSAlexey Klimov static int vidioc_g_frequency(struct file *file, void *priv,
4492aa72f3bSAlexey Klimov 				struct v4l2_frequency *f)
4502aa72f3bSAlexey Klimov {
4512aa72f3bSAlexey Klimov 	struct amradio_device *radio = video_get_drvdata(video_devdata(file));
4522aa72f3bSAlexey Klimov 
4533480130aSAlexey Klimov 	/* safety check */
4543480130aSAlexey Klimov 	if (radio->removed)
4553480130aSAlexey Klimov 		return -EIO;
4563480130aSAlexey Klimov 
4572aa72f3bSAlexey Klimov 	f->type = V4L2_TUNER_RADIO;
4582aa72f3bSAlexey Klimov 	f->frequency = radio->curfreq;
4592aa72f3bSAlexey Klimov 	return 0;
4602aa72f3bSAlexey Klimov }
4612aa72f3bSAlexey Klimov 
4622aa72f3bSAlexey Klimov /* vidioc_queryctrl - enumerate control items */
4632aa72f3bSAlexey Klimov static int vidioc_queryctrl(struct file *file, void *priv,
4642aa72f3bSAlexey Klimov 				struct v4l2_queryctrl *qc)
4652aa72f3bSAlexey Klimov {
4662aa72f3bSAlexey Klimov 	int i;
4672aa72f3bSAlexey Klimov 
4682aa72f3bSAlexey Klimov 	for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
4692aa72f3bSAlexey Klimov 		if (qc->id && qc->id == radio_qctrl[i].id) {
470e60b022eSAlexey Klimov 			memcpy(qc, &(radio_qctrl[i]), sizeof(*qc));
4712aa72f3bSAlexey Klimov 			return 0;
4722aa72f3bSAlexey Klimov 		}
4732aa72f3bSAlexey Klimov 	}
4742aa72f3bSAlexey Klimov 	return -EINVAL;
4752aa72f3bSAlexey Klimov }
4762aa72f3bSAlexey Klimov 
4772aa72f3bSAlexey Klimov /* vidioc_g_ctrl - get the value of a control */
4782aa72f3bSAlexey Klimov static int vidioc_g_ctrl(struct file *file, void *priv,
4792aa72f3bSAlexey Klimov 				struct v4l2_control *ctrl)
4802aa72f3bSAlexey Klimov {
4812aa72f3bSAlexey Klimov 	struct amradio_device *radio = video_get_drvdata(video_devdata(file));
4822aa72f3bSAlexey Klimov 
4833480130aSAlexey Klimov 	/* safety check */
4843480130aSAlexey Klimov 	if (radio->removed)
4853480130aSAlexey Klimov 		return -EIO;
4863480130aSAlexey Klimov 
4872aa72f3bSAlexey Klimov 	switch (ctrl->id) {
4882aa72f3bSAlexey Klimov 	case V4L2_CID_AUDIO_MUTE:
4892aa72f3bSAlexey Klimov 		ctrl->value = radio->muted;
4902aa72f3bSAlexey Klimov 		return 0;
4912aa72f3bSAlexey Klimov 	}
4922aa72f3bSAlexey Klimov 	return -EINVAL;
4932aa72f3bSAlexey Klimov }
4942aa72f3bSAlexey Klimov 
4952aa72f3bSAlexey Klimov /* vidioc_s_ctrl - set the value of a control */
4962aa72f3bSAlexey Klimov static int vidioc_s_ctrl(struct file *file, void *priv,
4972aa72f3bSAlexey Klimov 				struct v4l2_control *ctrl)
4982aa72f3bSAlexey Klimov {
4992aa72f3bSAlexey Klimov 	struct amradio_device *radio = video_get_drvdata(video_devdata(file));
500a5d69475SAlexey Klimov 	int retval;
5012aa72f3bSAlexey Klimov 
5023480130aSAlexey Klimov 	/* safety check */
5033480130aSAlexey Klimov 	if (radio->removed)
5043480130aSAlexey Klimov 		return -EIO;
5053480130aSAlexey Klimov 
5062aa72f3bSAlexey Klimov 	switch (ctrl->id) {
5072aa72f3bSAlexey Klimov 	case V4L2_CID_AUDIO_MUTE:
5082aa72f3bSAlexey Klimov 		if (ctrl->value) {
509db821804SAlexey Klimov 			retval = amradio_set_mute(radio, AMRADIO_STOP);
510a5d69475SAlexey Klimov 			if (retval < 0) {
511e60b022eSAlexey Klimov 				amradio_dev_warn(&radio->videodev->dev,
512e60b022eSAlexey Klimov 					"amradio_stop failed\n");
5132aa72f3bSAlexey Klimov 				return -1;
5142aa72f3bSAlexey Klimov 			}
5152aa72f3bSAlexey Klimov 		} else {
516db821804SAlexey Klimov 			retval = amradio_set_mute(radio, AMRADIO_START);
517a5d69475SAlexey Klimov 			if (retval < 0) {
518e60b022eSAlexey Klimov 				amradio_dev_warn(&radio->videodev->dev,
519e60b022eSAlexey Klimov 					"amradio_start failed\n");
5202aa72f3bSAlexey Klimov 				return -1;
5212aa72f3bSAlexey Klimov 			}
5222aa72f3bSAlexey Klimov 		}
5232aa72f3bSAlexey Klimov 		return 0;
5242aa72f3bSAlexey Klimov 	}
5252aa72f3bSAlexey Klimov 	return -EINVAL;
5262aa72f3bSAlexey Klimov }
5272aa72f3bSAlexey Klimov 
5282aa72f3bSAlexey Klimov /* vidioc_g_audio - get audio attributes */
5292aa72f3bSAlexey Klimov static int vidioc_g_audio(struct file *file, void *priv,
5302aa72f3bSAlexey Klimov 				struct v4l2_audio *a)
5312aa72f3bSAlexey Klimov {
5322aa72f3bSAlexey Klimov 	if (a->index > 1)
5332aa72f3bSAlexey Klimov 		return -EINVAL;
5342aa72f3bSAlexey Klimov 
5352aa72f3bSAlexey Klimov 	strcpy(a->name, "Radio");
5362aa72f3bSAlexey Klimov 	a->capability = V4L2_AUDCAP_STEREO;
5372aa72f3bSAlexey Klimov 	return 0;
5382aa72f3bSAlexey Klimov }
5392aa72f3bSAlexey Klimov 
5402aa72f3bSAlexey Klimov /* vidioc_s_audio - set audio attributes  */
5412aa72f3bSAlexey Klimov static int vidioc_s_audio(struct file *file, void *priv,
5422aa72f3bSAlexey Klimov 					struct v4l2_audio *a)
5432aa72f3bSAlexey Klimov {
5442aa72f3bSAlexey Klimov 	if (a->index != 0)
5452aa72f3bSAlexey Klimov 		return -EINVAL;
5462aa72f3bSAlexey Klimov 	return 0;
5472aa72f3bSAlexey Klimov }
5482aa72f3bSAlexey Klimov 
5492aa72f3bSAlexey Klimov /* vidioc_g_input - get input */
5502aa72f3bSAlexey Klimov static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
5512aa72f3bSAlexey Klimov {
5522aa72f3bSAlexey Klimov 	*i = 0;
5532aa72f3bSAlexey Klimov 	return 0;
5542aa72f3bSAlexey Klimov }
5552aa72f3bSAlexey Klimov 
5562aa72f3bSAlexey Klimov /* vidioc_s_input - set input */
5572aa72f3bSAlexey Klimov static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
5582aa72f3bSAlexey Klimov {
5592aa72f3bSAlexey Klimov 	if (i != 0)
5602aa72f3bSAlexey Klimov 		return -EINVAL;
5612aa72f3bSAlexey Klimov 	return 0;
5622aa72f3bSAlexey Klimov }
5632aa72f3bSAlexey Klimov 
5642aa72f3bSAlexey Klimov /* open device - amradio_start() and amradio_setfreq() */
565bec43661SHans Verkuil static int usb_amradio_open(struct file *file)
5662aa72f3bSAlexey Klimov {
5672aa72f3bSAlexey Klimov 	struct amradio_device *radio = video_get_drvdata(video_devdata(file));
568a5d69475SAlexey Klimov 	int retval;
5692aa72f3bSAlexey Klimov 
5700fabb783SAlexey Klimov 	lock_kernel();
5710fabb783SAlexey Klimov 
5722aa72f3bSAlexey Klimov 	radio->users = 1;
5732aa72f3bSAlexey Klimov 	radio->muted = 1;
5742aa72f3bSAlexey Klimov 
575db821804SAlexey Klimov 	retval = amradio_set_mute(radio, AMRADIO_START);
576a5d69475SAlexey Klimov 	if (retval < 0) {
577e60b022eSAlexey Klimov 		amradio_dev_warn(&radio->videodev->dev,
578e60b022eSAlexey Klimov 			"radio did not start up properly\n");
5792aa72f3bSAlexey Klimov 		radio->users = 0;
5800fabb783SAlexey Klimov 		unlock_kernel();
5812aa72f3bSAlexey Klimov 		return -EIO;
5822aa72f3bSAlexey Klimov 	}
583a5d69475SAlexey Klimov 
5841bb16d71SAlexey Klimov 	retval = amradio_set_stereo(radio, WANT_STEREO);
5851bb16d71SAlexey Klimov 	if (retval < 0)
5861bb16d71SAlexey Klimov 		amradio_dev_warn(&radio->videodev->dev,
5871bb16d71SAlexey Klimov 			"set stereo failed\n");
5881bb16d71SAlexey Klimov 
589a5d69475SAlexey Klimov 	retval = amradio_setfreq(radio, radio->curfreq);
590a5d69475SAlexey Klimov 	if (retval < 0)
591e60b022eSAlexey Klimov 		amradio_dev_warn(&radio->videodev->dev,
592e60b022eSAlexey Klimov 			"set frequency failed\n");
5930fabb783SAlexey Klimov 
5940fabb783SAlexey Klimov 	unlock_kernel();
5952aa72f3bSAlexey Klimov 	return 0;
5962aa72f3bSAlexey Klimov }
5972aa72f3bSAlexey Klimov 
598f4e9043eSAlexey Klimov /*close device */
599bec43661SHans Verkuil static int usb_amradio_close(struct file *file)
6002aa72f3bSAlexey Klimov {
6012aa72f3bSAlexey Klimov 	struct amradio_device *radio = video_get_drvdata(video_devdata(file));
6023480130aSAlexey Klimov 	int retval;
6032aa72f3bSAlexey Klimov 
6042aa72f3bSAlexey Klimov 	if (!radio)
6052aa72f3bSAlexey Klimov 		return -ENODEV;
6063480130aSAlexey Klimov 
60752433bbbSAlexey Klimov 	mutex_lock(&radio->lock);
6082aa72f3bSAlexey Klimov 	radio->users = 0;
60952433bbbSAlexey Klimov 	mutex_unlock(&radio->lock);
6103480130aSAlexey Klimov 
611f4e9043eSAlexey Klimov 	if (!radio->removed) {
612db821804SAlexey Klimov 		retval = amradio_set_mute(radio, AMRADIO_STOP);
6133480130aSAlexey Klimov 		if (retval < 0)
6143480130aSAlexey Klimov 			amradio_dev_warn(&radio->videodev->dev,
6153480130aSAlexey Klimov 				"amradio_stop failed\n");
6162aa72f3bSAlexey Klimov 	}
6173480130aSAlexey Klimov 
6182aa72f3bSAlexey Klimov 	return 0;
6192aa72f3bSAlexey Klimov }
6202aa72f3bSAlexey Klimov 
6212aa72f3bSAlexey Klimov /* Suspend device - stop device. Need to be checked and fixed */
6222aa72f3bSAlexey Klimov static int usb_amradio_suspend(struct usb_interface *intf, pm_message_t message)
6232aa72f3bSAlexey Klimov {
6242aa72f3bSAlexey Klimov 	struct amradio_device *radio = usb_get_intfdata(intf);
625a5d69475SAlexey Klimov 	int retval;
6262aa72f3bSAlexey Klimov 
627db821804SAlexey Klimov 	retval = amradio_set_mute(radio, AMRADIO_STOP);
628a5d69475SAlexey Klimov 	if (retval < 0)
629e60b022eSAlexey Klimov 		dev_warn(&intf->dev, "amradio_stop failed\n");
6302aa72f3bSAlexey Klimov 
631e60b022eSAlexey Klimov 	dev_info(&intf->dev, "going into suspend..\n");
6322aa72f3bSAlexey Klimov 
6332aa72f3bSAlexey Klimov 	return 0;
6342aa72f3bSAlexey Klimov }
6352aa72f3bSAlexey Klimov 
6362aa72f3bSAlexey Klimov /* Resume device - start device. Need to be checked and fixed */
6372aa72f3bSAlexey Klimov static int usb_amradio_resume(struct usb_interface *intf)
6382aa72f3bSAlexey Klimov {
6392aa72f3bSAlexey Klimov 	struct amradio_device *radio = usb_get_intfdata(intf);
640a5d69475SAlexey Klimov 	int retval;
6412aa72f3bSAlexey Klimov 
642db821804SAlexey Klimov 	retval = amradio_set_mute(radio, AMRADIO_START);
643a5d69475SAlexey Klimov 	if (retval < 0)
644e60b022eSAlexey Klimov 		dev_warn(&intf->dev, "amradio_start failed\n");
6452aa72f3bSAlexey Klimov 
646e60b022eSAlexey Klimov 	dev_info(&intf->dev, "coming out of suspend..\n");
6472aa72f3bSAlexey Klimov 
6482aa72f3bSAlexey Klimov 	return 0;
6492aa72f3bSAlexey Klimov }
6502aa72f3bSAlexey Klimov 
6512aa72f3bSAlexey Klimov /* File system interface */
652bec43661SHans Verkuil static const struct v4l2_file_operations usb_amradio_fops = {
6532aa72f3bSAlexey Klimov 	.owner		= THIS_MODULE,
6542aa72f3bSAlexey Klimov 	.open		= usb_amradio_open,
6552aa72f3bSAlexey Klimov 	.release	= usb_amradio_close,
6562aa72f3bSAlexey Klimov 	.ioctl		= video_ioctl2,
6572aa72f3bSAlexey Klimov };
6582aa72f3bSAlexey Klimov 
6592aa72f3bSAlexey Klimov static const struct v4l2_ioctl_ops usb_amradio_ioctl_ops = {
6602aa72f3bSAlexey Klimov 	.vidioc_querycap    = vidioc_querycap,
6612aa72f3bSAlexey Klimov 	.vidioc_g_tuner     = vidioc_g_tuner,
6622aa72f3bSAlexey Klimov 	.vidioc_s_tuner     = vidioc_s_tuner,
6632aa72f3bSAlexey Klimov 	.vidioc_g_frequency = vidioc_g_frequency,
6642aa72f3bSAlexey Klimov 	.vidioc_s_frequency = vidioc_s_frequency,
6652aa72f3bSAlexey Klimov 	.vidioc_queryctrl   = vidioc_queryctrl,
6662aa72f3bSAlexey Klimov 	.vidioc_g_ctrl      = vidioc_g_ctrl,
6672aa72f3bSAlexey Klimov 	.vidioc_s_ctrl      = vidioc_s_ctrl,
6682aa72f3bSAlexey Klimov 	.vidioc_g_audio     = vidioc_g_audio,
6692aa72f3bSAlexey Klimov 	.vidioc_s_audio     = vidioc_s_audio,
6702aa72f3bSAlexey Klimov 	.vidioc_g_input     = vidioc_g_input,
6712aa72f3bSAlexey Klimov 	.vidioc_s_input     = vidioc_s_input,
6722aa72f3bSAlexey Klimov };
6732aa72f3bSAlexey Klimov 
674f4e9043eSAlexey Klimov static void usb_amradio_device_release(struct video_device *videodev)
675f4e9043eSAlexey Klimov {
676f4e9043eSAlexey Klimov 	struct amradio_device *radio = video_get_drvdata(videodev);
677f4e9043eSAlexey Klimov 
678f4e9043eSAlexey Klimov 	/* we call v4l to free radio->videodev */
679f4e9043eSAlexey Klimov 	video_device_release(videodev);
680f4e9043eSAlexey Klimov 
681f4e9043eSAlexey Klimov 	/* free rest memory */
682f4e9043eSAlexey Klimov 	kfree(radio->buffer);
683f4e9043eSAlexey Klimov 	kfree(radio);
684f4e9043eSAlexey Klimov }
685f4e9043eSAlexey Klimov 
6862aa72f3bSAlexey Klimov /* V4L2 interface */
6872aa72f3bSAlexey Klimov static struct video_device amradio_videodev_template = {
6882aa72f3bSAlexey Klimov 	.name		= "AverMedia MR 800 USB FM Radio",
6892aa72f3bSAlexey Klimov 	.fops		= &usb_amradio_fops,
6902aa72f3bSAlexey Klimov 	.ioctl_ops 	= &usb_amradio_ioctl_ops,
691f4e9043eSAlexey Klimov 	.release	= usb_amradio_device_release,
6922aa72f3bSAlexey Klimov };
6932aa72f3bSAlexey Klimov 
694a5d69475SAlexey Klimov /* check if the device is present and register with v4l and usb if it is */
6952aa72f3bSAlexey Klimov static int usb_amradio_probe(struct usb_interface *intf,
6962aa72f3bSAlexey Klimov 				const struct usb_device_id *id)
6972aa72f3bSAlexey Klimov {
6982aa72f3bSAlexey Klimov 	struct amradio_device *radio;
699a5d69475SAlexey Klimov 	int retval;
7002aa72f3bSAlexey Klimov 
7012aa72f3bSAlexey Klimov 	radio = kmalloc(sizeof(struct amradio_device), GFP_KERNEL);
7022aa72f3bSAlexey Klimov 
7038edafcc6SAlexey Klimov 	if (!radio) {
7048edafcc6SAlexey Klimov 		dev_err(&intf->dev, "kmalloc for amradio_device failed\n");
7052aa72f3bSAlexey Klimov 		return -ENOMEM;
7068edafcc6SAlexey Klimov 	}
7072aa72f3bSAlexey Klimov 
7082aa72f3bSAlexey Klimov 	radio->buffer = kmalloc(BUFFER_LENGTH, GFP_KERNEL);
7092aa72f3bSAlexey Klimov 
7108edafcc6SAlexey Klimov 	if (!radio->buffer) {
7118edafcc6SAlexey Klimov 		dev_err(&intf->dev, "kmalloc for radio->buffer failed\n");
7122aa72f3bSAlexey Klimov 		kfree(radio);
7132aa72f3bSAlexey Klimov 		return -ENOMEM;
7142aa72f3bSAlexey Klimov 	}
7152aa72f3bSAlexey Klimov 
7162aa72f3bSAlexey Klimov 	radio->videodev = video_device_alloc();
7172aa72f3bSAlexey Klimov 
7188edafcc6SAlexey Klimov 	if (!radio->videodev) {
7198edafcc6SAlexey Klimov 		dev_err(&intf->dev, "video_device_alloc failed\n");
7202aa72f3bSAlexey Klimov 		kfree(radio->buffer);
7212aa72f3bSAlexey Klimov 		kfree(radio);
7222aa72f3bSAlexey Klimov 		return -ENOMEM;
7232aa72f3bSAlexey Klimov 	}
7242aa72f3bSAlexey Klimov 
7252aa72f3bSAlexey Klimov 	memcpy(radio->videodev, &amradio_videodev_template,
7262aa72f3bSAlexey Klimov 		sizeof(amradio_videodev_template));
7272aa72f3bSAlexey Klimov 
7282aa72f3bSAlexey Klimov 	radio->removed = 0;
7292aa72f3bSAlexey Klimov 	radio->users = 0;
7302aa72f3bSAlexey Klimov 	radio->usbdev = interface_to_usbdev(intf);
7312aa72f3bSAlexey Klimov 	radio->curfreq = 95.16 * FREQ_MUL;
7321bb16d71SAlexey Klimov 	radio->stereo = -1;
7332aa72f3bSAlexey Klimov 
7342aa72f3bSAlexey Klimov 	mutex_init(&radio->lock);
7352aa72f3bSAlexey Klimov 
7362aa72f3bSAlexey Klimov 	video_set_drvdata(radio->videodev, radio);
737a5d69475SAlexey Klimov 	retval = video_register_device(radio->videodev,	VFL_TYPE_RADIO,	radio_nr);
738a5d69475SAlexey Klimov 	if (retval < 0) {
73965c51dc9SAlexey Klimov 		dev_err(&intf->dev, "could not register video device\n");
7402aa72f3bSAlexey Klimov 		video_device_release(radio->videodev);
7412aa72f3bSAlexey Klimov 		kfree(radio->buffer);
7422aa72f3bSAlexey Klimov 		kfree(radio);
7432aa72f3bSAlexey Klimov 		return -EIO;
7442aa72f3bSAlexey Klimov 	}
7452aa72f3bSAlexey Klimov 
7462aa72f3bSAlexey Klimov 	usb_set_intfdata(intf, radio);
7472aa72f3bSAlexey Klimov 	return 0;
7482aa72f3bSAlexey Klimov }
7492aa72f3bSAlexey Klimov 
7502aa72f3bSAlexey Klimov static int __init amradio_init(void)
7512aa72f3bSAlexey Klimov {
7522aa72f3bSAlexey Klimov 	int retval = usb_register(&usb_amradio_driver);
7532aa72f3bSAlexey Klimov 
754e60b022eSAlexey Klimov 	pr_info(KBUILD_MODNAME
755e60b022eSAlexey Klimov 		": version " DRIVER_VERSION " " DRIVER_DESC "\n");
756e60b022eSAlexey Klimov 
7572aa72f3bSAlexey Klimov 	if (retval)
758e60b022eSAlexey Klimov 		pr_err(KBUILD_MODNAME
759e60b022eSAlexey Klimov 			": usb_register failed. Error number %d\n", retval);
760e60b022eSAlexey Klimov 
7612aa72f3bSAlexey Klimov 	return retval;
7622aa72f3bSAlexey Klimov }
7632aa72f3bSAlexey Klimov 
7642aa72f3bSAlexey Klimov static void __exit amradio_exit(void)
7652aa72f3bSAlexey Klimov {
7662aa72f3bSAlexey Klimov 	usb_deregister(&usb_amradio_driver);
7672aa72f3bSAlexey Klimov }
7682aa72f3bSAlexey Klimov 
7692aa72f3bSAlexey Klimov module_init(amradio_init);
7702aa72f3bSAlexey Klimov module_exit(amradio_exit);
7712aa72f3bSAlexey Klimov 
772