diff options
author | Jean Delvare <khali@linux-fr.org> | 2009-03-07 05:43:43 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2009-03-30 11:43:17 -0400 |
commit | c1089bdc07f06b90f0bc50d0789c2a4833097df7 (patch) | |
tree | 61e9ffa2bf7d1f7231730edef9751ab45c0ca1ba /drivers/media/video | |
parent | f263bac9f7181df80b732b7bc11a7a4e38ca962f (diff) |
V4L/DVB (10939): ir-kbd-i2c: Prevent general protection fault on rmmod
The removal of the timer which polls the infrared input is racy.
Replacing the timer with a delayed work solves the problem.
Signed-off-by: Jean Delvare <khali@linux-fr.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/video')
-rw-r--r-- | drivers/media/video/ir-kbd-i2c.c | 20 |
1 files changed, 5 insertions, 15 deletions
diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c index 2ee49b7ddcf0..092c7da0f37a 100644 --- a/drivers/media/video/ir-kbd-i2c.c +++ b/drivers/media/video/ir-kbd-i2c.c | |||
@@ -279,15 +279,9 @@ static void ir_key_poll(struct IR_i2c *ir) | |||
279 | } | 279 | } |
280 | } | 280 | } |
281 | 281 | ||
282 | static void ir_timer(unsigned long data) | ||
283 | { | ||
284 | struct IR_i2c *ir = (struct IR_i2c*)data; | ||
285 | schedule_work(&ir->work); | ||
286 | } | ||
287 | |||
288 | static void ir_work(struct work_struct *work) | 282 | static void ir_work(struct work_struct *work) |
289 | { | 283 | { |
290 | struct IR_i2c *ir = container_of(work, struct IR_i2c, work); | 284 | struct IR_i2c *ir = container_of(work, struct IR_i2c, work.work); |
291 | int polling_interval = 100; | 285 | int polling_interval = 100; |
292 | 286 | ||
293 | /* MSI TV@nywhere Plus requires more frequent polling | 287 | /* MSI TV@nywhere Plus requires more frequent polling |
@@ -296,7 +290,7 @@ static void ir_work(struct work_struct *work) | |||
296 | polling_interval = 50; | 290 | polling_interval = 50; |
297 | 291 | ||
298 | ir_key_poll(ir); | 292 | ir_key_poll(ir); |
299 | mod_timer(&ir->timer, jiffies + msecs_to_jiffies(polling_interval)); | 293 | schedule_delayed_work(&ir->work, msecs_to_jiffies(polling_interval)); |
300 | } | 294 | } |
301 | 295 | ||
302 | /* ----------------------------------------------------------------------- */ | 296 | /* ----------------------------------------------------------------------- */ |
@@ -452,11 +446,8 @@ static int ir_attach(struct i2c_adapter *adap, int addr, | |||
452 | ir->input->name, ir->input->phys, adap->name); | 446 | ir->input->name, ir->input->phys, adap->name); |
453 | 447 | ||
454 | /* start polling via eventd */ | 448 | /* start polling via eventd */ |
455 | INIT_WORK(&ir->work, ir_work); | 449 | INIT_DELAYED_WORK(&ir->work, ir_work); |
456 | init_timer(&ir->timer); | 450 | schedule_delayed_work(&ir->work, 0); |
457 | ir->timer.function = ir_timer; | ||
458 | ir->timer.data = (unsigned long)ir; | ||
459 | schedule_work(&ir->work); | ||
460 | 451 | ||
461 | return 0; | 452 | return 0; |
462 | 453 | ||
@@ -473,8 +464,7 @@ static int ir_detach(struct i2c_client *client) | |||
473 | struct IR_i2c *ir = i2c_get_clientdata(client); | 464 | struct IR_i2c *ir = i2c_get_clientdata(client); |
474 | 465 | ||
475 | /* kill outstanding polls */ | 466 | /* kill outstanding polls */ |
476 | del_timer_sync(&ir->timer); | 467 | cancel_delayed_work_sync(&ir->work); |
477 | flush_scheduled_work(); | ||
478 | 468 | ||
479 | /* unregister devices */ | 469 | /* unregister devices */ |
480 | input_unregister_device(ir->input); | 470 | input_unregister_device(ir->input); |