# 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

Reply via email to