Commit 95a376f3 authored by David Brownell's avatar David Brownell Committed by Greg Kroah-Hartman

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 <linux/completion.h> 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 ...)
parent 6567edc6
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
#include <linux/timer.h> #include <linux/timer.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/completion.h>
#include <linux/uts.h> /* for UTS_SYSNAME */ #include <linux/uts.h> /* for UTS_SYSNAME */
...@@ -169,9 +170,9 @@ static const u8 usb11_rh_dev_descriptor [18] = { ...@@ -169,9 +170,9 @@ static const u8 usb11_rh_dev_descriptor [18] = {
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
/* 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 */ /* one configuration */
0x09, /* __u8 bLength; */ 0x09, /* __u8 bLength; */
...@@ -215,7 +216,54 @@ static const u8 rh_config_descriptor [] = { ...@@ -215,7 +216,54 @@ static const u8 rh_config_descriptor [] = {
0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */
0x03, /* __u8 ep_bmAttributes; Interrupt */ 0x03, /* __u8 ep_bmAttributes; Interrupt */
0x02, 0x00, /* __u16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */ 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 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) ...@@ -333,8 +381,13 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
len = 18; len = 18;
break; break;
case USB_DT_CONFIG << 8: case USB_DT_CONFIG << 8:
bufp = rh_config_descriptor; if (hcd->driver->flags & HCD_USB2) {
len = sizeof rh_config_descriptor; bufp = hs_rh_config_descriptor;
len = sizeof hs_rh_config_descriptor;
} else {
bufp = fs_rh_config_descriptor;
len = sizeof fs_rh_config_descriptor;
}
break; break;
case USB_DT_STRING << 8: case USB_DT_STRING << 8:
urb->actual_length = rh_string ( urb->actual_length = rh_string (
...@@ -428,10 +481,8 @@ static int rh_status_urb (struct usb_hcd *hcd, struct urb *urb) ...@@ -428,10 +481,8 @@ static int rh_status_urb (struct usb_hcd *hcd, struct urb *urb)
init_timer (&hcd->rh_timer); init_timer (&hcd->rh_timer);
hcd->rh_timer.function = rh_report_status; hcd->rh_timer.function = rh_report_status;
hcd->rh_timer.data = (unsigned long) urb; hcd->rh_timer.data = (unsigned long) urb;
hcd->rh_timer.expires = jiffies /* USB 2.0 spec says 256msec; this is close enough */
+ (HZ * (urb->interval < 30 hcd->rh_timer.expires = jiffies + HZ/4;
? 30
: urb->interval)) / 1000;
add_timer (&hcd->rh_timer); add_timer (&hcd->rh_timer);
return 0; return 0;
} }
...@@ -1256,8 +1307,29 @@ static void hc_died (struct usb_hcd *hcd) ...@@ -1256,8 +1307,29 @@ static void hc_died (struct usb_hcd *hcd)
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
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 */ /* 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) static int hcd_submit_urb (struct urb *urb, int mem_flags)
{ {
...@@ -1265,13 +1337,14 @@ static int hcd_submit_urb (struct urb *urb, int mem_flags) ...@@ -1265,13 +1337,14 @@ static int hcd_submit_urb (struct urb *urb, int mem_flags)
struct usb_hcd *hcd; struct usb_hcd *hcd;
struct hcd_dev *dev; struct hcd_dev *dev;
unsigned long flags; unsigned long flags;
int pipe, temp; int pipe, temp, max;
if (!urb || urb->hcpriv || !urb->complete) if (!urb || urb->hcpriv || !urb->complete)
return -EINVAL; return -EINVAL;
urb->status = -EINPROGRESS; urb->status = -EINPROGRESS;
urb->actual_length = 0; urb->actual_length = 0;
urb->bandwidth = 0;
INIT_LIST_HEAD (&urb->urb_list); INIT_LIST_HEAD (&urb->urb_list);
if (!urb->dev || !urb->dev->bus || urb->dev->devnum <= 0) if (!urb->dev || !urb->dev->bus || urb->dev->devnum <= 0)
...@@ -1290,7 +1363,62 @@ static int hcd_submit_urb (struct urb *urb, int mem_flags) ...@@ -1290,7 +1363,62 @@ static int hcd_submit_urb (struct urb *urb, int mem_flags)
usb_pipeout (pipe))) usb_pipeout (pipe)))
return -EPIPE; 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 #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 orig_flags = urb->transfer_flags;
unsigned int allowed; unsigned int allowed;
...@@ -1315,10 +1443,12 @@ static int hcd_submit_urb (struct urb *urb, int mem_flags) ...@@ -1315,10 +1443,12 @@ static int hcd_submit_urb (struct urb *urb, int mem_flags)
} }
urb->transfer_flags &= allowed; urb->transfer_flags &= allowed;
/* warn if submitter gave bogus flags */ /* fail if submitter gave bogus flags */
if (urb->transfer_flags != orig_flags) if (urb->transfer_flags != orig_flags) {
err ("BOGUS urb flags, %x --> %x", err ("BOGUS urb flags, %x --> %x",
orig_flags, urb->transfer_flags); orig_flags, urb->transfer_flags);
return -EINVAL;
}
} }
#endif #endif
/* /*
...@@ -1366,6 +1496,7 @@ static int hcd_submit_urb (struct urb *urb, int mem_flags) ...@@ -1366,6 +1496,7 @@ static int hcd_submit_urb (struct urb *urb, int mem_flags)
urb->interval = temp; urb->interval = temp;
} }
/* /*
* FIXME: make urb timeouts be generic, keeping the HCD cores * FIXME: make urb timeouts be generic, keeping the HCD cores
* as simple as possible. * as simple as possible.
...@@ -1396,20 +1527,22 @@ static int hcd_submit_urb (struct urb *urb, int mem_flags) ...@@ -1396,20 +1527,22 @@ static int hcd_submit_urb (struct urb *urb, int mem_flags)
status = -ESHUTDOWN; status = -ESHUTDOWN;
} }
spin_unlock_irqrestore (&hcd_data_lock, flags); spin_unlock_irqrestore (&hcd_data_lock, flags);
if (status)
return status;
if (!status) { /* temporarily up refcount while queueing it in the HCD,
if (urb->dev == hcd->bus->root_hub) * since we report some queuing/setup errors ourselves
status = rh_urb_enqueue (hcd, urb); */
else urb = usb_get_urb (urb);
status = hcd->driver->urb_enqueue (hcd, urb, mem_flags); if (urb->dev == hcd->bus->root_hub)
} status = rh_urb_enqueue (hcd, urb);
if (status) { else
if (urb->dev) { status = hcd->driver->urb_enqueue (hcd, urb, mem_flags);
urb->status = status; /* urb->dev got nulled if hcd called giveback for us */
usb_hcd_giveback_urb (hcd, urb); if (status && urb->dev)
} urb_unlink (urb);
} usb_put_urb (urb);
return 0; return status;
} }
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
...@@ -1425,7 +1558,7 @@ static int hcd_get_frame_number (struct usb_device *udev) ...@@ -1425,7 +1558,7 @@ static int hcd_get_frame_number (struct usb_device *udev)
struct completion_splice { // modified urb context: struct completion_splice { // modified urb context:
/* did we complete? */ /* did we complete? */
int done; struct completion done;
/* original urb data */ /* original urb data */
void (*complete)(struct urb *); void (*complete)(struct urb *);
...@@ -1443,7 +1576,8 @@ static void unlink_complete (struct urb *urb) ...@@ -1443,7 +1576,8 @@ static void unlink_complete (struct urb *urb)
urb->context = splice->context; urb->context = splice->context;
urb->complete (urb); urb->complete (urb);
splice->done = 1; /* then let the synchronous unlink call complete */
complete (&splice->done);
} }
/* /*
...@@ -1510,7 +1644,7 @@ static int hcd_unlink_urb (struct urb *urb) ...@@ -1510,7 +1644,7 @@ static int hcd_unlink_urb (struct urb *urb)
case PIPE_CONTROL: case PIPE_CONTROL:
case PIPE_BULK: case PIPE_BULK:
if (urb->status != -EINPROGRESS) { if (urb->status != -EINPROGRESS) {
retval = 0; retval = -EINVAL;
goto done; goto done;
} }
} }
...@@ -1525,7 +1659,7 @@ static int hcd_unlink_urb (struct urb *urb) ...@@ -1525,7 +1659,7 @@ static int hcd_unlink_urb (struct urb *urb)
goto done; goto done;
} }
/* synchronous unlink: block till we see the completion */ /* synchronous unlink: block till we see the completion */
splice.done = 0; init_completion (&splice.done);
splice.complete = urb->complete; splice.complete = urb->complete;
splice.context = urb->context; splice.context = urb->context;
urb->complete = unlink_complete; urb->complete = unlink_complete;
...@@ -1551,12 +1685,9 @@ if (retval && urb->status == -ENOENT) err ("whoa! retval %d", retval); ...@@ -1551,12 +1685,9 @@ if (retval && urb->status == -ENOENT) err ("whoa! retval %d", retval);
if (!(urb->transfer_flags & (USB_ASYNC_UNLINK|USB_TIMEOUT_KILLED)) if (!(urb->transfer_flags & (USB_ASYNC_UNLINK|USB_TIMEOUT_KILLED))
&& HCD_IS_RUNNING (hcd->state) && HCD_IS_RUNNING (hcd->state)
&& !retval) { && !retval) {
while (!splice.done) { dbg ("%s: wait for giveback urb %p",
set_current_state (TASK_UNINTERRUPTIBLE); hcd->bus_name, urb);
schedule_timeout ((2/*msec*/ * HZ) / 1000); wait_for_completion (&splice.done);
dbg ("%s: wait for giveback urb %p",
hcd->bus_name, urb);
}
} else if ((urb->transfer_flags & USB_ASYNC_UNLINK) && retval == 0) { } else if ((urb->transfer_flags & USB_ASYNC_UNLINK) && retval == 0) {
return -EINPROGRESS; return -EINPROGRESS;
} }
...@@ -1657,27 +1788,13 @@ static void hcd_irq (int irq, void *__hcd, struct pt_regs * r) ...@@ -1657,27 +1788,13 @@ static void hcd_irq (int irq, void *__hcd, struct pt_regs * r)
* and will be reissued. They should just call their completion handlers * and will be reissued. They should just call their completion handlers
* until the urb is returned to the device driver by unlinking. * until the urb is returned to the device driver by unlinking.
* *
* In common cases, urb->next will be submitted before the completion * NOTE that no urb->next processing is done, even for isochronous URBs.
* function gets called. That's not done if the URB includes error * ISO streaming functionality can be achieved by having completion handlers
* status (including unlinking). * re-queue URBs. Such explicit queuing doesn't discard error reports.
*/ */
void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb) void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb)
{ {
unsigned long flags; urb_unlink (urb);
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);
// NOTE: a generic device/urb monitoring hook would go here. // NOTE: a generic device/urb monitoring hook would go here.
// hcd_monitor_hook(MONITOR_URB_FINISH, urb, dev) // hcd_monitor_hook(MONITOR_URB_FINISH, urb, dev)
...@@ -1689,24 +1806,7 @@ void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb) ...@@ -1689,24 +1806,7 @@ void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb)
dbg ("giveback urb %p status %d len %d", dbg ("giveback urb %p status %d len %d",
urb, urb->status, urb->actual_length); 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 */ /* pass ownership to the completion handler */
usb_dec_dev_use (dev);
urb->complete (urb); urb->complete (urb);
usb_put_urb (urb); usb_put_urb (urb);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment