On Sat, 14 Feb 2004, Alan Stern wrote:

> > Feb 14 10:50:38 fliwatut kernel: hub 1-0:1.0: new USB device on port 1, assigned 
> > address 2
> > Feb 14 10:50:38 fliwatut kernel: drivers/usb/input/hid-core.c: ctrl urb status -75 
> > received
> > Feb 14 10:50:38 fliwatut usb.agent[4385]: kernel driver hid already loaded
> > Feb 14 10:50:43 fliwatut kernel: usb 1-1: control timeout on ep0out
> > 
> > It does work on my Intel-based laptop, though. 
> 
> This sounds depressingly like the infamous "VIA USB controller shuts down 
> when receiving an overlong packet" problem.  That status -75 means the 
> device tried to send more data than the computer asked it for.  (This may 
> be a fairly common problem among low-speed HID devices.)  The "control 
> timeout" message is a good indication that the USB controller has turned 
> itself off.

IMHO it also sounds like a good example for what we have discussed 
recently, i.e. up-rounding the DATA-IN transfer_length to multiple 
maxpacket size.

Maybe something like the hack below. It compiles but is completely 
untested (Well, my mouse is still working ;-). In fact I think there might 
also be another issue in the hid-core which could easily trigger EOVERFLOW 
on the int-in endpoint - see the comment in the patch below.

However, in this particular case the overflow was apparently on the 
control-data. But I have no idea how hid would behave with the trailing 
babble-data. Well, if it works with Intel it might be worth trying it...

Martin

-----------------------

diff -urp linux-2.6.3-rc2/drivers/usb/input/hid-core.c 
v2.6.3-rc2-md/drivers/usb/input/hid-core.c
--- linux-2.6.3-rc2/drivers/usb/input/hid-core.c        Tue Feb 10 18:14:52 2004
+++ v2.6.3-rc2-md/drivers/usb/input/hid-core.c  Sat Feb 14 20:50:55 2004
@@ -1069,22 +1069,37 @@ static int hid_submit_ctrl(struct hid_de
 {
        struct hid_report *report;
        unsigned char dir;
+       int len;
 
        report = hid->ctrl[hid->ctrltail].report;
        dir = hid->ctrl[hid->ctrltail].dir;
 
-       if (dir == USB_DIR_OUT)
+       len = ((report->size - 1) >> 3) + 1 + (report->id > 0);
+       if (dir == USB_DIR_OUT) {
                hid_output_report(report, hid->ctrlbuf);
-
-       hid->urbctrl->transfer_buffer_length = ((report->size - 1) >> 3) + 1 + 
(report->id > 0);
-       hid->urbctrl->pipe = (dir == USB_DIR_OUT) ?  usb_sndctrlpipe(hid->dev, 0) : 
usb_rcvctrlpipe(hid->dev, 0);
+               hid->urbctrl->pipe = usb_sndctrlpipe(hid->dev, 0);
+               hid->urbctrl->transfer_buffer_length = len;
+       } else {
+               int maxpacket, padlen;
+
+               hid->urbctrl->pipe = usb_rcvctrlpipe(hid->dev, 0);
+               maxpacket = usb_maxpacket(hid->dev, hid->urbctrl->pipe, 0);
+               if (maxpacket > 0) {
+                       padlen = (len + maxpacket - 1) / maxpacket;
+                       padlen *= maxpacket;
+                       if (padlen > HID_BUFFER_SIZE);
+                               padlen = HID_BUFFER_SIZE;
+               } else
+                       padlen = 0;
+               hid->urbctrl->transfer_buffer_length = padlen;
+       }
        hid->urbctrl->dev = hid->dev;
 
        hid->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE | dir;
        hid->cr->bRequest = (dir == USB_DIR_OUT) ? HID_REQ_SET_REPORT : 
HID_REQ_GET_REPORT;
        hid->cr->wValue = cpu_to_le16(((report->type + 1) << 8) | report->id);
        hid->cr->wIndex = cpu_to_le16(hid->ifnum);
-       hid->cr->wLength = cpu_to_le16(hid->urbctrl->transfer_buffer_length);
+       hid->cr->wLength = cpu_to_le16(len);
 
        dbg("submitting ctrl urb");
 
@@ -1262,7 +1277,6 @@ void hid_init_reports(struct hid_device 
        struct hid_report_enum *report_enum;
        struct hid_report *report;
        struct list_head *list;
-       int len;
        int err, ret;
 
        report_enum = hid->report_enum + HID_INPUT_REPORT;
@@ -1296,10 +1310,35 @@ void hid_init_reports(struct hid_device 
        report_enum = hid->report_enum + HID_INPUT_REPORT;
        list = report_enum->report_list.next;
        while (list != &report_enum->report_list) {
+               int len;
+               int maxpacket, padlen;
+
                report = (struct hid_report *) list;
-               len = ((report->size - 1) >> 3) + 1 + report_enum->numbered;
-               if (len > hid->urbin->transfer_buffer_length)
-                       hid->urbin->transfer_buffer_length = len < HID_BUFFER_SIZE ? 
len : HID_BUFFER_SIZE;
+
+               /* looks suspicous: while looping over the report list
+                * we exec control urbs - but we do also modify the
+                * corresponding int-in urb's transfer_length on the fly.
+                * AFAICS nothing guarantees the expected int-in matching
+                * the _previous_ report was already completed before we
+                * modify its transfer_length according to the _next_ report
+                * in the list. However I might have missed something f.e. if
+                * the HID specs would require the device to make sure the
+                * control request wouldn't get completed before the int-in.
+                * But it sounds unlikely (to me at least), to require devices
+                * to deal with interleaving control and int transfers...
+                */
+               maxpacket = usb_maxpacket(hid->dev, hid->urbin->pipe, 0);
+               if (maxpacket > 0) {
+                       report = (struct hid_report *) list;
+                       len = ((report->size - 1) >> 3) + 1 + report_enum->numbered;
+                       padlen = (len + maxpacket - 1) / maxpacket;
+                       padlen *= maxpacket;
+                       if (padlen > HID_BUFFER_SIZE);
+                               padlen = HID_BUFFER_SIZE;
+               } else
+                       padlen = 0;
+               hid->urbin->transfer_buffer_length = padlen;
+
                usb_control_msg(hid->dev, usb_sndctrlpipe(hid->dev, 0),
                        0x0a, USB_TYPE_CLASS | USB_RECIP_INTERFACE, report->id,
                        hid->ifnum, NULL, 0, HZ * USB_CTRL_SET_TIMEOUT);
diff -urp linux-2.6.3-rc2/drivers/usb/input/hid.h v2.6.3-rc2-md/drivers/usb/input/hid.h
--- linux-2.6.3-rc2/drivers/usb/input/hid.h     Mon Jan 26 12:56:15 2004
+++ v2.6.3-rc2-md/drivers/usb/input/hid.h       Sat Feb 14 20:34:55 2004
@@ -309,7 +309,7 @@ struct hid_report_enum {
 
 #define HID_REPORT_TYPES 3
 
-#define HID_BUFFER_SIZE                32
+#define HID_BUFFER_SIZE                64
 #define HID_CONTROL_FIFO_SIZE  256             /* to init devices with >100 reports */
 #define HID_OUTPUT_FIFO_SIZE   64
 



-------------------------------------------------------
SF.Net is sponsored by: Speed Start Your Linux Apps Now.
Build and deploy apps & Web services for Linux with
a free DVD software kit from IBM. Click Now!
http://ads.osdn.com/?ad_id=1356&alloc_id=3438&op=click
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to