-/*
- * Input submission and I/O error handler.
- */
-
-static void hid_io_error(struct hid_device *hid);
-
-/* Start up the input URB */
-static int hid_start_in(struct hid_device *hid)
-{
- unsigned long flags;
- int rc = 0;
-
- spin_lock_irqsave(&hid->inlock, flags);
- if (hid->open > 0 && !test_bit(HID_SUSPENDED, &hid->iofl) &&
- !test_and_set_bit(HID_IN_RUNNING, &hid->iofl)) {
- rc = usb_submit_urb(hid->urbin, GFP_ATOMIC);
- if (rc != 0)
- clear_bit(HID_IN_RUNNING, &hid->iofl);
- }
- spin_unlock_irqrestore(&hid->inlock, flags);
- return rc;
-}
-
-/* I/O retry timer routine */
-static void hid_retry_timeout(unsigned long _hid)
-{
- struct hid_device *hid = (struct hid_device *) _hid;
-
- dev_dbg(&hid->intf->dev, "retrying intr urb\n");
- if (hid_start_in(hid))
- hid_io_error(hid);
-}
-
-/* Workqueue routine to reset the device */
-static void hid_reset(void *_hid)
-{
- struct hid_device *hid = (struct hid_device *) _hid;
- int rc_lock, rc;
-
- dev_dbg(&hid->intf->dev, "resetting device\n");
- rc = rc_lock = usb_lock_device_for_reset(hid->dev, hid->intf);
- if (rc_lock >= 0) {
- rc = usb_reset_device(hid->dev);
- if (rc_lock)
- usb_unlock_device(hid->dev);
- }
- clear_bit(HID_RESET_PENDING, &hid->iofl);
-
- if (rc == 0) {
- hid->retry_delay = 0;
- if (hid_start_in(hid))
- hid_io_error(hid);
- } else if (!(rc == -ENODEV || rc == -EHOSTUNREACH || rc == -EINTR))
- err("can't reset device, %s-%s/input%d, status %d",
- hid->dev->bus->bus_name,
- hid->dev->devpath,
- hid->ifnum, rc);
-}
-
-/* Main I/O error handler */
-static void hid_io_error(struct hid_device *hid)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&hid->inlock, flags);
-
- /* Stop when disconnected */
- if (usb_get_intfdata(hid->intf) == NULL)
- goto done;
-
- /* When an error occurs, retry at increasing intervals */
- if (hid->retry_delay == 0) {
- hid->retry_delay = 13; /* Then 26, 52, 104, 104, ... */
- hid->stop_retry = jiffies + msecs_to_jiffies(1000);
- } else if (hid->retry_delay < 100)
- hid->retry_delay *= 2;
-
- if (time_after(jiffies, hid->stop_retry)) {
-
- /* Retries failed, so do a port reset */
- if (!test_and_set_bit(HID_RESET_PENDING, &hid->iofl)) {
- if (schedule_work(&hid->reset_work))
- goto done;
- clear_bit(HID_RESET_PENDING, &hid->iofl);
- }
- }
-
- mod_timer(&hid->io_retry,
- jiffies + msecs_to_jiffies(hid->retry_delay));
-done:
- spin_unlock_irqrestore(&hid->inlock, flags);
-}
-