2008-10-01 16:40:59 +04:00
/*
* 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
*/
/*
2009-02-05 14:58:31 +03:00
* Big thanks to authors and contributors of dsbr100 . c and radio - si470x . c
2008-10-01 16:40:59 +04:00
*
* 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 ) .
2009-02-05 14:58:31 +03:00
* Also , Faidon Liambotis < paravoid @ debian . org > wrote nice driver for this radio
* in 2007. He allowed to use his driver to improve current mr800 radio driver .
* http : //kerneltrap.org/mailarchive/linux-usb-devel/2007/10/11/342492
2008-10-01 16:40:59 +04:00
*
* Version 0.01 : First working version .
* It ' s required to blacklist AverMedia USB Radio
* in usbhid / hid - quirks . c
2009-02-05 14:58:31 +03:00
* Version 0.10 : A lot of cleanups and fixes : unpluging the device ,
* few mutex locks were added , codinstyle issues , etc .
* Added stereo support . Thanks to
* Douglas Schilling Landgraf < dougsland @ gmail . com > and
* David Ellingsworth < david @ identd . dyndns . org >
* for discussion , help and support .
2009-04-04 01:45:27 +04:00
* Version 0.11 : Converted to v4l2_device .
2008-10-01 16:40:59 +04:00
*
* Many things to do :
2009-07-23 10:31:31 +04:00
* - Correct power management of device ( suspend & resume )
2008-10-01 16:40:59 +04:00
* - Add code for scanning and smooth tuning
* - 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>
2009-07-11 22:08:37 +04:00
# include <linux/smp_lock.h>
2008-10-01 16:40:59 +04:00
# include <linux/input.h>
# include <linux/videodev2.h>
2009-04-04 01:45:27 +04:00
# include <media/v4l2-device.h>
2008-10-01 16:40:59 +04:00
# include <media/v4l2-ioctl.h>
# include <linux/usb.h>
# include <linux/version.h> /* for KERNEL_VERSION MACRO */
2009-05-13 00:39:27 +04:00
# include <linux/mutex.h>
2008-10-01 16:40:59 +04:00
/* driver and module definitions */
# define DRIVER_AUTHOR "Alexey Klimov <klimov.linux@gmail.com>"
# define DRIVER_DESC "AverMedia MR 800 USB FM radio driver"
2009-04-04 01:45:27 +04:00
# define DRIVER_VERSION "0.11"
# define RADIO_VERSION KERNEL_VERSION(0, 1, 1)
2008-10-01 16:40:59 +04:00
MODULE_AUTHOR ( DRIVER_AUTHOR ) ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
MODULE_LICENSE ( " GPL " ) ;
# define USB_AMRADIO_VENDOR 0x07ca
# define USB_AMRADIO_PRODUCT 0xb800
2008-11-04 21:02:36 +03:00
/* dev_warn macro with driver name */
# define MR800_DRIVER_NAME "radio-mr800"
# define amradio_dev_warn(dev, fmt, arg...) \
dev_warn ( dev , MR800_DRIVER_NAME " - " fmt , # # arg )
2009-09-24 01:13:50 +04:00
# define amradio_dev_err(dev, fmt, arg...) \
dev_err ( dev , MR800_DRIVER_NAME " - " fmt , # # arg )
2008-10-01 16:40:59 +04:00
/* 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 Japanese
devices , that would be 76 and 91. */
# define FREQ_MIN 87.5
# define FREQ_MAX 108.0
# define FREQ_MUL 16000
2009-02-05 14:53:02 +03:00
/*
* Commands that device should understand
* List isnt full and will be updated with implementation of new functions
*/
2009-02-05 14:54:17 +03:00
# define AMRADIO_SET_FREQ 0xa4
2009-02-05 14:53:02 +03:00
# define AMRADIO_SET_MUTE 0xab
2009-02-05 14:56:07 +03:00
# define AMRADIO_SET_MONO 0xae
2009-02-05 14:53:02 +03:00
/* Comfortable defines for amradio_set_mute */
# define AMRADIO_START 0x00
# define AMRADIO_STOP 0x01
2009-02-05 14:56:07 +03:00
/* Comfortable defines for amradio_set_stereo */
# define WANT_STEREO 0x00
# define WANT_MONO 0x01
2008-10-01 16:40:59 +04:00
/* module parameter */
static int radio_nr = - 1 ;
module_param ( radio_nr , int , 0 ) ;
MODULE_PARM_DESC ( radio_nr , " Radio Nr " ) ;
static int usb_amradio_probe ( struct usb_interface * intf ,
const struct usb_device_id * id ) ;
static void usb_amradio_disconnect ( struct usb_interface * intf ) ;
2008-12-30 12:58:20 +03:00
static int usb_amradio_open ( struct file * file ) ;
static int usb_amradio_close ( struct file * file ) ;
2008-10-01 16:40:59 +04:00
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 ;
2009-09-23 04:37:30 +04:00
struct video_device videodev ;
2009-04-04 01:45:27 +04:00
struct v4l2_device v4l2_dev ;
2008-10-01 16:40:59 +04:00
unsigned char * buffer ;
struct mutex lock ; /* buffer locking */
int curfreq ;
int stereo ;
int muted ;
2009-09-24 01:13:50 +04:00
int initialized ;
2008-10-01 16:40:59 +04:00
} ;
2009-09-24 00:45:31 +04:00
# define vdev_to_amradio(r) container_of(r, struct amradio_device, videodev)
2008-10-01 16:40:59 +04:00
/* 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 = {
2008-11-04 21:02:36 +03:00
. name = MR800_DRIVER_NAME ,
2008-10-01 16:40:59 +04:00
. probe = usb_amradio_probe ,
. disconnect = usb_amradio_disconnect ,
. suspend = usb_amradio_suspend ,
. resume = usb_amradio_resume ,
. reset_resume = usb_amradio_resume ,
. id_table = usb_amradio_device_table ,
2008-12-28 03:31:49 +03:00
. supports_autosuspend = 0 ,
2008-10-01 16:40:59 +04:00
} ;
2009-02-05 14:53:02 +03:00
/* switch on/off the radio. Send 8 bytes to device */
static int amradio_set_mute ( struct amradio_device * radio , char argument )
2008-10-01 16:40:59 +04:00
{
int retval ;
int size ;
2009-09-23 04:22:19 +04:00
BUG_ON ( ! mutex_is_locked ( & radio - > lock ) ) ;
2008-10-01 16:40:59 +04:00
radio - > buffer [ 0 ] = 0x00 ;
radio - > buffer [ 1 ] = 0x55 ;
radio - > buffer [ 2 ] = 0xaa ;
radio - > buffer [ 3 ] = 0x00 ;
2009-02-05 14:53:02 +03:00
radio - > buffer [ 4 ] = AMRADIO_SET_MUTE ;
radio - > buffer [ 5 ] = argument ;
2008-10-01 16:40:59 +04:00
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 ) ;
2009-09-23 04:22:19 +04:00
if ( retval < 0 | | size ! = BUFFER_LENGTH )
2008-10-01 16:40:59 +04:00
return retval ;
2009-02-05 14:53:02 +03:00
radio - > muted = argument ;
2008-10-01 16:40:59 +04:00
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 ;
2009-02-05 14:54:17 +03:00
unsigned short freq_send = 0x10 + ( freq > > 3 ) / 25 ;
2008-10-01 16:40:59 +04:00
2009-09-23 04:22:19 +04:00
BUG_ON ( ! mutex_is_locked ( & radio - > lock ) ) ;
2008-10-01 16:40:59 +04:00
radio - > buffer [ 0 ] = 0x00 ;
radio - > buffer [ 1 ] = 0x55 ;
radio - > buffer [ 2 ] = 0xaa ;
radio - > buffer [ 3 ] = 0x03 ;
2009-02-05 14:54:17 +03:00
radio - > buffer [ 4 ] = AMRADIO_SET_FREQ ;
2008-10-01 16:40:59 +04:00
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 ) ;
2009-09-23 04:22:19 +04:00
if ( retval < 0 | | size ! = BUFFER_LENGTH )
2008-10-01 16:40:59 +04:00
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 ) ;
2009-02-05 14:56:07 +03:00
return retval ;
}
static int amradio_set_stereo ( struct amradio_device * radio , char argument )
{
int retval ;
int size ;
2009-09-23 04:22:19 +04:00
BUG_ON ( ! mutex_is_locked ( & radio - > lock ) ) ;
2009-02-05 14:56:07 +03:00
radio - > buffer [ 0 ] = 0x00 ;
radio - > buffer [ 1 ] = 0x55 ;
radio - > buffer [ 2 ] = 0xaa ;
radio - > buffer [ 3 ] = 0x00 ;
radio - > buffer [ 4 ] = AMRADIO_SET_MONO ;
radio - > buffer [ 5 ] = argument ;
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 ) ;
2009-09-24 01:16:38 +04:00
if ( retval < 0 | | size ! = BUFFER_LENGTH )
2009-02-05 14:56:07 +03:00
return retval ;
2009-09-24 01:16:38 +04:00
if ( argument = = WANT_STEREO )
radio - > stereo = 1 ;
else
radio - > stereo = 0 ;
2008-10-01 16:40:59 +04:00
return retval ;
}
2009-02-05 14:58:31 +03:00
/* Handle unplugging the device.
* We call video_unregister_device in any case .
* The last function called in this procedure is
* usb_amradio_device_release .
*/
2008-10-01 16:40:59 +04:00
static void usb_amradio_disconnect ( struct usb_interface * intf )
{
struct amradio_device * radio = usb_get_intfdata ( intf ) ;
2008-12-28 03:30:34 +03:00
mutex_lock ( & radio - > lock ) ;
2009-09-24 00:52:17 +04:00
radio - > usbdev = NULL ;
2008-12-28 03:30:34 +03:00
mutex_unlock ( & radio - > lock ) ;
2008-10-01 16:40:59 +04:00
2008-12-28 03:30:34 +03:00
usb_set_intfdata ( intf , NULL ) ;
2009-04-04 01:45:27 +04:00
v4l2_device_disconnect ( & radio - > v4l2_dev ) ;
2009-09-24 01:03:52 +04:00
video_unregister_device ( & radio - > videodev ) ;
2008-10-01 16:40:59 +04:00
}
/* vidioc_querycap - query device capabilities */
static int vidioc_querycap ( struct file * file , void * priv ,
struct v4l2_capability * v )
{
2009-09-24 00:45:31 +04:00
struct amradio_device * radio = file - > private_data ;
2009-01-26 02:05:58 +03:00
2008-10-01 16:40:59 +04:00
strlcpy ( v - > driver , " radio-mr800 " , sizeof ( v - > driver ) ) ;
strlcpy ( v - > card , " AverMedia MR 800 USB FM Radio " , sizeof ( v - > card ) ) ;
2009-01-26 02:05:58 +03:00
usb_make_path ( radio - > usbdev , v - > bus_info , sizeof ( v - > bus_info ) ) ;
2008-10-01 16:40:59 +04:00
v - > version = RADIO_VERSION ;
v - > capabilities = V4L2_CAP_TUNER ;
return 0 ;
}
/* vidioc_g_tuner - get tuner attributes */
static int vidioc_g_tuner ( struct file * file , void * priv ,
struct v4l2_tuner * v )
{
2009-09-24 00:45:31 +04:00
struct amradio_device * radio = file - > private_data ;
2009-02-05 14:56:07 +03:00
int retval ;
2008-10-01 16:40:59 +04:00
2009-09-24 00:49:36 +04:00
if ( v - > index > 0 )
return - EINVAL ;
2008-10-01 16:40:59 +04:00
/* TODO: Add function which look is signal stereo or not
* amradio_getstat ( radio ) ;
*/
2009-02-05 14:56:07 +03:00
/* we call amradio_set_stereo to set radio->stereo
* Honestly , amradio_getstat should cover this in future and
* amradio_set_stereo shouldn ' t be here
*/
retval = amradio_set_stereo ( radio , WANT_STEREO ) ;
if ( retval < 0 )
2009-09-23 04:37:30 +04:00
amradio_dev_warn ( & radio - > videodev . dev ,
2009-02-05 14:56:07 +03:00
" set stereo failed \n " ) ;
2008-10-01 16:40:59 +04:00
strcpy ( v - > name , " FM " ) ;
v - > type = V4L2_TUNER_RADIO ;
v - > rangelow = FREQ_MIN * FREQ_MUL ;
v - > rangehigh = FREQ_MAX * FREQ_MUL ;
v - > rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO ;
v - > capability = V4L2_TUNER_CAP_LOW ;
if ( radio - > stereo )
v - > audmode = V4L2_TUNER_MODE_STEREO ;
else
v - > audmode = V4L2_TUNER_MODE_MONO ;
v - > signal = 0xffff ; /* Can't get the signal strength, sad.. */
v - > afc = 0 ; /* Don't know what is this */
2009-09-23 04:22:19 +04:00
return retval ;
2008-10-01 16:40:59 +04:00
}
/* vidioc_s_tuner - set tuner attributes */
static int vidioc_s_tuner ( struct file * file , void * priv ,
struct v4l2_tuner * v )
{
2009-09-24 00:45:31 +04:00
struct amradio_device * radio = file - > private_data ;
2009-09-24 00:49:36 +04:00
int retval = - EINVAL ;
2008-11-19 06:36:29 +03:00
2009-09-24 00:49:36 +04:00
if ( v - > index > 0 )
return - EINVAL ;
2009-02-05 14:56:07 +03:00
/* mono/stereo selector */
switch ( v - > audmode ) {
case V4L2_TUNER_MODE_MONO :
retval = amradio_set_stereo ( radio , WANT_MONO ) ;
if ( retval < 0 )
2009-09-23 04:37:30 +04:00
amradio_dev_warn ( & radio - > videodev . dev ,
2009-02-05 14:56:07 +03:00
" set mono failed \n " ) ;
break ;
case V4L2_TUNER_MODE_STEREO :
retval = amradio_set_stereo ( radio , WANT_STEREO ) ;
if ( retval < 0 )
2009-09-23 04:37:30 +04:00
amradio_dev_warn ( & radio - > videodev . dev ,
2009-02-05 14:56:07 +03:00
" set stereo failed \n " ) ;
break ;
}
2009-09-23 04:22:19 +04:00
return retval ;
2008-10-01 16:40:59 +04:00
}
/* vidioc_s_frequency - set tuner radio frequency */
static int vidioc_s_frequency ( struct file * file , void * priv ,
struct v4l2_frequency * f )
{
2009-09-24 00:45:31 +04:00
struct amradio_device * radio = file - > private_data ;
2009-09-24 00:49:36 +04:00
int retval = 0 ;
2008-11-19 06:36:29 +03:00
2008-10-01 16:40:59 +04:00
radio - > curfreq = f - > frequency ;
2009-02-05 14:57:19 +03:00
2009-02-05 14:48:43 +03:00
retval = amradio_setfreq ( radio , radio - > curfreq ) ;
if ( retval < 0 )
2009-09-23 04:37:30 +04:00
amradio_dev_warn ( & radio - > videodev . dev ,
2008-11-04 21:02:36 +03:00
" set frequency failed \n " ) ;
2009-09-23 04:22:19 +04:00
return retval ;
2008-10-01 16:40:59 +04:00
}
/* vidioc_g_frequency - get tuner radio frequency */
static int vidioc_g_frequency ( struct file * file , void * priv ,
struct v4l2_frequency * f )
{
2009-09-24 00:45:31 +04:00
struct amradio_device * radio = file - > private_data ;
2008-11-19 06:36:29 +03:00
2008-10-01 16:40:59 +04:00
f - > type = V4L2_TUNER_RADIO ;
f - > frequency = radio - > curfreq ;
2009-09-23 04:22:19 +04:00
2009-09-24 00:49:36 +04:00
return 0 ;
2008-10-01 16:40:59 +04:00
}
/* vidioc_queryctrl - enumerate control items */
static int vidioc_queryctrl ( struct file * file , void * priv ,
struct v4l2_queryctrl * qc )
{
2009-04-04 01:45:27 +04:00
switch ( qc - > id ) {
case V4L2_CID_AUDIO_MUTE :
return v4l2_ctrl_query_fill ( qc , 0 , 1 , 1 , 1 ) ;
2008-10-01 16:40:59 +04:00
}
2009-04-04 01:45:27 +04:00
2008-10-01 16:40:59 +04:00
return - EINVAL ;
}
/* vidioc_g_ctrl - get the value of a control */
static int vidioc_g_ctrl ( struct file * file , void * priv ,
struct v4l2_control * ctrl )
{
2009-09-24 00:45:31 +04:00
struct amradio_device * radio = file - > private_data ;
2008-11-19 06:36:29 +03:00
2008-10-01 16:40:59 +04:00
switch ( ctrl - > id ) {
case V4L2_CID_AUDIO_MUTE :
ctrl - > value = radio - > muted ;
2009-09-24 00:49:36 +04:00
return 0 ;
2008-10-01 16:40:59 +04:00
}
2009-09-23 04:22:19 +04:00
2009-09-24 00:49:36 +04:00
return - EINVAL ;
2008-10-01 16:40:59 +04:00
}
/* vidioc_s_ctrl - set the value of a control */
static int vidioc_s_ctrl ( struct file * file , void * priv ,
struct v4l2_control * ctrl )
{
2009-09-24 00:45:31 +04:00
struct amradio_device * radio = file - > private_data ;
2009-09-23 04:22:19 +04:00
int retval = - EINVAL ;
2008-10-01 16:40:59 +04:00
switch ( ctrl - > id ) {
case V4L2_CID_AUDIO_MUTE :
if ( ctrl - > value ) {
2009-02-05 14:53:02 +03:00
retval = amradio_set_mute ( radio , AMRADIO_STOP ) ;
2009-02-05 14:48:43 +03:00
if ( retval < 0 ) {
2009-09-23 04:37:30 +04:00
amradio_dev_warn ( & radio - > videodev . dev ,
2008-11-04 21:02:36 +03:00
" amradio_stop failed \n " ) ;
2008-10-01 16:40:59 +04:00
}
} else {
2009-02-05 14:53:02 +03:00
retval = amradio_set_mute ( radio , AMRADIO_START ) ;
2009-02-05 14:48:43 +03:00
if ( retval < 0 ) {
2009-09-23 04:37:30 +04:00
amradio_dev_warn ( & radio - > videodev . dev ,
2008-11-04 21:02:36 +03:00
" amradio_start failed \n " ) ;
2008-10-01 16:40:59 +04:00
}
}
2009-09-23 04:22:19 +04:00
break ;
2008-10-01 16:40:59 +04:00
}
2009-09-23 04:22:19 +04:00
return retval ;
2008-10-01 16:40:59 +04:00
}
/* vidioc_g_audio - get audio attributes */
static int vidioc_g_audio ( struct file * file , void * priv ,
struct v4l2_audio * a )
{
if ( a - > index > 1 )
return - EINVAL ;
strcpy ( a - > name , " Radio " ) ;
a - > capability = V4L2_AUDCAP_STEREO ;
return 0 ;
}
/* vidioc_s_audio - set audio attributes */
static int vidioc_s_audio ( struct file * file , void * priv ,
struct v4l2_audio * a )
{
if ( a - > index ! = 0 )
return - EINVAL ;
return 0 ;
}
/* vidioc_g_input - get input */
static int vidioc_g_input ( struct file * filp , void * priv , unsigned int * i )
{
* i = 0 ;
return 0 ;
}
/* vidioc_s_input - set input */
static int vidioc_s_input ( struct file * filp , void * priv , unsigned int i )
{
if ( i ! = 0 )
return - EINVAL ;
return 0 ;
}
2009-09-24 01:13:50 +04:00
static int usb_amradio_init ( struct amradio_device * radio )
{
int retval ;
retval = amradio_set_mute ( radio , AMRADIO_STOP ) ;
if ( retval < 0 ) {
amradio_dev_warn ( & radio - > videodev . dev , " amradio_stop failed \n " ) ;
goto out_err ;
}
retval = amradio_set_stereo ( radio , WANT_STEREO ) ;
if ( retval < 0 ) {
amradio_dev_warn ( & radio - > videodev . dev , " set stereo failed \n " ) ;
goto out_err ;
}
radio - > initialized = 1 ;
goto out ;
out_err :
amradio_dev_err ( & radio - > videodev . dev , " initialization failed \n " ) ;
out :
return retval ;
}
2008-10-01 16:40:59 +04:00
/* open device - amradio_start() and amradio_setfreq() */
2008-12-30 12:58:20 +03:00
static int usb_amradio_open ( struct file * file )
2008-10-01 16:40:59 +04:00
{
2009-09-24 00:45:31 +04:00
struct amradio_device * radio = vdev_to_amradio ( video_devdata ( file ) ) ;
2009-09-23 04:22:19 +04:00
int retval = 0 ;
2008-10-01 16:40:59 +04:00
2009-09-23 04:22:19 +04:00
mutex_lock ( & radio - > lock ) ;
2009-09-24 00:52:17 +04:00
if ( ! radio - > usbdev ) {
2009-09-23 04:22:19 +04:00
retval = - EIO ;
goto unlock ;
}
2008-10-20 06:56:23 +04:00
2009-09-24 00:45:31 +04:00
file - > private_data = radio ;
2008-10-20 06:56:23 +04:00
2009-09-24 01:13:50 +04:00
if ( unlikely ( ! radio - > initialized ) )
retval = usb_amradio_init ( radio ) ;
2009-09-23 04:22:19 +04:00
unlock :
mutex_unlock ( & radio - > lock ) ;
return retval ;
2008-10-01 16:40:59 +04:00
}
2008-12-28 03:30:34 +03:00
/*close device */
2008-12-30 12:58:20 +03:00
static int usb_amradio_close ( struct file * file )
2008-10-01 16:40:59 +04:00
{
2009-09-24 00:45:31 +04:00
struct amradio_device * radio = file - > private_data ;
2009-09-23 04:22:19 +04:00
int retval = 0 ;
2008-11-19 06:36:29 +03:00
2009-02-05 14:57:19 +03:00
mutex_lock ( & radio - > lock ) ;
2009-09-23 04:22:19 +04:00
2009-09-24 01:09:01 +04:00
if ( ! radio - > usbdev )
2009-09-23 04:22:19 +04:00
retval = - EIO ;
2008-11-19 06:36:29 +03:00
2009-09-23 04:22:19 +04:00
mutex_unlock ( & radio - > lock ) ;
return retval ;
2008-10-01 16:40:59 +04:00
}
2009-09-24 00:49:36 +04:00
static long usb_amradio_ioctl ( struct file * file , unsigned int cmd ,
unsigned long arg )
{
struct amradio_device * radio = file - > private_data ;
long retval = 0 ;
mutex_lock ( & radio - > lock ) ;
2009-09-24 00:52:17 +04:00
if ( ! radio - > usbdev ) {
2009-09-24 00:49:36 +04:00
retval = - EIO ;
goto unlock ;
}
retval = video_ioctl2 ( file , cmd , arg ) ;
unlock :
mutex_unlock ( & radio - > lock ) ;
return retval ;
}
2008-10-01 16:40:59 +04:00
/* Suspend device - stop device. Need to be checked and fixed */
static int usb_amradio_suspend ( struct usb_interface * intf , pm_message_t message )
{
struct amradio_device * radio = usb_get_intfdata ( intf ) ;
2009-02-05 14:48:43 +03:00
int retval ;
2008-10-01 16:40:59 +04:00
2009-09-23 04:22:19 +04:00
mutex_lock ( & radio - > lock ) ;
2009-09-24 01:24:47 +04:00
if ( ! radio - > muted & & radio - > initialized ) {
retval = amradio_set_mute ( radio , AMRADIO_STOP ) ;
if ( retval < 0 )
dev_warn ( & intf - > dev , " amradio_stop failed \n " ) ;
radio - > muted = 0 ;
}
2008-10-01 16:40:59 +04:00
2008-11-04 21:02:36 +03:00
dev_info ( & intf - > dev , " going into suspend.. \n " ) ;
2008-10-01 16:40:59 +04:00
2009-09-23 04:22:19 +04:00
mutex_unlock ( & radio - > lock ) ;
2008-10-01 16:40:59 +04:00
return 0 ;
}
/* Resume device - start device. Need to be checked and fixed */
static int usb_amradio_resume ( struct usb_interface * intf )
{
struct amradio_device * radio = usb_get_intfdata ( intf ) ;
2009-02-05 14:48:43 +03:00
int retval ;
2008-10-01 16:40:59 +04:00
2009-09-23 04:22:19 +04:00
mutex_lock ( & radio - > lock ) ;
2009-09-24 01:24:47 +04:00
if ( unlikely ( ! radio - > initialized ) )
goto unlock ;
if ( radio - > stereo )
retval = amradio_set_stereo ( radio , WANT_STEREO ) ;
else
retval = amradio_set_stereo ( radio , WANT_MONO ) ;
2009-02-05 14:48:43 +03:00
if ( retval < 0 )
2009-09-24 01:24:47 +04:00
amradio_dev_warn ( & radio - > videodev . dev , " set stereo failed \n " ) ;
2008-10-01 16:40:59 +04:00
2009-09-24 01:24:47 +04:00
retval = amradio_setfreq ( radio , radio - > curfreq ) ;
if ( retval < 0 )
amradio_dev_warn ( & radio - > videodev . dev ,
" set frequency failed \n " ) ;
if ( ! radio - > muted ) {
retval = amradio_set_mute ( radio , AMRADIO_START ) ;
if ( retval < 0 )
dev_warn ( & radio - > videodev . dev ,
" amradio_start failed \n " ) ;
}
unlock :
2008-11-04 21:02:36 +03:00
dev_info ( & intf - > dev , " coming out of suspend.. \n " ) ;
2008-10-01 16:40:59 +04:00
2009-09-23 04:22:19 +04:00
mutex_unlock ( & radio - > lock ) ;
2008-10-01 16:40:59 +04:00
return 0 ;
}
/* File system interface */
2008-12-30 12:58:20 +03:00
static const struct v4l2_file_operations usb_amradio_fops = {
2008-10-01 16:40:59 +04:00
. owner = THIS_MODULE ,
. open = usb_amradio_open ,
. release = usb_amradio_close ,
2009-09-24 00:49:36 +04:00
. ioctl = usb_amradio_ioctl ,
2008-10-01 16:40:59 +04:00
} ;
static const struct v4l2_ioctl_ops usb_amradio_ioctl_ops = {
. vidioc_querycap = vidioc_querycap ,
. vidioc_g_tuner = vidioc_g_tuner ,
. vidioc_s_tuner = vidioc_s_tuner ,
. vidioc_g_frequency = vidioc_g_frequency ,
. vidioc_s_frequency = vidioc_s_frequency ,
. vidioc_queryctrl = vidioc_queryctrl ,
. vidioc_g_ctrl = vidioc_g_ctrl ,
. vidioc_s_ctrl = vidioc_s_ctrl ,
. vidioc_g_audio = vidioc_g_audio ,
. vidioc_s_audio = vidioc_s_audio ,
. vidioc_g_input = vidioc_g_input ,
. vidioc_s_input = vidioc_s_input ,
} ;
2009-04-04 01:45:27 +04:00
static void usb_amradio_video_device_release ( struct video_device * videodev )
2008-12-28 03:30:34 +03:00
{
2009-09-24 00:45:31 +04:00
struct amradio_device * radio = vdev_to_amradio ( videodev ) ;
2008-12-28 03:30:34 +03:00
2009-04-04 01:45:27 +04:00
v4l2_device_unregister ( & radio - > v4l2_dev ) ;
2008-12-28 03:30:34 +03:00
/* free rest memory */
kfree ( radio - > buffer ) ;
kfree ( radio ) ;
}
2009-02-05 14:48:43 +03:00
/* check if the device is present and register with v4l and usb if it is */
2008-10-01 16:40:59 +04:00
static int usb_amradio_probe ( struct usb_interface * intf ,
const struct usb_device_id * id )
{
struct amradio_device * radio ;
2009-09-23 04:43:19 +04:00
int retval = 0 ;
2008-10-01 16:40:59 +04:00
2009-04-04 01:45:27 +04:00
radio = kzalloc ( sizeof ( struct amradio_device ) , GFP_KERNEL ) ;
2008-10-01 16:40:59 +04:00
2009-02-05 14:51:51 +03:00
if ( ! radio ) {
dev_err ( & intf - > dev , " kmalloc for amradio_device failed \n " ) ;
2009-09-23 04:43:19 +04:00
retval = - ENOMEM ;
goto err ;
2009-02-05 14:51:51 +03:00
}
2008-10-01 16:40:59 +04:00
radio - > buffer = kmalloc ( BUFFER_LENGTH , GFP_KERNEL ) ;
2009-02-05 14:51:51 +03:00
if ( ! radio - > buffer ) {
dev_err ( & intf - > dev , " kmalloc for radio->buffer failed \n " ) ;
2009-09-23 04:43:19 +04:00
retval = - ENOMEM ;
goto err_nobuf ;
2008-10-01 16:40:59 +04:00
}
2009-09-23 04:48:43 +04:00
retval = v4l2_device_register ( & intf - > dev , & radio - > v4l2_dev ) ;
2009-04-04 01:45:27 +04:00
if ( retval < 0 ) {
dev_err ( & intf - > dev , " couldn't register v4l2_device \n " ) ;
2009-09-23 04:43:19 +04:00
goto err_v4l2 ;
2009-04-04 01:45:27 +04:00
}
2009-09-23 04:48:43 +04:00
strlcpy ( radio - > videodev . name , radio - > v4l2_dev . name ,
2009-09-23 04:37:30 +04:00
sizeof ( radio - > videodev . name ) ) ;
2009-09-23 04:48:43 +04:00
radio - > videodev . v4l2_dev = & radio - > v4l2_dev ;
2009-09-23 04:37:30 +04:00
radio - > videodev . fops = & usb_amradio_fops ;
radio - > videodev . ioctl_ops = & usb_amradio_ioctl_ops ;
radio - > videodev . release = usb_amradio_video_device_release ;
2008-10-01 16:40:59 +04:00
radio - > usbdev = interface_to_usbdev ( intf ) ;
radio - > curfreq = 95.16 * FREQ_MUL ;
mutex_init ( & radio - > lock ) ;
2009-09-23 04:37:30 +04:00
video_set_drvdata ( & radio - > videodev , radio ) ;
2009-04-04 01:45:27 +04:00
2009-09-23 04:37:30 +04:00
retval = video_register_device ( & radio - > videodev , VFL_TYPE_RADIO ,
radio_nr ) ;
2009-02-05 14:48:43 +03:00
if ( retval < 0 ) {
2009-02-05 14:49:58 +03:00
dev_err ( & intf - > dev , " could not register video device \n " ) ;
2009-09-23 04:43:19 +04:00
goto err_vdev ;
2008-10-01 16:40:59 +04:00
}
usb_set_intfdata ( intf , radio ) ;
return 0 ;
2009-09-23 04:43:19 +04:00
err_vdev :
2009-09-23 04:48:43 +04:00
v4l2_device_unregister ( & radio - > v4l2_dev ) ;
2009-09-23 04:43:19 +04:00
err_v4l2 :
kfree ( radio - > buffer ) ;
err_nobuf :
kfree ( radio ) ;
err :
return retval ;
2008-10-01 16:40:59 +04:00
}
static int __init amradio_init ( void )
{
int retval = usb_register ( & usb_amradio_driver ) ;
2008-11-04 21:02:36 +03:00
pr_info ( KBUILD_MODNAME
" : version " DRIVER_VERSION " " DRIVER_DESC " \n " ) ;
2008-10-01 16:40:59 +04:00
if ( retval )
2008-11-04 21:02:36 +03:00
pr_err ( KBUILD_MODNAME
" : usb_register failed. Error number %d \n " , retval ) ;
2008-10-01 16:40:59 +04:00
return retval ;
}
static void __exit amradio_exit ( void )
{
usb_deregister ( & usb_amradio_driver ) ;
}
module_init ( amradio_init ) ;
module_exit ( amradio_exit ) ;