2005-04-17 02:20:36 +04:00
/* radio-cadet.c - A video4linux driver for the ADS Cadet AM/FM Radio Card
*
* by Fred Gleason < fredg @ wava . com >
* Version 0.3 .3
*
* ( Loosely ) based on code for the Aztech radio card by
*
* Russell Kroll ( rkroll @ exploits . org )
* Quay Ly
* Donald Song
2006-04-08 23:06:16 +04:00
* Jason Lewis ( jlewis @ twilight . vtc . vsc . edu )
2005-04-17 02:20:36 +04:00
* Scott McGrath ( smcgrath @ twilight . vtc . vsc . edu )
* William McGrath ( wmcgrath @ twilight . vtc . vsc . edu )
*
* History :
* 2000 - 04 - 29 Russell Kroll < rkroll @ exploits . org >
* Added ISAPnP detection for Linux 2.3 / 2.4
*
* 2001 - 01 - 10 Russell Kroll < rkroll @ exploits . org >
* Removed dead CONFIG_RADIO_CADET_PORT code
* PnP detection on load is now default ( no args necessary )
*
* 2002 - 01 - 17 Adam Belay < ambx1 @ neo . rr . com >
* Updated to latest pnp code
*
2008-10-27 21:13:47 +03:00
* 2003 - 01 - 31 Alan Cox < alan @ lxorguk . ukuu . org . uk >
2005-04-17 02:20:36 +04:00
* Cleaned up locking , delay code , general odds and ends
2006-08-08 16:10:12 +04:00
*
* 2006 - 07 - 30 Hans J . Koch < koch @ hjk - az . de >
* Changed API to V4L2
2005-04-17 02:20:36 +04:00
*/
2006-08-08 17:52:22 +04:00
# include <linux/version.h>
2005-04-17 02:20:36 +04:00
# include <linux/module.h> /* Modules */
# include <linux/init.h> /* Initdata */
2005-09-13 12:25:15 +04:00
# include <linux/ioport.h> /* request_region */
2005-04-17 02:20:36 +04:00
# include <linux/delay.h> /* udelay */
2006-08-08 16:10:12 +04:00
# include <linux/videodev2.h> /* V4L2 API defs */
2005-04-17 02:20:36 +04:00
# include <linux/param.h>
# include <linux/pnp.h>
2009-10-04 16:11:37 +04:00
# include <linux/sched.h>
2009-03-06 19:48:47 +03:00
# include <linux/io.h> /* outb, outb_p */
# include <media/v4l2-device.h>
# include <media/v4l2-ioctl.h>
MODULE_AUTHOR ( " Fred Gleason, Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath " ) ;
MODULE_DESCRIPTION ( " A driver for the ADS Cadet AM/FM/RDS radio card. " ) ;
MODULE_LICENSE ( " GPL " ) ;
static int io = - 1 ; /* default to isapnp activation */
static int radio_nr = - 1 ;
module_param ( io , int , 0 ) ;
MODULE_PARM_DESC ( io , " I/O address of Cadet card (0x330,0x332,0x334,0x336,0x338,0x33a,0x33c,0x33e) " ) ;
module_param ( radio_nr , int , 0 ) ;
# define CADET_VERSION KERNEL_VERSION(0, 3, 3)
2005-04-17 02:20:36 +04:00
# define RDS_BUFFER 256
2006-08-08 16:10:12 +04:00
# define RDS_RX_FLAG 1
# define MBS_RX_FLAG 2
2009-03-06 19:48:47 +03:00
struct cadet {
struct v4l2_device v4l2_dev ;
struct video_device vdev ;
int io ;
int users ;
int curtuner ;
int tunestat ;
int sigstrength ;
wait_queue_head_t read_queue ;
struct timer_list readtimer ;
__u8 rdsin , rdsout , rdsstat ;
unsigned char rdsbuf [ RDS_BUFFER ] ;
struct mutex lock ;
int reading ;
2007-05-07 23:17:16 +04:00
} ;
2009-03-06 19:48:47 +03:00
static struct cadet cadet_card ;
2005-04-17 02:20:36 +04:00
/*
* Signal Strength Threshold Values
2006-04-08 23:06:16 +04:00
* The V4L API spec does not define any particular unit for the signal
2005-04-17 02:20:36 +04:00
* strength value . These values are in microvolts of RF at the tuner ' s input .
*/
2009-03-06 19:48:47 +03:00
static __u16 sigtable [ 2 ] [ 4 ] = {
{ 5 , 10 , 30 , 150 } ,
{ 28 , 40 , 63 , 1000 }
} ;
2005-04-17 02:20:36 +04:00
2009-03-06 19:48:47 +03:00
static int cadet_getstereo ( struct cadet * dev )
2005-04-17 02:20:36 +04:00
{
2006-08-08 16:10:12 +04:00
int ret = V4L2_TUNER_SUB_MONO ;
2009-03-06 19:48:47 +03:00
if ( dev - > curtuner ! = 0 ) /* Only FM has stereo capability! */
2006-08-08 16:10:12 +04:00
return V4L2_TUNER_SUB_MONO ;
2005-04-17 02:20:36 +04:00
2009-03-06 19:48:47 +03:00
mutex_lock ( & dev - > lock ) ;
outb ( 7 , dev - > io ) ; /* Select tuner control */
if ( ( inb ( dev - > io + 1 ) & 0x40 ) = = 0 )
2006-08-08 16:10:12 +04:00
ret = V4L2_TUNER_SUB_STEREO ;
2009-03-06 19:48:47 +03:00
mutex_unlock ( & dev - > lock ) ;
2006-04-08 23:06:16 +04:00
return ret ;
2005-04-17 02:20:36 +04:00
}
2009-03-06 19:48:47 +03:00
static unsigned cadet_gettune ( struct cadet * dev )
2005-04-17 02:20:36 +04:00
{
2009-03-06 19:48:47 +03:00
int curvol , i ;
unsigned fifo = 0 ;
2005-04-17 02:20:36 +04:00
2006-04-08 23:06:16 +04:00
/*
* Prepare for read
*/
2005-04-17 02:20:36 +04:00
2009-03-06 19:48:47 +03:00
mutex_lock ( & dev - > lock ) ;
2006-04-08 23:06:16 +04:00
2009-03-06 19:48:47 +03:00
outb ( 7 , dev - > io ) ; /* Select tuner control */
curvol = inb ( dev - > io + 1 ) ; /* Save current volume/mute setting */
outb ( 0x00 , dev - > io + 1 ) ; /* Ensure WRITE-ENABLE is LOW */
dev - > tunestat = 0xffff ;
2005-04-17 02:20:36 +04:00
2006-04-08 23:06:16 +04:00
/*
* Read the shift register
*/
2009-03-06 19:48:47 +03:00
for ( i = 0 ; i < 25 ; i + + ) {
fifo = ( fifo < < 1 ) | ( ( inb ( dev - > io + 1 ) > > 7 ) & 0x01 ) ;
if ( i < 24 ) {
outb ( 0x01 , dev - > io + 1 ) ;
dev - > tunestat & = inb ( dev - > io + 1 ) ;
outb ( 0x00 , dev - > io + 1 ) ;
2006-04-08 23:06:16 +04:00
}
}
/*
* Restore volume / mute setting
*/
2009-03-06 19:48:47 +03:00
outb ( curvol , dev - > io + 1 ) ;
mutex_unlock ( & dev - > lock ) ;
2005-04-17 02:20:36 +04:00
return fifo ;
}
2009-03-06 19:48:47 +03:00
static unsigned cadet_getfreq ( struct cadet * dev )
2005-04-17 02:20:36 +04:00
{
2006-04-08 23:06:16 +04:00
int i ;
2009-03-06 19:48:47 +03:00
unsigned freq = 0 , test , fifo = 0 ;
2005-04-17 02:20:36 +04:00
/*
* Read current tuning
*/
2009-03-06 19:48:47 +03:00
fifo = cadet_gettune ( dev ) ;
2005-04-17 02:20:36 +04:00
2006-04-08 23:06:16 +04:00
/*
* Convert to actual frequency
*/
2009-03-06 19:48:47 +03:00
if ( dev - > curtuner = = 0 ) { /* FM */
test = 12500 ;
for ( i = 0 ; i < 14 ; i + + ) {
if ( ( fifo & 0x01 ) ! = 0 )
freq + = test ;
test = test < < 1 ;
fifo = fifo > > 1 ;
2006-04-08 23:06:16 +04:00
}
2009-03-06 19:48:47 +03:00
freq - = 10700000 ; /* IF frequency is 10.7 MHz */
freq = ( freq * 16 ) / 1000000 ; /* Make it 1/16 MHz */
2005-04-17 02:20:36 +04:00
}
2009-03-06 19:48:47 +03:00
if ( dev - > curtuner = = 1 ) /* AM */
freq = ( ( fifo & 0x7fff ) - 2010 ) * 16 ;
2005-04-17 02:20:36 +04:00
2006-04-08 23:06:16 +04:00
return freq ;
2005-04-17 02:20:36 +04:00
}
2009-03-06 19:48:47 +03:00
static void cadet_settune ( struct cadet * dev , unsigned fifo )
2005-04-17 02:20:36 +04:00
{
2006-04-08 23:06:16 +04:00
int i ;
unsigned test ;
2005-04-17 02:20:36 +04:00
2009-03-06 19:48:47 +03:00
mutex_lock ( & dev - > lock ) ;
2006-04-08 23:06:16 +04:00
2009-03-06 19:48:47 +03:00
outb ( 7 , dev - > io ) ; /* Select tuner control */
2005-04-17 02:20:36 +04:00
/*
* Write the shift register
*/
2009-03-06 19:48:47 +03:00
test = 0 ;
test = ( fifo > > 23 ) & 0x02 ; /* Align data for SDO */
test | = 0x1c ; /* SDM=1, SWE=1, SEN=1, SCK=0 */
outb ( 7 , dev - > io ) ; /* Select tuner control */
outb ( test , dev - > io + 1 ) ; /* Initialize for write */
for ( i = 0 ; i < 25 ; i + + ) {
test | = 0x01 ; /* Toggle SCK High */
outb ( test , dev - > io + 1 ) ;
test & = 0xfe ; /* Toggle SCK Low */
outb ( test , dev - > io + 1 ) ;
fifo = fifo < < 1 ; /* Prepare the next bit */
test = 0x1c | ( ( fifo > > 23 ) & 0x02 ) ;
outb ( test , dev - > io + 1 ) ;
2005-04-17 02:20:36 +04:00
}
2009-03-06 19:48:47 +03:00
mutex_unlock ( & dev - > lock ) ;
2005-04-17 02:20:36 +04:00
}
2009-03-06 19:48:47 +03:00
static void cadet_setfreq ( struct cadet * dev , unsigned freq )
2005-04-17 02:20:36 +04:00
{
2006-04-08 23:06:16 +04:00
unsigned fifo ;
2009-03-06 19:48:47 +03:00
int i , j , test ;
2006-04-08 23:06:16 +04:00
int curvol ;
2005-04-17 02:20:36 +04:00
2006-04-08 23:06:16 +04:00
/*
* Formulate a fifo command
*/
2009-03-06 19:48:47 +03:00
fifo = 0 ;
if ( dev - > curtuner = = 0 ) { /* FM */
test = 102400 ;
freq = ( freq * 1000 ) / 16 ; /* Make it kHz */
freq + = 10700 ; /* IF is 10700 kHz */
for ( i = 0 ; i < 14 ; i + + ) {
fifo = fifo < < 1 ;
if ( freq > = test ) {
fifo | = 0x01 ;
freq - = test ;
2006-04-08 23:06:16 +04:00
}
2009-03-06 19:48:47 +03:00
test = test > > 1 ;
2006-04-08 23:06:16 +04:00
}
2005-04-17 02:20:36 +04:00
}
2009-03-06 19:48:47 +03:00
if ( dev - > curtuner = = 1 ) { /* AM */
fifo = ( freq / 16 ) + 2010 ; /* Make it kHz */
fifo | = 0x100000 ; /* Select AM Band */
2005-04-17 02:20:36 +04:00
}
2006-04-08 23:06:16 +04:00
/*
* Save current volume / mute setting
*/
2005-04-17 02:20:36 +04:00
2009-03-06 19:48:47 +03:00
mutex_lock ( & dev - > lock ) ;
outb ( 7 , dev - > io ) ; /* Select tuner control */
curvol = inb ( dev - > io + 1 ) ;
mutex_unlock ( & dev - > lock ) ;
2005-04-17 02:20:36 +04:00
/*
* Tune the card
*/
2009-03-06 19:48:47 +03:00
for ( j = 3 ; j > - 1 ; j - - ) {
cadet_settune ( dev , fifo | ( j < < 16 ) ) ;
2006-04-08 23:06:16 +04:00
2009-03-06 19:48:47 +03:00
mutex_lock ( & dev - > lock ) ;
outb ( 7 , dev - > io ) ; /* Select tuner control */
outb ( curvol , dev - > io + 1 ) ;
mutex_unlock ( & dev - > lock ) ;
2006-04-08 23:06:16 +04:00
2005-04-17 02:20:36 +04:00
msleep ( 100 ) ;
2009-03-06 19:48:47 +03:00
cadet_gettune ( dev ) ;
if ( ( dev - > tunestat & 0x40 ) = = 0 ) { /* Tuned */
dev - > sigstrength = sigtable [ dev - > curtuner ] [ j ] ;
2005-04-17 02:20:36 +04:00
return ;
}
}
2009-03-06 19:48:47 +03:00
dev - > sigstrength = 0 ;
2005-04-17 02:20:36 +04:00
}
2009-03-06 19:48:47 +03:00
static int cadet_getvol ( struct cadet * dev )
2005-04-17 02:20:36 +04:00
{
int ret = 0 ;
2006-04-08 23:06:16 +04:00
2009-03-06 19:48:47 +03:00
mutex_lock ( & dev - > lock ) ;
2006-04-08 23:06:16 +04:00
2009-03-06 19:48:47 +03:00
outb ( 7 , dev - > io ) ; /* Select tuner control */
if ( ( inb ( dev - > io + 1 ) & 0x20 ) ! = 0 )
2006-04-08 23:06:16 +04:00
ret = 0xffff ;
2009-03-06 19:48:47 +03:00
mutex_unlock ( & dev - > lock ) ;
2006-04-08 23:06:16 +04:00
return ret ;
2005-04-17 02:20:36 +04:00
}
2009-03-06 19:48:47 +03:00
static void cadet_setvol ( struct cadet * dev , int vol )
2005-04-17 02:20:36 +04:00
{
2009-03-06 19:48:47 +03:00
mutex_lock ( & dev - > lock ) ;
outb ( 7 , dev - > io ) ; /* Select tuner control */
if ( vol > 0 )
outb ( 0x20 , dev - > io + 1 ) ;
2006-04-08 23:06:16 +04:00
else
2009-03-06 19:48:47 +03:00
outb ( 0x00 , dev - > io + 1 ) ;
mutex_unlock ( & dev - > lock ) ;
2006-04-08 23:06:16 +04:00
}
2005-04-17 02:20:36 +04:00
2009-03-06 19:48:47 +03:00
static void cadet_handler ( unsigned long data )
2005-04-17 02:20:36 +04:00
{
2009-03-06 19:48:47 +03:00
struct cadet * dev = ( void * ) data ;
2005-04-17 02:20:36 +04:00
2009-03-06 19:48:47 +03:00
/* Service the RDS fifo */
if ( mutex_trylock ( & dev - > lock ) ) {
outb ( 0x3 , dev - > io ) ; /* Select RDS Decoder Control */
if ( ( inb ( dev - > io + 1 ) & 0x20 ) ! = 0 )
2006-04-08 23:06:16 +04:00
printk ( KERN_CRIT " cadet: RDS fifo overflow \n " ) ;
2009-03-06 19:48:47 +03:00
outb ( 0x80 , dev - > io ) ; /* Select RDS fifo */
while ( ( inb ( dev - > io ) & 0x80 ) ! = 0 ) {
dev - > rdsbuf [ dev - > rdsin ] = inb ( dev - > io + 1 ) ;
if ( dev - > rdsin = = dev - > rdsout )
2006-04-08 23:06:16 +04:00
printk ( KERN_WARNING " cadet: RDS buffer overflow \n " ) ;
2005-04-17 02:20:36 +04:00
else
2009-03-06 19:48:47 +03:00
dev - > rdsin + + ;
2005-04-17 02:20:36 +04:00
}
2009-03-06 19:48:47 +03:00
mutex_unlock ( & dev - > lock ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Service pending read
*/
2009-03-06 19:48:47 +03:00
if ( dev - > rdsin ! = dev - > rdsout )
wake_up_interruptible ( & dev - > read_queue ) ;
2005-04-17 02:20:36 +04:00
2006-04-08 23:06:16 +04:00
/*
2005-04-17 02:20:36 +04:00
* Clean up and exit
*/
2009-03-06 19:48:47 +03:00
init_timer ( & dev - > readtimer ) ;
dev - > readtimer . function = cadet_handler ;
dev - > readtimer . data = ( unsigned long ) 0 ;
dev - > readtimer . expires = jiffies + msecs_to_jiffies ( 50 ) ;
add_timer ( & dev - > readtimer ) ;
2005-04-17 02:20:36 +04:00
}
2009-03-06 19:48:47 +03:00
static ssize_t cadet_read ( struct file * file , char __user * data , size_t count , loff_t * ppos )
2005-04-17 02:20:36 +04:00
{
2009-03-06 19:48:47 +03:00
struct cadet * dev = video_drvdata ( file ) ;
2005-04-17 02:20:36 +04:00
unsigned char readbuf [ RDS_BUFFER ] ;
2009-03-06 19:48:47 +03:00
int i = 0 ;
if ( dev - > rdsstat = = 0 ) {
mutex_lock ( & dev - > lock ) ;
dev - > rdsstat = 1 ;
outb ( 0x80 , dev - > io ) ; /* Select RDS fifo */
mutex_unlock ( & dev - > lock ) ;
init_timer ( & dev - > readtimer ) ;
dev - > readtimer . function = cadet_handler ;
dev - > readtimer . data = ( unsigned long ) dev ;
dev - > readtimer . expires = jiffies + msecs_to_jiffies ( 50 ) ;
add_timer ( & dev - > readtimer ) ;
2005-04-17 02:20:36 +04:00
}
2009-03-06 19:48:47 +03:00
if ( dev - > rdsin = = dev - > rdsout ) {
2006-04-08 23:06:16 +04:00
if ( file - > f_flags & O_NONBLOCK )
return - EWOULDBLOCK ;
2009-03-06 19:48:47 +03:00
interruptible_sleep_on ( & dev - > read_queue ) ;
2006-04-08 23:06:16 +04:00
}
2009-03-06 19:48:47 +03:00
while ( i < count & & dev - > rdsin ! = dev - > rdsout )
readbuf [ i + + ] = dev - > rdsbuf [ dev - > rdsout + + ] ;
2005-04-17 02:20:36 +04:00
2009-03-06 19:48:47 +03:00
if ( copy_to_user ( data , readbuf , i ) )
2006-04-08 23:06:16 +04:00
return - EFAULT ;
2005-04-17 02:20:36 +04:00
return i ;
}
2007-05-07 23:17:16 +04:00
static int vidioc_querycap ( struct file * file , void * priv ,
struct v4l2_capability * v )
{
2009-03-06 19:48:47 +03:00
strlcpy ( v - > driver , " ADS Cadet " , sizeof ( v - > driver ) ) ;
strlcpy ( v - > card , " ADS Cadet " , sizeof ( v - > card ) ) ;
strlcpy ( v - > bus_info , " ISA " , sizeof ( v - > bus_info ) ) ;
2007-05-07 23:17:16 +04:00
v - > version = CADET_VERSION ;
2009-06-20 13:19:09 +04:00
v - > capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO |
V4L2_CAP_READWRITE | V4L2_CAP_RDS_CAPTURE ;
2007-05-07 23:17:16 +04:00
return 0 ;
}
2005-04-17 02:20:36 +04:00
2007-05-07 23:17:16 +04:00
static int vidioc_g_tuner ( struct file * file , void * priv ,
struct v4l2_tuner * v )
2005-04-17 02:20:36 +04:00
{
2009-03-06 19:48:47 +03:00
struct cadet * dev = video_drvdata ( file ) ;
2007-05-07 23:17:16 +04:00
v - > type = V4L2_TUNER_RADIO ;
switch ( v - > index ) {
case 0 :
2009-03-06 19:48:47 +03:00
strlcpy ( v - > name , " FM " , sizeof ( v - > name ) ) ;
2009-06-20 13:19:09 +04:00
v - > capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS ;
2007-05-07 23:17:16 +04:00
v - > rangelow = 1400 ; /* 87.5 MHz */
v - > rangehigh = 1728 ; /* 108.0 MHz */
2009-03-06 19:48:47 +03:00
v - > rxsubchans = cadet_getstereo ( dev ) ;
switch ( v - > rxsubchans ) {
2007-05-07 23:17:16 +04:00
case V4L2_TUNER_SUB_MONO :
v - > audmode = V4L2_TUNER_MODE_MONO ;
break ;
case V4L2_TUNER_SUB_STEREO :
v - > audmode = V4L2_TUNER_MODE_STEREO ;
break ;
2009-03-06 19:48:47 +03:00
default :
break ;
2005-04-17 02:20:36 +04:00
}
2009-06-20 13:19:09 +04:00
v - > rxsubchans | = V4L2_TUNER_SUB_RDS ;
2007-05-07 23:17:16 +04:00
break ;
case 1 :
2009-03-06 19:48:47 +03:00
strlcpy ( v - > name , " AM " , sizeof ( v - > name ) ) ;
2007-05-07 23:17:16 +04:00
v - > capability = V4L2_TUNER_CAP_LOW ;
v - > rangelow = 8320 ; /* 520 kHz */
v - > rangehigh = 26400 ; /* 1650 kHz */
v - > rxsubchans = V4L2_TUNER_SUB_MONO ;
v - > audmode = V4L2_TUNER_MODE_MONO ;
break ;
default :
return - EINVAL ;
}
2009-03-06 19:48:47 +03:00
v - > signal = dev - > sigstrength ; /* We might need to modify scaling of this */
2007-05-07 23:17:16 +04:00
return 0 ;
}
2006-08-08 16:10:12 +04:00
2007-05-07 23:17:16 +04:00
static int vidioc_s_tuner ( struct file * file , void * priv ,
struct v4l2_tuner * v )
{
2009-03-06 19:48:47 +03:00
struct cadet * dev = video_drvdata ( file ) ;
if ( v - > index ! = 0 & & v - > index ! = 1 )
2007-05-07 23:17:16 +04:00
return - EINVAL ;
2009-03-06 19:48:47 +03:00
dev - > curtuner = v - > index ;
2007-05-07 23:17:16 +04:00
return 0 ;
}
2006-08-08 16:10:12 +04:00
2007-05-07 23:17:16 +04:00
static int vidioc_g_frequency ( struct file * file , void * priv ,
struct v4l2_frequency * f )
{
2009-03-06 19:48:47 +03:00
struct cadet * dev = video_drvdata ( file ) ;
f - > tuner = dev - > curtuner ;
2007-05-07 23:17:16 +04:00
f - > type = V4L2_TUNER_RADIO ;
2009-03-06 19:48:47 +03:00
f - > frequency = cadet_getfreq ( dev ) ;
2007-05-07 23:17:16 +04:00
return 0 ;
}
static int vidioc_s_frequency ( struct file * file , void * priv ,
struct v4l2_frequency * f )
{
2009-03-06 19:48:47 +03:00
struct cadet * dev = video_drvdata ( file ) ;
2007-05-07 23:17:16 +04:00
if ( f - > type ! = V4L2_TUNER_RADIO )
return - EINVAL ;
2009-03-06 19:48:47 +03:00
if ( dev - > curtuner = = 0 & & ( f - > frequency < 1400 | | f - > frequency > 1728 ) )
2007-05-07 23:17:16 +04:00
return - EINVAL ;
2009-03-06 19:48:47 +03:00
if ( dev - > curtuner = = 1 & & ( f - > frequency < 8320 | | f - > frequency > 26400 ) )
2007-05-07 23:17:16 +04:00
return - EINVAL ;
2009-03-06 19:48:47 +03:00
cadet_setfreq ( dev , f - > frequency ) ;
2007-05-07 23:17:16 +04:00
return 0 ;
}
static int vidioc_queryctrl ( struct file * file , void * priv ,
struct v4l2_queryctrl * qc )
{
2009-03-06 19:48:47 +03:00
switch ( qc - > id ) {
case V4L2_CID_AUDIO_MUTE :
return v4l2_ctrl_query_fill ( qc , 0 , 1 , 1 , 1 ) ;
case V4L2_CID_AUDIO_VOLUME :
return v4l2_ctrl_query_fill ( qc , 0 , 0xff , 1 , 0xff ) ;
2007-05-07 23:17:16 +04:00
}
return - EINVAL ;
}
2006-08-08 16:10:12 +04:00
2007-05-07 23:17:16 +04:00
static int vidioc_g_ctrl ( struct file * file , void * priv ,
struct v4l2_control * ctrl )
{
2009-03-06 19:48:47 +03:00
struct cadet * dev = video_drvdata ( file ) ;
switch ( ctrl - > id ) {
2007-05-07 23:17:16 +04:00
case V4L2_CID_AUDIO_MUTE : /* TODO: Handle this correctly */
2009-03-06 19:48:47 +03:00
ctrl - > value = ( cadet_getvol ( dev ) = = 0 ) ;
2007-05-07 23:17:16 +04:00
break ;
case V4L2_CID_AUDIO_VOLUME :
2009-03-06 19:48:47 +03:00
ctrl - > value = cadet_getvol ( dev ) ;
2007-05-07 23:17:16 +04:00
break ;
default :
return - EINVAL ;
2005-04-17 02:20:36 +04:00
}
2007-05-07 23:17:16 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2007-05-07 23:17:16 +04:00
static int vidioc_s_ctrl ( struct file * file , void * priv ,
struct v4l2_control * ctrl )
2005-04-17 02:20:36 +04:00
{
2009-03-06 19:48:47 +03:00
struct cadet * dev = video_drvdata ( file ) ;
2007-05-07 23:17:16 +04:00
switch ( ctrl - > id ) {
case V4L2_CID_AUDIO_MUTE : /* TODO: Handle this correctly */
if ( ctrl - > value )
2009-03-06 19:48:47 +03:00
cadet_setvol ( dev , 0 ) ;
2007-05-07 23:17:16 +04:00
else
2009-03-06 19:48:47 +03:00
cadet_setvol ( dev , 0xffff ) ;
2007-05-07 23:17:16 +04:00
break ;
case V4L2_CID_AUDIO_VOLUME :
2009-03-06 19:48:47 +03:00
cadet_setvol ( dev , ctrl - > value ) ;
2007-05-07 23:17:16 +04:00
break ;
default :
return - EINVAL ;
}
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 )
{
2009-03-06 19:48:47 +03:00
return i ? - EINVAL : 0 ;
}
static int vidioc_g_audio ( struct file * file , void * priv ,
struct v4l2_audio * a )
{
a - > index = 0 ;
strlcpy ( a - > name , " Radio " , sizeof ( a - > name ) ) ;
a - > capability = V4L2_AUDCAP_STEREO ;
2007-05-07 23:17:16 +04:00
return 0 ;
}
static int vidioc_s_audio ( struct file * file , void * priv ,
struct v4l2_audio * a )
{
2009-03-06 19:48:47 +03:00
return a - > index ? - EINVAL : 0 ;
2005-04-17 02:20:36 +04:00
}
2009-03-06 19:48:47 +03:00
static int cadet_open ( struct file * file )
2005-04-17 02:20:36 +04:00
{
2009-03-06 19:48:47 +03:00
struct cadet * dev = video_drvdata ( file ) ;
dev - > users + + ;
if ( 1 = = dev - > users )
init_waitqueue_head ( & dev - > read_queue ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2009-03-06 19:48:47 +03:00
static int cadet_release ( struct file * file )
2005-04-17 02:20:36 +04:00
{
2009-03-06 19:48:47 +03:00
struct cadet * dev = video_drvdata ( file ) ;
dev - > users - - ;
if ( 0 = = dev - > users ) {
del_timer_sync ( & dev - > readtimer ) ;
dev - > rdsstat = 0 ;
2006-08-08 16:10:12 +04:00
}
return 0 ;
}
2009-03-06 19:48:47 +03:00
static unsigned int cadet_poll ( struct file * file , struct poll_table_struct * wait )
2006-08-08 16:10:12 +04:00
{
2009-03-06 19:48:47 +03:00
struct cadet * dev = video_drvdata ( file ) ;
poll_wait ( file , & dev - > read_queue , wait ) ;
if ( dev - > rdsin ! = dev - > rdsout )
2006-08-08 16:10:12 +04:00
return POLLIN | POLLRDNORM ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2008-12-30 12:58:20 +03:00
static const struct v4l2_file_operations cadet_fops = {
2005-04-17 02:20:36 +04:00
. owner = THIS_MODULE ,
. open = cadet_open ,
. release = cadet_release ,
. read = cadet_read ,
2007-05-07 23:17:16 +04:00
. ioctl = video_ioctl2 ,
2006-08-08 16:10:12 +04:00
. poll = cadet_poll ,
2005-04-17 02:20:36 +04:00
} ;
2008-07-21 09:57:38 +04:00
static const struct v4l2_ioctl_ops cadet_ioctl_ops = {
2007-05-07 23:17:16 +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 ,
2005-04-17 02:20:36 +04:00
} ;
2008-04-01 04:21:48 +04:00
# ifdef CONFIG_PNP
2005-04-17 02:20:36 +04:00
static struct pnp_device_id cadet_pnp_devices [ ] = {
/* ADS Cadet AM/FM Radio Card */
{ . id = " MSM0c24 " , . driver_data = 0 } ,
{ . id = " " }
} ;
MODULE_DEVICE_TABLE ( pnp , cadet_pnp_devices ) ;
2009-03-06 19:48:47 +03:00
static int cadet_pnp_probe ( struct pnp_dev * dev , const struct pnp_device_id * dev_id )
2005-04-17 02:20:36 +04:00
{
if ( ! dev )
return - ENODEV ;
/* only support one device */
if ( io > 0 )
return - EBUSY ;
2009-03-06 19:48:47 +03:00
if ( ! pnp_port_valid ( dev , 0 ) )
2005-04-17 02:20:36 +04:00
return - ENODEV ;
io = pnp_port_start ( dev , 0 ) ;
2009-03-06 19:48:47 +03:00
printk ( KERN_INFO " radio-cadet: PnP reports device at %#x \n " , io ) ;
2005-04-17 02:20:36 +04:00
return io ;
}
static struct pnp_driver cadet_pnp_driver = {
. name = " radio-cadet " ,
. id_table = cadet_pnp_devices ,
. probe = cadet_pnp_probe ,
. remove = NULL ,
} ;
2008-04-01 04:21:48 +04:00
# else
static struct pnp_driver cadet_pnp_driver ;
# endif
2009-03-06 19:48:47 +03:00
static void cadet_probe ( struct cadet * dev )
2005-04-17 02:20:36 +04:00
{
2009-03-06 19:48:47 +03:00
static int iovals [ 8 ] = { 0x330 , 0x332 , 0x334 , 0x336 , 0x338 , 0x33a , 0x33c , 0x33e } ;
2005-04-17 02:20:36 +04:00
int i ;
2009-03-06 19:48:47 +03:00
for ( i = 0 ; i < 8 ; i + + ) {
dev - > io = iovals [ i ] ;
if ( request_region ( dev - > io , 2 , " cadet-probe " ) ) {
cadet_setfreq ( dev , 1410 ) ;
if ( cadet_getfreq ( dev ) = = 1410 ) {
release_region ( dev - > io , 2 ) ;
return ;
2005-04-17 02:20:36 +04:00
}
2009-03-06 19:48:47 +03:00
release_region ( dev - > io , 2 ) ;
2005-04-17 02:20:36 +04:00
}
}
2009-03-06 19:48:47 +03:00
dev - > io = - 1 ;
2005-04-17 02:20:36 +04:00
}
2006-04-08 23:06:16 +04:00
/*
2005-04-17 02:20:36 +04:00
* io should only be set if the user has used something like
* isapnp ( the userspace program ) to initialize this card for us
*/
static int __init cadet_init ( void )
{
2009-03-06 19:48:47 +03:00
struct cadet * dev = & cadet_card ;
struct v4l2_device * v4l2_dev = & dev - > v4l2_dev ;
int res ;
2006-04-08 23:06:16 +04:00
2009-03-06 19:48:47 +03:00
strlcpy ( v4l2_dev - > name , " cadet " , sizeof ( v4l2_dev - > name ) ) ;
mutex_init ( & dev - > lock ) ;
/* If a probe was requested then probe ISAPnP first (safest) */
2005-04-17 02:20:36 +04:00
if ( io < 0 )
pnp_register_driver ( & cadet_pnp_driver ) ;
2009-03-06 19:48:47 +03:00
dev - > io = io ;
2005-04-17 02:20:36 +04:00
2009-03-06 19:48:47 +03:00
/* If that fails then probe unsafely if probe is requested */
if ( dev - > io < 0 )
cadet_probe ( dev ) ;
2006-04-08 23:06:16 +04:00
2009-03-06 19:48:47 +03:00
/* Else we bail out */
if ( dev - > io < 0 ) {
2006-04-08 23:06:16 +04:00
# ifdef MODULE
2009-03-09 14:11:21 +03:00
v4l2_err ( v4l2_dev , " you must set an I/O address with io=0x330, 0x332, 0x334, \n " ) ;
v4l2_err ( v4l2_dev , " 0x336, 0x338, 0x33a, 0x33c or 0x33e \n " ) ;
2005-04-17 02:20:36 +04:00
# endif
2006-04-08 23:06:16 +04:00
goto fail ;
2005-04-17 02:20:36 +04:00
}
2009-03-06 19:48:47 +03:00
if ( ! request_region ( dev - > io , 2 , " cadet " ) )
goto fail ;
res = v4l2_device_register ( NULL , v4l2_dev ) ;
if ( res < 0 ) {
release_region ( dev - > io , 2 ) ;
v4l2_err ( v4l2_dev , " could not register v4l2_device \n " ) ;
2005-04-17 02:20:36 +04:00
goto fail ;
2009-03-06 19:48:47 +03:00
}
strlcpy ( dev - > vdev . name , v4l2_dev - > name , sizeof ( dev - > vdev . name ) ) ;
dev - > vdev . v4l2_dev = v4l2_dev ;
dev - > vdev . fops = & cadet_fops ;
dev - > vdev . ioctl_ops = & cadet_ioctl_ops ;
dev - > vdev . release = video_device_release_empty ;
video_set_drvdata ( & dev - > vdev , dev ) ;
if ( video_register_device ( & dev - > vdev , VFL_TYPE_RADIO , radio_nr ) < 0 ) {
v4l2_device_unregister ( v4l2_dev ) ;
release_region ( dev - > io , 2 ) ;
2005-04-17 02:20:36 +04:00
goto fail ;
}
2009-03-06 19:48:47 +03:00
v4l2_info ( v4l2_dev , " ADS Cadet Radio Card at 0x%x \n " , dev - > io ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
fail :
pnp_unregister_driver ( & cadet_pnp_driver ) ;
2009-03-06 19:48:47 +03:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
}
2009-03-06 19:48:47 +03:00
static void __exit cadet_exit ( void )
2005-04-17 02:20:36 +04:00
{
2009-03-06 19:48:47 +03:00
struct cadet * dev = & cadet_card ;
video_unregister_device ( & dev - > vdev ) ;
v4l2_device_unregister ( & dev - > v4l2_dev ) ;
release_region ( dev - > io , 2 ) ;
2005-04-17 02:20:36 +04:00
pnp_unregister_driver ( & cadet_pnp_driver ) ;
}
module_init ( cadet_init ) ;
2009-03-06 19:48:47 +03:00
module_exit ( cadet_exit ) ;
2005-04-17 02:20:36 +04:00