# 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.573 -> 1.574 # drivers/usb/hcd.c 1.13 -> 1.14 # # The following is the BitKeeper ChangeSet Log # -------------------------------------------- # 02/03/28 david-b@pacbell.net 1.574 # USB hcd driver updates # # - Nitpickey bugfix to root hub config descriptors ... can't use # the same one for high and full speed, since the encoding # is different (255 ms FS == 0xff, 256 ms HS == 0x12). # - Related, force period to 1/4 second rather than doing # any sanity checking for the roothub timer (from Georg) # - Don't "giveback" urbs on submit path errors (from Georg) # ... means they don't get completion callbacks # - Additional error checks on URB data (from Georg) # - Uses for unlink synchronization # - The "already unlinking" error case is reported like other # unlinking errors (not as success) # - Ripped out urb->next handling ... it wasn't compatible # with the ISO loop model, and at this point I believe it # should be completely replaced with queuing urbs inside # of the HCDs. (Every HCD handles it for ISO, UHCI needs # a magic flag to enable it for bulk ...) # -------------------------------------------- # diff -Nru a/drivers/usb/hcd.c b/drivers/usb/hcd.c --- a/drivers/usb/hcd.c Wed Apr 3 16:39:16 2002 +++ b/drivers/usb/hcd.c Wed Apr 3 16:39:16 2002 @@ -37,6 +37,7 @@ #include #include #include +#include #include /* for UTS_SYSNAME */ @@ -169,9 +170,9 @@ /*-------------------------------------------------------------------------*/ -/* Configuration descriptor for all our root hubs */ +/* Configuration descriptors for our root hubs */ -static const u8 rh_config_descriptor [] = { +static const u8 fs_rh_config_descriptor [] = { /* one configuration */ 0x09, /* __u8 bLength; */ @@ -215,7 +216,54 @@ 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ 0x03, /* __u8 ep_bmAttributes; Interrupt */ 0x02, 0x00, /* __u16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */ - 0x0c /* __u8 ep_bInterval; (12ms -- usb 2.0 spec) */ + 0xff /* __u8 ep_bInterval; (255ms -- usb 2.0 spec) */ +}; + +static const u8 hs_rh_config_descriptor [] = { + + /* one configuration */ + 0x09, /* __u8 bLength; */ + 0x02, /* __u8 bDescriptorType; Configuration */ + 0x19, 0x00, /* __u16 wTotalLength; */ + 0x01, /* __u8 bNumInterfaces; (1) */ + 0x01, /* __u8 bConfigurationValue; */ + 0x00, /* __u8 iConfiguration; */ + 0x40, /* __u8 bmAttributes; + Bit 7: Bus-powered, + 6: Self-powered, + 5 Remote-wakwup, + 4..0: resvd */ + 0x00, /* __u8 MaxPower; */ + + /* USB 1.1: + * USB 2.0, single TT organization (mandatory): + * one interface, protocol 0 + * + * USB 2.0, multiple TT organization (optional): + * two interfaces, protocols 1 (like single TT) + * and 2 (multiple TT mode) ... config is + * sometimes settable + * NOT IMPLEMENTED + */ + + /* one interface */ + 0x09, /* __u8 if_bLength; */ + 0x04, /* __u8 if_bDescriptorType; Interface */ + 0x00, /* __u8 if_bInterfaceNumber; */ + 0x00, /* __u8 if_bAlternateSetting; */ + 0x01, /* __u8 if_bNumEndpoints; */ + 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ + 0x00, /* __u8 if_bInterfaceSubClass; */ + 0x00, /* __u8 if_bInterfaceProtocol; [usb1.1 or single tt] */ + 0x00, /* __u8 if_iInterface; */ + + /* one endpoint (status change endpoint) */ + 0x07, /* __u8 ep_bLength; */ + 0x05, /* __u8 ep_bDescriptorType; Endpoint */ + 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* __u8 ep_bmAttributes; Interrupt */ + 0x02, 0x00, /* __u16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */ + 0x0c /* __u8 ep_bInterval; (256ms -- usb 2.0 spec) */ }; /*-------------------------------------------------------------------------*/ @@ -333,8 +381,13 @@ len = 18; break; case USB_DT_CONFIG << 8: - bufp = rh_config_descriptor; - len = sizeof rh_config_descriptor; + if (hcd->driver->flags & HCD_USB2) { + bufp = hs_rh_config_descriptor; + len = sizeof hs_rh_config_descriptor; + } else { + bufp = fs_rh_config_descriptor; + len = sizeof fs_rh_config_descriptor; + } break; case USB_DT_STRING << 8: urb->actual_length = rh_string ( @@ -428,10 +481,8 @@ init_timer (&hcd->rh_timer); hcd->rh_timer.function = rh_report_status; hcd->rh_timer.data = (unsigned long) urb; - hcd->rh_timer.expires = jiffies - + (HZ * (urb->interval < 30 - ? 30 - : urb->interval)) / 1000; + /* USB 2.0 spec says 256msec; this is close enough */ + hcd->rh_timer.expires = jiffies + HZ/4; add_timer (&hcd->rh_timer); return 0; } @@ -1256,8 +1307,29 @@ /*-------------------------------------------------------------------------*/ +static void urb_unlink (struct urb *urb) +{ + unsigned long flags; + struct usb_device *dev; + + /* Release any periodic transfer bandwidth */ + if (urb->bandwidth) + usb_release_bandwidth (urb->dev, urb, + usb_pipeisoc (urb->pipe)); + + /* clear all state linking urb to this dev (and hcd) */ + + spin_lock_irqsave (&hcd_data_lock, flags); + list_del_init (&urb->urb_list); + dev = urb->dev; + urb->dev = NULL; + usb_dec_dev_use (dev); + spin_unlock_irqrestore (&hcd_data_lock, flags); +} + + /* may be called in any context with a valid urb->dev usecount */ -/* caller surrenders "ownership" of urb (and chain at urb->next). */ +/* caller surrenders "ownership" of urb */ static int hcd_submit_urb (struct urb *urb, int mem_flags) { @@ -1265,13 +1337,14 @@ struct usb_hcd *hcd; struct hcd_dev *dev; unsigned long flags; - int pipe, temp; + int pipe, temp, max; if (!urb || urb->hcpriv || !urb->complete) return -EINVAL; urb->status = -EINPROGRESS; urb->actual_length = 0; + urb->bandwidth = 0; INIT_LIST_HEAD (&urb->urb_list); if (!urb->dev || !urb->dev->bus || urb->dev->devnum <= 0) @@ -1290,7 +1363,62 @@ usb_pipeout (pipe))) return -EPIPE; + /* FIXME there should be a sharable lock protecting us against + * config/altsetting changes and disconnects, kicking in here. + */ + + /* Sanity check, so HCDs can rely on clean data */ + max = usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe)); + if (max <= 0) { + err ("bogus endpoint (bad maxpacket)"); + return -EINVAL; + } + + /* "high bandwidth" mode, 1-3 packets/uframe? */ + if (urb->dev->speed == USB_SPEED_HIGH) { + int mult; + switch (temp) { + case PIPE_ISOCHRONOUS: + case PIPE_INTERRUPT: + mult = 1 + ((max >> 11) & 0x03); + max &= 0x03ff; + max *= mult; + } + } + + /* periodic transfers limit size per frame/uframe */ + switch (temp) { + case PIPE_ISOCHRONOUS: { + int n, len; + + if (urb->number_of_packets <= 0) + return -EINVAL; + for (n = 0; n < urb->number_of_packets; n++) { + len = urb->iso_frame_desc [n].length; + if (len < 0 || len > max) + return -EINVAL; + } + + } + break; + case PIPE_INTERRUPT: + if (urb->transfer_buffer_length > max) + return -EINVAL; + } + + /* the I/O buffer must usually be mapped/unmapped */ + if (urb->transfer_buffer_length < 0) + return -EINVAL; + + if (urb->next) { + warn ("use explicit queuing not urb->next"); + return -EINVAL; + } + #ifdef DEBUG + /* stuff that drivers shouldn't do, but which shouldn't + * cause problems in HCDs if they get it wrong. + */ { unsigned int orig_flags = urb->transfer_flags; unsigned int allowed; @@ -1315,10 +1443,12 @@ } urb->transfer_flags &= allowed; - /* warn if submitter gave bogus flags */ - if (urb->transfer_flags != orig_flags) + /* fail if submitter gave bogus flags */ + if (urb->transfer_flags != orig_flags) { err ("BOGUS urb flags, %x --> %x", orig_flags, urb->transfer_flags); + return -EINVAL; + } } #endif /* @@ -1366,6 +1496,7 @@ urb->interval = temp; } + /* * FIXME: make urb timeouts be generic, keeping the HCD cores * as simple as possible. @@ -1396,20 +1527,22 @@ status = -ESHUTDOWN; } spin_unlock_irqrestore (&hcd_data_lock, flags); + if (status) + return status; - if (!status) { - if (urb->dev == hcd->bus->root_hub) - status = rh_urb_enqueue (hcd, urb); - else - status = hcd->driver->urb_enqueue (hcd, urb, mem_flags); - } - if (status) { - if (urb->dev) { - urb->status = status; - usb_hcd_giveback_urb (hcd, urb); - } - } - return 0; + /* temporarily up refcount while queueing it in the HCD, + * since we report some queuing/setup errors ourselves + */ + urb = usb_get_urb (urb); + if (urb->dev == hcd->bus->root_hub) + status = rh_urb_enqueue (hcd, urb); + else + status = hcd->driver->urb_enqueue (hcd, urb, mem_flags); + /* urb->dev got nulled if hcd called giveback for us */ + if (status && urb->dev) + urb_unlink (urb); + usb_put_urb (urb); + return status; } /*-------------------------------------------------------------------------*/ @@ -1425,7 +1558,7 @@ struct completion_splice { // modified urb context: /* did we complete? */ - int done; + struct completion done; /* original urb data */ void (*complete)(struct urb *); @@ -1443,7 +1576,8 @@ urb->context = splice->context; urb->complete (urb); - splice->done = 1; + /* then let the synchronous unlink call complete */ + complete (&splice->done); } /* @@ -1510,7 +1644,7 @@ case PIPE_CONTROL: case PIPE_BULK: if (urb->status != -EINPROGRESS) { - retval = 0; + retval = -EINVAL; goto done; } } @@ -1525,7 +1659,7 @@ goto done; } /* synchronous unlink: block till we see the completion */ - splice.done = 0; + init_completion (&splice.done); splice.complete = urb->complete; splice.context = urb->context; urb->complete = unlink_complete; @@ -1551,12 +1685,9 @@ if (!(urb->transfer_flags & (USB_ASYNC_UNLINK|USB_TIMEOUT_KILLED)) && HCD_IS_RUNNING (hcd->state) && !retval) { - while (!splice.done) { - set_current_state (TASK_UNINTERRUPTIBLE); - schedule_timeout ((2/*msec*/ * HZ) / 1000); - dbg ("%s: wait for giveback urb %p", - hcd->bus_name, urb); - } + dbg ("%s: wait for giveback urb %p", + hcd->bus_name, urb); + wait_for_completion (&splice.done); } else if ((urb->transfer_flags & USB_ASYNC_UNLINK) && retval == 0) { return -EINPROGRESS; } @@ -1657,27 +1788,13 @@ * and will be reissued. They should just call their completion handlers * until the urb is returned to the device driver by unlinking. * - * In common cases, urb->next will be submitted before the completion - * function gets called. That's not done if the URB includes error - * status (including unlinking). + * NOTE that no urb->next processing is done, even for isochronous URBs. + * ISO streaming functionality can be achieved by having completion handlers + * re-queue URBs. Such explicit queuing doesn't discard error reports. */ void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb) { - unsigned long flags; - struct usb_device *dev; - - /* Release periodic transfer bandwidth */ - if (urb->bandwidth) - usb_release_bandwidth (urb->dev, urb, - usb_pipeisoc (urb->pipe)); - - /* clear all state linking urb to this dev (and hcd) */ - - spin_lock_irqsave (&hcd_data_lock, flags); - list_del_init (&urb->urb_list); - dev = urb->dev; - urb->dev = NULL; - spin_unlock_irqrestore (&hcd_data_lock, flags); + urb_unlink (urb); // NOTE: a generic device/urb monitoring hook would go here. // hcd_monitor_hook(MONITOR_URB_FINISH, urb, dev) @@ -1689,24 +1806,7 @@ dbg ("giveback urb %p status %d len %d", urb, urb->status, urb->actual_length); - /* if no error, make sure urb->next progresses */ - else if (urb->next) { - int status; - - status = usb_submit_urb (urb->next, GFP_ATOMIC); - if (status) { - dbg ("urb %p chain fail, %d", urb->next, status); - urb->next->status = -ENOTCONN; - } - - /* HCDs never modify the urb->next chain, and only use it here, - * so that if urb->complete sees an URB there with -ENOTCONN, - * it knows the driver chained it but it couldn't be submitted. - */ - } - /* pass ownership to the completion handler */ - usb_dec_dev_use (dev); urb->complete (urb); usb_put_urb (urb); }