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 <e9hack@xxxxxxxxxxxxxx> 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 linux-dvb@xxxxxxxxxxx http://www.linuxtv.org/cgi-bin/mailman/listinfo/linux-dvb