Hello, I'd like to post the incomplete patch I've written so far and see if anybody has anything to say about it, and perhaps see if anyone can help me with a couple little problems I'm having trouble with. It's taken me a while to come back to this because I've been busy trying to make sure that the code I've written so far isn't a complete mess. I decided to post it in this thread since it already discusses the problem - but for anyone who hasn't read the prior posts, here is a summary: On some OHCI controllers, the usbhid driver needs a second input URB to prevent data being lost when one URB is being read on a frame boundary and incoming interrupt data is learned about on the next frame. I'm completely new to programming anything for the kernel and I'm trying to write a patch to fix this. Here's some thoughts about what I've coded so far: It occurs to me that this only happens on "some" controllers. So, I thought it'd be nice if I could find a way to test for the necessity of a second (or maybe even a third? who knows) input URB instead of just giving every usbhid device two of them. wrt that: 1. Am I on the right track thinking like that? 2. Does the USB stuff in the kernel already have a way to test this? 3. If not, I'm really stumped about how I can do this, so any advice would be great. :p Aside from that - in hid_start_in(), would it have been better if I'd used goto and a label instead of breaking from the for loop? I wouldn't have to test if (rc == 0) to clear that HID_NO_BANDWIDTH bit that way, but it seemed more messy. Please let me know if I've made even the slightest mistake so I can learn from this! Regards, Josef -- diff -uprN -X linux-3.10-vanilla/Documentation/dontdiff linux-3.10-vanilla/drivers/hid/usbhid/hid-core.c linux-3.10-dev/drivers/hid/usbhid/hid-core.c --- linux-3.10-vanilla/drivers/hid/usbhid/hid-core.c 2013-06-30 17:13:29.000000000 -0500 +++ linux-3.10-dev/drivers/hid/usbhid/hid-core.c 2013-07-22 16:00:24.785950521 -0500 @@ -80,20 +80,26 @@ static int hid_start_in(struct hid_devic unsigned long flags; int rc = 0; struct usbhid_device *usbhid = hid->driver_data; + int i; spin_lock_irqsave(&usbhid->lock, flags); if (hid->open > 0 && !test_bit(HID_DISCONNECTED, &usbhid->iofl) && !test_bit(HID_SUSPENDED, &usbhid->iofl) && !test_and_set_bit(HID_IN_RUNNING, &usbhid->iofl)) { - rc = usb_submit_urb(usbhid->urbin, GFP_ATOMIC); - if (rc != 0) { - clear_bit(HID_IN_RUNNING, &usbhid->iofl); - if (rc == -ENOSPC) - set_bit(HID_NO_BANDWIDTH, &usbhid->iofl); - } else { - clear_bit(HID_NO_BANDWIDTH, &usbhid->iofl); + for (i = 0; i < usbhid->n_inurbs; i++) { + rc = usb_submit_urb(usbhid->inurbs[i], GFP_ATOMIC); + if (rc != 0) { + clear_bit(HID_IN_RUNNING, &usbhid->iofl); + if (rc == -ENOSPC) + set_bit(HID_NO_BANDWIDTH, &usbhid->iofl); + + break; + } } + + if (rc == 0) + clear_bit(HID_NO_BANDWIDTH, &usbhid->iofl); } spin_unlock_irqrestore(&usbhid->lock, flags); return rc; @@ -120,7 +126,7 @@ static void hid_reset(struct work_struct if (test_bit(HID_CLEAR_HALT, &usbhid->iofl)) { dev_dbg(&usbhid->intf->dev, "clear halt\n"); - rc = usb_clear_halt(hid_to_usb_dev(hid), usbhid->urbin->pipe); + rc = usb_clear_halt(hid_to_usb_dev(hid), usbhid->inurbs[0]->pipe); clear_bit(HID_CLEAR_HALT, &usbhid->iofl); hid_start_in(hid); } @@ -780,6 +786,7 @@ done: void usbhid_close(struct hid_device *hid) { struct usbhid_device *usbhid = hid->driver_data; + int i; mutex_lock(&hid_open_mut); @@ -791,7 +798,10 @@ void usbhid_close(struct hid_device *hid if (!--hid->open) { spin_unlock_irq(&usbhid->lock); hid_cancel_delayed_stuff(usbhid); - usb_kill_urb(usbhid->urbin); + + for (i = 0; i < usbhid->n_inurbs; i++) + usb_kill_urb(usbhid->inurbs[i]); + usbhid->intf->needs_remote_wakeup = 0; } else { spin_unlock_irq(&usbhid->lock); @@ -1087,6 +1097,7 @@ static int usbhid_start(struct hid_devic struct usb_device *dev = interface_to_usbdev(intf); struct usbhid_device *usbhid = hid->driver_data; unsigned int n, insize = 0; + int i; int ret; clear_bit(HID_DISCONNECTED, &usbhid->iofl); @@ -1133,16 +1144,33 @@ static int usbhid_start(struct hid_devic interval = hid_mousepoll_interval; ret = -ENOMEM; + + /* + * Some controllers need more than one input URB to prevent data from being lost + * while processing a prior completed URB. + */ + usbhid->n_inurbs = 2; + + if (usbhid->inurbs) + continue; + + usbhid->inurbs = kmalloc(usbhid->n_inurbs * sizeof(struct urb *), GFP_KERNEL); + if (!(usbhid->inurbs)) { + ret = -ENOMEM; + goto fail; + } + if (usb_endpoint_dir_in(endpoint)) { - if (usbhid->urbin) - continue; - if (!(usbhid->urbin = usb_alloc_urb(0, GFP_KERNEL))) - goto fail; pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); - usb_fill_int_urb(usbhid->urbin, dev, pipe, usbhid->inbuf, insize, - hid_irq_in, hid, interval); - usbhid->urbin->transfer_dma = usbhid->inbuf_dma; - usbhid->urbin->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + for (i = 0; i < usbhid->n_inurbs; i++) { + if (!(usbhid->inurbs[i] = usb_alloc_urb(0, GFP_KERNEL))) + goto fail; + usb_fill_int_urb(usbhid->inurbs[i], dev, pipe, usbhid->inbuf, insize, + hid_irq_in, hid, interval); + usbhid->inurbs[i]->transfer_dma = usbhid->inbuf_dma; + usbhid->inurbs[i]->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + } } else { if (usbhid->urbout) continue; @@ -1187,10 +1215,16 @@ static int usbhid_start(struct hid_devic return 0; fail: - usb_free_urb(usbhid->urbin); + if (usbhid->inurbs) { + for (i = 0; i < usbhid->n_inurbs; i++) + usb_free_urb(usbhid->inurbs[i]); + + kfree(usbhid->inurbs); + usbhid->inurbs = NULL; + } + usb_free_urb(usbhid->urbout); usb_free_urb(usbhid->urbctrl); - usbhid->urbin = NULL; usbhid->urbout = NULL; usbhid->urbctrl = NULL; hid_free_buffers(dev, hid); @@ -1200,6 +1234,7 @@ fail: static void usbhid_stop(struct hid_device *hid) { struct usbhid_device *usbhid = hid->driver_data; + int i; if (WARN_ON(!usbhid)) return; @@ -1208,7 +1243,15 @@ static void usbhid_stop(struct hid_devic spin_lock_irq(&usbhid->lock); /* Sync with error and led handlers */ set_bit(HID_DISCONNECTED, &usbhid->iofl); spin_unlock_irq(&usbhid->lock); - usb_kill_urb(usbhid->urbin); + + for (i = 0; i < usbhid->n_inurbs; i++) { + usb_kill_urb(usbhid->inurbs[i]); + usb_free_urb(usbhid->inurbs[i]); + } + + kfree(usbhid->inurbs); + usbhid->inurbs = NULL; + usb_kill_urb(usbhid->urbout); usb_kill_urb(usbhid->urbctrl); @@ -1216,10 +1259,8 @@ static void usbhid_stop(struct hid_devic hid->claimed = 0; - usb_free_urb(usbhid->urbin); usb_free_urb(usbhid->urbctrl); usb_free_urb(usbhid->urbout); - usbhid->urbin = NULL; /* don't mess up next start */ usbhid->urbctrl = NULL; usbhid->urbout = NULL; @@ -1407,8 +1448,13 @@ static void hid_cancel_delayed_stuff(str static void hid_cease_io(struct usbhid_device *usbhid) { + int i; + del_timer_sync(&usbhid->io_retry); - usb_kill_urb(usbhid->urbin); + + for (i = 0; i < usbhid->n_inurbs; i++) + usb_kill_urb(usbhid->inurbs[i]); + usb_kill_urb(usbhid->urbctrl); usb_kill_urb(usbhid->urbout); } diff -uprN -X linux-3.10-vanilla/Documentation/dontdiff linux-3.10-vanilla/drivers/hid/usbhid/usbhid.h linux-3.10-dev/drivers/hid/usbhid/usbhid.h --- linux-3.10-vanilla/drivers/hid/usbhid/usbhid.h 2013-06-30 17:13:29.000000000 -0500 +++ linux-3.10-dev/drivers/hid/usbhid/usbhid.h 2013-07-22 06:17:24.413184441 -0500 @@ -66,7 +66,8 @@ struct usbhid_device { unsigned int bufsize; /* URB buffer size */ - struct urb *urbin; /* Input URB */ + int n_inurbs; /* Number of input URBs */ + struct urb **inurbs; /* Input URBs */ char *inbuf; /* Input buffer */ dma_addr_t inbuf_dma; /* Input buffer dma */ -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html