On Thu, 6 Oct 2005 16:43:44 -0400 (EDT), Alan Stern <[EMAIL PROTECTED]> wrote:

> This patch (as576) removes some mistaken tests for disconnection from the 
> HID driver.  -EILSEQ refers to an arbitrary low-level protocol error, not 
> necessarily a disconnection.  Also, a completion routine will never see a 
> status of -EPERM; that's used only to indicate a failure during 
> usb_submit_urb.

> --- usb-2.6.orig/drivers/usb/input/hid-core.c
> +++ usb-2.6/drivers/usb/input/hid-core.c
> @@ -923,9 +923,7 @@ static void hid_irq_in(struct urb *urb, 
>                       break;
>               case -ECONNRESET:       /* unlink */
>               case -ENOENT:
> -             case -EPERM:
>               case -ESHUTDOWN:        /* unplug */
> -             case -EILSEQ:           /* unplug timeout on uhci */
>                       return;
>               case -ETIMEDOUT:        /* NAK */
>                       break;

No freaking way, man. Is there a particular scenario which this patch
fixes? Because that code is there for a reason. You cannot just do
it because some retarded document in Documentation/ suggests that it
might be a good idea.

I have a bug assigned (bz#167070), which is, unfortunately, private.
But the summary goes like this

    HID driver completion handler prints -84(-EILSEQ) error messages
    when a USB keyboard or mouse was unplugged.

    When a USB keyboard or mouse was unplugged, 
    HID driver completion handler hid_irq_in() (@drivers/usb/input/hid-core.c) 
    continuously prints an error message "input irq status -84 received".
    Once the USB was unplugged, the machine just hungs up.

    By the way, I have already found the patch to fix this
    problem at http://lkml.org/lkml/2005/1/27/176.
    The problem was settled by applying this patch to 
    drivers/usb/input/hid-core.c.

Attached is a patch we may be shipping as a hotfix for RHEL 4 U2 and
which is going to be shipped in U3 by normal means.

I can guarantee you Vojtech and Greg see the same with SLES.

On RHEL 3 I had the same problem, and there we cannot officially
rely on error code. But any driver which submits interrupt URBs
has to handle this correctly. Check this (it's MCT_U232, but HID
is exactly the same):
 http://lwn.net/Articles/123025/

-- Pete

diff -urp -X dontdiff linux-2.6.9-16.EL/drivers/usb/input/hid-core.c 
linux-2.6.9-16.EL.z1/drivers/usb/input/hid-core.c
--- linux-2.6.9-16.EL/drivers/usb/input/hid-core.c      2005-08-16 
18:27:59.000000000 -0700
+++ linux-2.6.9-16.EL.z1/drivers/usb/input/hid-core.c   2005-09-01 
23:51:32.000000000 -0700
@@ -918,14 +918,26 @@ static void hid_irq_in(struct urb *urb, 
        struct hid_device       *hid = urb->context;
        int                     status;
 
+       if (urb->status == -EILSEQ) {   /* unplug (on uhci) or other error */
+               if (hid->error_count >= 2) {
+                       info("not resubmitting, input%d", hid->ifnum);
+                       return;
+               }
+               hid->error_count++;
+       } else {
+               hid->error_count = 0;
+       }
+
        switch (urb->status) {
                case 0:                 /* success */
                        hid_input_report(HID_INPUT_REPORT, urb, regs);
                        break;
                case -ECONNRESET:       /* unlink */
                case -ENOENT:
-               case -ESHUTDOWN:
+               case -EPERM:
+               case -ESHUTDOWN:        /* unplug */
                        return;
+               case -EILSEQ:
                case -ETIMEDOUT:        /* NAK */
                        break;
                default:                /* error */
@@ -1134,12 +1146,15 @@ static void hid_irq_out(struct urb *urb,
 {
        struct hid_device *hid = urb->context;
        unsigned long flags;
+       int unplug = 0;
 
        switch (urb->status) {
                case 0:                 /* success */
+               case -ESHUTDOWN:        /* unplug */
+               case -EILSEQ:           /* unplug timeout on uhci */
+                       unplug = 1;
                case -ECONNRESET:       /* unlink */
                case -ENOENT:
-               case -ESHUTDOWN:
                        break;
                default:                /* error */
                        warn("output irq status %d received", urb->status);
@@ -1147,7 +1162,10 @@ static void hid_irq_out(struct urb *urb,
 
        spin_lock_irqsave(&hid->outlock, flags);
 
-       hid->outtail = (hid->outtail + 1) & (HID_OUTPUT_FIFO_SIZE - 1);
+       if (unplug)
+               hid->outtail = hid->outhead;
+       else
+               hid->outtail = (hid->outtail + 1) & (HID_OUTPUT_FIFO_SIZE - 1);
 
        if (hid->outhead != hid->outtail) {
                if (hid_submit_out(hid)) {
@@ -1171,6 +1189,7 @@ static void hid_ctrl(struct urb *urb, st
 {
        struct hid_device *hid = urb->context;
        unsigned long flags;
+       int unplug = 0;
 
        spin_lock_irqsave(&hid->ctrllock, flags);
 
@@ -1178,16 +1197,21 @@ static void hid_ctrl(struct urb *urb, st
                case 0:                 /* success */
                        if (hid->ctrl[hid->ctrltail].dir == USB_DIR_IN) 
                                
hid_input_report(hid->ctrl[hid->ctrltail].report->type, urb, regs);
+               case -ESHUTDOWN:        /* unplug */
+               case -EILSEQ:           /* unplug timectrl on uhci */
+                       unplug = 1;
                case -ECONNRESET:       /* unlink */
                case -ENOENT:
-               case -ESHUTDOWN:
                case -EPIPE:            /* report not available */
                        break;
                default:                /* error */
                        warn("ctrl urb status %d received", urb->status);
        }
 
-       hid->ctrltail = (hid->ctrltail + 1) & (HID_CONTROL_FIFO_SIZE - 1);
+       if (unplug)
+               hid->ctrltail = hid->ctrlhead;
+       else
+               hid->ctrltail = (hid->ctrltail + 1) & (HID_CONTROL_FIFO_SIZE - 
1);
 
        if (hid->ctrlhead != hid->ctrltail) {
                if (hid_submit_ctrl(hid)) {
diff -urp -X dontdiff linux-2.6.9-16.EL/drivers/usb/input/hid.h 
linux-2.6.9-16.EL.z1/drivers/usb/input/hid.h
--- linux-2.6.9-16.EL/drivers/usb/input/hid.h   2004-10-18 14:53:06.000000000 
-0700
+++ linux-2.6.9-16.EL.z1/drivers/usb/input/hid.h        2005-09-01 
22:26:57.000000000 -0700
@@ -382,6 +382,8 @@ struct hid_device {                                         
        /* device repo
        char phys[64];                                                  /* 
Device physical location */
        char uniq[64];                                                  /* 
Device unique identifier (serial #) */
 
+       int error_count;
+
        void *ff_private;                                               /* 
Private data for the force-feedback driver */
        void (*ff_exit)(struct hid_device*);                            /* 
Called by hid_exit_ff(hid) */
        int (*ff_event)(struct hid_device *hid, struct input_dev *input,


-------------------------------------------------------
This SF.Net email is sponsored by:
Power Architecture Resource Center: Free content, downloads, discussions,
and more. http://solutions.newsforge.com/ibmarch.tmpl
_______________________________________________
[email protected]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to