Path: blob/master/drivers/media/radio/radio-mr800.c
15111 views
/*1* A driver for the AverMedia MR 800 USB FM radio. This device plugs2* into both the USB and an analog audio input, so this thing3* only deals with initialization and frequency setting, the4* audio data has to be handled by a sound driver.5*6* Copyright (c) 2008 Alexey Klimov <[email protected]>7*8* This program is free software; you can redistribute it and/or modify9* it under the terms of the GNU General Public License as published by10* the Free Software Foundation; either version 2 of the License, or11* (at your option) any later version.12*13* This program is distributed in the hope that it will be useful,14* but WITHOUT ANY WARRANTY; without even the implied warranty of15* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the16* GNU General Public License for more details.17*18* You should have received a copy of the GNU General Public License19* along with this program; if not, write to the Free Software20* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA21*/2223/*24* Big thanks to authors and contributors of dsbr100.c and radio-si470x.c25*26* When work was looked pretty good, i discover this:27* http://av-usbradio.sourceforge.net/index.php28* http://sourceforge.net/projects/av-usbradio/29* Latest release of theirs project was in 2005.30* Probably, this driver could be improved through using their31* achievements (specifications given).32* Also, Faidon Liambotis <[email protected]> wrote nice driver for this radio33* in 2007. He allowed to use his driver to improve current mr800 radio driver.34* http://kerneltrap.org/mailarchive/linux-usb-devel/2007/10/11/34249235*36* Version 0.01: First working version.37* It's required to blacklist AverMedia USB Radio38* in usbhid/hid-quirks.c39* Version 0.10: A lot of cleanups and fixes: unpluging the device,40* few mutex locks were added, codinstyle issues, etc.41* Added stereo support. Thanks to42* Douglas Schilling Landgraf <[email protected]> and43* David Ellingsworth <[email protected]>44* for discussion, help and support.45* Version 0.11: Converted to v4l2_device.46*47* Many things to do:48* - Correct power management of device (suspend & resume)49* - Add code for scanning and smooth tuning50* - Add code for sensitivity value51* - Correct mistakes52* - In Japan another FREQ_MIN and FREQ_MAX53*/5455/* kernel includes */56#include <linux/kernel.h>57#include <linux/module.h>58#include <linux/init.h>59#include <linux/slab.h>60#include <linux/input.h>61#include <linux/videodev2.h>62#include <media/v4l2-device.h>63#include <media/v4l2-ioctl.h>64#include <linux/usb.h>65#include <linux/version.h> /* for KERNEL_VERSION MACRO */66#include <linux/mutex.h>6768/* driver and module definitions */69#define DRIVER_AUTHOR "Alexey Klimov <[email protected]>"70#define DRIVER_DESC "AverMedia MR 800 USB FM radio driver"71#define DRIVER_VERSION "0.11"72#define RADIO_VERSION KERNEL_VERSION(0, 1, 1)7374MODULE_AUTHOR(DRIVER_AUTHOR);75MODULE_DESCRIPTION(DRIVER_DESC);76MODULE_LICENSE("GPL");7778#define USB_AMRADIO_VENDOR 0x07ca79#define USB_AMRADIO_PRODUCT 0xb8008081/* dev_warn macro with driver name */82#define MR800_DRIVER_NAME "radio-mr800"83#define amradio_dev_warn(dev, fmt, arg...) \84dev_warn(dev, MR800_DRIVER_NAME " - " fmt, ##arg)8586#define amradio_dev_err(dev, fmt, arg...) \87dev_err(dev, MR800_DRIVER_NAME " - " fmt, ##arg)8889/* Probably USB_TIMEOUT should be modified in module parameter */90#define BUFFER_LENGTH 891#define USB_TIMEOUT 5009293/* Frequency limits in MHz -- these are European values. For Japanese94devices, that would be 76 and 91. */95#define FREQ_MIN 87.596#define FREQ_MAX 108.097#define FREQ_MUL 160009899/*100* Commands that device should understand101* List isn't full and will be updated with implementation of new functions102*/103#define AMRADIO_SET_FREQ 0xa4104#define AMRADIO_SET_MUTE 0xab105#define AMRADIO_SET_MONO 0xae106107/* Comfortable defines for amradio_set_mute */108#define AMRADIO_START 0x00109#define AMRADIO_STOP 0x01110111/* Comfortable defines for amradio_set_stereo */112#define WANT_STEREO 0x00113#define WANT_MONO 0x01114115/* module parameter */116static int radio_nr = -1;117module_param(radio_nr, int, 0);118MODULE_PARM_DESC(radio_nr, "Radio Nr");119120static int usb_amradio_probe(struct usb_interface *intf,121const struct usb_device_id *id);122static void usb_amradio_disconnect(struct usb_interface *intf);123static int usb_amradio_open(struct file *file);124static int usb_amradio_close(struct file *file);125static int usb_amradio_suspend(struct usb_interface *intf,126pm_message_t message);127static int usb_amradio_resume(struct usb_interface *intf);128129/* Data for one (physical) device */130struct amradio_device {131/* reference to USB and video device */132struct usb_device *usbdev;133struct usb_interface *intf;134struct video_device videodev;135struct v4l2_device v4l2_dev;136137unsigned char *buffer;138struct mutex lock; /* buffer locking */139int curfreq;140int stereo;141int muted;142int initialized;143};144145static inline struct amradio_device *to_amradio_dev(struct v4l2_device *v4l2_dev)146{147return container_of(v4l2_dev, struct amradio_device, v4l2_dev);148}149150/* USB Device ID List */151static struct usb_device_id usb_amradio_device_table[] = {152{USB_DEVICE_AND_INTERFACE_INFO(USB_AMRADIO_VENDOR, USB_AMRADIO_PRODUCT,153USB_CLASS_HID, 0, 0) },154{ } /* Terminating entry */155};156157MODULE_DEVICE_TABLE(usb, usb_amradio_device_table);158159/* USB subsystem interface */160static struct usb_driver usb_amradio_driver = {161.name = MR800_DRIVER_NAME,162.probe = usb_amradio_probe,163.disconnect = usb_amradio_disconnect,164.suspend = usb_amradio_suspend,165.resume = usb_amradio_resume,166.reset_resume = usb_amradio_resume,167.id_table = usb_amradio_device_table,168.supports_autosuspend = 1,169};170171/* switch on/off the radio. Send 8 bytes to device */172static int amradio_set_mute(struct amradio_device *radio, char argument)173{174int retval;175int size;176177radio->buffer[0] = 0x00;178radio->buffer[1] = 0x55;179radio->buffer[2] = 0xaa;180radio->buffer[3] = 0x00;181radio->buffer[4] = AMRADIO_SET_MUTE;182radio->buffer[5] = argument;183radio->buffer[6] = 0x00;184radio->buffer[7] = 0x00;185186retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2),187(void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT);188189if (retval < 0 || size != BUFFER_LENGTH) {190amradio_dev_warn(&radio->videodev.dev, "set mute failed\n");191return retval;192}193194radio->muted = argument;195196return retval;197}198199/* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */200static int amradio_setfreq(struct amradio_device *radio, int freq)201{202int retval;203int size;204unsigned short freq_send = 0x10 + (freq >> 3) / 25;205206radio->buffer[0] = 0x00;207radio->buffer[1] = 0x55;208radio->buffer[2] = 0xaa;209radio->buffer[3] = 0x03;210radio->buffer[4] = AMRADIO_SET_FREQ;211radio->buffer[5] = 0x00;212radio->buffer[6] = 0x00;213radio->buffer[7] = 0x08;214215retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2),216(void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT);217218if (retval < 0 || size != BUFFER_LENGTH)219goto out_err;220221/* frequency is calculated from freq_send and placed in first 2 bytes */222radio->buffer[0] = (freq_send >> 8) & 0xff;223radio->buffer[1] = freq_send & 0xff;224radio->buffer[2] = 0x01;225radio->buffer[3] = 0x00;226radio->buffer[4] = 0x00;227/* 5 and 6 bytes of buffer already = 0x00 */228radio->buffer[7] = 0x00;229230retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2),231(void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT);232233if (retval < 0 || size != BUFFER_LENGTH)234goto out_err;235236radio->curfreq = freq;237goto out;238239out_err:240amradio_dev_warn(&radio->videodev.dev, "set frequency failed\n");241out:242return retval;243}244245static int amradio_set_stereo(struct amradio_device *radio, char argument)246{247int retval;248int size;249250radio->buffer[0] = 0x00;251radio->buffer[1] = 0x55;252radio->buffer[2] = 0xaa;253radio->buffer[3] = 0x00;254radio->buffer[4] = AMRADIO_SET_MONO;255radio->buffer[5] = argument;256radio->buffer[6] = 0x00;257radio->buffer[7] = 0x00;258259retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2),260(void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT);261262if (retval < 0 || size != BUFFER_LENGTH) {263amradio_dev_warn(&radio->videodev.dev, "set stereo failed\n");264return retval;265}266267if (argument == WANT_STEREO)268radio->stereo = 1;269else270radio->stereo = 0;271272return retval;273}274275/* Handle unplugging the device.276* We call video_unregister_device in any case.277* The last function called in this procedure is278* usb_amradio_device_release.279*/280static void usb_amradio_disconnect(struct usb_interface *intf)281{282struct amradio_device *radio = to_amradio_dev(usb_get_intfdata(intf));283284mutex_lock(&radio->lock);285/* increase the device node's refcount */286get_device(&radio->videodev.dev);287v4l2_device_disconnect(&radio->v4l2_dev);288video_unregister_device(&radio->videodev);289mutex_unlock(&radio->lock);290/* decrease the device node's refcount, allowing it to be released */291put_device(&radio->videodev.dev);292}293294/* vidioc_querycap - query device capabilities */295static int vidioc_querycap(struct file *file, void *priv,296struct v4l2_capability *v)297{298struct amradio_device *radio = file->private_data;299300strlcpy(v->driver, "radio-mr800", sizeof(v->driver));301strlcpy(v->card, "AverMedia MR 800 USB FM Radio", sizeof(v->card));302usb_make_path(radio->usbdev, v->bus_info, sizeof(v->bus_info));303v->version = RADIO_VERSION;304v->capabilities = V4L2_CAP_TUNER;305return 0;306}307308/* vidioc_g_tuner - get tuner attributes */309static int vidioc_g_tuner(struct file *file, void *priv,310struct v4l2_tuner *v)311{312struct amradio_device *radio = file->private_data;313int retval;314315if (v->index > 0)316return -EINVAL;317318/* TODO: Add function which look is signal stereo or not319* amradio_getstat(radio);320*/321322/* we call amradio_set_stereo to set radio->stereo323* Honestly, amradio_getstat should cover this in future and324* amradio_set_stereo shouldn't be here325*/326retval = amradio_set_stereo(radio, WANT_STEREO);327328strcpy(v->name, "FM");329v->type = V4L2_TUNER_RADIO;330v->rangelow = FREQ_MIN * FREQ_MUL;331v->rangehigh = FREQ_MAX * FREQ_MUL;332v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;333v->capability = V4L2_TUNER_CAP_LOW;334if (radio->stereo)335v->audmode = V4L2_TUNER_MODE_STEREO;336else337v->audmode = V4L2_TUNER_MODE_MONO;338v->signal = 0xffff; /* Can't get the signal strength, sad.. */339v->afc = 0; /* Don't know what is this */340341return retval;342}343344/* vidioc_s_tuner - set tuner attributes */345static int vidioc_s_tuner(struct file *file, void *priv,346struct v4l2_tuner *v)347{348struct amradio_device *radio = file->private_data;349int retval = -EINVAL;350351if (v->index > 0)352return -EINVAL;353354/* mono/stereo selector */355switch (v->audmode) {356case V4L2_TUNER_MODE_MONO:357retval = amradio_set_stereo(radio, WANT_MONO);358break;359case V4L2_TUNER_MODE_STEREO:360retval = amradio_set_stereo(radio, WANT_STEREO);361break;362}363364return retval;365}366367/* vidioc_s_frequency - set tuner radio frequency */368static int vidioc_s_frequency(struct file *file, void *priv,369struct v4l2_frequency *f)370{371struct amradio_device *radio = file->private_data;372373if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)374return -EINVAL;375return amradio_setfreq(radio, f->frequency);376}377378/* vidioc_g_frequency - get tuner radio frequency */379static int vidioc_g_frequency(struct file *file, void *priv,380struct v4l2_frequency *f)381{382struct amradio_device *radio = file->private_data;383384if (f->tuner != 0)385return -EINVAL;386f->type = V4L2_TUNER_RADIO;387f->frequency = radio->curfreq;388389return 0;390}391392/* vidioc_queryctrl - enumerate control items */393static int vidioc_queryctrl(struct file *file, void *priv,394struct v4l2_queryctrl *qc)395{396switch (qc->id) {397case V4L2_CID_AUDIO_MUTE:398return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);399}400401return -EINVAL;402}403404/* vidioc_g_ctrl - get the value of a control */405static int vidioc_g_ctrl(struct file *file, void *priv,406struct v4l2_control *ctrl)407{408struct amradio_device *radio = file->private_data;409410switch (ctrl->id) {411case V4L2_CID_AUDIO_MUTE:412ctrl->value = radio->muted;413return 0;414}415416return -EINVAL;417}418419/* vidioc_s_ctrl - set the value of a control */420static int vidioc_s_ctrl(struct file *file, void *priv,421struct v4l2_control *ctrl)422{423struct amradio_device *radio = file->private_data;424int retval = -EINVAL;425426switch (ctrl->id) {427case V4L2_CID_AUDIO_MUTE:428if (ctrl->value)429retval = amradio_set_mute(radio, AMRADIO_STOP);430else431retval = amradio_set_mute(radio, AMRADIO_START);432433break;434}435436return retval;437}438439/* vidioc_g_audio - get audio attributes */440static int vidioc_g_audio(struct file *file, void *priv,441struct v4l2_audio *a)442{443if (a->index > 1)444return -EINVAL;445446strcpy(a->name, "Radio");447a->capability = V4L2_AUDCAP_STEREO;448return 0;449}450451/* vidioc_s_audio - set audio attributes */452static int vidioc_s_audio(struct file *file, void *priv,453struct v4l2_audio *a)454{455if (a->index != 0)456return -EINVAL;457return 0;458}459460/* vidioc_g_input - get input */461static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)462{463*i = 0;464return 0;465}466467/* vidioc_s_input - set input */468static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)469{470if (i != 0)471return -EINVAL;472return 0;473}474475static int usb_amradio_init(struct amradio_device *radio)476{477int retval;478479retval = amradio_set_mute(radio, AMRADIO_STOP);480if (retval)481goto out_err;482483retval = amradio_set_stereo(radio, WANT_STEREO);484if (retval)485goto out_err;486487radio->initialized = 1;488goto out;489490out_err:491amradio_dev_err(&radio->videodev.dev, "initialization failed\n");492out:493return retval;494}495496/* open device - amradio_start() and amradio_setfreq() */497static int usb_amradio_open(struct file *file)498{499struct amradio_device *radio = video_drvdata(file);500int retval;501502file->private_data = radio;503retval = usb_autopm_get_interface(radio->intf);504if (retval)505return retval;506507if (unlikely(!radio->initialized)) {508retval = usb_amradio_init(radio);509if (retval)510usb_autopm_put_interface(radio->intf);511}512return retval;513}514515/*close device */516static int usb_amradio_close(struct file *file)517{518struct amradio_device *radio = file->private_data;519520if (video_is_registered(&radio->videodev))521usb_autopm_put_interface(radio->intf);522return 0;523}524525/* Suspend device - stop device. Need to be checked and fixed */526static int usb_amradio_suspend(struct usb_interface *intf, pm_message_t message)527{528struct amradio_device *radio = to_amradio_dev(usb_get_intfdata(intf));529530mutex_lock(&radio->lock);531if (!radio->muted && radio->initialized) {532amradio_set_mute(radio, AMRADIO_STOP);533radio->muted = 0;534}535mutex_unlock(&radio->lock);536537dev_info(&intf->dev, "going into suspend..\n");538return 0;539}540541/* Resume device - start device. Need to be checked and fixed */542static int usb_amradio_resume(struct usb_interface *intf)543{544struct amradio_device *radio = to_amradio_dev(usb_get_intfdata(intf));545546mutex_lock(&radio->lock);547if (unlikely(!radio->initialized))548goto unlock;549550if (radio->stereo)551amradio_set_stereo(radio, WANT_STEREO);552else553amradio_set_stereo(radio, WANT_MONO);554555amradio_setfreq(radio, radio->curfreq);556557if (!radio->muted)558amradio_set_mute(radio, AMRADIO_START);559560unlock:561mutex_unlock(&radio->lock);562563dev_info(&intf->dev, "coming out of suspend..\n");564return 0;565}566567/* File system interface */568static const struct v4l2_file_operations usb_amradio_fops = {569.owner = THIS_MODULE,570.open = usb_amradio_open,571.release = usb_amradio_close,572.unlocked_ioctl = video_ioctl2,573};574575static const struct v4l2_ioctl_ops usb_amradio_ioctl_ops = {576.vidioc_querycap = vidioc_querycap,577.vidioc_g_tuner = vidioc_g_tuner,578.vidioc_s_tuner = vidioc_s_tuner,579.vidioc_g_frequency = vidioc_g_frequency,580.vidioc_s_frequency = vidioc_s_frequency,581.vidioc_queryctrl = vidioc_queryctrl,582.vidioc_g_ctrl = vidioc_g_ctrl,583.vidioc_s_ctrl = vidioc_s_ctrl,584.vidioc_g_audio = vidioc_g_audio,585.vidioc_s_audio = vidioc_s_audio,586.vidioc_g_input = vidioc_g_input,587.vidioc_s_input = vidioc_s_input,588};589590static void usb_amradio_video_device_release(struct video_device *videodev)591{592struct amradio_device *radio = video_get_drvdata(videodev);593594/* free rest memory */595kfree(radio->buffer);596kfree(radio);597}598599/* check if the device is present and register with v4l and usb if it is */600static int usb_amradio_probe(struct usb_interface *intf,601const struct usb_device_id *id)602{603struct amradio_device *radio;604int retval = 0;605606radio = kzalloc(sizeof(struct amradio_device), GFP_KERNEL);607608if (!radio) {609dev_err(&intf->dev, "kmalloc for amradio_device failed\n");610retval = -ENOMEM;611goto err;612}613614radio->buffer = kmalloc(BUFFER_LENGTH, GFP_KERNEL);615616if (!radio->buffer) {617dev_err(&intf->dev, "kmalloc for radio->buffer failed\n");618retval = -ENOMEM;619goto err_nobuf;620}621622retval = v4l2_device_register(&intf->dev, &radio->v4l2_dev);623if (retval < 0) {624dev_err(&intf->dev, "couldn't register v4l2_device\n");625goto err_v4l2;626}627628mutex_init(&radio->lock);629630strlcpy(radio->videodev.name, radio->v4l2_dev.name,631sizeof(radio->videodev.name));632radio->videodev.v4l2_dev = &radio->v4l2_dev;633radio->videodev.fops = &usb_amradio_fops;634radio->videodev.ioctl_ops = &usb_amradio_ioctl_ops;635radio->videodev.release = usb_amradio_video_device_release;636radio->videodev.lock = &radio->lock;637638radio->usbdev = interface_to_usbdev(intf);639radio->intf = intf;640radio->curfreq = 95.16 * FREQ_MUL;641642video_set_drvdata(&radio->videodev, radio);643644retval = video_register_device(&radio->videodev, VFL_TYPE_RADIO,645radio_nr);646if (retval < 0) {647dev_err(&intf->dev, "could not register video device\n");648goto err_vdev;649}650651return 0;652653err_vdev:654v4l2_device_unregister(&radio->v4l2_dev);655err_v4l2:656kfree(radio->buffer);657err_nobuf:658kfree(radio);659err:660return retval;661}662663static int __init amradio_init(void)664{665int retval = usb_register(&usb_amradio_driver);666667pr_info(KBUILD_MODNAME668": version " DRIVER_VERSION " " DRIVER_DESC "\n");669670if (retval)671pr_err(KBUILD_MODNAME672": usb_register failed. Error number %d\n", retval);673674return retval;675}676677static void __exit amradio_exit(void)678{679usb_deregister(&usb_amradio_driver);680}681682module_init(amradio_init);683module_exit(amradio_exit);684685686687