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++;
