aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnssi Hannula <anssi.hannula@gmail.com>2008-10-04 08:44:06 -0400
committerJiri Kosina <jkosina@suse.cz>2008-10-14 17:51:02 -0400
commitf129ea6d1efe0eddcbb1f0faaec5623788ad9e58 (patch)
tree4a2b72a2f98b19efeb5e28837ed1bfacffbbc961
parentdded364bf4e1f0de67d7d7b9e77c06b23a9f081f (diff)
HID: fix a lockup regression when using force feedback on a PID device
Commit 8006479c9b75fb6594a7b746af3d7f1fbb68f18f introduced a spinlock in input_dev->event_lock, which is locked when handling input events. However, the hid-pidff driver sleeps when handling events as it waits for reports being sent to the device before changing the report contents again. This causes a system lockup when trying to use force feedback with a PID device, a regression introduced in 2.6.24 and 2.6.23.15. Fix it by extracting the raw report data from struct hid_report immediately when hid_submit_report() is called, therefore allowing drivers to change the contents of struct hid_report immediately without affecting the already-queued transfer. In hid-pidff, re-add the removed usbhid_wait_io() to pidff_erase_effect() instead, to prevent a full report queue from causing the submission to fail, thus not freeing up device memory. pidff_erase_effect() is not called while dev->event_lock is held. Signed-off-by: Anssi Hannula <anssi.hannula@gmail.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
-rw-r--r--drivers/hid/usbhid/hid-core.c31
-rw-r--r--drivers/hid/usbhid/hid-pidff.c5
-rw-r--r--drivers/hid/usbhid/usbhid.h2
-rw-r--r--include/linux/hid.h6
4 files changed, 37 insertions, 7 deletions
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
index 07840df56c63..1ae047cd4fa1 100644
--- a/drivers/hid/usbhid/hid-core.c
+++ b/drivers/hid/usbhid/hid-core.c
@@ -232,13 +232,16 @@ static void hid_irq_in(struct urb *urb)
232static int hid_submit_out(struct hid_device *hid) 232static int hid_submit_out(struct hid_device *hid)
233{ 233{
234 struct hid_report *report; 234 struct hid_report *report;
235 char *raw_report;
235 struct usbhid_device *usbhid = hid->driver_data; 236 struct usbhid_device *usbhid = hid->driver_data;
236 237
237 report = usbhid->out[usbhid->outtail]; 238 report = usbhid->out[usbhid->outtail].report;
239 raw_report = usbhid->out[usbhid->outtail].raw_report;
238 240
239 hid_output_report(report, usbhid->outbuf);
240 usbhid->urbout->transfer_buffer_length = ((report->size - 1) >> 3) + 1 + (report->id > 0); 241 usbhid->urbout->transfer_buffer_length = ((report->size - 1) >> 3) + 1 + (report->id > 0);
241 usbhid->urbout->dev = hid_to_usb_dev(hid); 242 usbhid->urbout->dev = hid_to_usb_dev(hid);
243 memcpy(usbhid->outbuf, raw_report, usbhid->urbout->transfer_buffer_length);
244 kfree(raw_report);
242 245
243 dbg_hid("submitting out urb\n"); 246 dbg_hid("submitting out urb\n");
244 247
@@ -254,17 +257,20 @@ static int hid_submit_ctrl(struct hid_device *hid)
254{ 257{
255 struct hid_report *report; 258 struct hid_report *report;
256 unsigned char dir; 259 unsigned char dir;
260 char *raw_report;
257 int len; 261 int len;
258 struct usbhid_device *usbhid = hid->driver_data; 262 struct usbhid_device *usbhid = hid->driver_data;
259 263
260 report = usbhid->ctrl[usbhid->ctrltail].report; 264 report = usbhid->ctrl[usbhid->ctrltail].report;
265 raw_report = usbhid->ctrl[usbhid->ctrltail].raw_report;
261 dir = usbhid->ctrl[usbhid->ctrltail].dir; 266 dir = usbhid->ctrl[usbhid->ctrltail].dir;
262 267
263 len = ((report->size - 1) >> 3) + 1 + (report->id > 0); 268 len = ((report->size - 1) >> 3) + 1 + (report->id > 0);
264 if (dir == USB_DIR_OUT) { 269 if (dir == USB_DIR_OUT) {
265 hid_output_report(report, usbhid->ctrlbuf);
266 usbhid->urbctrl->pipe = usb_sndctrlpipe(hid_to_usb_dev(hid), 0); 270 usbhid->urbctrl->pipe = usb_sndctrlpipe(hid_to_usb_dev(hid), 0);
267 usbhid->urbctrl->transfer_buffer_length = len; 271 usbhid->urbctrl->transfer_buffer_length = len;
272 memcpy(usbhid->ctrlbuf, raw_report, len);
273 kfree(raw_report);
268 } else { 274 } else {
269 int maxpacket, padlen; 275 int maxpacket, padlen;
270 276
@@ -401,6 +407,7 @@ void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, uns
401 int head; 407 int head;
402 unsigned long flags; 408 unsigned long flags;
403 struct usbhid_device *usbhid = hid->driver_data; 409 struct usbhid_device *usbhid = hid->driver_data;
410 int len = ((report->size - 1) >> 3) + 1 + (report->id > 0);
404 411
405 if ((hid->quirks & HID_QUIRK_NOGET) && dir == USB_DIR_IN) 412 if ((hid->quirks & HID_QUIRK_NOGET) && dir == USB_DIR_IN)
406 return; 413 return;
@@ -415,7 +422,14 @@ void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, uns
415 return; 422 return;
416 } 423 }
417 424
418 usbhid->out[usbhid->outhead] = report; 425 usbhid->out[usbhid->outhead].raw_report = kmalloc(len, GFP_ATOMIC);
426 if (!usbhid->out[usbhid->outhead].raw_report) {
427 spin_unlock_irqrestore(&usbhid->outlock, flags);
428 warn("output queueing failed");
429 return;
430 }
431 hid_output_report(report, usbhid->out[usbhid->outhead].raw_report);
432 usbhid->out[usbhid->outhead].report = report;
419 usbhid->outhead = head; 433 usbhid->outhead = head;
420 434
421 if (!test_and_set_bit(HID_OUT_RUNNING, &usbhid->iofl)) 435 if (!test_and_set_bit(HID_OUT_RUNNING, &usbhid->iofl))
@@ -434,6 +448,15 @@ void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, uns
434 return; 448 return;
435 } 449 }
436 450
451 if (dir == USB_DIR_OUT) {
452 usbhid->ctrl[usbhid->ctrlhead].raw_report = kmalloc(len, GFP_ATOMIC);
453 if (!usbhid->ctrl[usbhid->ctrlhead].raw_report) {
454 spin_unlock_irqrestore(&usbhid->ctrllock, flags);
455 warn("control queueing failed");
456 return;
457 }
458 hid_output_report(report, usbhid->ctrl[usbhid->ctrlhead].raw_report);
459 }
437 usbhid->ctrl[usbhid->ctrlhead].report = report; 460 usbhid->ctrl[usbhid->ctrlhead].report = report;
438 usbhid->ctrl[usbhid->ctrlhead].dir = dir; 461 usbhid->ctrl[usbhid->ctrlhead].dir = dir;
439 usbhid->ctrlhead = head; 462 usbhid->ctrlhead = head;
diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c
index 011326178c06..484e3eec2f88 100644
--- a/drivers/hid/usbhid/hid-pidff.c
+++ b/drivers/hid/usbhid/hid-pidff.c
@@ -397,7 +397,6 @@ static void pidff_set_condition_report(struct pidff_device *pidff,
397 effect->u.condition[i].left_saturation); 397 effect->u.condition[i].left_saturation);
398 pidff_set(&pidff->set_condition[PID_DEAD_BAND], 398 pidff_set(&pidff->set_condition[PID_DEAD_BAND],
399 effect->u.condition[i].deadband); 399 effect->u.condition[i].deadband);
400 usbhid_wait_io(pidff->hid);
401 usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_CONDITION], 400 usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_CONDITION],
402 USB_DIR_OUT); 401 USB_DIR_OUT);
403 } 402 }
@@ -512,7 +511,6 @@ static void pidff_playback_pid(struct pidff_device *pidff, int pid_id, int n)
512 pidff->effect_operation[PID_LOOP_COUNT].value[0] = n; 511 pidff->effect_operation[PID_LOOP_COUNT].value[0] = n;
513 } 512 }
514 513
515 usbhid_wait_io(pidff->hid);
516 usbhid_submit_report(pidff->hid, pidff->reports[PID_EFFECT_OPERATION], 514 usbhid_submit_report(pidff->hid, pidff->reports[PID_EFFECT_OPERATION],
517 USB_DIR_OUT); 515 USB_DIR_OUT);
518} 516}
@@ -548,6 +546,9 @@ static int pidff_erase_effect(struct input_dev *dev, int effect_id)
548 int pid_id = pidff->pid_id[effect_id]; 546 int pid_id = pidff->pid_id[effect_id];
549 547
550 debug("starting to erase %d/%d", effect_id, pidff->pid_id[effect_id]); 548 debug("starting to erase %d/%d", effect_id, pidff->pid_id[effect_id]);
549 /* Wait for the queue to clear. We do not want a full fifo to
550 prevent the effect removal. */
551 usbhid_wait_io(pidff->hid);
551 pidff_playback_pid(pidff, pid_id, 0); 552 pidff_playback_pid(pidff, pid_id, 0);
552 pidff_erase_pid(pidff, pid_id); 553 pidff_erase_pid(pidff, pid_id);
553 554
diff --git a/drivers/hid/usbhid/usbhid.h b/drivers/hid/usbhid/usbhid.h
index b47f991867e9..abedb13c623e 100644
--- a/drivers/hid/usbhid/usbhid.h
+++ b/drivers/hid/usbhid/usbhid.h
@@ -67,7 +67,7 @@ struct usbhid_device {
67 spinlock_t ctrllock; /* Control fifo spinlock */ 67 spinlock_t ctrllock; /* Control fifo spinlock */
68 68
69 struct urb *urbout; /* Output URB */ 69 struct urb *urbout; /* Output URB */
70 struct hid_report *out[HID_CONTROL_FIFO_SIZE]; /* Output pipe fifo */ 70 struct hid_output_fifo out[HID_CONTROL_FIFO_SIZE]; /* Output pipe fifo */
71 unsigned char outhead, outtail; /* Output pipe fifo head & tail */ 71 unsigned char outhead, outtail; /* Output pipe fifo head & tail */
72 char *outbuf; /* Output buffer */ 72 char *outbuf; /* Output buffer */
73 dma_addr_t outbuf_dma; /* Output buffer dma */ 73 dma_addr_t outbuf_dma; /* Output buffer dma */
diff --git a/include/linux/hid.h b/include/linux/hid.h
index dcdef0bb4bba..f13bca2dd53b 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -388,6 +388,12 @@ struct hid_report_enum {
388struct hid_control_fifo { 388struct hid_control_fifo {
389 unsigned char dir; 389 unsigned char dir;
390 struct hid_report *report; 390 struct hid_report *report;
391 char *raw_report;
392};
393
394struct hid_output_fifo {
395 struct hid_report *report;
396 char *raw_report;
391}; 397};
392 398
393#define HID_CLAIMED_INPUT 1 399#define HID_CLAIMED_INPUT 1