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