# This is a BitKeeper generated patch for the following project: # Project Name: Linux kernel tree # This patch format is intended for GNU patch command version 2.5 or higher. # This patch includes the following deltas: # ChangeSet 1.549 -> 1.550 # drivers/usb/devices.c 1.7 -> 1.8 # # The following is the BitKeeper ChangeSet Log # -------------------------------------------- # 02/03/19 david-b@pacbell.net 1.550 # USB usbfs periodic endpoint/bandwidth reporting # # This is an updated version of a patch I sent around a # while back. It's against 2.5.7-pre1 (so presumably is # fine on 2.5.7), and addresses feedback against that # earlier patch. # # It's bugfixes, mostly for highspeed support, to what # /proc/bus/usb/devices shows: # # - Shows isochronous periods correctly (logarithmic # encoding, possibly 1/2/4 microframes if highspeed) # - Likewise for high-speed interrupt periods (similar) # - Makes high bandwidth endpoints look like they # just do bigger packets (up to 3 KBytes/uframe) # - Shows highspeed bandwidth correctlly (80% reserved, # vs 90% reserved for full/low speed). # -------------------------------------------- # diff -Nru a/drivers/usb/devices.c b/drivers/usb/devices.c --- a/drivers/usb/devices.c Wed Apr 3 16:40:00 2002 +++ b/drivers/usb/devices.c Wed Apr 3 16:40:00 2002 @@ -105,8 +105,8 @@ "I: If#=%2d Alt=%2d #EPs=%2d Cls=%02x(%-5s) Sub=%02x Prot=%02x Driver=%s\n"; static char *format_endpt = -/* E: Ad=xx(s) Atr=xx(ssss) MxPS=dddd Ivl=dddms */ - "E: Ad=%02x(%c) Atr=%02x(%-4s) MxPS=%4d Ivl=%3dms\n"; +/* E: Ad=xx(s) Atr=xx(ssss) MxPS=dddd Ivl=D?s */ + "E: Ad=%02x(%c) Atr=%02x(%-4s) MxPS=%4d Ivl=%d%cs\n"; /* @@ -166,24 +166,71 @@ return (clas_info[ix].class_name); } -static char *usb_dump_endpoint_descriptor(char *start, char *end, const struct usb_endpoint_descriptor *desc) -{ - char *EndpointType [4] = {"Ctrl", "Isoc", "Bulk", "Int."}; +static char *usb_dump_endpoint_descriptor ( + int speed, + char *start, + char *end, + const struct usb_endpoint_descriptor *desc +) +{ + char dir, unit, *type; + unsigned interval, in, bandwidth = 1; if (start > end) return start; - start += sprintf(start, format_endpt, desc->bEndpointAddress, - (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_CONTROL - ? 'B' : /* bidirectional */ - (desc->bEndpointAddress & USB_DIR_IN) ? 'I' : 'O', - desc->bmAttributes, EndpointType[desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK], - desc->wMaxPacketSize, desc->bInterval); - return start; -} + in = (desc->bEndpointAddress & USB_DIR_IN); + dir = in ? 'I' : 'O'; + if (speed == USB_SPEED_HIGH) { + switch (desc->wMaxPacketSize & (0x03 << 11)) { + case 1 << 11: bandwidth = 2; break; + case 2 << 11: bandwidth = 3; break; + } + } -static char *usb_dump_endpoint(char *start, char *end, const struct usb_endpoint_descriptor *endpoint) -{ - return usb_dump_endpoint_descriptor(start, end, endpoint); + /* this isn't checking for illegal values */ + switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { + case USB_ENDPOINT_XFER_CONTROL: + type = "Ctrl"; + if (speed == USB_SPEED_HIGH) /* uframes per NAK */ + interval = desc->bInterval; + else + interval = 0; + dir = 'B'; /* ctrl is bidirectional */ + break; + case USB_ENDPOINT_XFER_ISOC: + type = "Isoc"; + interval = 1 << (desc->bInterval - 1); + break; + case USB_ENDPOINT_XFER_BULK: + type = "Bulk"; + if (speed == USB_SPEED_HIGH && !in) /* uframes per NAK */ + interval = desc->bInterval; + else + interval = 0; + break; + case USB_ENDPOINT_XFER_INT: + type = "Int."; + if (speed == USB_SPEED_HIGH) { + interval = 1 << (desc->bInterval - 1); + } else + interval = desc->bInterval; + break; + default: /* "can't happen" */ + return start; + } + interval *= (speed == USB_SPEED_HIGH) ? 125 : 1000; + if (interval % 1000) + unit = 'u'; + else { + unit = 'm'; + interval /= 1000; + } + + start += sprintf(start, format_endpt, desc->bEndpointAddress, dir, + desc->bmAttributes, type, + (desc->wMaxPacketSize & 0x07ff) * bandwidth, + interval, unit); + return start; } static char *usb_dump_interface_descriptor(char *start, char *end, const struct usb_interface *iface, int setno) @@ -204,8 +251,13 @@ return start; } -static char *usb_dump_interface(char *start, char *end, const struct usb_interface *iface, int setno) -{ +static char *usb_dump_interface( + int speed, + char *start, + char *end, + const struct usb_interface *iface, + int setno +) { struct usb_interface_descriptor *desc = &iface->altsetting[setno]; int i; @@ -213,7 +265,8 @@ for (i = 0; i < desc->bNumEndpoints; i++) { if (start > end) return start; - start = usb_dump_endpoint(start, end, desc->endpoint + i); + start = usb_dump_endpoint_descriptor(speed, + start, end, desc->endpoint + i); } return start; } @@ -238,7 +291,13 @@ return start; } -static char *usb_dump_config(char *start, char *end, const struct usb_config_descriptor *config, int active) +static char *usb_dump_config ( + int speed, + char *start, + char *end, + const struct usb_config_descriptor *config, + int active +) { int i, j; struct usb_interface *interface; @@ -255,7 +314,8 @@ for (j = 0; j < interface->num_altsetting; j++) { if (start > end) return start; - start = usb_dump_interface(start, end, interface, j); + start = usb_dump_interface(speed, + start, end, interface, j); } } return start; @@ -336,8 +396,10 @@ for (i = 0; i < dev->descriptor.bNumConfigurations; i++) { if (start > end) return start; - start = usb_dump_config(start, end, dev->config + i, - (dev->config + i) == dev->actconfig); /* active ? */ + start = usb_dump_config(dev->speed, + start, end, dev->config + i, + /* active ? */ + (dev->config + i) == dev->actconfig); } return start; } @@ -429,16 +491,27 @@ * count = device count at this level */ /* If this is the root hub, display the bandwidth information */ - /* FIXME high speed reserves 20% frametime for non-periodic, - * while full/low speed reserves only 10% ... so this is wrong - * for high speed busses. also, change how bandwidth is recorded. - */ - if (level == 0) - data_end += sprintf(data_end, format_bandwidth, bus->bandwidth_allocated, - FRAME_TIME_MAX_USECS_ALLOC, - (100 * bus->bandwidth_allocated + FRAME_TIME_MAX_USECS_ALLOC / 2) / FRAME_TIME_MAX_USECS_ALLOC, - bus->bandwidth_int_reqs, bus->bandwidth_isoc_reqs); + if (level == 0) { + int max; + + /* high speed reserves 80%, full/low reserves 90% */ + if (usbdev->speed == USB_SPEED_HIGH) + max = 800; + else + max = FRAME_TIME_MAX_USECS_ALLOC; + + /* report "average" periodic allocation over a microsecond. + * the schedules are actually bursty, HCDs need to deal with + * that and just compute/report this average. + */ + data_end += sprintf(data_end, format_bandwidth, + bus->bandwidth_allocated, max, + (100 * bus->bandwidth_allocated + max / 2) + / max, + bus->bandwidth_int_reqs, + bus->bandwidth_isoc_reqs); + } data_end = usb_dump_desc(data_end, pages_start + (2 * PAGE_SIZE) - 256, usbdev); if (data_end > (pages_start + (2 * PAGE_SIZE) - 256))