diff options
| author | Alan Stern <stern@rowland.harvard.edu> | 2006-10-18 12:35:24 -0400 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@suse.de> | 2006-12-01 17:23:27 -0500 |
| commit | 6d8fc4d28deaf828606c19fb743fbe94aeab4caf (patch) | |
| tree | 677eac24fac15cc68307a39011ec6ebed4d01ba0 /drivers/usb/input | |
| parent | 88fafff9d73c0a506c0b08e7cd637c89d8b604e1 (diff) | |
USB HID: Handle STALL on interrupt endpoint
The USB HID driver doesn't include any code to handle a STALL on the
interrupt endpoint. While this may be uncommon, it does happen
sometimes. This patch (as805) adds a fix.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/input')
| -rw-r--r-- | drivers/usb/input/hid-core.c | 39 | ||||
| -rw-r--r-- | drivers/usb/input/hid.h | 1 |
2 files changed, 27 insertions, 13 deletions
diff --git a/drivers/usb/input/hid-core.c b/drivers/usb/input/hid-core.c index 6d08a3bcc952..5de931cf4cfb 100644 --- a/drivers/usb/input/hid-core.c +++ b/drivers/usb/input/hid-core.c | |||
| @@ -968,20 +968,29 @@ static void hid_retry_timeout(unsigned long _hid) | |||
| 968 | hid_io_error(hid); | 968 | hid_io_error(hid); |
| 969 | } | 969 | } |
| 970 | 970 | ||
| 971 | /* Workqueue routine to reset the device */ | 971 | /* Workqueue routine to reset the device or clear a halt */ |
| 972 | static void hid_reset(void *_hid) | 972 | static void hid_reset(void *_hid) |
| 973 | { | 973 | { |
| 974 | struct hid_device *hid = (struct hid_device *) _hid; | 974 | struct hid_device *hid = (struct hid_device *) _hid; |
| 975 | int rc_lock, rc; | 975 | int rc_lock, rc = 0; |
| 976 | 976 | ||
| 977 | dev_dbg(&hid->intf->dev, "resetting device\n"); | 977 | if (test_bit(HID_CLEAR_HALT, &hid->iofl)) { |
| 978 | rc = rc_lock = usb_lock_device_for_reset(hid->dev, hid->intf); | 978 | dev_dbg(&hid->intf->dev, "clear halt\n"); |
| 979 | if (rc_lock >= 0) { | 979 | rc = usb_clear_halt(hid->dev, hid->urbin->pipe); |
| 980 | rc = usb_reset_composite_device(hid->dev, hid->intf); | 980 | clear_bit(HID_CLEAR_HALT, &hid->iofl); |
| 981 | if (rc_lock) | 981 | hid_start_in(hid); |
| 982 | usb_unlock_device(hid->dev); | 982 | } |
| 983 | |||
| 984 | else if (test_bit(HID_RESET_PENDING, &hid->iofl)) { | ||
| 985 | dev_dbg(&hid->intf->dev, "resetting device\n"); | ||
| 986 | rc = rc_lock = usb_lock_device_for_reset(hid->dev, hid->intf); | ||
| 987 | if (rc_lock >= 0) { | ||
| 988 | rc = usb_reset_composite_device(hid->dev, hid->intf); | ||
| 989 | if (rc_lock) | ||
| 990 | usb_unlock_device(hid->dev); | ||
| 991 | } | ||
| 992 | clear_bit(HID_RESET_PENDING, &hid->iofl); | ||
| 983 | } | 993 | } |
| 984 | clear_bit(HID_RESET_PENDING, &hid->iofl); | ||
| 985 | 994 | ||
| 986 | switch (rc) { | 995 | switch (rc) { |
| 987 | case 0: | 996 | case 0: |
| @@ -1023,9 +1032,8 @@ static void hid_io_error(struct hid_device *hid) | |||
| 1023 | 1032 | ||
| 1024 | /* Retries failed, so do a port reset */ | 1033 | /* Retries failed, so do a port reset */ |
| 1025 | if (!test_and_set_bit(HID_RESET_PENDING, &hid->iofl)) { | 1034 | if (!test_and_set_bit(HID_RESET_PENDING, &hid->iofl)) { |
| 1026 | if (schedule_work(&hid->reset_work)) | 1035 | schedule_work(&hid->reset_work); |
| 1027 | goto done; | 1036 | goto done; |
| 1028 | clear_bit(HID_RESET_PENDING, &hid->iofl); | ||
| 1029 | } | 1037 | } |
| 1030 | } | 1038 | } |
| 1031 | 1039 | ||
| @@ -1049,6 +1057,11 @@ static void hid_irq_in(struct urb *urb) | |||
| 1049 | hid->retry_delay = 0; | 1057 | hid->retry_delay = 0; |
| 1050 | hid_input_report(HID_INPUT_REPORT, urb, 1); | 1058 | hid_input_report(HID_INPUT_REPORT, urb, 1); |
| 1051 | break; | 1059 | break; |
| 1060 | case -EPIPE: /* stall */ | ||
| 1061 | clear_bit(HID_IN_RUNNING, &hid->iofl); | ||
| 1062 | set_bit(HID_CLEAR_HALT, &hid->iofl); | ||
| 1063 | schedule_work(&hid->reset_work); | ||
| 1064 | return; | ||
| 1052 | case -ECONNRESET: /* unlink */ | 1065 | case -ECONNRESET: /* unlink */ |
| 1053 | case -ENOENT: | 1066 | case -ENOENT: |
| 1054 | case -ESHUTDOWN: /* unplug */ | 1067 | case -ESHUTDOWN: /* unplug */ |
diff --git a/drivers/usb/input/hid.h b/drivers/usb/input/hid.h index 0e76e6dcac37..2a9bf07944c0 100644 --- a/drivers/usb/input/hid.h +++ b/drivers/usb/input/hid.h | |||
| @@ -385,6 +385,7 @@ struct hid_control_fifo { | |||
| 385 | #define HID_IN_RUNNING 3 | 385 | #define HID_IN_RUNNING 3 |
| 386 | #define HID_RESET_PENDING 4 | 386 | #define HID_RESET_PENDING 4 |
| 387 | #define HID_SUSPENDED 5 | 387 | #define HID_SUSPENDED 5 |
| 388 | #define HID_CLEAR_HALT 6 | ||
| 388 | 389 | ||
| 389 | struct hid_input { | 390 | struct hid_input { |
| 390 | struct list_head list; | 391 | struct list_head list; |
