Commit 1a76fede authored by Alan Cox's avatar Alan Cox Committed by Linus Torvalds

[PATCH] fix radio_cadet driver locking

Forward port the replacement to the horribly broken locking in 2.5
radio_cadet driver.
parent 53efd89f
...@@ -23,7 +23,9 @@ ...@@ -23,7 +23,9 @@
* 2002-01-17 Adam Belay <ambx1@neo.rr.com> * 2002-01-17 Adam Belay <ambx1@neo.rr.com>
* Updated to latest pnp code * Updated to latest pnp code
* *
*/ * 2003-01-31 Alan Cox <alan@redhat.com>
* Cleaned up locking, delay code, general odds and ends
*/
#include <linux/module.h> /* Modules */ #include <linux/module.h> /* Modules */
#include <linux/init.h> /* Initdata */ #include <linux/init.h> /* Initdata */
...@@ -43,11 +45,11 @@ static int users=0; ...@@ -43,11 +45,11 @@ static int users=0;
static int curtuner=0; static int curtuner=0;
static int tunestat=0; static int tunestat=0;
static int sigstrength=0; static int sigstrength=0;
static wait_queue_head_t tunerq,rdsq,readq; static wait_queue_head_t readq;
struct timer_list tunertimer,rdstimer,readtimer; struct timer_list tunertimer,rdstimer,readtimer;
static __u8 rdsin=0,rdsout=0,rdsstat=0; static __u8 rdsin=0,rdsout=0,rdsstat=0;
static unsigned char rdsbuf[RDS_BUFFER]; static unsigned char rdsbuf[RDS_BUFFER];
static int cadet_lock=0; static spinlock_t cadet_io_lock;
static int cadet_probe(void); static int cadet_probe(void);
...@@ -58,37 +60,19 @@ static int cadet_probe(void); ...@@ -58,37 +60,19 @@ static int cadet_probe(void);
*/ */
static __u16 sigtable[2][4]={{5,10,30,150},{28,40,63,1000}}; static __u16 sigtable[2][4]={{5,10,30,150},{28,40,63,1000}};
static void cadet_wake(unsigned long qnum)
{
switch(qnum) {
case 0: /* cadet_setfreq */
wake_up(&tunerq);
break;
case 1: /* cadet_getrds */
wake_up(&rdsq);
break;
}
}
static int cadet_getrds(void) static int cadet_getrds(void)
{ {
int rdsstat=0; int rdsstat=0;
cadet_lock++; spin_lock(&cadet_io_lock);
outb(3,io); /* Select Decoder Control/Status */ outb(3,io); /* Select Decoder Control/Status */
outb(inb(io+1)&0x7f,io+1); /* Reset RDS detection */ outb(inb(io+1)&0x7f,io+1); /* Reset RDS detection */
cadet_lock--; spin_unlock(&cadet_io_lock);
init_timer(&rdstimer);
rdstimer.function=cadet_wake; set_current_state(TASK_UNINTERRUPTIBLE);
rdstimer.data=(unsigned long)1; schedule_timeout(HZ/10);
rdstimer.expires=jiffies+(HZ/10);
init_waitqueue_head(&rdsq); spin_lock(&cadet_io_lock);
add_timer(&rdstimer);
sleep_on(&rdsq);
cadet_lock++;
outb(3,io); /* Select Decoder Control/Status */ outb(3,io); /* Select Decoder Control/Status */
if((inb(io+1)&0x80)!=0) { if((inb(io+1)&0x80)!=0) {
rdsstat|=VIDEO_TUNER_RDS_ON; rdsstat|=VIDEO_TUNER_RDS_ON;
...@@ -96,32 +80,24 @@ static int cadet_getrds(void) ...@@ -96,32 +80,24 @@ static int cadet_getrds(void)
if((inb(io+1)&0x10)!=0) { if((inb(io+1)&0x10)!=0) {
rdsstat|=VIDEO_TUNER_MBS_ON; rdsstat|=VIDEO_TUNER_MBS_ON;
} }
cadet_lock--; spin_unlock(&cadet_io_lock);
return rdsstat; return rdsstat;
} }
static int cadet_getstereo(void) static int cadet_getstereo(void)
{ {
if(curtuner!=0) { /* Only FM has stereo capability! */ int ret = 0;
if(curtuner != 0) /* Only FM has stereo capability! */
return 0; return 0;
}
cadet_lock++; spin_lock(&cadet_io_lock);
outb(7,io); /* Select tuner control */ outb(7,io); /* Select tuner control */
if((inb(io+1)&0x40)==0) { if( (inb(io+1) & 0x40) == 0)
cadet_lock--; ret = 1;
return 1; /* Stereo pilot detected */ spin_unlock(&cadet_io_lock);
} return ret;
else {
cadet_lock--;
return 0; /* Mono */
}
} }
static unsigned cadet_gettune(void) static unsigned cadet_gettune(void)
{ {
int curvol,i; int curvol,i;
...@@ -130,7 +106,9 @@ static unsigned cadet_gettune(void) ...@@ -130,7 +106,9 @@ static unsigned cadet_gettune(void)
/* /*
* Prepare for read * Prepare for read
*/ */
cadet_lock++;
spin_lock(&cadet_io_lock);
outb(7,io); /* Select tuner control */ outb(7,io); /* Select tuner control */
curvol=inb(io+1); /* Save current volume/mute setting */ curvol=inb(io+1); /* Save current volume/mute setting */
outb(0x00,io+1); /* Ensure WRITE-ENABLE is LOW */ outb(0x00,io+1); /* Ensure WRITE-ENABLE is LOW */
...@@ -152,13 +130,11 @@ static unsigned cadet_gettune(void) ...@@ -152,13 +130,11 @@ static unsigned cadet_gettune(void)
* Restore volume/mute setting * Restore volume/mute setting
*/ */
outb(curvol,io+1); outb(curvol,io+1);
cadet_lock--; spin_unlock(&cadet_io_lock);
return fifo; return fifo;
} }
static unsigned cadet_getfreq(void) static unsigned cadet_getfreq(void)
{ {
int i; int i;
...@@ -191,14 +167,13 @@ static unsigned cadet_getfreq(void) ...@@ -191,14 +167,13 @@ static unsigned cadet_getfreq(void)
return freq; return freq;
} }
static void cadet_settune(unsigned fifo) static void cadet_settune(unsigned fifo)
{ {
int i; int i;
unsigned test; unsigned test;
cadet_lock++; spin_lock(&cadet_io_lock);
outb(7,io); /* Select tuner control */ outb(7,io); /* Select tuner control */
/* /*
* Write the shift register * Write the shift register
...@@ -217,11 +192,9 @@ static void cadet_settune(unsigned fifo) ...@@ -217,11 +192,9 @@ static void cadet_settune(unsigned fifo)
test=0x1c|((fifo>>23)&0x02); test=0x1c|((fifo>>23)&0x02);
outb(test,io+1); outb(test,io+1);
} }
cadet_lock--; spin_unlock(&cadet_io_lock);
} }
static void cadet_setfreq(unsigned freq) static void cadet_setfreq(unsigned freq)
{ {
unsigned fifo; unsigned fifo;
...@@ -253,92 +226,90 @@ static void cadet_setfreq(unsigned freq) ...@@ -253,92 +226,90 @@ static void cadet_setfreq(unsigned freq)
/* /*
* Save current volume/mute setting * Save current volume/mute setting
*/ */
cadet_lock++;
spin_lock(&cadet_io_lock);
outb(7,io); /* Select tuner control */ outb(7,io); /* Select tuner control */
curvol=inb(io+1); curvol=inb(io+1);
spin_unlock(&cadet_io_lock);
/* /*
* Tune the card * Tune the card
*/ */
for(j=3;j>-1;j--) { for(j=3;j>-1;j--) {
cadet_settune(fifo|(j<<16)); cadet_settune(fifo|(j<<16));
spin_lock(&cadet_io_lock);
outb(7,io); /* Select tuner control */ outb(7,io); /* Select tuner control */
outb(curvol,io+1); outb(curvol,io+1);
cadet_lock--; spin_unlock(&cadet_io_lock);
init_timer(&tunertimer);
tunertimer.function=cadet_wake; set_current_state(TASK_UNINTERRUPTIBLE);
tunertimer.data=(unsigned long)0; schedule_timeout(HZ/10);
tunertimer.expires=jiffies+(HZ/10);
init_waitqueue_head(&tunerq);
add_timer(&tunertimer);
sleep_on(&tunerq);
cadet_gettune(); cadet_gettune();
if((tunestat&0x40)==0) { /* Tuned */ if((tunestat & 0x40) == 0) { /* Tuned */
sigstrength=sigtable[curtuner][j]; sigstrength=sigtable[curtuner][j];
return; return;
} }
cadet_lock++;
} }
cadet_lock--;
sigstrength=0; sigstrength=0;
} }
static int cadet_getvol(void) static int cadet_getvol(void)
{ {
cadet_lock++; int ret = 0;
spin_lock(&cadet_io_lock);
outb(7,io); /* Select tuner control */ outb(7,io); /* Select tuner control */
if((inb(io+1)&0x20)!=0) { if((inb(io + 1) & 0x20) != 0)
cadet_lock--; ret = 0xffff;
return 0xffff;
} spin_unlock(&cadet_io_lock);
else { return ret;
cadet_lock--;
return 0;
}
} }
static void cadet_setvol(int vol) static void cadet_setvol(int vol)
{ {
cadet_lock++; spin_lock(&cadet_io_lock);
outb(7,io); /* Select tuner control */ outb(7,io); /* Select tuner control */
if(vol>0) { if(vol>0)
outb(0x20,io+1); outb(0x20,io+1);
} else
else {
outb(0x00,io+1); outb(0x00,io+1);
} spin_unlock(&cadet_io_lock);
cadet_lock--;
} }
void cadet_handler(unsigned long data) void cadet_handler(unsigned long data)
{ {
/* /*
* Service the RDS fifo * Service the RDS fifo
*/ */
if(cadet_lock==0) {
if(spin_trylock(&cadet_io_lock))
{
outb(0x3,io); /* Select RDS Decoder Control */ outb(0x3,io); /* Select RDS Decoder Control */
if((inb(io+1)&0x20)!=0) { if((inb(io+1)&0x20)!=0) {
printk(KERN_CRIT "cadet: RDS fifo overflow\n"); printk(KERN_CRIT "cadet: RDS fifo overflow\n");
} }
outb(0x80,io); /* Select RDS fifo */ outb(0x80,io); /* Select RDS fifo */
while((inb(io)&0x80)!=0) { while((inb(io)&0x80)!=0) {
rdsbuf[rdsin++]=inb(io+1); rdsbuf[rdsin]=inb(io+1);
if(rdsin==rdsout) { if(rdsin==rdsout)
printk(KERN_CRIT "cadet: RDS buffer overflow\n"); printk(KERN_WARNING "cadet: RDS buffer overflow\n");
} else
rdsin++;
} }
spin_unlock(&cadet_io_lock);
} }
/* /*
* Service pending read * Service pending read
*/ */
if( rdsin!=rdsout) { if( rdsin!=rdsout)
wake_up_interruptible(&readq); wake_up_interruptible(&readq);
}
/* /*
* Clean up and exit * Clean up and exit
...@@ -359,10 +330,10 @@ static ssize_t cadet_read(struct file *file, char *data, ...@@ -359,10 +330,10 @@ static ssize_t cadet_read(struct file *file, char *data,
unsigned char readbuf[RDS_BUFFER]; unsigned char readbuf[RDS_BUFFER];
if(rdsstat==0) { if(rdsstat==0) {
cadet_lock++; spin_lock(&cadet_io_lock);
rdsstat=1; rdsstat=1;
outb(0x80,io); /* Select RDS fifo */ outb(0x80,io); /* Select RDS fifo */
cadet_lock--; spin_unlock(&cadet_io_lock);
init_timer(&readtimer); init_timer(&readtimer);
readtimer.function=cadet_handler; readtimer.function=cadet_handler;
readtimer.data=(unsigned long)0; readtimer.data=(unsigned long)0;
...@@ -370,14 +341,13 @@ static ssize_t cadet_read(struct file *file, char *data, ...@@ -370,14 +341,13 @@ static ssize_t cadet_read(struct file *file, char *data,
add_timer(&readtimer); add_timer(&readtimer);
} }
if(rdsin==rdsout) { if(rdsin==rdsout) {
if (file->f_flags & O_NONBLOCK) { if (file->f_flags & O_NONBLOCK)
return -EWOULDBLOCK; return -EWOULDBLOCK;
}
interruptible_sleep_on(&readq); interruptible_sleep_on(&readq);
} }
while((i<count)&&(rdsin!=rdsout)) { while( i<count && rdsin!=rdsout)
readbuf[i++]=rdsbuf[rdsout++]; readbuf[i++]=rdsbuf[rdsout++];
}
if (copy_to_user(data,readbuf,i)) if (copy_to_user(data,readbuf,i))
return -EFAULT; return -EFAULT;
return i; return i;
...@@ -515,10 +485,8 @@ static int cadet_open(struct inode *inode, struct file *file) ...@@ -515,10 +485,8 @@ static int cadet_open(struct inode *inode, struct file *file)
static int cadet_release(struct inode *inode, struct file *file) static int cadet_release(struct inode *inode, struct file *file)
{ {
if(rdsstat==1) { del_timer_sync(&readtimer);
del_timer(&readtimer);
rdsstat=0; rdsstat=0;
}
users--; users--;
return 0; return 0;
} }
...@@ -595,13 +563,15 @@ static int cadet_probe(void) ...@@ -595,13 +563,15 @@ static int cadet_probe(void)
return -1; return -1;
} }
/* /*
* io should only be set if the user has used something like * io should only be set if the user has used something like
* isapnp (the userspace program) to initialize this card for us * isapnp (the userspace program) to initialize this card for us
*/ */
static int __init cadet_init(void) static int __init cadet_init(void)
{ {
spin_lock_init(&cadet_io_lock);
/* /*
* If a probe was requested then probe ISAPnP first (safest) * If a probe was requested then probe ISAPnP first (safest)
*/ */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment