[PATCH 1/3] Fix a problem during the access to the IER and ISR registers of the SA7146

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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

[Index of Archives]     [Linux Media]     [Video 4 Linux]     [Asterisk]     [Samba]     [Xorg]     [Xfree86]     [Linux USB]

  Powered by Linux