aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/cx88
diff options
context:
space:
mode:
authorJean Delvare <khali@linux-fr.org>2009-03-07 05:42:12 -0500
committerMauro Carvalho Chehab <mchehab@redhat.com>2009-03-30 11:43:17 -0400
commit569b7ec73abf576f9a9e4070d213aadf2cce73cb (patch)
treeb84ef4286f669db0e072745f0ceb99bd619035b2 /drivers/media/video/cx88
parentfb6991d417a9f06978119ea597dc5955b3eb784d (diff)
V4L/DVB (10943): cx88: Prevent general protection fault on rmmod
When unloading the cx8800 driver I sometimes get a general protection fault. Analysis revealed a race in cx88_ir_stop(). It can be solved by using a delayed work instead of a timer for infrared input polling. Signed-off-by: Jean Delvare <khali@linux-fr.org> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/video/cx88')
-rw-r--r--drivers/media/video/cx88/cx88-input.c25
1 files changed, 7 insertions, 18 deletions
diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c
index ad0842136d8..ec05312a9b6 100644
--- a/drivers/media/video/cx88/cx88-input.c
+++ b/drivers/media/video/cx88/cx88-input.c
@@ -48,8 +48,7 @@ struct cx88_IR {
48 48
49 /* poll external decoder */ 49 /* poll external decoder */
50 int polling; 50 int polling;
51 struct work_struct work; 51 struct delayed_work work;
52 struct timer_list timer;
53 u32 gpio_addr; 52 u32 gpio_addr;
54 u32 last_gpio; 53 u32 last_gpio;
55 u32 mask_keycode; 54 u32 mask_keycode;
@@ -143,27 +142,19 @@ static void cx88_ir_handle_key(struct cx88_IR *ir)
143 } 142 }
144} 143}
145 144
146static void ir_timer(unsigned long data)
147{
148 struct cx88_IR *ir = (struct cx88_IR *)data;
149
150 schedule_work(&ir->work);
151}
152
153static void cx88_ir_work(struct work_struct *work) 145static void cx88_ir_work(struct work_struct *work)
154{ 146{
155 struct cx88_IR *ir = container_of(work, struct cx88_IR, work); 147 struct cx88_IR *ir = container_of(work, struct cx88_IR, work.work);
156 148
157 cx88_ir_handle_key(ir); 149 cx88_ir_handle_key(ir);
158 mod_timer(&ir->timer, jiffies + msecs_to_jiffies(ir->polling)); 150 schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling));
159} 151}
160 152
161void cx88_ir_start(struct cx88_core *core, struct cx88_IR *ir) 153void cx88_ir_start(struct cx88_core *core, struct cx88_IR *ir)
162{ 154{
163 if (ir->polling) { 155 if (ir->polling) {
164 setup_timer(&ir->timer, ir_timer, (unsigned long)ir); 156 INIT_DELAYED_WORK(&ir->work, cx88_ir_work);
165 INIT_WORK(&ir->work, cx88_ir_work); 157 schedule_delayed_work(&ir->work, 0);
166 schedule_work(&ir->work);
167 } 158 }
168 if (ir->sampling) { 159 if (ir->sampling) {
169 core->pci_irqmask |= PCI_INT_IR_SMPINT; 160 core->pci_irqmask |= PCI_INT_IR_SMPINT;
@@ -179,10 +170,8 @@ void cx88_ir_stop(struct cx88_core *core, struct cx88_IR *ir)
179 core->pci_irqmask &= ~PCI_INT_IR_SMPINT; 170 core->pci_irqmask &= ~PCI_INT_IR_SMPINT;
180 } 171 }
181 172
182 if (ir->polling) { 173 if (ir->polling)
183 del_timer_sync(&ir->timer); 174 cancel_delayed_work_sync(&ir->work);
184 flush_scheduled_work();
185 }
186} 175}
187 176
188/* ---------------------------------------------------------------------- */ 177/* ---------------------------------------------------------------------- */