diff options
Diffstat (limited to 'drivers/hid/usbhid')
-rw-r--r-- | drivers/hid/usbhid/hid-core.c | 71 |
1 files changed, 44 insertions, 27 deletions
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 4309c03..dedd8e4 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -993,9 +993,10 @@ static int usbhid_output_raw_report(struct hid_device *hid, __u8 *buf, size_t co static void usbhid_restart_queues(struct usbhid_device *usbhid) { - if (usbhid->urbout) + if (usbhid->urbout && !test_bit(HID_OUT_RUNNING, &usbhid->iofl)) usbhid_restart_out_queue(usbhid); - usbhid_restart_ctrl_queue(usbhid); + if (!test_bit(HID_CTRL_RUNNING, &usbhid->iofl)) + usbhid_restart_ctrl_queue(usbhid); } static void hid_free_buffers(struct usb_device *dev, struct hid_device *hid) @@ -1462,11 +1463,38 @@ void usbhid_put_power(struct hid_device *hid) #ifdef CONFIG_PM +static int hid_resume_common(struct hid_device *hid, bool driver_suspended) +{ + struct usbhid_device *usbhid = hid->driver_data; + int status; + + spin_lock_irq(&usbhid->lock); + clear_bit(HID_SUSPENDED, &usbhid->iofl); + usbhid_mark_busy(usbhid); + + if (test_bit(HID_CLEAR_HALT, &usbhid->iofl) || + test_bit(HID_RESET_PENDING, &usbhid->iofl)) + schedule_work(&usbhid->reset_work); + usbhid->retry_delay = 0; + + usbhid_restart_queues(usbhid); + spin_unlock_irq(&usbhid->lock); + + status = hid_start_in(hid); + if (status < 0) + hid_io_error(hid); + + if (driver_suspended && hid->driver && hid->driver->resume) + status = hid->driver->resume(hid); + return status; +} + static int hid_suspend(struct usb_interface *intf, pm_message_t message) { struct hid_device *hid = usb_get_intfdata(intf); struct usbhid_device *usbhid = hid->driver_data; int status; + bool driver_suspended = false; if (PMSG_IS_AUTO(message)) { spin_lock_irq(&usbhid->lock); /* Sync with error handler */ @@ -1482,8 +1510,9 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message) if (hid->driver && hid->driver->suspend) { status = hid->driver->suspend(hid, message); if (status < 0) - return status; + goto failed; } + driver_suspended = true; } else { usbhid_mark_busy(usbhid); spin_unlock_irq(&usbhid->lock); @@ -1496,11 +1525,14 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message) if (status < 0) return status; } + driver_suspended = true; spin_lock_irq(&usbhid->lock); set_bit(HID_SUSPENDED, &usbhid->iofl); spin_unlock_irq(&usbhid->lock); - if (usbhid_wait_io(hid) < 0) - return -EIO; + if (usbhid_wait_io(hid) < 0) { + status = -EIO; + goto failed; + } } hid_cancel_delayed_stuff(usbhid); @@ -1508,14 +1540,15 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message) if (PMSG_IS_AUTO(message) && test_bit(HID_KEYS_PRESSED, &usbhid->iofl)) { /* lost race against keypresses */ - status = hid_start_in(hid); - if (status < 0) - hid_io_error(hid); - usbhid_mark_busy(usbhid); - return -EBUSY; + status = -EBUSY; + goto failed; } dev_dbg(&intf->dev, "suspend\n"); return 0; + + failed: + hid_resume_common(hid, driver_suspended); + return status; } static int hid_resume(struct usb_interface *intf) @@ -1527,23 +1560,7 @@ static int hid_resume(struct usb_interface *intf) if (!test_bit(HID_STARTED, &usbhid->iofl)) return 0; - clear_bit(HID_SUSPENDED, &usbhid->iofl); - usbhid_mark_busy(usbhid); - - if (test_bit(HID_CLEAR_HALT, &usbhid->iofl) || - test_bit(HID_RESET_PENDING, &usbhid->iofl)) - schedule_work(&usbhid->reset_work); - usbhid->retry_delay = 0; - status = hid_start_in(hid); - if (status < 0) - hid_io_error(hid); - usbhid_restart_queues(usbhid); - - if (status >= 0 && hid->driver && hid->driver->resume) { - int ret = hid->driver->resume(hid); - if (ret < 0) - status = ret; - } + status = hid_resume_common(hid, true); dev_dbg(&intf->dev, "resume status %d\n", status); return 0; } |