On Thu, 12 Jan 2006, Willi Mann wrote:

> > Willi, perhaps you can try this patch in place of the one I sent you 
> > earlier.  Hopefully it will work just as well; if it doesn't please let us 
> > know.

> And I had to reboot because the machine froze. (no mouse, no keyboard, 
> no network, frozen X11 screen) This is self-compiled kernel 2.6.15 from 
> debian sources with your patch attached.
> 
> Note: I've been playing with X today so the reason might be outside of 
> your patch. However, the first thing that froze was my USB Keyboard, (I 
> could still move the mouse for some seconds) so I think this is quite 
> unlikely.

There was a mistake in my patch, an == that should have been !=.  I forgot 
to check this before sending it to you, sorry.

Here's the updated patch.

Alan Stern



Index: usb-2.6/drivers/usb/input/hid-core.c
===================================================================
--- usb-2.6.orig/drivers/usb/input/hid-core.c
+++ usb-2.6/drivers/usb/input/hid-core.c
@@ -26,6 +26,7 @@
 #include <asm/byteorder.h>
 #include <linux/input.h>
 #include <linux/wait.h>
+#include <linux/workqueue.h>
 
 #undef DEBUG
 #undef DEBUG_DATA
@@ -911,6 +912,107 @@ static int hid_input_report(int type, st
 }
 
 /*
+ * 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, gfp_t mem_flags)
+{
+       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, mem_flags);
+               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, GFP_ATOMIC) != 0)
+               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);
+       }
+       kfree(hid->reset_work);
+       clear_bit(HID_RESET_PENDING, &hid->iofl);
+
+       if (rc == 0) {
+               hid->retry_delay = 0;
+               if (hid_start_in(hid, GFP_KERNEL))
+                       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)) {
+                       hid->reset_work = kmalloc(sizeof(struct work_struct),
+                                       GFP_ATOMIC);
+                       if (hid->reset_work) {
+                               INIT_WORK(hid->reset_work, hid_reset, hid);
+                               if (schedule_work(hid->reset_work) != 0)
+                                       goto done;
+                               kfree(hid->reset_work);
+                       }
+                       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);
+}
+
+/*
  * Input interrupt completion handler.
  */
 
@@ -921,25 +1023,35 @@ static void hid_irq_in(struct urb *urb, 
 
        switch (urb->status) {
                case 0:                 /* success */
+                       hid->retry_delay = 0;
                        hid_input_report(HID_INPUT_REPORT, urb, 1, regs);
                        break;
                case -ECONNRESET:       /* unlink */
                case -ENOENT:
-               case -EPERM:
                case -ESHUTDOWN:        /* unplug */
-               case -EILSEQ:           /* unplug timeout on uhci */
+                       clear_bit(HID_IN_RUNNING, &hid->iofl);
                        return;
+               case -EILSEQ:           /* protocol error or unplug */
+               case -EPROTO:           /* protocol error or unplug */
                case -ETIMEDOUT:        /* NAK */
-                       break;
+                       clear_bit(HID_IN_RUNNING, &hid->iofl);
+                       hid_io_error(hid);
+                       return;
                default:                /* error */
                        warn("input irq status %d received", urb->status);
        }
 
        status = usb_submit_urb(urb, SLAB_ATOMIC);
-       if (status)
-               err("can't resubmit intr, %s-%s/input%d, status %d",
-                               hid->dev->bus->bus_name, hid->dev->devpath,
-                               hid->ifnum, status);
+       if (status) {
+               clear_bit(HID_IN_RUNNING, &hid->iofl);
+               if (status != -EPERM) {
+                       err("can't resubmit intr, %s-%s/input%d, status %d",
+                                       hid->dev->bus->bus_name,
+                                       hid->dev->devpath,
+                                       hid->ifnum, status);
+                       hid_io_error(hid);
+               }
+       }
 }
 
 /*
@@ -1101,8 +1213,9 @@ static void hid_irq_out(struct urb *urb,
                case 0:                 /* success */
                        break;
                case -ESHUTDOWN:        /* unplug */
-               case -EILSEQ:           /* unplug timeout on uhci */
                        unplug = 1;
+               case -EILSEQ:           /* protocol error or unplug */
+               case -EPROTO:           /* protocol error or unplug */
                case -ECONNRESET:       /* unlink */
                case -ENOENT:
                        break;
@@ -1149,8 +1262,9 @@ static void hid_ctrl(struct urb *urb, st
                                
hid_input_report(hid->ctrl[hid->ctrltail].report->type, urb, 0, regs);
                        break;
                case -ESHUTDOWN:        /* unplug */
-               case -EILSEQ:           /* unplug timectrl on uhci */
                        unplug = 1;
+               case -EILSEQ:           /* protocol error or unplug */
+               case -EPROTO:           /* protocol error or unplug */
                case -ECONNRESET:       /* unlink */
                case -ENOENT:
                case -EPIPE:            /* report not available */
@@ -1263,14 +1377,9 @@ static int hid_get_class_descriptor(stru
 
 int hid_open(struct hid_device *hid)
 {
-       if (hid->open++)
-               return 0;
-
-       hid->urbin->dev = hid->dev;
-
-       if (usb_submit_urb(hid->urbin, GFP_KERNEL))
+       ++hid->open;
+       if (hid_start_in(hid, GFP_KERNEL) != 0)
                return -EIO;
-
        return 0;
 }
 
@@ -1753,6 +1862,9 @@ static struct hid_device *usb_hid_config
 
        init_waitqueue_head(&hid->wait);
 
+       setup_timer(&hid->io_retry, hid_retry_timeout, (unsigned long) hid);
+
+       spin_lock_init(&hid->inlock);
        spin_lock_init(&hid->outlock);
        spin_lock_init(&hid->ctrllock);
 
@@ -1824,11 +1936,16 @@ static void hid_disconnect(struct usb_in
        if (!hid)
                return;
 
+       spin_lock_irq(&hid->inlock);    /* Sync with error handler */
        usb_set_intfdata(intf, NULL);
+       spin_unlock_irq(&hid->inlock);
        usb_kill_urb(hid->urbin);
        usb_kill_urb(hid->urbout);
        usb_kill_urb(hid->urbctrl);
 
+       del_timer_sync(&hid->io_retry);
+       flush_scheduled_work();
+
        if (hid->claimed & HID_CLAIMED_INPUT)
                hidinput_disconnect(hid);
        if (hid->claimed & HID_CLAIMED_HIDDEV)
@@ -1903,6 +2020,10 @@ static int hid_suspend(struct usb_interf
 {
        struct hid_device *hid = usb_get_intfdata (intf);
 
+       spin_lock_irq(&hid->inlock);    /* Sync with error handler */
+       set_bit(HID_SUSPENDED, &hid->iofl);
+       spin_unlock_irq(&hid->inlock);
+       del_timer(&hid->io_retry);
        usb_kill_urb(hid->urbin);
        dev_dbg(&intf->dev, "suspend\n");
        return 0;
@@ -1913,10 +2034,8 @@ static int hid_resume(struct usb_interfa
        struct hid_device *hid = usb_get_intfdata (intf);
        int status;
 
-       if (hid->open)
-               status = usb_submit_urb(hid->urbin, GFP_NOIO);
-       else
-               status = 0;
+       clear_bit(HID_SUSPENDED, &hid->iofl);
+       status = hid_start_in(hid, GFP_KERNEL);
        dev_dbg(&intf->dev, "resume status %d\n", status);
        return status;
 }
Index: usb-2.6/drivers/usb/input/hid.h
===================================================================
--- usb-2.6.orig/drivers/usb/input/hid.h
+++ usb-2.6/drivers/usb/input/hid.h
@@ -31,6 +31,7 @@
 #include <linux/types.h>
 #include <linux/slab.h>
 #include <linux/list.h>
+#include <linux/timer.h>
 
 /*
  * USB HID (Human Interface Device) interface class code
@@ -367,6 +368,9 @@ struct hid_control_fifo {
 
 #define HID_CTRL_RUNNING       1
 #define HID_OUT_RUNNING                2
+#define HID_IN_RUNNING         3
+#define HID_RESET_PENDING      4
+#define HID_SUSPENDED          5
 
 struct hid_input {
        struct list_head list;
@@ -390,12 +394,17 @@ struct hid_device {                                       
                /* device repo
        int ifnum;                                                      /* USB 
interface number */
 
        unsigned long iofl;                                             /* I/O 
flags (CTRL_RUNNING, OUT_RUNNING) */
+       struct timer_list io_retry;                                     /* 
Retry timer */
+       unsigned long stop_retry;                                       /* Time 
to give up, in jiffies */
+       unsigned int retry_delay;                                       /* 
Delay length in ms */
+       struct work_struct *reset_work;                                 /* Task 
context for resets */
 
        unsigned int bufsize;                                           /* URB 
buffer size */
 
        struct urb *urbin;                                              /* 
Input URB */
        char *inbuf;                                                    /* 
Input buffer */
        dma_addr_t inbuf_dma;                                           /* 
Input buffer dma */
+       spinlock_t inlock;                                              /* 
Input fifo spinlock */
 
        struct urb *urbctrl;                                            /* 
Control URB */
        struct usb_ctrlrequest *cr;                                     /* 
Control request struct */



-------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc. Do you grep through log files
for problems?  Stop!  Download the new AJAX search engine that makes
searching your log files as easy as surfing the  web.  DOWNLOAD SPLUNK!
http://ads.osdn.com/?ad_id=7637&alloc_id=16865&op=click
_______________________________________________
linux-usb-devel@lists.sourceforge.net
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to