2 * A driver for the AverMedia MR 800 USB FM radio. This device plugs
3 * into both the USB and an analog audio input, so this thing
4 * only deals with initialization and frequency setting, the
5 * audio data has to be handled by a sound driver.
7 * Copyright (c) 2008 Alexey Klimov <klimov.linux@gmail.com>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 * Big thanks to authors of dsbr100.c and radio-si470x.c
27 * When work was looked pretty good, i discover this:
28 * http://av-usbradio.sourceforge.net/index.php
29 * http://sourceforge.net/projects/av-usbradio/
30 * Latest release of theirs project was in 2005.
31 * Probably, this driver could be improved trough using their
32 * achievements (specifications given).
33 * So, we have smth to begin with.
36 * Version 0.01: First working version.
37 * It's required to blacklist AverMedia USB Radio
38 * in usbhid/hid-quirks.c
41 * - Correct power managment of device (suspend & resume)
42 * - Make x86 independance (little-endian and big-endian stuff)
43 * - Add code for scanning and smooth tuning
44 * - Checked and add stereo&mono stuff
45 * - Add code for sensitivity value
47 * - In Japan another FREQ_MIN and FREQ_MAX
51 #include <linux/kernel.h>
52 #include <linux/module.h>
53 #include <linux/init.h>
54 #include <linux/slab.h>
55 #include <linux/input.h>
56 #include <linux/videodev2.h>
57 #include <media/v4l2-common.h>
58 #include <media/v4l2-ioctl.h>
59 #include <linux/usb.h>
60 #include <linux/version.h> /* for KERNEL_VERSION MACRO */
62 /* driver and module definitions */
63 #define DRIVER_AUTHOR "Alexey Klimov <klimov.linux@gmail.com>"
64 #define DRIVER_DESC "AverMedia MR 800 USB FM radio driver"
65 #define DRIVER_VERSION "0.01"
66 #define RADIO_VERSION KERNEL_VERSION(0, 0, 1)
68 MODULE_AUTHOR(DRIVER_AUTHOR);
69 MODULE_DESCRIPTION(DRIVER_DESC);
70 MODULE_LICENSE("GPL");
72 #define USB_AMRADIO_VENDOR 0x07ca
73 #define USB_AMRADIO_PRODUCT 0xb800
75 /* Probably USB_TIMEOUT should be modified in module parameter */
76 #define BUFFER_LENGTH 8
77 #define USB_TIMEOUT 500
79 /* Frequency limits in MHz -- these are European values. For Japanese
80 devices, that would be 76 and 91. */
82 #define FREQ_MAX 108.0
83 #define FREQ_MUL 16000
85 /* module parameter */
86 static int radio_nr = -1;
87 module_param(radio_nr, int, 0);
88 MODULE_PARM_DESC(radio_nr, "Radio Nr");
90 static struct v4l2_queryctrl radio_qctrl[] = {
92 .id = V4L2_CID_AUDIO_MUTE,
98 .type = V4L2_CTRL_TYPE_BOOLEAN,
100 /* HINT: the disabled controls are only here to satify kradio and such apps */
101 { .id = V4L2_CID_AUDIO_VOLUME,
102 .flags = V4L2_CTRL_FLAG_DISABLED,
105 .id = V4L2_CID_AUDIO_BALANCE,
106 .flags = V4L2_CTRL_FLAG_DISABLED,
109 .id = V4L2_CID_AUDIO_BASS,
110 .flags = V4L2_CTRL_FLAG_DISABLED,
113 .id = V4L2_CID_AUDIO_TREBLE,
114 .flags = V4L2_CTRL_FLAG_DISABLED,
117 .id = V4L2_CID_AUDIO_LOUDNESS,
118 .flags = V4L2_CTRL_FLAG_DISABLED,
122 static int usb_amradio_probe(struct usb_interface *intf,
123 const struct usb_device_id *id);
124 static void usb_amradio_disconnect(struct usb_interface *intf);
125 static int usb_amradio_open(struct inode *inode, struct file *file);
126 static int usb_amradio_close(struct inode *inode, struct file *file);
127 static int usb_amradio_suspend(struct usb_interface *intf,
128 pm_message_t message);
129 static int usb_amradio_resume(struct usb_interface *intf);
131 /* Data for one (physical) device */
132 struct amradio_device {
133 /* reference to USB and video device */
134 struct usb_device *usbdev;
135 struct video_device *videodev;
137 unsigned char *buffer;
138 struct mutex lock; /* buffer locking */
146 /* USB Device ID List */
147 static struct usb_device_id usb_amradio_device_table[] = {
148 {USB_DEVICE_AND_INTERFACE_INFO(USB_AMRADIO_VENDOR, USB_AMRADIO_PRODUCT,
149 USB_CLASS_HID, 0, 0) },
150 { } /* Terminating entry */
153 MODULE_DEVICE_TABLE(usb, usb_amradio_device_table);
155 /* USB subsystem interface */
156 static struct usb_driver usb_amradio_driver = {
157 .name = "radio-mr800",
158 .probe = usb_amradio_probe,
159 .disconnect = usb_amradio_disconnect,
160 .suspend = usb_amradio_suspend,
161 .resume = usb_amradio_resume,
162 .reset_resume = usb_amradio_resume,
163 .id_table = usb_amradio_device_table,
164 .supports_autosuspend = 1,
167 /* switch on radio. Send 8 bytes to device. */
168 static int amradio_start(struct amradio_device *radio)
173 mutex_lock(&radio->lock);
175 radio->buffer[0] = 0x00;
176 radio->buffer[1] = 0x55;
177 radio->buffer[2] = 0xaa;
178 radio->buffer[3] = 0x00;
179 radio->buffer[4] = 0xab;
180 radio->buffer[5] = 0x00;
181 radio->buffer[6] = 0x00;
182 radio->buffer[7] = 0x00;
184 retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2),
185 (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT);
188 mutex_unlock(&radio->lock);
192 mutex_unlock(&radio->lock);
199 /* switch off radio */
200 static int amradio_stop(struct amradio_device *radio)
205 mutex_lock(&radio->lock);
207 radio->buffer[0] = 0x00;
208 radio->buffer[1] = 0x55;
209 radio->buffer[2] = 0xaa;
210 radio->buffer[3] = 0x00;
211 radio->buffer[4] = 0xab;
212 radio->buffer[5] = 0x01;
213 radio->buffer[6] = 0x00;
214 radio->buffer[7] = 0x00;
216 retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2),
217 (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT);
220 mutex_unlock(&radio->lock);
224 mutex_unlock(&radio->lock);
231 /* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */
232 static int amradio_setfreq(struct amradio_device *radio, int freq)
236 unsigned short freq_send = 0x13 + (freq >> 3) / 25;
238 mutex_lock(&radio->lock);
240 radio->buffer[0] = 0x00;
241 radio->buffer[1] = 0x55;
242 radio->buffer[2] = 0xaa;
243 radio->buffer[3] = 0x03;
244 radio->buffer[4] = 0xa4;
245 radio->buffer[5] = 0x00;
246 radio->buffer[6] = 0x00;
247 radio->buffer[7] = 0x08;
249 retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2),
250 (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT);
253 mutex_unlock(&radio->lock);
257 /* frequency is calculated from freq_send and placed in first 2 bytes */
258 radio->buffer[0] = (freq_send >> 8) & 0xff;
259 radio->buffer[1] = freq_send & 0xff;
260 radio->buffer[2] = 0x01;
261 radio->buffer[3] = 0x00;
262 radio->buffer[4] = 0x00;
263 /* 5 and 6 bytes of buffer already = 0x00 */
264 radio->buffer[7] = 0x00;
266 retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2),
267 (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT);
270 mutex_unlock(&radio->lock);
274 mutex_unlock(&radio->lock);
281 /* USB subsystem interface begins here */
283 /* handle unplugging of the device, release data structures
284 if nothing keeps us from doing it. If something is still
285 keeping us busy, the release callback of v4l will take care
287 static void usb_amradio_disconnect(struct usb_interface *intf)
289 struct amradio_device *radio = usb_get_intfdata(intf);
291 usb_set_intfdata(intf, NULL);
294 video_unregister_device(radio->videodev);
295 radio->videodev = NULL;
297 kfree(radio->buffer);
305 /* vidioc_querycap - query device capabilities */
306 static int vidioc_querycap(struct file *file, void *priv,
307 struct v4l2_capability *v)
309 strlcpy(v->driver, "radio-mr800", sizeof(v->driver));
310 strlcpy(v->card, "AverMedia MR 800 USB FM Radio", sizeof(v->card));
311 sprintf(v->bus_info, "USB");
312 v->version = RADIO_VERSION;
313 v->capabilities = V4L2_CAP_TUNER;
317 /* vidioc_g_tuner - get tuner attributes */
318 static int vidioc_g_tuner(struct file *file, void *priv,
319 struct v4l2_tuner *v)
321 struct amradio_device *radio = video_get_drvdata(video_devdata(file));
326 /* TODO: Add function which look is signal stereo or not
327 * amradio_getstat(radio);
330 strcpy(v->name, "FM");
331 v->type = V4L2_TUNER_RADIO;
332 v->rangelow = FREQ_MIN * FREQ_MUL;
333 v->rangehigh = FREQ_MAX * FREQ_MUL;
334 v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
335 v->capability = V4L2_TUNER_CAP_LOW;
337 v->audmode = V4L2_TUNER_MODE_STEREO;
339 v->audmode = V4L2_TUNER_MODE_MONO;
340 v->signal = 0xffff; /* Can't get the signal strength, sad.. */
341 v->afc = 0; /* Don't know what is this */
345 /* vidioc_s_tuner - set tuner attributes */
346 static int vidioc_s_tuner(struct file *file, void *priv,
347 struct v4l2_tuner *v)
354 /* vidioc_s_frequency - set tuner radio frequency */
355 static int vidioc_s_frequency(struct file *file, void *priv,
356 struct v4l2_frequency *f)
358 struct amradio_device *radio = video_get_drvdata(video_devdata(file));
360 radio->curfreq = f->frequency;
361 if (amradio_setfreq(radio, radio->curfreq) < 0)
362 warn("Set frequency failed");
366 /* vidioc_g_frequency - get tuner radio frequency */
367 static int vidioc_g_frequency(struct file *file, void *priv,
368 struct v4l2_frequency *f)
370 struct amradio_device *radio = video_get_drvdata(video_devdata(file));
372 f->type = V4L2_TUNER_RADIO;
373 f->frequency = radio->curfreq;
377 /* vidioc_queryctrl - enumerate control items */
378 static int vidioc_queryctrl(struct file *file, void *priv,
379 struct v4l2_queryctrl *qc)
383 for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
384 if (qc->id && qc->id == radio_qctrl[i].id) {
385 memcpy(qc, &(radio_qctrl[i]),
393 /* vidioc_g_ctrl - get the value of a control */
394 static int vidioc_g_ctrl(struct file *file, void *priv,
395 struct v4l2_control *ctrl)
397 struct amradio_device *radio = video_get_drvdata(video_devdata(file));
400 case V4L2_CID_AUDIO_MUTE:
401 ctrl->value = radio->muted;
407 /* vidioc_s_ctrl - set the value of a control */
408 static int vidioc_s_ctrl(struct file *file, void *priv,
409 struct v4l2_control *ctrl)
411 struct amradio_device *radio = video_get_drvdata(video_devdata(file));
414 case V4L2_CID_AUDIO_MUTE:
416 if (amradio_stop(radio) < 0) {
417 warn("amradio_stop() failed");
421 if (amradio_start(radio) < 0) {
422 warn("amradio_start() failed");
431 /* vidioc_g_audio - get audio attributes */
432 static int vidioc_g_audio(struct file *file, void *priv,
433 struct v4l2_audio *a)
438 strcpy(a->name, "Radio");
439 a->capability = V4L2_AUDCAP_STEREO;
443 /* vidioc_s_audio - set audio attributes */
444 static int vidioc_s_audio(struct file *file, void *priv,
445 struct v4l2_audio *a)
452 /* vidioc_g_input - get input */
453 static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
459 /* vidioc_s_input - set input */
460 static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
467 /* open device - amradio_start() and amradio_setfreq() */
468 static int usb_amradio_open(struct inode *inode, struct file *file)
470 struct amradio_device *radio = video_get_drvdata(video_devdata(file));
477 if (amradio_start(radio) < 0) {
478 warn("Radio did not start up properly");
483 if (amradio_setfreq(radio, radio->curfreq) < 0)
484 warn("Set frequency failed");
490 /*close device - free driver structures */
491 static int usb_amradio_close(struct inode *inode, struct file *file)
493 struct amradio_device *radio = video_get_drvdata(video_devdata(file));
498 if (radio->removed) {
499 kfree(radio->buffer);
505 /* Suspend device - stop device. Need to be checked and fixed */
506 static int usb_amradio_suspend(struct usb_interface *intf, pm_message_t message)
508 struct amradio_device *radio = usb_get_intfdata(intf);
510 if (amradio_stop(radio) < 0)
511 warn("amradio_stop() failed");
513 info("radio-mr800: Going into suspend..");
518 /* Resume device - start device. Need to be checked and fixed */
519 static int usb_amradio_resume(struct usb_interface *intf)
521 struct amradio_device *radio = usb_get_intfdata(intf);
523 if (amradio_start(radio) < 0)
524 warn("amradio_start() failed");
526 info("radio-mr800: Coming out of suspend..");
531 /* File system interface */
532 static const struct file_operations usb_amradio_fops = {
533 .owner = THIS_MODULE,
534 .open = usb_amradio_open,
535 .release = usb_amradio_close,
536 .ioctl = video_ioctl2,
538 .compat_ioctl = v4l_compat_ioctl32,
543 static const struct v4l2_ioctl_ops usb_amradio_ioctl_ops = {
544 .vidioc_querycap = vidioc_querycap,
545 .vidioc_g_tuner = vidioc_g_tuner,
546 .vidioc_s_tuner = vidioc_s_tuner,
547 .vidioc_g_frequency = vidioc_g_frequency,
548 .vidioc_s_frequency = vidioc_s_frequency,
549 .vidioc_queryctrl = vidioc_queryctrl,
550 .vidioc_g_ctrl = vidioc_g_ctrl,
551 .vidioc_s_ctrl = vidioc_s_ctrl,
552 .vidioc_g_audio = vidioc_g_audio,
553 .vidioc_s_audio = vidioc_s_audio,
554 .vidioc_g_input = vidioc_g_input,
555 .vidioc_s_input = vidioc_s_input,
559 static struct video_device amradio_videodev_template = {
560 .name = "AverMedia MR 800 USB FM Radio",
561 .fops = &usb_amradio_fops,
562 .ioctl_ops = &usb_amradio_ioctl_ops,
563 .release = video_device_release,
566 /* check if the device is present and register with v4l and
568 static int usb_amradio_probe(struct usb_interface *intf,
569 const struct usb_device_id *id)
571 struct amradio_device *radio;
573 radio = kmalloc(sizeof(struct amradio_device), GFP_KERNEL);
578 radio->buffer = kmalloc(BUFFER_LENGTH, GFP_KERNEL);
580 if (!(radio->buffer)) {
585 radio->videodev = video_device_alloc();
587 if (!(radio->videodev)) {
588 kfree(radio->buffer);
593 memcpy(radio->videodev, &amradio_videodev_template,
594 sizeof(amradio_videodev_template));
598 radio->usbdev = interface_to_usbdev(intf);
599 radio->curfreq = 95.16 * FREQ_MUL;
601 mutex_init(&radio->lock);
603 video_set_drvdata(radio->videodev, radio);
604 if (video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr)) {
605 warn("Could not register video device");
606 video_device_release(radio->videodev);
607 kfree(radio->buffer);
612 usb_set_intfdata(intf, radio);
616 static int __init amradio_init(void)
618 int retval = usb_register(&usb_amradio_driver);
620 info(DRIVER_VERSION " " DRIVER_DESC);
622 err("usb_register failed. Error number %d", retval);
626 static void __exit amradio_exit(void)
628 usb_deregister(&usb_amradio_driver);
631 module_init(amradio_init);
632 module_exit(amradio_exit);