Seems that the real problem is in hid-core.c:
endpoint = &interface->endpoint[n].desc; if ((endpoint->bmAttributes & 3) != 3) /* Not an interrupt endpoint */ continue;
if (endpoint->bEndpointAddress & USB_DIR_IN) { if (hid->urbin) continue; if (!(hid->urbin = usb_alloc_urb(0, GFP_KERNEL))) goto fail; pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); usb_fill_int_urb(hid->urbin, dev, pipe, hid->inbuf, 0, hid_irq_in, hid, endpoint->bInterval); hid->urbin->transfer_dma = hid->inbuf_dma; hid->urbin->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; } else { if (hid->urbout) continue; if (!(hid->urbout = usb_alloc_urb(0, GFP_KERNEL))) goto fail; pipe = usb_sndbulkpipe(dev, endpoint->bEndpointAddress); usb_fill_bulk_urb(hid->urbout, dev, pipe, hid->outbuf, 0, hid_irq_out, hid); hid->urbout->transfer_dma = hid->outbuf_dma; hid->urbout->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; }
Notice that for the input endpoint it correctly uses usb_rcvintpipe() and usb_fill_int_urb(), but for the output endpoint usb_sndbulkpipe() and usb_fill_bulk_urb() are used (even though it is really an interrupt endpoint). How could this work?
So it is the HID core which must be fixed. Unfortunately, I don't have any HID device which would have an interrupt out endpoint...
Attached is a patch doing two things:
hid-lgff.c: Fixes an obvious list handling issue.
hid-core.c: - Use INT out urbs instead of bulk out urbs. That's the way it should be
- Remove some report->id magic.
IMPORTANT NOTE: You probably do not want to apply this patch yet. I do not understand what the report->id thing is supposed to do, but I know it prevents my driver to work (no complaint from usb_submit_urb, but my device just won't react to it)
-- Johann Deneux
diff -u linux-2.6.0-test9-old/drivers/usb/input/hid-core.c linux-2.6.0-test9-new/drivers/usb/input/hid-core.c --- linux-2.6.0-test9-old/drivers/usb/input/hid-core.c 2003-10-25 18:43:52.000000000 +0000 +++ linux-2.6.0-test9-new/drivers/usb/input/hid-core.c 2003-12-15 23:14:05.000000000 +0000 @@ -953,9 +953,6 @@ { unsigned n; - if (report->id > 0) - *data++ = report->id; - for (n = 0; n < report->maxfield; n++) hid_output_field(report->field[n], data); } @@ -1046,17 +1043,18 @@ static int hid_submit_out(struct hid_device *hid) { struct hid_report *report; + int err; report = hid->out[hid->outtail]; hid_output_report(report, hid->outbuf); - hid->urbout->transfer_buffer_length = ((report->size - 1) >> 3) + 1 + (report->id > 0); + hid->urbout->transfer_buffer_length = ((report->size - 1) >> 3) + 1; hid->urbout->dev = hid->dev; - dbg("submitting out urb"); + dbg("submitting out urb with size %d", hid->urbout->transfer_buffer_length); - if (usb_submit_urb(hid->urbout, GFP_ATOMIC)) { - err("usb_submit_urb(out) failed"); + if ((err = usb_submit_urb(hid->urbout, GFP_ATOMIC))) { + err("usb_submit_urb(out) failed (%d)", err); return -1; } @@ -1521,9 +1519,9 @@ continue; if (!(hid->urbout = usb_alloc_urb(0, GFP_KERNEL))) goto fail; - pipe = usb_sndbulkpipe(dev, endpoint->bEndpointAddress); - usb_fill_bulk_urb(hid->urbout, dev, pipe, hid->outbuf, 0, - hid_irq_out, hid); + pipe = usb_sndintpipe(dev, endpoint->bEndpointAddress); + usb_fill_int_urb(hid->urbout, dev, pipe, hid->outbuf, 0, + hid_irq_out, hid, 1); hid->urbout->transfer_dma = hid->outbuf_dma; hid->urbout->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; } diff -u linux-2.6.0-test9-old/drivers/usb/input/hid-lgff.c linux-2.6.0-test9-new/drivers/usb/input/hid-lgff.c --- linux-2.6.0-test9-old/drivers/usb/input/hid-lgff.c 2003-10-25 18:44:16.000000000 +0000 +++ linux-2.6.0-test9-new/drivers/usb/input/hid-lgff.c 2003-12-15 23:11:44.000000000 +0000 @@ -31,7 +31,7 @@ #include <linux/input.h> #include <linux/sched.h> -#define DEBUG +//#define DEBUG #include <linux/usb.h> #include <linux/circ_buf.h> @@ -254,7 +254,7 @@ signed short* ff; u16 idVendor = hid->dev->descriptor.idVendor; u16 idProduct = hid->dev->descriptor.idProduct; - struct hid_input *hidinput = list_entry(&hid->inputs, struct hid_input, list); + struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); while (dev->idVendor && (idVendor != dev->idVendor || idProduct != dev->idProduct)) dev++;