Hi,
it exist some macros to access the IER and ISR registers of the SAA7146. This
macros are using a read and a write
operation and this macros are executed inside of the interrupt handler of the
SAA7146 and outside of it. It exist a
reentrant problem. The interrupt handler may intercept the execution of such a
macro and may also access the IER and/or
ISR registers. The access to the IER and ISR register must be protect by a
locking primitive. The attached patch does
fix this problem.
The patch does not fix the stradis driver. This driver has the same problem.
- Hartmut
Signed-of-by: Hartmut Birr <[EMAIL PROTECTED]>
Protect the access to the IER/ISR register of the SAA7146 by the device spinlock.
diff -r 5e9d301ef13b linux/drivers/media/common/saa7146_core.c
--- a/linux/drivers/media/common/saa7146_core.c Thu Oct 26 10:10:56 2006 -0300
+++ b/linux/drivers/media/common/saa7146_core.c Fri Oct 27 22:03:45 2006 +0200
@@ -238,6 +238,7 @@ static irqreturn_t interrupt_hw(int irq,
{
struct saa7146_dev *dev = dev_id;
u32 isr = 0;
+ unsigned long flags;
/* read out the interrupt status register */
isr = saa7146_read(dev, ISR);
@@ -274,7 +275,9 @@ static irqreturn_t interrupt_hw(int irq,
if (0 != (isr & (MASK_16|MASK_17))) {
u32 status = saa7146_read(dev, I2C_STATUS);
if( (0x3 == (status & 0x3)) || (0 == (status & 0x1)) ) {
+ spin_lock_irqsave(&dev->slock, flags);
SAA7146_IER_DISABLE(dev, MASK_16|MASK_17);
+ spin_unlock_irqrestore(&dev->slock, flags);
/* only wake up if we expect something */
if( 0 != dev->i2c_op ) {
u32 psr = (saa7146_read(dev, PSR) >> 16) & 0x2;
@@ -293,7 +296,9 @@ static irqreturn_t interrupt_hw(int irq,
if( 0 != isr ) {
ERR(("warning: interrupt enabled, but not handled properly.(0x%08x)\n",isr));
ERR(("disabling interrupt source(s)!\n"));
+ spin_lock_irqsave(&dev->slock, flags);
SAA7146_IER_DISABLE(dev,isr);
+ spin_unlock_irqrestore(&dev->slock, flags);
}
return IRQ_HANDLED;
}
diff -r 5e9d301ef13b linux/drivers/media/common/saa7146_i2c.c
--- a/linux/drivers/media/common/saa7146_i2c.c Thu Oct 26 10:10:56 2006 -0300
+++ b/linux/drivers/media/common/saa7146_i2c.c Fri Oct 27 22:03:45 2006 +0200
@@ -180,6 +180,7 @@ static int saa7146_i2c_writeout(struct s
u32 status = 0, mc2 = 0;
int trial = 0;
unsigned long timeout;
+ unsigned long flags;
/* write out i2c-command */
DEB_I2C(("before: 0x%08x (status: 0x%08x), %d\n",*dword,saa7146_read(dev, I2C_STATUS), dev->i2c_op));
@@ -190,7 +191,9 @@ static int saa7146_i2c_writeout(struct s
saa7146_write(dev, I2C_TRANSFER, *dword);
dev->i2c_op = 1;
+ spin_lock_irqsave(&dev->slock, flags);
SAA7146_IER_ENABLE(dev, MASK_16|MASK_17);
+ spin_unlock_irqrestore(&dev->slock, flags);
saa7146_write(dev, MC2, (MASK_00 | MASK_16));
wait_event_interruptible(dev->i2c_wq, dev->i2c_op == 0);
diff -r 5e9d301ef13b linux/drivers/media/common/saa7146_vbi.c
--- a/linux/drivers/media/common/saa7146_vbi.c Thu Oct 26 10:10:56 2006 -0300
+++ b/linux/drivers/media/common/saa7146_vbi.c Fri Oct 27 22:03:45 2006 +0200
@@ -12,6 +12,7 @@ static int vbi_workaround(struct saa7146
int count = 0;
int i;
+ unsigned long flags;
DECLARE_WAITQUEUE(wait, current);
@@ -92,7 +93,9 @@ static int vbi_workaround(struct saa7146
saa7146_write(dev, MC2, MASK_04|MASK_20);
/* enable rps1 irqs */
+ spin_lock_irqsave(&dev->slock, flags);
SAA7146_IER_ENABLE(dev,MASK_28);
+ spin_unlock_irqrestore(&dev->slock, flags);
/* prepare to wait to be woken up by the irq-handler */
add_wait_queue(&vv->vbi_wq, &wait);
@@ -110,7 +113,9 @@ static int vbi_workaround(struct saa7146
current->state = TASK_RUNNING;
/* disable rps1 irqs */
+ spin_lock_irqsave(&dev->slock, flags);
SAA7146_IER_DISABLE(dev,MASK_28);
+ spin_unlock_irqrestore(&dev->slock, flags);
/* stop video-dma3 */
saa7146_write(dev, MC1, MASK_20);
@@ -140,6 +145,7 @@ static void saa7146_set_vbi_capture(stru
int count = 0;
unsigned long e_wait = vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_E_FID_A : CMD_E_FID_B;
unsigned long o_wait = vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_O_FID_A : CMD_O_FID_B;
+ unsigned long flags;
/*
vdma3.base_even = 0xc8000000+2560*70;
@@ -192,7 +198,9 @@ static void saa7146_set_vbi_capture(stru
WRITE_RPS1(CMD_STOP);
/* enable rps1 irqs */
+ spin_lock_irqsave(&dev->slock, flags);
SAA7146_IER_ENABLE(dev, MASK_28);
+ spin_unlock_irqrestore(&dev->slock, flags);
/* write the address of the rps-program */
saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle);
diff -r 5e9d301ef13b linux/drivers/media/common/saa7146_video.c
--- a/linux/drivers/media/common/saa7146_video.c Thu Oct 26 10:10:56 2006 -0300
+++ b/linux/drivers/media/common/saa7146_video.c Fri Oct 27 22:03:45 2006 +0200
@@ -717,6 +717,7 @@ static int video_begin(struct saa7146_fh
struct saa7146_format *fmt = NULL;
unsigned int resource;
int ret = 0, err = 0;
+ unsigned long flags;
DEB_EE(("dev:%p, fh:%p\n",dev,fh));
@@ -763,7 +764,9 @@ static int video_begin(struct saa7146_fh
saa7146_write(dev, MC2, MASK_27 );
/* enable rps0 irqs */
+ spin_lock_irqsave(&dev->slock, flags);
SAA7146_IER_ENABLE(dev, MASK_27);
+ spin_unlock_irqrestore(&dev->slock, flags);
vv->video_fh = fh;
vv->video_status = STATUS_CAPTURE;
diff -r 5e9d301ef13b linux/drivers/media/dvb/ttpci/av7110.c
--- a/linux/drivers/media/dvb/ttpci/av7110.c Thu Oct 26 10:10:56 2006 -0300
+++ b/linux/drivers/media/dvb/ttpci/av7110.c Fri Oct 27 22:03:45 2006 +0200
@@ -349,14 +349,16 @@ static inline void start_debi_dma(struct
static inline void start_debi_dma(struct av7110 *av7110, int dir,
unsigned long addr, unsigned int len)
{
+ unsigned long flags;
dprintk(8, "%c %08lx %u\n", dir == DEBI_READ ? 'R' : 'W', addr, len);
if (saa7146_wait_for_debi_done(av7110->dev, 0)) {
printk(KERN_ERR "%s: saa7146_wait_for_debi_done timed out\n", __FUNCTION__);
return;
}
-
+ spin_lock_irqsave(&av7110->dev->slock, flags);
SAA7146_ISR_CLEAR(av7110->dev, MASK_19); /* for good measure */
SAA7146_IER_ENABLE(av7110->dev, MASK_19);
+ spin_unlock_irqrestore(&av7110->dev->slock, flags);
if (len < 5)
len = 5; /* we want a real DEBI DMA */
if (dir == DEBI_WRITE)
@@ -1170,18 +1172,22 @@ static int av7110_diseqc_send_burst(stru
/* simplified code from budget-core.c */
static int stop_ts_capture(struct av7110 *budget)
{
+ unsigned long flags;
dprintk(2, "budget: %p\n", budget);
if (--budget->feeding1)
return budget->feeding1;
saa7146_write(budget->dev, MC1, MASK_20); /* DMA3 off */
+ spin_lock_irqsave(&budget->dev->slock, flags);
SAA7146_IER_DISABLE(budget->dev, MASK_10);
SAA7146_ISR_CLEAR(budget->dev, MASK_10);
+ spin_unlock_irqrestore(&budget->dev->slock, flags);
return 0;
}
static int start_ts_capture(struct av7110 *budget)
{
+ unsigned long flags;
dprintk(2, "budget: %p\n", budget);
if (budget->feeding1)
@@ -1189,7 +1195,9 @@ static int start_ts_capture(struct av711
memset(budget->grabbing, 0x00, TS_HEIGHT * TS_WIDTH);
budget->tsf = 0xff;
budget->ttbp = 0;
+ spin_lock_irqsave(&budget->dev->slock, flags);
SAA7146_IER_ENABLE(budget->dev, MASK_10); /* VPE */
+ spin_unlock_irqrestore(&budget->dev->slock, flags);
saa7146_write(budget->dev, MC1, (MASK_04 | MASK_20)); /* DMA3 on */
return ++budget->feeding1;
}
@@ -2689,6 +2697,7 @@ static int __devexit av7110_detach(struc
static int __devexit av7110_detach(struct saa7146_dev* saa)
{
struct av7110 *av7110 = saa->ext_priv;
+ unsigned long flags;
dprintk(4, "%p\n", av7110);
#if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE)
@@ -2700,8 +2709,10 @@ static int __devexit av7110_detach(struc
/* VSYNC LOW (inactive) */
saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO);
saa7146_write(saa, MC1, MASK_20); /* DMA3 off */
+ spin_lock_irqsave(&saa->slock, flags);
SAA7146_IER_DISABLE(saa, MASK_10);
SAA7146_ISR_CLEAR(saa, MASK_10);
+ spin_unlock_irqrestore(&saa->slock, flags);
msleep(50);
tasklet_kill(&av7110->vpe_tasklet);
saa7146_pgtable_free(saa->pci, &av7110->pt);
@@ -2715,8 +2726,10 @@ static int __devexit av7110_detach(struc
dvb_unregister(av7110);
+ spin_lock_irqsave(&saa->slock, flags);
SAA7146_IER_DISABLE(saa, MASK_19 | MASK_03);
SAA7146_ISR_CLEAR(saa, MASK_19 | MASK_03);
+ spin_unlock_irqrestore(&saa->slock, flags);
av7110_ca_exit(av7110);
av7110_av_exit(av7110);
@@ -2744,6 +2757,7 @@ static void av7110_irq(struct saa7146_de
static void av7110_irq(struct saa7146_dev* dev, u32 *isr)
{
struct av7110 *av7110 = dev->ext_priv;
+ unsigned long flags;
//print_time("av7110_irq");
@@ -2768,8 +2782,10 @@ static void av7110_irq(struct saa7146_de
* (like the gpio irqs sadly are) temporarily we would likely
* loose some. This sucks :-(
*/
+ spin_lock_irqsave(&av7110->dev->slock, flags);
SAA7146_IER_DISABLE(av7110->dev, MASK_19);
SAA7146_ISR_CLEAR(av7110->dev, MASK_19);
+ spin_unlock_irqrestore(&av7110->dev->slock, flags);
tasklet_schedule(&av7110->debi_tasklet);
}
diff -r 5e9d301ef13b linux/drivers/media/dvb/ttpci/av7110_hw.c
--- a/linux/drivers/media/dvb/ttpci/av7110_hw.c Thu Oct 26 10:10:56 2006 -0300
+++ b/linux/drivers/media/dvb/ttpci/av7110_hw.c Fri Oct 27 22:03:45 2006 +0200
@@ -108,19 +108,25 @@ u32 av7110_debiread(struct av7110 *av711
#if 0 /* keep */
void av7110_reset_arm(struct av7110 *av7110)
{
+ unsigned long flags;
+
saa7146_setgpio(av7110->dev, RESET_LINE, SAA7146_GPIO_OUTLO);
/* Disable DEBI and GPIO irq */
+ spin_lock_irqsave(&av7110->dev->slock, flags);
SAA7146_IER_DISABLE(av7110->dev, MASK_19 | MASK_03);
SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03);
+ spin_unlock_irqrestore(&av7110->dev->slock, flags);
saa7146_setgpio(av7110->dev, RESET_LINE, SAA7146_GPIO_OUTHI);
msleep(30); /* the firmware needs some time to initialize */
ARM_ResetMailBox(av7110);
+ spin_lock_irqsave(&av7110->dev->slock, flags);
SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03);
SAA7146_IER_ENABLE(av7110->dev, MASK_03);
+ spin_unlock_irqrestore(&av7110->dev->slock, flags);
av7110->arm_ready = 1;
dprintk(1, "reset ARM\n");
@@ -226,6 +232,7 @@ int av7110_bootarm(struct av7110 *av7110
struct saa7146_dev *dev = av7110->dev;
u32 ret;
int i;
+ unsigned long flags;
dprintk(4, "%p\n", av7110);
@@ -234,8 +241,10 @@ int av7110_bootarm(struct av7110 *av7110
saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTLO);
/* Disable DEBI and GPIO irq */
+ spin_lock_irqsave(&dev->slock, flags);
SAA7146_IER_DISABLE(av7110->dev, MASK_03 | MASK_19);
SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03);
+ spin_unlock_irqrestore(&dev->slock, flags);
/* enable DEBI */
saa7146_write(av7110->dev, MC1, 0x08800880);
@@ -297,8 +306,10 @@ int av7110_bootarm(struct av7110 *av7110
//ARM_ClearIrq(av7110);
ARM_ResetMailBox(av7110);
+ spin_lock_irqsave(&dev->slock, flags);
SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03);
SAA7146_IER_ENABLE(av7110->dev, MASK_03);
+ spin_unlock_irqrestore(&dev->slock, flags);
av7110->arm_errors = 0;
av7110->arm_ready = 1;
diff -r 5e9d301ef13b linux/drivers/media/dvb/ttpci/budget-core.c
--- a/linux/drivers/media/dvb/ttpci/budget-core.c Thu Oct 26 10:10:56 2006 -0300
+++ b/linux/drivers/media/dvb/ttpci/budget-core.c Fri Oct 27 22:03:45 2006 +0200
@@ -61,16 +61,20 @@ MODULE_PARM_DESC(bufsize, "DMA buffer si
static int stop_ts_capture(struct budget *budget)
{
+ unsigned long flags;
dprintk(2, "budget: %p\n", budget);
saa7146_write(budget->dev, MC1, MASK_20); // DMA3 off
+ spin_lock_irqsave(&budget->dev->slock, flags);
SAA7146_IER_DISABLE(budget->dev, MASK_10);
+ spin_unlock_irqrestore(&budget->dev->slock, flags);
return 0;
}
static int start_ts_capture(struct budget *budget)
{
struct saa7146_dev *dev = budget->dev;
+ unsigned long flags;
dprintk(2, "budget: %p\n", budget);
@@ -132,8 +136,10 @@ static int start_ts_capture(struct budge
saa7146_write(dev, MC2, (MASK_04 | MASK_20));
- SAA7146_ISR_CLEAR(budget->dev, MASK_10); /* VPE */
- SAA7146_IER_ENABLE(budget->dev, MASK_10); /* VPE */
+ spin_lock_irqsave(&dev->slock, flags);
+ SAA7146_ISR_CLEAR(dev, MASK_10); /* VPE */
+ SAA7146_IER_ENABLE(dev, MASK_10); /* VPE */
+ spin_unlock_irqrestore(&dev->slock, flags);
saa7146_write(dev, MC1, (MASK_04 | MASK_20)); /* DMA3 on */
return 0;
_______________________________________________
linux-dvb mailing list
[email protected]
http://www.linuxtv.org/cgi-bin/mailman/listinfo/linux-dvb