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 .
*/
/*
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.
tree-wide: fix assorted typos all over the place
That is "success", "unknown", "through", "performance", "[re|un]mapping"
, "access", "default", "reasonable", "[con]currently", "temperature"
, "channel", "[un]used", "application", "example","hierarchy", "therefore"
, "[over|under]flow", "contiguous", "threshold", "enough" and others.
Signed-off-by: André Goddard Rosa <andre.goddard@gmail.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2009-11-14 18:09:05 +03:00
* Probably , this driver could be improved through using their
2008-10-01 16:40:59 +04:00
* 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 .
2014-06-09 19:55:25 +04:00
* http : //www.spinics.net/lists/linux-usb-devel/msg10109.html
2008-10-01 16:40:59 +04:00
*
* Version 0.01 : First working version .
2018-01-04 14:47:28 +03:00
* 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 ,
2018-01-04 14:47:28 +03:00
* 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 :
2018-01-04 14:47:28 +03:00
* - Correct power management of device ( suspend & resume )
* - Add code for scanning and smooth tuning
* - Add code for sensitivity value
* - Correct mistakes
* - In Japan another FREQ_MIN and FREQ_MAX
2008-10-01 16:40:59 +04:00
*/
/* 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>
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>
2012-04-27 19:30:40 +04:00
# include <media/v4l2-ctrls.h>
# include <media/v4l2-event.h>
2008-10-01 16:40:59 +04:00
# include <linux/usb.h>
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"
2011-06-25 17:15:42 +04:00
# define DRIVER_VERSION "0.1.2"
2008-10-01 16:40:59 +04:00
MODULE_AUTHOR ( DRIVER_AUTHOR ) ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
MODULE_LICENSE ( " GPL " ) ;
2011-06-25 17:15:42 +04:00
MODULE_VERSION ( DRIVER_VERSION ) ;
2008-10-01 16:40:59 +04:00
# 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
2011-03-31 05:57:33 +04:00
* List isn ' t full and will be updated with implementation of new functions
2009-02-05 14:53:02 +03:00
*/
2009-02-05 14:54:17 +03:00
# define AMRADIO_SET_FREQ 0xa4
2012-04-28 01:39:37 +04:00
# define AMRADIO_GET_READY_FLAG 0xa5
2012-04-27 20:28:34 +04:00
# define AMRADIO_GET_SIGNAL 0xa7
2012-04-28 01:39:37 +04:00
# define AMRADIO_GET_FREQ 0xa8
# define AMRADIO_SET_SEARCH_UP 0xa9
# define AMRADIO_SET_SEARCH_DOWN 0xaa
2009-02-05 14:53:02 +03:00
# define AMRADIO_SET_MUTE 0xab
2012-04-28 01:39:37 +04:00
# define AMRADIO_SET_RIGHT_MUTE 0xac
# define AMRADIO_SET_LEFT_MUTE 0xad
2009-02-05 14:56:07 +03:00
# define AMRADIO_SET_MONO 0xae
2012-04-28 01:39:37 +04:00
# define AMRADIO_SET_SEARCH_LVL 0xb0
# define AMRADIO_STOP_SEARCH 0xb1
2009-02-05 14:53:02 +03:00
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 " ) ;
/* Data for one (physical) device */
struct amradio_device {
/* reference to USB and video device */
struct usb_device * usbdev ;
2009-11-10 01:09:49 +03:00
struct usb_interface * intf ;
2012-04-27 19:30:40 +04:00
struct video_device vdev ;
2009-04-04 01:45:27 +04:00
struct v4l2_device v4l2_dev ;
2012-04-27 19:30:40 +04:00
struct v4l2_ctrl_handler hdl ;
2008-10-01 16:40:59 +04:00
2012-04-28 01:39:37 +04:00
u8 * buffer ;
2008-10-01 16:40:59 +04:00
struct mutex lock ; /* buffer locking */
int curfreq ;
int stereo ;
int muted ;
} ;
2010-05-02 12:36:32 +04:00
static inline struct amradio_device * to_amradio_dev ( struct v4l2_device * v4l2_dev )
{
return container_of ( v4l2_dev , struct amradio_device , v4l2_dev ) ;
}
2009-09-24 00:45:31 +04:00
2012-04-28 01:39:37 +04:00
static int amradio_send_cmd ( struct amradio_device * radio , u8 cmd , u8 arg ,
u8 * extra , u8 extralen , bool reply )
2008-10-01 16:40:59 +04:00
{
int retval ;
int size ;
radio - > buffer [ 0 ] = 0x00 ;
radio - > buffer [ 1 ] = 0x55 ;
radio - > buffer [ 2 ] = 0xaa ;
2012-04-28 01:39:37 +04:00
radio - > buffer [ 3 ] = extralen ;
radio - > buffer [ 4 ] = cmd ;
radio - > buffer [ 5 ] = arg ;
2008-10-01 16:40:59 +04:00
radio - > buffer [ 6 ] = 0x00 ;
2012-04-28 01:39:37 +04:00
radio - > buffer [ 7 ] = extra | | reply ? 8 : 0 ;
2008-10-01 16:40:59 +04:00
retval = usb_bulk_msg ( radio - > usbdev , usb_sndintpipe ( radio - > usbdev , 2 ) ,
2012-04-28 01:39:37 +04:00
radio - > buffer , BUFFER_LENGTH , & size , USB_TIMEOUT ) ;
2008-10-01 16:40:59 +04:00
2009-02-05 15:00:03 +03:00
if ( retval < 0 | | size ! = BUFFER_LENGTH ) {
2012-04-28 01:39:37 +04:00
if ( video_is_registered ( & radio - > vdev ) )
amradio_dev_warn ( & radio - > vdev . dev ,
" cmd %02x failed \n " , cmd ) ;
return retval ? retval : - EIO ;
2008-10-01 16:40:59 +04:00
}
2012-04-28 01:39:37 +04:00
if ( ! extra & & ! reply )
return 0 ;
if ( extra ) {
memcpy ( radio - > buffer , extra , extralen ) ;
memset ( radio - > buffer + extralen , 0 , 8 - extralen ) ;
retval = usb_bulk_msg ( radio - > usbdev , usb_sndintpipe ( radio - > usbdev , 2 ) ,
radio - > buffer , BUFFER_LENGTH , & size , USB_TIMEOUT ) ;
} else {
memset ( radio - > buffer , 0 , 8 ) ;
retval = usb_bulk_msg ( radio - > usbdev , usb_rcvbulkpipe ( radio - > usbdev , 0x81 ) ,
radio - > buffer , BUFFER_LENGTH , & size , USB_TIMEOUT ) ;
}
if ( retval = = 0 & & size = = BUFFER_LENGTH )
return 0 ;
if ( video_is_registered ( & radio - > vdev ) & & cmd ! = AMRADIO_GET_READY_FLAG )
amradio_dev_warn ( & radio - > vdev . dev , " follow-up to cmd %02x failed \n " , cmd ) ;
return retval ? retval : - EIO ;
}
/* switch on/off the radio. Send 8 bytes to device */
static int amradio_set_mute ( struct amradio_device * radio , bool mute )
{
int ret = amradio_send_cmd ( radio ,
AMRADIO_SET_MUTE , mute , NULL , 0 , false ) ;
if ( ! ret )
radio - > muted = mute ;
return ret ;
2008-10-01 16:40:59 +04:00
}
/* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */
2012-04-28 01:39:37 +04:00
static int amradio_set_freq ( struct amradio_device * radio , int freq )
2008-10-01 16:40:59 +04:00
{
2013-03-15 01:12:06 +04:00
unsigned short freq_send ;
2012-04-28 01:39:37 +04:00
u8 buf [ 3 ] ;
2008-10-01 16:40:59 +04:00
int retval ;
2013-03-15 01:12:06 +04:00
/* we need to be sure that frequency isn't out of range */
freq = clamp_t ( unsigned , freq , FREQ_MIN * FREQ_MUL , FREQ_MAX * FREQ_MUL ) ;
freq_send = 0x10 + ( freq > > 3 ) / 25 ;
2008-10-01 16:40:59 +04:00
/* frequency is calculated from freq_send and placed in first 2 bytes */
2012-04-28 01:39:37 +04:00
buf [ 0 ] = ( freq_send > > 8 ) & 0xff ;
buf [ 1 ] = freq_send & 0xff ;
buf [ 2 ] = 0x01 ;
2008-10-01 16:40:59 +04:00
2012-04-28 01:39:37 +04:00
retval = amradio_send_cmd ( radio , AMRADIO_SET_FREQ , 0 , buf , 3 , false ) ;
if ( retval )
return retval ;
radio - > curfreq = freq ;
msleep ( 40 ) ;
return 0 ;
2009-02-05 14:56:07 +03:00
}
2012-04-28 01:39:37 +04:00
static int amradio_set_stereo ( struct amradio_device * radio , bool stereo )
2009-02-05 14:56:07 +03:00
{
2012-04-28 01:39:37 +04:00
int ret = amradio_send_cmd ( radio ,
AMRADIO_SET_MONO , ! stereo , NULL , 0 , false ) ;
2009-02-05 14:56:07 +03:00
2012-04-28 01:39:37 +04:00
if ( ! ret )
radio - > stereo = stereo ;
return ret ;
2008-10-01 16:40:59 +04:00
}
2012-04-27 20:28:34 +04:00
static int amradio_get_stat ( struct amradio_device * radio , bool * is_stereo , u32 * signal )
{
2012-04-28 01:39:37 +04:00
int ret = amradio_send_cmd ( radio ,
AMRADIO_GET_SIGNAL , 0 , NULL , 0 , true ) ;
2012-04-27 20:28:34 +04:00
2012-04-28 01:39:37 +04:00
if ( ret )
return ret ;
2012-04-27 20:28:34 +04:00
* is_stereo = radio - > buffer [ 2 ] > > 7 ;
* signal = ( radio - > buffer [ 3 ] & 0xf0 ) < < 8 ;
return 0 ;
}
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 )
{
2010-05-02 12:36:32 +04:00
struct amradio_device * radio = to_amradio_dev ( usb_get_intfdata ( intf ) ) ;
2008-10-01 16:40:59 +04:00
2008-12-28 03:30:34 +03:00
mutex_lock ( & radio - > lock ) ;
2012-04-27 19:30:40 +04:00
video_unregister_device ( & radio - > vdev ) ;
2012-04-28 01:39:37 +04:00
amradio_set_mute ( radio , true ) ;
usb_set_intfdata ( intf , NULL ) ;
2009-04-04 01:45:27 +04:00
v4l2_device_disconnect ( & radio - > v4l2_dev ) ;
2010-10-17 16:26:18 +04:00
mutex_unlock ( & radio - > lock ) ;
2012-04-27 19:30:40 +04:00
v4l2_device_put ( & radio - > v4l2_dev ) ;
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 )
{
2012-04-27 19:30:40 +04:00
struct amradio_device * radio = video_drvdata ( file ) ;
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 ) ) ;
2012-04-28 01:39:37 +04:00
v - > device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER |
V4L2_CAP_HW_FREQ_SEEK ;
2012-04-27 19:30:40 +04:00
v - > capabilities = v - > device_caps | V4L2_CAP_DEVICE_CAPS ;
2008-10-01 16:40:59 +04:00
return 0 ;
}
/* vidioc_g_tuner - get tuner attributes */
static int vidioc_g_tuner ( struct file * file , void * priv ,
struct v4l2_tuner * v )
{
2012-04-27 19:30:40 +04:00
struct amradio_device * radio = video_drvdata ( file ) ;
2012-04-27 20:28:34 +04:00
bool is_stereo = false ;
int retval ;
2008-10-01 16:40:59 +04:00
if ( v - > index > 0 )
return - EINVAL ;
2012-04-27 20:28:34 +04:00
v - > signal = 0 ;
retval = amradio_get_stat ( radio , & is_stereo , & v - > signal ) ;
if ( retval )
return retval ;
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 ;
2012-05-27 14:25:06 +04:00
v - > capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
V4L2_TUNER_CAP_HWSEEK_WRAP ;
2012-04-27 20:28:34 +04:00
v - > rxsubchans = is_stereo ? V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO ;
2012-04-27 19:30:40 +04:00
v - > audmode = radio - > stereo ?
V4L2_TUNER_MODE_STEREO : V4L2_TUNER_MODE_MONO ;
return 0 ;
2008-10-01 16:40:59 +04:00
}
/* vidioc_s_tuner - set tuner attributes */
static int vidioc_s_tuner ( struct file * file , void * priv ,
2013-03-15 13:10:06 +04:00
const struct v4l2_tuner * v )
2008-10-01 16:40:59 +04:00
{
2012-04-27 19:30:40 +04:00
struct amradio_device * radio = video_drvdata ( file ) ;
2008-11-19 06:36:29 +03:00
2008-10-01 16:40:59 +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 :
2012-04-27 19:30:40 +04:00
return amradio_set_stereo ( radio , WANT_MONO ) ;
default :
return amradio_set_stereo ( radio , WANT_STEREO ) ;
2009-02-05 14:56:07 +03:00
}
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 ,
2013-03-19 11:09:26 +04:00
const struct v4l2_frequency * f )
2008-10-01 16:40:59 +04:00
{
2012-04-27 19:30:40 +04:00
struct amradio_device * radio = video_drvdata ( file ) ;
2008-10-01 16:40:59 +04:00
2012-04-27 19:30:40 +04:00
if ( f - > tuner ! = 0 )
2009-11-27 10:33:25 +03:00
return - EINVAL ;
2013-03-15 01:12:06 +04:00
return amradio_set_freq ( radio , f - > frequency ) ;
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 )
{
2012-04-27 19:30:40 +04:00
struct amradio_device * radio = video_drvdata ( file ) ;
2008-11-19 06:36:29 +03:00
2012-04-27 19:30:40 +04:00
if ( f - > tuner ! = 0 | | f - > type ! = V4L2_TUNER_RADIO )
2009-11-27 10:33:25 +03:00
return - EINVAL ;
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
2008-10-01 16:40:59 +04:00
return 0 ;
}
2012-04-28 01:39:37 +04:00
static int vidioc_s_hw_freq_seek ( struct file * file , void * priv ,
2012-09-14 14:41:18 +04:00
const struct v4l2_hw_freq_seek * seek )
2012-04-28 01:39:37 +04:00
{
static u8 buf [ 8 ] = {
0x3d , 0x32 , 0x0f , 0x08 , 0x3d , 0x32 , 0x0f , 0x08
} ;
struct amradio_device * radio = video_drvdata ( file ) ;
unsigned long timeout ;
int retval ;
if ( seek - > tuner ! = 0 | | ! seek - > wrap_around )
return - EINVAL ;
2012-09-21 16:33:35 +04:00
if ( file - > f_flags & O_NONBLOCK )
return - EWOULDBLOCK ;
2012-04-28 01:39:37 +04:00
retval = amradio_send_cmd ( radio ,
AMRADIO_SET_SEARCH_LVL , 0 , buf , 8 , false ) ;
if ( retval )
return retval ;
amradio_set_freq ( radio , radio - > curfreq ) ;
retval = amradio_send_cmd ( radio ,
seek - > seek_upward ? AMRADIO_SET_SEARCH_UP : AMRADIO_SET_SEARCH_DOWN ,
0 , NULL , 0 , false ) ;
if ( retval )
return retval ;
timeout = jiffies + msecs_to_jiffies ( 30000 ) ;
for ( ; ; ) {
if ( time_after ( jiffies , timeout ) ) {
2012-05-27 14:25:06 +04:00
retval = - ENODATA ;
2012-04-28 01:39:37 +04:00
break ;
}
if ( schedule_timeout_interruptible ( msecs_to_jiffies ( 10 ) ) ) {
retval = - ERESTARTSYS ;
break ;
}
retval = amradio_send_cmd ( radio , AMRADIO_GET_READY_FLAG ,
0 , NULL , 0 , true ) ;
if ( retval )
continue ;
amradio_send_cmd ( radio , AMRADIO_GET_FREQ , 0 , NULL , 0 , true ) ;
if ( radio - > buffer [ 1 ] | | radio - > buffer [ 2 ] ) {
2013-03-15 01:12:06 +04:00
/* To check: sometimes radio->curfreq is set to out of range value */
2012-04-28 01:39:37 +04:00
radio - > curfreq = ( radio - > buffer [ 1 ] < < 8 ) | radio - > buffer [ 2 ] ;
radio - > curfreq = ( radio - > curfreq - 0x10 ) * 200 ;
amradio_send_cmd ( radio , AMRADIO_STOP_SEARCH ,
0 , NULL , 0 , false ) ;
amradio_set_freq ( radio , radio - > curfreq ) ;
retval = 0 ;
break ;
}
}
amradio_send_cmd ( radio , AMRADIO_STOP_SEARCH , 0 , NULL , 0 , false ) ;
amradio_set_freq ( radio , radio - > curfreq ) ;
return retval ;
}
2012-04-27 19:30:40 +04:00
static int usb_amradio_s_ctrl ( struct v4l2_ctrl * ctrl )
2008-10-01 16:40:59 +04:00
{
2012-04-27 19:30:40 +04:00
struct amradio_device * radio =
container_of ( ctrl - > handler , struct amradio_device , hdl ) ;
2008-11-19 06:36:29 +03:00
2008-10-01 16:40:59 +04:00
switch ( ctrl - > id ) {
case V4L2_CID_AUDIO_MUTE :
2012-04-28 01:39:37 +04:00
return amradio_set_mute ( radio , ctrl - > val ) ;
2008-10-01 16:40:59 +04:00
}
2009-09-23 04:22:19 +04:00
2008-10-01 16:40:59 +04:00
return - EINVAL ;
}
2009-09-24 01:13:50 +04:00
static int usb_amradio_init ( struct amradio_device * radio )
2008-10-01 16:40:59 +04:00
{
2009-02-05 14:48:43 +03:00
int retval ;
2008-10-01 16:40:59 +04:00
2012-04-28 01:39:37 +04:00
retval = amradio_set_mute ( radio , true ) ;
2009-09-24 01:28:02 +04:00
if ( retval )
2009-09-24 01:13:50 +04:00
goto out_err ;
2012-04-28 01:39:37 +04:00
retval = amradio_set_stereo ( radio , true ) ;
2009-09-24 01:28:02 +04:00
if ( retval )
2009-09-24 01:13:50 +04:00
goto out_err ;
2012-04-28 01:39:37 +04:00
retval = amradio_set_freq ( radio , radio - > curfreq ) ;
if ( retval )
goto out_err ;
return 0 ;
2009-09-24 01:13:50 +04:00
out_err :
2012-04-27 19:30:40 +04:00
amradio_dev_err ( & radio - > vdev . dev , " initialization failed \n " ) ;
2009-09-24 01:13:50 +04:00
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 )
{
2010-05-02 12:36:32 +04:00
struct amradio_device * radio = to_amradio_dev ( usb_get_intfdata ( intf ) ) ;
2008-10-01 16:40:59 +04:00
2010-10-17 16:26:18 +04:00
mutex_lock ( & radio - > lock ) ;
2012-04-27 19:30:40 +04:00
if ( ! radio - > muted ) {
2012-04-28 01:39:37 +04:00
amradio_set_mute ( radio , true ) ;
radio - > muted = false ;
2009-09-24 01:24:47 +04:00
}
2010-10-17 16:26:18 +04:00
mutex_unlock ( & radio - > lock ) ;
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
return 0 ;
}
/* Resume device - start device. Need to be checked and fixed */
static int usb_amradio_resume ( struct usb_interface * intf )
{
2010-05-02 12:36:32 +04:00
struct amradio_device * radio = to_amradio_dev ( usb_get_intfdata ( intf ) ) ;
2008-10-01 16:40:59 +04:00
2010-10-17 16:26:18 +04:00
mutex_lock ( & radio - > lock ) ;
2012-04-28 01:39:37 +04:00
amradio_set_stereo ( radio , radio - > stereo ) ;
amradio_set_freq ( radio , radio - > curfreq ) ;
2008-10-01 16:40:59 +04:00
2009-09-24 01:28:02 +04:00
if ( ! radio - > muted )
2012-04-28 01:39:37 +04:00
amradio_set_mute ( radio , false ) ;
2009-09-24 01:24:47 +04:00
2010-10-17 16:26:18 +04:00
mutex_unlock ( & radio - > lock ) ;
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
return 0 ;
}
2012-04-27 19:30:40 +04:00
static const struct v4l2_ctrl_ops usb_amradio_ctrl_ops = {
. s_ctrl = usb_amradio_s_ctrl ,
} ;
2008-10-01 16:40:59 +04:00
/* 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 ,
2012-04-27 19:30:40 +04:00
. open = v4l2_fh_open ,
. release = v4l2_fh_release ,
. poll = v4l2_ctrl_poll ,
2010-09-26 15:01:18 +04:00
. unlocked_ioctl = video_ioctl2 ,
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 ,
2012-04-28 01:39:37 +04:00
. vidioc_s_hw_freq_seek = vidioc_s_hw_freq_seek ,
2012-04-27 19:30:40 +04:00
. vidioc_log_status = v4l2_ctrl_log_status ,
. vidioc_subscribe_event = v4l2_ctrl_subscribe_event ,
. vidioc_unsubscribe_event = v4l2_event_unsubscribe ,
2008-10-01 16:40:59 +04:00
} ;
2012-04-27 19:30:40 +04:00
static void usb_amradio_release ( struct v4l2_device * v4l2_dev )
2008-12-28 03:30:34 +03:00
{
2012-04-27 19:30:40 +04:00
struct amradio_device * radio = to_amradio_dev ( v4l2_dev ) ;
2009-04-04 01:45:27 +04:00
2008-12-28 03:30:34 +03:00
/* free rest memory */
2012-04-27 19:30:40 +04:00
v4l2_ctrl_handler_free ( & radio - > hdl ) ;
v4l2_device_unregister ( & radio - > v4l2_dev ) ;
2008-12-28 03:30:34 +03:00
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 ;
2018-02-22 23:45:47 +03:00
int retval ;
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
}
2012-04-27 19:30:40 +04:00
v4l2_ctrl_handler_init ( & radio - > hdl , 1 ) ;
v4l2_ctrl_new_std ( & radio - > hdl , & usb_amradio_ctrl_ops ,
V4L2_CID_AUDIO_MUTE , 0 , 1 , 1 , 1 ) ;
if ( radio - > hdl . error ) {
retval = radio - > hdl . error ;
dev_err ( & intf - > dev , " couldn't register control \n " ) ;
goto err_ctrl ;
}
2010-09-26 15:01:18 +04:00
mutex_init ( & radio - > lock ) ;
2012-04-27 19:30:40 +04:00
radio - > v4l2_dev . ctrl_handler = & radio - > hdl ;
radio - > v4l2_dev . release = usb_amradio_release ;
strlcpy ( radio - > vdev . name , radio - > v4l2_dev . name ,
sizeof ( radio - > vdev . name ) ) ;
radio - > vdev . v4l2_dev = & radio - > v4l2_dev ;
radio - > vdev . fops = & usb_amradio_fops ;
radio - > vdev . ioctl_ops = & usb_amradio_ioctl_ops ;
radio - > vdev . release = video_device_release_empty ;
radio - > vdev . lock = & radio - > lock ;
2008-10-01 16:40:59 +04:00
radio - > usbdev = interface_to_usbdev ( intf ) ;
2009-11-10 01:09:49 +03:00
radio - > intf = intf ;
2012-04-27 19:30:40 +04:00
usb_set_intfdata ( intf , & radio - > v4l2_dev ) ;
2008-10-01 16:40:59 +04:00
radio - > curfreq = 95.16 * FREQ_MUL ;
2012-04-27 19:30:40 +04:00
video_set_drvdata ( & radio - > vdev , radio ) ;
retval = usb_amradio_init ( radio ) ;
if ( retval )
goto err_vdev ;
2009-04-04 01:45:27 +04:00
2012-04-27 19:30:40 +04:00
retval = video_register_device ( & radio - > vdev , VFL_TYPE_RADIO ,
2009-09-23 04:37:30 +04:00
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
}
return 0 ;
2009-09-23 04:43:19 +04:00
err_vdev :
2012-04-27 19:30:40 +04:00
v4l2_ctrl_handler_free ( & radio - > hdl ) ;
err_ctrl :
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
}
2012-04-27 19:30:40 +04:00
/* USB Device ID List */
2017-08-13 11:54:45 +03:00
static const struct usb_device_id usb_amradio_device_table [ ] = {
2012-04-27 19:30:40 +04:00
{ 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 = MR800_DRIVER_NAME ,
. 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 ,
} ;
2011-11-18 21:46:12 +04:00
module_usb_driver ( usb_amradio_driver ) ;