# 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.469 -> 1.470 # drivers/usb/core/urb.c 1.2 -> 1.3 # drivers/usb/core/hcd.c 1.22 -> 1.23 # # The following is the BitKeeper ChangeSet Log # -------------------------------------------- # 02/06/07 david-b@pacbell.net 1.470 # [PATCH] relocate error checks # # As was discussed a few weeks back, this moves most of the # sanity checks and input conditioning for the HCD framework's # usb_submit_urb() support directly into usb_submit_urb(), so # that all HCDs (not just those using the sharable HCD framework # support) can rely on them. # -------------------------------------------- # diff -Nru a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c --- a/drivers/usb/core/hcd.c Sat Jun 8 15:16:06 2002 +++ b/drivers/usb/core/hcd.c Sat Jun 8 15:16:06 2002 @@ -967,170 +967,21 @@ } -/* may be called in any context with a valid urb->dev usecount */ -/* caller surrenders "ownership" of urb */ - +/* may be called in any context with a valid urb->dev usecount + * caller surrenders "ownership" of urb + * expects usb_submit_urb() to have sanity checked and conditioned all + * inputs in the urb + */ static int hcd_submit_urb (struct urb *urb, int mem_flags) { int status; - struct usb_hcd *hcd; - struct hcd_dev *dev; + struct usb_hcd *hcd = urb->dev->bus->hcpriv; + struct hcd_dev *dev = urb->dev->hcpriv; unsigned long flags; - 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) - return -ENODEV; - hcd = urb->dev->bus->hcpriv; - dev = urb->dev->hcpriv; if (!hcd || !dev) return -ENODEV; - /* can't submit new urbs when quiescing, halted, ... */ - if (hcd->state == USB_STATE_QUIESCING || !HCD_IS_RUNNING (hcd->state)) - return -ESHUTDOWN; - pipe = urb->pipe; - temp = usb_pipetype (urb->pipe); - if (usb_endpoint_halted (urb->dev, usb_pipeendpoint (pipe), - 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; - -#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; - - /* enforce simple/standard policy */ - allowed = USB_ASYNC_UNLINK; // affects later unlinks - allowed |= USB_NO_FSBR; // only affects UHCI - switch (temp) { - case PIPE_CONTROL: - allowed |= USB_DISABLE_SPD; - break; - case PIPE_BULK: - allowed |= USB_DISABLE_SPD | USB_QUEUE_BULK - | USB_ZERO_PACKET | URB_NO_INTERRUPT; - break; - case PIPE_INTERRUPT: - allowed |= USB_DISABLE_SPD; - break; - case PIPE_ISOCHRONOUS: - allowed |= USB_ISO_ASAP; - break; - } - urb->transfer_flags &= allowed; - - /* 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 - /* - * Force periodic transfer intervals to be legal values that are - * a power of two (so HCDs don't need to). - * - * FIXME want bus->{intr,iso}_sched_horizon values here. Each HC - * supports different values... this uses EHCI/UHCI defaults (and - * EHCI can use smaller non-default values). - */ - switch (temp) { - case PIPE_ISOCHRONOUS: - case PIPE_INTERRUPT: - /* too small? */ - if (urb->interval <= 0) - return -EINVAL; - /* too big? */ - switch (urb->dev->speed) { - case USB_SPEED_HIGH: /* units are microframes */ - // NOTE usb handles 2^15 - if (urb->interval > (1024 * 8)) - urb->interval = 1024 * 8; - temp = 1024 * 8; - break; - case USB_SPEED_FULL: /* units are frames/msec */ - case USB_SPEED_LOW: - if (temp == PIPE_INTERRUPT) { - if (urb->interval > 255) - return -EINVAL; - // NOTE ohci only handles up to 32 - temp = 128; - } else { - if (urb->interval > 1024) - urb->interval = 1024; - // NOTE usb and ohci handle up to 2^15 - temp = 1024; - } - break; - default: - return -EINVAL; - } - /* power of two? */ - while (temp > urb->interval) - temp >>= 1; - urb->interval = temp; - } - - /* * FIXME: make urb timeouts be generic, keeping the HCD cores * as simple as possible. @@ -1140,9 +991,6 @@ // hcd_monitor_hook(MONITOR_URB_SUBMIT, urb) // It would catch submission paths for all urbs. - /* increment urb's reference count, we now control it. */ - urb = usb_get_urb(urb); - /* * Atomically queue the urb, first to our records, then to the HCD. * Access to urb->status is controlled by urb->lock ... changes on @@ -1164,18 +1012,15 @@ if (status) return status; - /* temporarily up refcount while queueing it in the HCD, - * since we report some queuing/setup errors ourselves + /* increment urb's reference count as part of giving it to the HCD + * (which now controls it). HCD guarantees that it either returns + * an error or calls giveback(), but not both. */ urb = usb_get_urb (urb); if (urb->dev == hcd->self.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; } diff -Nru a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c --- a/drivers/usb/core/urb.c Sat Jun 8 15:16:06 2002 +++ b/drivers/usb/core/urb.c Sat Jun 8 15:16:06 2002 @@ -182,15 +182,170 @@ */ int usb_submit_urb(struct urb *urb, int mem_flags) { + int pipe, temp, max; + struct usb_device *dev; + struct usb_operations *op; + int is_out; - if (urb && urb->dev && urb->dev->bus && urb->dev->bus->op) { - if (usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)) <= 0) { - err("%s: pipe %x has invalid size (<= 0)", __FUNCTION__, urb->pipe); + if (!urb || urb->hcpriv || !urb->complete) + return -EINVAL; + if (!(dev = urb->dev) || !dev->bus || dev->devnum <= 0) + return -ENODEV; + if (!(op = dev->bus->op) || !op->submit_urb) + return -ENODEV; + + urb->status = -EINPROGRESS; + urb->actual_length = 0; + urb->bandwidth = 0; + + /* Lots of sanity checks, so HCDs can rely on clean data + * and don't need to duplicate tests + */ + pipe = urb->pipe; + temp = usb_pipetype (pipe); + is_out = usb_pipeout (pipe); + + /* (actually HCDs may need to duplicate this, endpoint might yet + * stall due to queued bulk/intr transactions that complete after + * we check) + */ + if (usb_endpoint_halted (dev, usb_pipeendpoint (pipe), is_out)) + return -EPIPE; + + /* FIXME there should be a sharable lock protecting us against + * config/altsetting changes and disconnects, kicking in here. + * (here == before maxpacket, and eventually endpoint type, + * checks get made.) + */ + + max = usb_maxpacket (dev, pipe, is_out); + if (max <= 0) { + dbg ("%s: bogus endpoint %d-%s on usb-%s-%s (bad maxpacket %d)", + __FUNCTION__, + usb_pipeendpoint (pipe), is_out ? "OUT" : "IN", + dev->bus->bus_name, dev->devpath, + max); + return -EMSGSIZE; + } + + /* "high bandwidth" mode, 1-3 packets/uframe? */ + if (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 -EMSGSIZE; + } + + } + break; + case PIPE_INTERRUPT: + if (urb->transfer_buffer_length > max) return -EMSGSIZE; + } + + /* the I/O buffer must be mapped/unmapped, except when length=0 */ + if (urb->transfer_buffer_length < 0) + return -EMSGSIZE; + +#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; + + /* enforce simple/standard policy */ + allowed = USB_ASYNC_UNLINK; // affects later unlinks + allowed |= USB_NO_FSBR; // only affects UHCI + switch (temp) { + case PIPE_CONTROL: + allowed |= USB_DISABLE_SPD; + break; + case PIPE_BULK: + allowed |= USB_DISABLE_SPD | USB_QUEUE_BULK + | USB_ZERO_PACKET | URB_NO_INTERRUPT; + break; + case PIPE_INTERRUPT: + allowed |= USB_DISABLE_SPD; + break; + case PIPE_ISOCHRONOUS: + allowed |= USB_ISO_ASAP; + break; + } + urb->transfer_flags &= allowed; + + /* 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 + /* + * Force periodic transfer intervals to be legal values that are + * a power of two (so HCDs don't need to). + * + * FIXME want bus->{intr,iso}_sched_horizon values here. Each HC + * supports different values... this uses EHCI/UHCI defaults (and + * EHCI can use smaller non-default values). + */ + switch (temp) { + case PIPE_ISOCHRONOUS: + case PIPE_INTERRUPT: + /* too small? */ + if (urb->interval <= 0) + return -EINVAL; + /* too big? */ + switch (dev->speed) { + case USB_SPEED_HIGH: /* units are microframes */ + // NOTE usb handles 2^15 + if (urb->interval > (1024 * 8)) + urb->interval = 1024 * 8; + temp = 1024 * 8; + break; + case USB_SPEED_FULL: /* units are frames/msec */ + case USB_SPEED_LOW: + if (temp == PIPE_INTERRUPT) { + if (urb->interval > 255) + return -EINVAL; + // NOTE ohci only handles up to 32 + temp = 128; + } else { + if (urb->interval > 1024) + urb->interval = 1024; + // NOTE usb and ohci handle up to 2^15 + temp = 1024; + } + break; + default: + return -EINVAL; } - return urb->dev->bus->op->submit_urb(urb, mem_flags); + /* power of two? */ + while (temp > urb->interval) + temp >>= 1; + urb->interval = temp; } - return -ENODEV; + + return op->submit_urb (urb, mem_flags); } /*-------------------------------------------------------------------*/