📄 radio-mr800.c
字号:
/* * A driver for the AverMedia MR 800 USB FM radio. This device plugs * into both the USB and an analog audio input, so this thing * only deals with initialization and frequency setting, the * audio data has to be handled by a sound driver. * * Copyright (c) 2008 Alexey Klimov <klimov.linux@gmail.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *//* * Big thanks to authors of dsbr100.c and radio-si470x.c * * When work was looked pretty good, i discover this: * http://av-usbradio.sourceforge.net/index.php * http://sourceforge.net/projects/av-usbradio/ * Latest release of theirs project was in 2005. * Probably, this driver could be improved trough using their * achievements (specifications given). * So, we have smth to begin with. * * History: * Version 0.01: First working version. * It's required to blacklist AverMedia USB Radio * in usbhid/hid-quirks.c * * Many things to do: * - Correct power managment of device (suspend & resume) * - Make x86 independance (little-endian and big-endian stuff) * - Add code for scanning and smooth tuning * - Checked and add stereo&mono stuff * - Add code for sensitivity value * - Correct mistakes * - In Japan another FREQ_MIN and FREQ_MAX *//* kernel includes */#include <linux/kernel.h>#include <linux/module.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/input.h>#include <linux/videodev2.h>#include <media/v4l2-common.h>#include <media/v4l2-ioctl.h>#include <linux/usb.h>#include <linux/version.h> /* for KERNEL_VERSION MACRO */#include "compat.h"/* driver and module definitions */#define DRIVER_AUTHOR "Alexey Klimov <klimov.linux@gmail.com>"#define DRIVER_DESC "AverMedia MR 800 USB FM radio driver"#define DRIVER_VERSION "0.01"#define RADIO_VERSION KERNEL_VERSION(0, 0, 1)MODULE_AUTHOR(DRIVER_AUTHOR);MODULE_DESCRIPTION(DRIVER_DESC);MODULE_LICENSE("GPL");#define USB_AMRADIO_VENDOR 0x07ca#define USB_AMRADIO_PRODUCT 0xb800/* Probably USB_TIMEOUT should be modified in module parameter */#define BUFFER_LENGTH 8#define USB_TIMEOUT 500/* Frequency limits in MHz -- these are European values. For Japanesedevices, that would be 76 and 91. */#define FREQ_MIN 87.5#define FREQ_MAX 108.0#define FREQ_MUL 16000/* module parameter */static int radio_nr = -1;module_param(radio_nr, int, 0);MODULE_PARM_DESC(radio_nr, "Radio Nr");static struct v4l2_queryctrl radio_qctrl[] = { { .id = V4L2_CID_AUDIO_MUTE, .name = "Mute", .minimum = 0, .maximum = 1, .step = 1, .default_value = 1, .type = V4L2_CTRL_TYPE_BOOLEAN, },/* HINT: the disabled controls are only here to satify kradio and such apps */ { .id = V4L2_CID_AUDIO_VOLUME, .flags = V4L2_CTRL_FLAG_DISABLED, }, { .id = V4L2_CID_AUDIO_BALANCE, .flags = V4L2_CTRL_FLAG_DISABLED, }, { .id = V4L2_CID_AUDIO_BASS, .flags = V4L2_CTRL_FLAG_DISABLED, }, { .id = V4L2_CID_AUDIO_TREBLE, .flags = V4L2_CTRL_FLAG_DISABLED, }, { .id = V4L2_CID_AUDIO_LOUDNESS, .flags = V4L2_CTRL_FLAG_DISABLED, },};static int usb_amradio_probe(struct usb_interface *intf, const struct usb_device_id *id);static void usb_amradio_disconnect(struct usb_interface *intf);static int usb_amradio_open(struct inode *inode, struct file *file);static int usb_amradio_close(struct inode *inode, struct file *file);static int usb_amradio_suspend(struct usb_interface *intf, pm_message_t message);static int usb_amradio_resume(struct usb_interface *intf);/* Data for one (physical) device */struct amradio_device { /* reference to USB and video device */ struct usb_device *usbdev; struct video_device *videodev; unsigned char *buffer; struct mutex lock; /* buffer locking */ int curfreq; int stereo; int users; int removed; int muted;};/* USB Device ID List */static struct usb_device_id usb_amradio_device_table[] = { {USB_DEVICE_AND_INTERFACE_INFO(USB_AMRADIO_VENDOR, USB_AMRADIO_PRODUCT, USB_CLASS_HID, 0, 0) }, { } /* Terminating entry */};MODULE_DEVICE_TABLE(usb, usb_amradio_device_table);/* USB subsystem interface */static struct usb_driver usb_amradio_driver = { .name = "radio-mr800", .probe = usb_amradio_probe, .disconnect = usb_amradio_disconnect, .suspend = usb_amradio_suspend, .resume = usb_amradio_resume,#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23) .reset_resume = usb_amradio_resume,#endif .id_table = usb_amradio_device_table, .supports_autosuspend = 1,};/* switch on radio. Send 8 bytes to device. */static int amradio_start(struct amradio_device *radio){ int retval; int size; mutex_lock(&radio->lock); radio->buffer[0] = 0x00; radio->buffer[1] = 0x55; radio->buffer[2] = 0xaa; radio->buffer[3] = 0x00; radio->buffer[4] = 0xab; radio->buffer[5] = 0x00; radio->buffer[6] = 0x00; radio->buffer[7] = 0x00; retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2), (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT); if (retval) { mutex_unlock(&radio->lock); return retval; } mutex_unlock(&radio->lock); radio->muted = 0; return retval;}/* switch off radio */static int amradio_stop(struct amradio_device *radio){ int retval; int size; mutex_lock(&radio->lock); radio->buffer[0] = 0x00; radio->buffer[1] = 0x55; radio->buffer[2] = 0xaa; radio->buffer[3] = 0x00; radio->buffer[4] = 0xab; radio->buffer[5] = 0x01; radio->buffer[6] = 0x00; radio->buffer[7] = 0x00; retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2), (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT); if (retval) { mutex_unlock(&radio->lock); return retval; } mutex_unlock(&radio->lock); radio->muted = 1; return retval;}/* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */static int amradio_setfreq(struct amradio_device *radio, int freq){ int retval; int size; unsigned short freq_send = 0x13 + (freq >> 3) / 25; mutex_lock(&radio->lock); radio->buffer[0] = 0x00; radio->buffer[1] = 0x55; radio->buffer[2] = 0xaa; radio->buffer[3] = 0x03; radio->buffer[4] = 0xa4; radio->buffer[5] = 0x00; radio->buffer[6] = 0x00; radio->buffer[7] = 0x08; retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2), (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT); if (retval) { mutex_unlock(&radio->lock); return retval; } /* frequency is calculated from freq_send and placed in first 2 bytes */ radio->buffer[0] = (freq_send >> 8) & 0xff; radio->buffer[1] = freq_send & 0xff; radio->buffer[2] = 0x01; radio->buffer[3] = 0x00; radio->buffer[4] = 0x00; /* 5 and 6 bytes of buffer already = 0x00 */ radio->buffer[7] = 0x00; retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2), (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT); if (retval) { mutex_unlock(&radio->lock); return retval; } mutex_unlock(&radio->lock); radio->stereo = 0; return retval;}/* USB subsystem interface begins here *//* handle unplugging of the device, release data structuresif nothing keeps us from doing it. If something is stillkeeping us busy, the release callback of v4l will take careof releasing it. */static void usb_amradio_disconnect(struct usb_interface *intf){ struct amradio_device *radio = usb_get_intfdata(intf); usb_set_intfdata(intf, NULL); if (radio) { video_unregister_device(radio->videodev); radio->videodev = NULL; if (radio->users) { kfree(radio->buffer); kfree(radio); } else { radio->removed = 1; } }}/* vidioc_querycap - query device capabilities */static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *v){ strlcpy(v->driver, "radio-mr800", sizeof(v->driver)); strlcpy(v->card, "AverMedia MR 800 USB FM Radio", sizeof(v->card)); sprintf(v->bus_info, "USB"); v->version = RADIO_VERSION; v->capabilities = V4L2_CAP_TUNER; return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -