Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Input: HID - handle multi-transascion reports

Fixes handling of multi-transaction reports for HID devices. New
function hid_size_buffers() that calculates the longest report
for each endpoint and stores the result in the hid_device object.
These lengths are used to allocate buffers that are large enough
to store any report on the endpoint. For compatibility, the minimum
size for an endpoint buffer set to HID_BUFFER_SIZE rather than the
known optimal case (the longest report length).

It fixes bug #3063 in bugzilla.

Signed-off-by: Michael Haboustak <mike-@cinci.rr.com>

I simplified the patch a bit to use just a single buffer size.

Signed-off-by: Vojtech Pavlik <vojtech@suse.cz>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>

authored by

Michael Haboustak and committed by
Dmitry Torokhov
bf0964dc 903b126b

+48 -19
+44 -18
drivers/usb/input/hid-core.c
··· 2 2 * USB HID support for Linux 3 3 * 4 4 * Copyright (c) 1999 Andreas Gal 5 - * Copyright (c) 2000-2001 Vojtech Pavlik <vojtech@suse.cz> 5 + * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> 6 + * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc 6 7 */ 7 8 8 9 /* ··· 39 38 * Version Information 40 39 */ 41 40 42 - #define DRIVER_VERSION "v2.01" 41 + #define DRIVER_VERSION "v2.6" 43 42 #define DRIVER_AUTHOR "Andreas Gal, Vojtech Pavlik" 44 43 #define DRIVER_DESC "USB HID core driver" 45 44 #define DRIVER_LICENSE "GPL" ··· 1059 1058 if (maxpacket > 0) { 1060 1059 padlen = (len + maxpacket - 1) / maxpacket; 1061 1060 padlen *= maxpacket; 1062 - if (padlen > HID_BUFFER_SIZE) 1063 - padlen = HID_BUFFER_SIZE; 1061 + if (padlen > hid->bufsize) 1062 + padlen = hid->bufsize; 1064 1063 } else 1065 1064 padlen = 0; 1066 1065 hid->urbctrl->transfer_buffer_length = padlen; ··· 1285 1284 struct hid_report *report; 1286 1285 int err, ret; 1287 1286 1288 - list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT].report_list, list) { 1289 - int size = ((report->size - 1) >> 3) + 1 + hid->report_enum[HID_INPUT_REPORT].numbered; 1290 - if (size > HID_BUFFER_SIZE) size = HID_BUFFER_SIZE; 1291 - if (size > hid->urbin->transfer_buffer_length) 1292 - hid->urbin->transfer_buffer_length = size; 1287 + list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT].report_list, list) 1293 1288 hid_submit_report(hid, report, USB_DIR_IN); 1294 - } 1295 1289 1296 1290 list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT].report_list, list) 1297 1291 hid_submit_report(hid, report, USB_DIR_IN); ··· 1560 1564 { 0, 0 } 1561 1565 }; 1562 1566 1567 + /* 1568 + * Traverse the supplied list of reports and find the longest 1569 + */ 1570 + static void hid_find_max_report(struct hid_device *hid, unsigned int type, int *max) 1571 + { 1572 + struct hid_report *report; 1573 + int size; 1574 + 1575 + list_for_each_entry(report, &hid->report_enum[type].report_list, list) { 1576 + size = ((report->size - 1) >> 3) + 1; 1577 + if (type == HID_INPUT_REPORT && hid->report_enum[type].numbered) 1578 + size++; 1579 + if (*max < size) 1580 + *max = size; 1581 + } 1582 + } 1583 + 1563 1584 static int hid_alloc_buffers(struct usb_device *dev, struct hid_device *hid) 1564 1585 { 1565 - if (!(hid->inbuf = usb_buffer_alloc(dev, HID_BUFFER_SIZE, SLAB_ATOMIC, &hid->inbuf_dma))) 1586 + if (!(hid->inbuf = usb_buffer_alloc(dev, hid->bufsize, SLAB_ATOMIC, &hid->inbuf_dma))) 1566 1587 return -1; 1567 - if (!(hid->outbuf = usb_buffer_alloc(dev, HID_BUFFER_SIZE, SLAB_ATOMIC, &hid->outbuf_dma))) 1588 + if (!(hid->outbuf = usb_buffer_alloc(dev, hid->bufsize, SLAB_ATOMIC, &hid->outbuf_dma))) 1568 1589 return -1; 1569 1590 if (!(hid->cr = usb_buffer_alloc(dev, sizeof(*(hid->cr)), SLAB_ATOMIC, &hid->cr_dma))) 1570 1591 return -1; 1571 - if (!(hid->ctrlbuf = usb_buffer_alloc(dev, HID_BUFFER_SIZE, SLAB_ATOMIC, &hid->ctrlbuf_dma))) 1592 + if (!(hid->ctrlbuf = usb_buffer_alloc(dev, hid->bufsize, SLAB_ATOMIC, &hid->ctrlbuf_dma))) 1572 1593 return -1; 1573 1594 1574 1595 return 0; ··· 1594 1581 static void hid_free_buffers(struct usb_device *dev, struct hid_device *hid) 1595 1582 { 1596 1583 if (hid->inbuf) 1597 - usb_buffer_free(dev, HID_BUFFER_SIZE, hid->inbuf, hid->inbuf_dma); 1584 + usb_buffer_free(dev, hid->bufsize, hid->inbuf, hid->inbuf_dma); 1598 1585 if (hid->outbuf) 1599 - usb_buffer_free(dev, HID_BUFFER_SIZE, hid->outbuf, hid->outbuf_dma); 1586 + usb_buffer_free(dev, hid->bufsize, hid->outbuf, hid->outbuf_dma); 1600 1587 if (hid->cr) 1601 1588 usb_buffer_free(dev, sizeof(*(hid->cr)), hid->cr, hid->cr_dma); 1602 1589 if (hid->ctrlbuf) 1603 - usb_buffer_free(dev, HID_BUFFER_SIZE, hid->ctrlbuf, hid->ctrlbuf_dma); 1590 + usb_buffer_free(dev, hid->bufsize, hid->ctrlbuf, hid->ctrlbuf_dma); 1604 1591 } 1605 1592 1606 1593 static struct hid_device *usb_hid_configure(struct usb_interface *intf) ··· 1611 1598 struct hid_device *hid; 1612 1599 unsigned quirks = 0, rsize = 0; 1613 1600 char *buf, *rdesc; 1614 - int n; 1601 + int n, insize = 0; 1615 1602 1616 1603 for (n = 0; hid_blacklist[n].idVendor; n++) 1617 1604 if ((hid_blacklist[n].idVendor == le16_to_cpu(dev->descriptor.idVendor)) && ··· 1665 1652 kfree(rdesc); 1666 1653 hid->quirks = quirks; 1667 1654 1655 + hid->bufsize = HID_MIN_BUFFER_SIZE; 1656 + hid_find_max_report(hid, HID_INPUT_REPORT, &hid->bufsize); 1657 + hid_find_max_report(hid, HID_OUTPUT_REPORT, &hid->bufsize); 1658 + hid_find_max_report(hid, HID_FEATURE_REPORT, &hid->bufsize); 1659 + 1660 + if (hid->bufsize > HID_MAX_BUFFER_SIZE) 1661 + hid->bufsize = HID_MAX_BUFFER_SIZE; 1662 + 1663 + hid_find_max_report(hid, HID_INPUT_REPORT, &insize); 1664 + 1665 + if (insize > HID_MAX_BUFFER_SIZE) 1666 + insize = HID_MAX_BUFFER_SIZE; 1667 + 1668 1668 if (hid_alloc_buffers(dev, hid)) { 1669 1669 hid_free_buffers(dev, hid); 1670 1670 goto fail; ··· 1708 1682 if (!(hid->urbin = usb_alloc_urb(0, GFP_KERNEL))) 1709 1683 goto fail; 1710 1684 pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); 1711 - usb_fill_int_urb(hid->urbin, dev, pipe, hid->inbuf, 0, 1685 + usb_fill_int_urb(hid->urbin, dev, pipe, hid->inbuf, insize, 1712 1686 hid_irq_in, hid, interval); 1713 1687 hid->urbin->transfer_dma = hid->inbuf_dma; 1714 1688 hid->urbin->transfer_flags |=(URB_NO_TRANSFER_DMA_MAP | URB_ASYNC_UNLINK);
+4 -1
drivers/usb/input/hid.h
··· 351 351 352 352 #define HID_REPORT_TYPES 3 353 353 354 - #define HID_BUFFER_SIZE 64 /* use 64 for compatibility with all possible packetlen */ 354 + #define HID_MIN_BUFFER_SIZE 64 /* make sure there is at least a packet size of space */ 355 + #define HID_MAX_BUFFER_SIZE 4096 /* 4kb */ 355 356 #define HID_CONTROL_FIFO_SIZE 256 /* to init devices with >100 reports */ 356 357 #define HID_OUTPUT_FIFO_SIZE 64 357 358 ··· 389 388 int ifnum; /* USB interface number */ 390 389 391 390 unsigned long iofl; /* I/O flags (CTRL_RUNNING, OUT_RUNNING) */ 391 + 392 + unsigned int bufsize; /* URB buffer size */ 392 393 393 394 struct urb *urbin; /* Input URB */ 394 395 char *inbuf; /* Input buffer */