2008-12-28 04:33:54 +03:00
/* A driver for the D-Link DSB-R100 USB radio and Gemtek USB Radio 21.
The device plugs into both the USB and an analog audio input , so this thing
2005-04-17 02:20:36 +04:00
only deals with initialisation and frequency setting , the
audio data has to be handled by a sound driver .
Major issue : I can ' t find out where the device reports the signal
strength , and indeed the windows software appearantly just looks
at the stereo indicator as well . So , scanning will only find
stereo stations . Sad , but I can ' t help it .
Also , the windows program sends oodles of messages over to the
device , and I couldn ' t figure out their meaning . My suspicion
is that they don ' t have any : - )
You might find some interesting stuff about this module at
http : //unimut.fsk.uni-heidelberg.de/unimut/demi/dsbr
Copyright ( c ) 2000 Markus Demleitner < msdemlei @ cl . uni - heidelberg . de >
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
History :
2008-12-28 04:35:21 +03:00
Version 0.44 :
Add suspend / resume functions , fix unplug of device ,
a lot of cleanups and fixes by Alexey Klimov < klimov . linux @ gmail . com >
2007-12-03 12:48:43 +03:00
Version 0.43 :
Oliver Neukum : avoided DMA coherency issue
2007-05-07 23:43:01 +04:00
Version 0.42 :
Converted dsbr100 to use video_ioctl2
by Douglas Landgraf < dougsland @ gmail . com >
2006-08-08 22:47:50 +04:00
Version 0.41 - ac1 :
Alan Cox : Some cleanups and fixes
Version 0.41 :
Converted to V4L2 API by Mauro Carvalho Chehab < mchehab @ infradead . org >
2005-04-17 02:20:36 +04:00
Version 0.40 :
2006-08-08 22:47:50 +04:00
Markus : Updates for 2.6 . x kernels , code layout changes , name sanitizing
2005-04-17 02:20:36 +04:00
Version 0.30 :
2006-03-25 15:19:53 +03:00
Markus : Updates for 2.5 . x kernel and more ISO compliant source
2005-04-17 02:20:36 +04:00
Version 0.25 :
2006-03-25 15:19:53 +03:00
PSL and Markus : Cleanup , radio now doesn ' t stop on device close
2005-04-17 02:20:36 +04:00
Version 0.24 :
2006-03-25 15:19:53 +03:00
Markus : Hope I got these silly VIDEO_TUNER_LOW issues finally
2005-04-17 02:20:36 +04:00
right . Some minor cleanup , improved standalone compilation
Version 0.23 :
2006-03-25 15:19:53 +03:00
Markus : Sign extension bug fixed by declaring transfer_buffer unsigned
2005-04-17 02:20:36 +04:00
Version 0.22 :
2006-03-25 15:19:53 +03:00
Markus : Some ( brown bag ) cleanup in what VIDIOCSTUNER returns ,
2005-04-17 02:20:36 +04:00
thanks to Mike Cox for pointing the problem out .
Version 0.21 :
2006-03-25 15:19:53 +03:00
Markus : Minor cleanup , warnings if something goes wrong , lame attempt
2005-04-17 02:20:36 +04:00
to adhere to Documentation / CodingStyle
2006-03-25 15:19:53 +03:00
Version 0.2 :
Brad Hards < bradh @ dynamite . com . au > : Fixes to make it work as non - module
2005-04-17 02:20:36 +04:00
Markus : Copyright clarification
Version 0.01 : Markus : initial release
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/slab.h>
# include <linux/input.h>
2006-08-08 22:47:50 +04:00
# include <linux/videodev2.h>
2006-06-05 17:26:32 +04:00
# include <media/v4l2-common.h>
2008-07-20 15:12:02 +04:00
# include <media/v4l2-ioctl.h>
2005-04-17 02:20:36 +04:00
# include <linux/usb.h>
/*
* Version Information
*/
2006-08-08 22:47:50 +04:00
# include <linux/version.h> /* for KERNEL_VERSION MACRO */
2008-12-28 04:35:21 +03:00
# define DRIVER_VERSION "v0.44"
# define RADIO_VERSION KERNEL_VERSION(0, 4, 4)
2006-08-08 22:47:50 +04:00
static struct v4l2_queryctrl radio_qctrl [ ] = {
{
. id = V4L2_CID_AUDIO_MUTE ,
. name = " Mute " ,
. minimum = 0 ,
. maximum = 1 ,
. default_value = 1 ,
. type = V4L2_CTRL_TYPE_BOOLEAN ,
2008-11-08 06:31:50 +03:00
} ,
/* 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 ,
} ,
2006-08-08 22:47:50 +04:00
} ;
2005-04-17 02:20:36 +04:00
# define DRIVER_AUTHOR "Markus Demleitner <msdemlei@tucana.harvard.edu>"
# define DRIVER_DESC "D-Link DSB-R100 USB FM radio driver"
# define DSB100_VENDOR 0x04b4
# define DSB100_PRODUCT 0x1002
/* Commands the device appears to understand */
# define DSB100_TUNE 1
# define DSB100_ONOFF 2
# define TB_LEN 16
/* 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
2008-12-28 03:32:49 +03:00
# define videodev_to_radio(d) container_of(d, struct dsbr100_device, videodev)
2005-04-17 02:20:36 +04:00
static int usb_dsbr100_probe ( struct usb_interface * intf ,
const struct usb_device_id * id ) ;
static void usb_dsbr100_disconnect ( struct usb_interface * intf ) ;
2008-12-30 12:58:20 +03:00
static int usb_dsbr100_open ( struct file * file ) ;
static int usb_dsbr100_close ( struct file * file ) ;
2008-11-08 06:40:46 +03:00
static int usb_dsbr100_suspend ( struct usb_interface * intf ,
pm_message_t message ) ;
static int usb_dsbr100_resume ( struct usb_interface * intf ) ;
2005-04-17 02:20:36 +04:00
static int radio_nr = - 1 ;
module_param ( radio_nr , int , 0 ) ;
/* Data for one (physical) device */
2006-08-08 22:47:50 +04:00
struct dsbr100_device {
2005-04-17 02:20:36 +04:00
struct usb_device * usbdev ;
2008-12-28 03:32:49 +03:00
struct video_device videodev ;
2007-12-03 12:48:43 +03:00
u8 * transfer_buffer ;
2008-12-28 03:32:49 +03:00
struct mutex lock ; /* buffer locking */
2005-04-17 02:20:36 +04:00
int curfreq ;
int stereo ;
int users ;
int removed ;
2006-08-08 22:47:50 +04:00
int muted ;
} ;
2005-04-17 02:20:36 +04:00
static struct usb_device_id usb_dsbr100_device_table [ ] = {
{ USB_DEVICE ( DSB100_VENDOR , DSB100_PRODUCT ) } ,
{ } /* Terminating entry */
} ;
MODULE_DEVICE_TABLE ( usb , usb_dsbr100_device_table ) ;
/* USB subsystem interface */
static struct usb_driver usb_dsbr100_driver = {
2008-11-08 06:40:46 +03:00
. name = " dsbr100 " ,
. probe = usb_dsbr100_probe ,
. disconnect = usb_dsbr100_disconnect ,
. id_table = usb_dsbr100_device_table ,
. suspend = usb_dsbr100_suspend ,
. resume = usb_dsbr100_resume ,
. reset_resume = usb_dsbr100_resume ,
. supports_autosuspend = 0 ,
2005-04-17 02:20:36 +04:00
} ;
/* Low-level device interface begins here */
/* switch on radio */
2006-08-08 22:47:50 +04:00
static int dsbr100_start ( struct dsbr100_device * radio )
2005-04-17 02:20:36 +04:00
{
2008-12-28 04:30:29 +03:00
int retval ;
int request ;
2008-12-28 03:32:49 +03:00
mutex_lock ( & radio - > lock ) ;
2008-12-28 04:30:29 +03:00
retval = usb_control_msg ( radio - > usbdev ,
usb_rcvctrlpipe ( radio - > usbdev , 0 ) ,
USB_REQ_GET_STATUS ,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN ,
0x00 , 0xC7 , radio - > transfer_buffer , 8 , 300 ) ;
if ( retval < 0 ) {
request = USB_REQ_GET_STATUS ;
goto usb_control_msg_failed ;
2008-12-28 03:32:49 +03:00
}
2008-12-28 04:30:29 +03:00
retval = usb_control_msg ( radio - > usbdev ,
usb_rcvctrlpipe ( radio - > usbdev , 0 ) ,
DSB100_ONOFF ,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN ,
0x01 , 0x00 , radio - > transfer_buffer , 8 , 300 ) ;
if ( retval < 0 ) {
request = DSB100_ONOFF ;
goto usb_control_msg_failed ;
}
radio - > muted = 0 ;
2008-12-28 03:32:49 +03:00
mutex_unlock ( & radio - > lock ) ;
2005-04-17 02:20:36 +04:00
return ( radio - > transfer_buffer ) [ 0 ] ;
2008-12-28 04:30:29 +03:00
usb_control_msg_failed :
mutex_unlock ( & radio - > lock ) ;
dev_err ( & radio - > usbdev - > dev ,
" %s - usb_control_msg returned %i, request %i \n " ,
__func__ , retval , request ) ;
2008-12-28 04:40:57 +03:00
return retval ;
2008-12-28 04:30:29 +03:00
2005-04-17 02:20:36 +04:00
}
/* switch off radio */
2006-08-08 22:47:50 +04:00
static int dsbr100_stop ( struct dsbr100_device * radio )
2005-04-17 02:20:36 +04:00
{
2008-12-28 04:30:29 +03:00
int retval ;
int request ;
2008-12-28 03:32:49 +03:00
mutex_lock ( & radio - > lock ) ;
2008-12-28 04:30:29 +03:00
retval = usb_control_msg ( radio - > usbdev ,
usb_rcvctrlpipe ( radio - > usbdev , 0 ) ,
USB_REQ_GET_STATUS ,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN ,
0x16 , 0x1C , radio - > transfer_buffer , 8 , 300 ) ;
if ( retval < 0 ) {
request = USB_REQ_GET_STATUS ;
goto usb_control_msg_failed ;
}
retval = usb_control_msg ( radio - > usbdev ,
usb_rcvctrlpipe ( radio - > usbdev , 0 ) ,
DSB100_ONOFF ,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN ,
0x00 , 0x00 , radio - > transfer_buffer , 8 , 300 ) ;
if ( retval < 0 ) {
request = DSB100_ONOFF ;
goto usb_control_msg_failed ;
2008-12-28 03:32:49 +03:00
}
2008-12-28 04:30:29 +03:00
radio - > muted = 1 ;
2008-12-28 03:32:49 +03:00
mutex_unlock ( & radio - > lock ) ;
2005-04-17 02:20:36 +04:00
return ( radio - > transfer_buffer ) [ 0 ] ;
2008-12-28 04:30:29 +03:00
usb_control_msg_failed :
mutex_unlock ( & radio - > lock ) ;
dev_err ( & radio - > usbdev - > dev ,
" %s - usb_control_msg returned %i, request %i \n " ,
__func__ , retval , request ) ;
2008-12-28 04:40:57 +03:00
return retval ;
2008-12-28 04:30:29 +03:00
2005-04-17 02:20:36 +04:00
}
/* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */
2006-08-08 22:47:50 +04:00
static int dsbr100_setfreq ( struct dsbr100_device * radio , int freq )
2005-04-17 02:20:36 +04:00
{
2008-12-28 04:30:29 +03:00
int retval ;
int request ;
2008-10-20 06:50:27 +04:00
freq = ( freq / 16 * 80 ) / 1000 + 856 ;
2008-12-28 03:32:49 +03:00
mutex_lock ( & radio - > lock ) ;
2008-12-28 04:30:29 +03:00
retval = usb_control_msg ( radio - > usbdev ,
usb_rcvctrlpipe ( radio - > usbdev , 0 ) ,
DSB100_TUNE ,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN ,
( freq > > 8 ) & 0x00ff , freq & 0xff ,
radio - > transfer_buffer , 8 , 300 ) ;
if ( retval < 0 ) {
request = DSB100_TUNE ;
goto usb_control_msg_failed ;
}
retval = usb_control_msg ( radio - > usbdev ,
usb_rcvctrlpipe ( radio - > usbdev , 0 ) ,
USB_REQ_GET_STATUS ,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN ,
0x96 , 0xB7 , radio - > transfer_buffer , 8 , 300 ) ;
if ( retval < 0 ) {
request = USB_REQ_GET_STATUS ;
goto usb_control_msg_failed ;
}
retval = usb_control_msg ( radio - > usbdev ,
usb_rcvctrlpipe ( radio - > usbdev , 0 ) ,
USB_REQ_GET_STATUS ,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN ,
0x00 , 0x24 , radio - > transfer_buffer , 8 , 300 ) ;
if ( retval < 0 ) {
request = USB_REQ_GET_STATUS ;
goto usb_control_msg_failed ;
2005-04-17 02:20:36 +04:00
}
2008-12-28 03:32:49 +03:00
2008-10-20 06:50:27 +04:00
radio - > stereo = ! ( ( radio - > transfer_buffer ) [ 0 ] & 0x01 ) ;
2008-12-28 03:32:49 +03:00
mutex_unlock ( & radio - > lock ) ;
2005-04-17 02:20:36 +04:00
return ( radio - > transfer_buffer ) [ 0 ] ;
2008-12-28 04:30:29 +03:00
usb_control_msg_failed :
radio - > stereo = - 1 ;
mutex_unlock ( & radio - > lock ) ;
dev_err ( & radio - > usbdev - > dev ,
" %s - usb_control_msg returned %i, request %i \n " ,
__func__ , retval , request ) ;
2008-12-28 04:40:57 +03:00
return retval ;
2005-04-17 02:20:36 +04:00
}
/* return the device status. This is, in effect, just whether it
sees a stereo signal or not . Pity . */
2006-08-08 22:47:50 +04:00
static void dsbr100_getstat ( struct dsbr100_device * radio )
2005-04-17 02:20:36 +04:00
{
2008-12-28 04:30:29 +03:00
int retval ;
2008-12-28 03:32:49 +03:00
mutex_lock ( & radio - > lock ) ;
2008-12-28 04:30:29 +03:00
retval = usb_control_msg ( radio - > usbdev ,
usb_rcvctrlpipe ( radio - > usbdev , 0 ) ,
2006-03-25 15:19:53 +03:00
USB_REQ_GET_STATUS ,
2005-04-17 02:20:36 +04:00
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN ,
2008-12-28 04:30:29 +03:00
0x00 , 0x24 , radio - > transfer_buffer , 8 , 300 ) ;
if ( retval < 0 ) {
2005-04-17 02:20:36 +04:00
radio - > stereo = - 1 ;
2008-12-28 04:30:29 +03:00
dev_err ( & radio - > usbdev - > dev ,
" %s - usb_control_msg returned %i, request %i \n " ,
__func__ , retval , USB_REQ_GET_STATUS ) ;
} else {
2008-10-20 06:50:27 +04:00
radio - > stereo = ! ( radio - > transfer_buffer [ 0 ] & 0x01 ) ;
2008-12-28 04:30:29 +03:00
}
2008-12-28 03:32:49 +03:00
mutex_unlock ( & radio - > lock ) ;
2005-04-17 02:20:36 +04:00
}
/* USB subsystem interface begins here */
2008-12-28 04:33:54 +03:00
/*
* Handle unplugging of the device .
* We call video_unregister_device in any case .
* The last function called in this procedure is
* usb_dsbr100_video_device_release
*/
2005-04-17 02:20:36 +04:00
static void usb_dsbr100_disconnect ( struct usb_interface * intf )
{
2006-08-08 22:47:50 +04:00
struct dsbr100_device * radio = usb_get_intfdata ( intf ) ;
2005-04-17 02:20:36 +04:00
usb_set_intfdata ( intf , NULL ) ;
2008-12-28 03:32:49 +03:00
mutex_lock ( & radio - > lock ) ;
radio - > removed = 1 ;
mutex_unlock ( & radio - > lock ) ;
video_unregister_device ( & radio - > videodev ) ;
2005-04-17 02:20:36 +04:00
}
2007-05-07 23:43:01 +04:00
static int vidioc_querycap ( struct file * file , void * priv ,
struct v4l2_capability * v )
{
2009-01-26 02:05:58 +03:00
struct dsbr100_device * radio = video_drvdata ( file ) ;
2007-05-07 23:43:01 +04:00
strlcpy ( v - > driver , " dsbr100 " , sizeof ( v - > driver ) ) ;
strlcpy ( v - > card , " D-Link R-100 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 ) ) ;
2007-05-07 23:43:01 +04:00
v - > version = RADIO_VERSION ;
v - > capabilities = V4L2_CAP_TUNER ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
2007-05-07 23:43:01 +04:00
static int vidioc_g_tuner ( struct file * file , void * priv ,
struct v4l2_tuner * v )
2005-04-17 02:20:36 +04:00
{
2008-08-23 15:32:09 +04:00
struct dsbr100_device * radio = video_drvdata ( file ) ;
2007-05-07 23:43:01 +04:00
2008-12-28 03:32:49 +03:00
/* safety check */
if ( radio - > removed )
return - EIO ;
2007-05-07 23:43:01 +04:00
if ( v - > index > 0 )
return - EINVAL ;
dsbr100_getstat ( radio ) ;
strcpy ( v - > name , " FM " ) ;
v - > type = V4L2_TUNER_RADIO ;
2008-10-20 06:50:27 +04:00
v - > rangelow = FREQ_MIN * FREQ_MUL ;
v - > rangehigh = FREQ_MAX * FREQ_MUL ;
v - > rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO ;
2007-05-07 23:43:01 +04:00
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 ; /* We can't get the signal strength */
return 0 ;
}
2005-04-17 02:20:36 +04:00
2007-05-07 23:43:01 +04:00
static int vidioc_s_tuner ( struct file * file , void * priv ,
struct v4l2_tuner * v )
{
2008-12-28 03:32:49 +03:00
struct dsbr100_device * radio = video_drvdata ( file ) ;
/* safety check */
if ( radio - > removed )
return - EIO ;
2007-05-07 23:43:01 +04:00
if ( v - > index > 0 )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
2007-05-07 23:43:01 +04:00
return 0 ;
}
2006-08-08 22:47:50 +04:00
2007-05-07 23:43:01 +04:00
static int vidioc_s_frequency ( struct file * file , void * priv ,
struct v4l2_frequency * f )
{
2008-08-23 15:32:09 +04:00
struct dsbr100_device * radio = video_drvdata ( file ) ;
2008-12-28 04:30:29 +03:00
int retval ;
2005-04-17 02:20:36 +04:00
2008-12-28 03:32:49 +03:00
/* safety check */
if ( radio - > removed )
return - EIO ;
2009-02-08 08:00:14 +03:00
mutex_lock ( & radio - > lock ) ;
2007-05-07 23:43:01 +04:00
radio - > curfreq = f - > frequency ;
2009-02-08 08:00:14 +03:00
mutex_unlock ( & radio - > lock ) ;
2008-12-28 04:30:29 +03:00
retval = dsbr100_setfreq ( radio , radio - > curfreq ) ;
2008-12-28 04:40:57 +03:00
if ( retval < 0 )
2008-08-14 20:37:34 +04:00
dev_warn ( & radio - > usbdev - > dev , " Set frequency failed \n " ) ;
2007-05-07 23:43:01 +04:00
return 0 ;
}
2006-08-08 22:47:50 +04:00
2007-05-07 23:43:01 +04:00
static int vidioc_g_frequency ( struct file * file , void * priv ,
struct v4l2_frequency * f )
{
2008-08-23 15:32:09 +04:00
struct dsbr100_device * radio = video_drvdata ( file ) ;
2006-08-08 22:47:50 +04:00
2008-12-28 03:32:49 +03:00
/* safety check */
if ( radio - > removed )
return - EIO ;
2007-05-07 23:43:01 +04:00
f - > type = V4L2_TUNER_RADIO ;
f - > frequency = radio - > curfreq ;
return 0 ;
}
2006-08-08 22:47:50 +04:00
2007-05-07 23:43:01 +04:00
static int vidioc_queryctrl ( struct file * file , void * priv ,
struct v4l2_queryctrl * qc )
{
int i ;
2005-04-17 02:20:36 +04:00
2007-05-07 23:43:01 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( radio_qctrl ) ; i + + ) {
if ( qc - > id & & qc - > id = = radio_qctrl [ i ] . id ) {
2008-10-20 06:50:27 +04:00
memcpy ( qc , & ( radio_qctrl [ i ] ) , sizeof ( * qc ) ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2007-05-07 23:43:01 +04:00
}
return - EINVAL ;
}
2006-08-08 22:47:50 +04:00
2007-05-07 23:43:01 +04:00
static int vidioc_g_ctrl ( struct file * file , void * priv ,
struct v4l2_control * ctrl )
{
2008-08-23 15:32:09 +04:00
struct dsbr100_device * radio = video_drvdata ( file ) ;
2005-04-17 02:20:36 +04:00
2008-12-28 03:32:49 +03:00
/* safety check */
if ( radio - > removed )
return - EIO ;
2007-05-07 23:43:01 +04:00
switch ( ctrl - > id ) {
case V4L2_CID_AUDIO_MUTE :
ctrl - > value = radio - > muted ;
return 0 ;
}
return - EINVAL ;
}
2006-08-08 22:47:50 +04:00
2007-05-07 23:43:01 +04:00
static int vidioc_s_ctrl ( struct file * file , void * priv ,
struct v4l2_control * ctrl )
{
2008-08-23 15:32:09 +04:00
struct dsbr100_device * radio = video_drvdata ( file ) ;
2008-12-28 04:30:29 +03:00
int retval ;
2006-08-08 22:47:50 +04:00
2008-12-28 03:32:49 +03:00
/* safety check */
if ( radio - > removed )
return - EIO ;
2007-05-07 23:43:01 +04:00
switch ( ctrl - > id ) {
case V4L2_CID_AUDIO_MUTE :
if ( ctrl - > value ) {
2008-12-28 04:30:29 +03:00
retval = dsbr100_stop ( radio ) ;
2008-12-28 04:40:57 +03:00
if ( retval < 0 ) {
2008-08-14 20:37:34 +04:00
dev_warn ( & radio - > usbdev - > dev ,
" Radio did not respond properly \n " ) ;
2008-10-09 20:42:32 +04:00
return - EBUSY ;
}
2007-05-07 23:43:01 +04:00
} else {
2008-12-28 04:30:29 +03:00
retval = dsbr100_start ( radio ) ;
2008-12-28 04:40:57 +03:00
if ( retval < 0 ) {
2008-08-14 20:37:34 +04:00
dev_warn ( & radio - > usbdev - > dev ,
" Radio did not respond properly \n " ) ;
2008-10-09 20:42:32 +04:00
return - EBUSY ;
}
2005-04-17 02:20:36 +04:00
}
2007-05-07 23:43:01 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2007-05-07 23:43:01 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
}
2007-05-07 23:43:01 +04:00
static int vidioc_g_audio ( struct file * file , void * priv ,
struct v4l2_audio * a )
2005-04-17 02:20:36 +04:00
{
2007-05-07 23:43:01 +04:00
if ( a - > index > 1 )
return - EINVAL ;
strcpy ( a - > name , " Radio " ) ;
a - > capability = V4L2_AUDCAP_STEREO ;
return 0 ;
}
static int vidioc_g_input ( struct file * filp , void * priv , unsigned int * i )
{
* i = 0 ;
return 0 ;
}
static int vidioc_s_input ( struct file * filp , void * priv , unsigned int i )
{
if ( i ! = 0 )
return - EINVAL ;
return 0 ;
}
static int vidioc_s_audio ( struct file * file , void * priv ,
struct v4l2_audio * a )
{
if ( a - > index ! = 0 )
return - EINVAL ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2008-12-30 12:58:20 +03:00
static int usb_dsbr100_open ( struct file * file )
2005-04-17 02:20:36 +04:00
{
2008-08-23 15:32:09 +04:00
struct dsbr100_device * radio = video_drvdata ( file ) ;
2008-10-20 07:00:03 +04:00
int retval ;
2005-04-17 02:20:36 +04:00
2008-07-30 15:43:36 +04:00
lock_kernel ( ) ;
2005-04-17 02:20:36 +04:00
radio - > users = 1 ;
2006-08-08 22:47:50 +04:00
radio - > muted = 1 ;
2008-12-28 04:30:29 +03:00
retval = dsbr100_start ( radio ) ;
if ( retval < 0 ) {
2008-08-14 20:37:34 +04:00
dev_warn ( & radio - > usbdev - > dev ,
" Radio did not start up properly \n " ) ;
2005-04-17 02:20:36 +04:00
radio - > users = 0 ;
2008-07-30 15:43:36 +04:00
unlock_kernel ( ) ;
2005-04-17 02:20:36 +04:00
return - EIO ;
}
2008-10-20 07:00:03 +04:00
retval = dsbr100_setfreq ( radio , radio - > curfreq ) ;
2008-12-28 04:40:57 +03:00
if ( retval < 0 )
2008-12-28 03:42:39 +03:00
dev_warn ( & radio - > usbdev - > dev ,
" set frequency failed \n " ) ;
2008-10-20 07:00:03 +04:00
2008-07-30 15:43:36 +04:00
unlock_kernel ( ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2008-12-30 12:58:20 +03:00
static int usb_dsbr100_close ( struct file * file )
2005-04-17 02:20:36 +04:00
{
2008-08-23 15:32:09 +04:00
struct dsbr100_device * radio = video_drvdata ( file ) ;
2008-12-28 03:32:49 +03:00
int retval ;
2005-04-17 02:20:36 +04:00
if ( ! radio )
return - ENODEV ;
2008-12-28 03:32:49 +03:00
2009-02-08 08:00:14 +03:00
mutex_lock ( & radio - > lock ) ;
2005-04-17 02:20:36 +04:00
radio - > users = 0 ;
2009-02-08 08:00:14 +03:00
mutex_unlock ( & radio - > lock ) ;
2008-12-28 03:32:49 +03:00
if ( ! radio - > removed ) {
retval = dsbr100_stop ( radio ) ;
2008-12-28 04:40:57 +03:00
if ( retval < 0 ) {
2008-12-28 03:32:49 +03:00
dev_warn ( & radio - > usbdev - > dev ,
" dsbr100_stop failed \n " ) ;
}
2005-04-17 02:20:36 +04:00
}
return 0 ;
}
2008-11-08 06:40:46 +03:00
/* Suspend device - stop device. */
static int usb_dsbr100_suspend ( struct usb_interface * intf , pm_message_t message )
{
struct dsbr100_device * radio = usb_get_intfdata ( intf ) ;
int retval ;
retval = dsbr100_stop ( radio ) ;
2008-12-28 04:40:57 +03:00
if ( retval < 0 )
2008-11-08 06:40:46 +03:00
dev_warn ( & intf - > dev , " dsbr100_stop failed \n " ) ;
dev_info ( & intf - > dev , " going into suspend.. \n " ) ;
return 0 ;
}
/* Resume device - start device. */
static int usb_dsbr100_resume ( struct usb_interface * intf )
{
struct dsbr100_device * radio = usb_get_intfdata ( intf ) ;
int retval ;
retval = dsbr100_start ( radio ) ;
2008-12-28 04:40:57 +03:00
if ( retval < 0 )
2008-11-08 06:40:46 +03:00
dev_warn ( & intf - > dev , " dsbr100_start failed \n " ) ;
dev_info ( & intf - > dev , " coming out of suspend.. \n " ) ;
return 0 ;
}
2008-12-28 04:33:54 +03:00
/* free data structures */
2008-12-28 03:32:49 +03:00
static void usb_dsbr100_video_device_release ( struct video_device * videodev )
{
struct dsbr100_device * radio = videodev_to_radio ( videodev ) ;
kfree ( radio - > transfer_buffer ) ;
kfree ( radio ) ;
}
2007-05-07 23:43:01 +04:00
/* File system interface */
2008-12-30 12:58:20 +03:00
static const struct v4l2_file_operations usb_dsbr100_fops = {
2007-05-07 23:43:01 +04:00
. owner = THIS_MODULE ,
. open = usb_dsbr100_open ,
. release = usb_dsbr100_close ,
. ioctl = video_ioctl2 ,
} ;
2008-07-21 09:57:38 +04:00
static const struct v4l2_ioctl_ops usb_dsbr100_ioctl_ops = {
2007-05-07 23:43:01 +04:00
. 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 ,
} ;
2008-07-21 09:57:38 +04:00
/* V4L2 interface */
2008-12-28 03:32:49 +03:00
static struct video_device dsbr100_videodev_data = {
2008-07-21 09:57:38 +04:00
. name = " D-Link DSB-R 100 " ,
. fops = & usb_dsbr100_fops ,
. ioctl_ops = & usb_dsbr100_ioctl_ops ,
2008-12-28 03:32:49 +03:00
. release = usb_dsbr100_video_device_release ,
2008-07-21 09:57:38 +04:00
} ;
2008-12-28 04:33:54 +03:00
/* check if the device is present and register with v4l and usb if it is */
2007-05-07 23:43:01 +04:00
static int usb_dsbr100_probe ( struct usb_interface * intf ,
const struct usb_device_id * id )
{
struct dsbr100_device * radio ;
2008-12-28 04:30:29 +03:00
int retval ;
2007-05-07 23:43:01 +04:00
2008-10-20 06:50:27 +04:00
radio = kmalloc ( sizeof ( struct dsbr100_device ) , GFP_KERNEL ) ;
if ( ! radio )
2007-05-07 23:43:01 +04:00
return - ENOMEM ;
2008-10-20 06:50:27 +04:00
radio - > transfer_buffer = kmalloc ( TB_LEN , GFP_KERNEL ) ;
if ( ! ( radio - > transfer_buffer ) ) {
2007-12-03 12:48:43 +03:00
kfree ( radio ) ;
return - ENOMEM ;
}
2008-10-20 06:50:27 +04:00
2008-12-28 03:32:49 +03:00
mutex_init ( & radio - > lock ) ;
radio - > videodev = dsbr100_videodev_data ;
2007-05-07 23:43:01 +04:00
radio - > removed = 0 ;
radio - > users = 0 ;
radio - > usbdev = interface_to_usbdev ( intf ) ;
2008-10-20 06:50:27 +04:00
radio - > curfreq = FREQ_MIN * FREQ_MUL ;
2008-12-28 03:32:49 +03:00
video_set_drvdata ( & radio - > videodev , radio ) ;
2008-12-28 04:30:29 +03:00
retval = video_register_device ( & radio - > videodev , VFL_TYPE_RADIO , radio_nr ) ;
if ( retval < 0 ) {
2008-12-28 04:31:52 +03:00
dev_err ( & intf - > dev , " couldn't register video device \n " ) ;
2007-12-03 12:48:43 +03:00
kfree ( radio - > transfer_buffer ) ;
2007-05-07 23:43:01 +04:00
kfree ( radio ) ;
return - EIO ;
}
usb_set_intfdata ( intf , radio ) ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
static int __init dsbr100_init ( void )
{
int retval = usb_register ( & usb_dsbr100_driver ) ;
2008-10-10 12:08:23 +04:00
printk ( KERN_INFO KBUILD_MODNAME " : " DRIVER_VERSION " : "
DRIVER_DESC " \n " ) ;
2005-04-17 02:20:36 +04:00
return retval ;
}
static void __exit dsbr100_exit ( void )
{
usb_deregister ( & usb_dsbr100_driver ) ;
}
module_init ( dsbr100_init ) ;
module_exit ( dsbr100_exit ) ;
MODULE_AUTHOR ( DRIVER_AUTHOR ) ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
MODULE_LICENSE ( " GPL " ) ;