# 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 [EMAIL PROTECTED] 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 <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 ...)
# --------------------------------------------
#
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 <linux/timer.h>
#include <linux/list.h>
#include <linux/interrupt.h>
+#include <linux/completion.h>
#include <linux/uts.h> /* 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);
}
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel