> Here's a patch against 2.5.20 that splits out the pci specific parts of
 > hcd.c into hcd-pci.c.
 >
 >      ...
 >
 > Christopher, does this patch make your patch easier?
 >
 > David, any complaints about this patch?

Only the interference with this patch.  It's certainly
along the right lines, and I'll look at it at bit more
later this AM.

It does a few things, could be partially split up:

- The symbol exports from Christopher's patch
    ... with a couple renames, and most importantly
    with kerneldoc for the new symbols

- Moves some urb submit logic into the generic urb
    submit code (slightly affected by the above)

- Minor/separable cleanup to "uhci-hcd"

Let me suggest that if the sorted-out version of Greg's "hcd-pci.c"
patch plus this one both behave as expected (in "2.5.21"?) that
Christopher do his patch against the 2.5.latest tree with those
patches rather than against 2.5.18rmk or 2.5.20 ...

- Dave

p.s. Also I notice that in this case the copyright notices
        were copied -- "hcd-pci.c" is basically all my code
        from what I saw, nothing from those other folk (I don't
        think I even did any cut/paste from usb-ohci.c) -- and
        in the other newish core files none of them were.
        Worth fixing such stuff.


--- ./drivers/usb-dist/core/hcd.h       Mon Jun  3 11:11:11 2002
+++ ./drivers/usb/core/hcd.h    Wed Jun  5 19:46:00 2002
@@ -50,9 +50,9 @@
        int                     irq;            /* irq allocated */
        void                    *regs;          /* device memory/io */
 
-#ifdef CONFIG_PCI
        /* a few non-PCI controllers exist, mostly for OHCI */
-       struct pci_dev          *pdev;          /* pci is typical */
+       struct pci_dev          *pdev;          /* may be emulated */
+#ifdef CONFIG_PCI
        int                     region;         /* pci region for regs */
        u32                     pci_state [16]; /* for PM state save */
        atomic_t                resume_count;   /* multiple resumes issue */
@@ -162,6 +162,7 @@
 
 extern void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb);
 
+/* PCI-specific bus glue, used for most USB host controller drivers */
 #ifdef CONFIG_PCI
 struct pci_dev;
 struct pci_device_id;
@@ -179,6 +180,13 @@
 
 #endif /* CONFIG_PCI */
 
+/* generic bus glue, needed for host controllers that don't use PCI */
+extern struct usb_operations usb_hcd_operations;
+extern void usb_hc_died (struct usb_hcd *hcd);
+struct pt_regs;
+extern void usb_hcd_irq (int irq, void *__hcd, struct pt_regs * r);
+
+
 /* -------------------------------------------------------------------------- */
 
 /* Enumeration is only for the hub driver, or HCD virtual root hubs */
@@ -297,6 +305,7 @@
 extern struct list_head usb_bus_list;
 extern struct semaphore usb_bus_list_lock;
 
+extern void usb_bus_init (struct usb_bus *bus);
 extern void usb_bus_get (struct usb_bus *bus);
 extern void usb_bus_put (struct usb_bus *bus);
 
--- ./drivers/usb-dist/core/hcd.c       Mon Jun  3 11:11:11 2002
+++ ./drivers/usb/core/hcd.c    Wed Jun  5 19:45:01 2002
@@ -112,8 +112,6 @@
 /* used when updating hcd data */
 static spinlock_t hcd_data_lock = SPIN_LOCK_UNLOCKED;
 
-static struct usb_operations hcd_operations;
-
 /*-------------------------------------------------------------------------*/
 
 /*
@@ -589,8 +587,14 @@
 
 /*-------------------------------------------------------------------------*/
 
-/* shared initialization code */
-static void usb_init_bus (struct usb_bus *bus)
+/**
+ * usb_bus_init - shared initialization code
+ * @bus: the bus structure being initialized
+ *
+ * This code is used to initialize a usb_bus structure, memory for which is
+ * separately managed.
+ */
+void usb_bus_init (struct usb_bus *bus)
 {
        memset (&bus->devmap, 0, sizeof(struct usb_devmap));
 
@@ -609,6 +613,7 @@
 
        atomic_set (&bus->refcnt, 1);
 }
+EXPORT_SYMBOL (usb_bus_init);
 
 /**
  * usb_alloc_bus - creates a new USB host controller structure
@@ -629,7 +634,7 @@
        bus = kmalloc (sizeof *bus, GFP_KERNEL);
        if (!bus)
                return NULL;
-       usb_init_bus (bus);
+       usb_bus_init (bus);
        bus->op = op;
        return bus;
 }
@@ -928,9 +933,6 @@
 
 /* PCI-based HCs are normal, but custom bus glue should be ok */
 
-static void hcd_irq (int irq, void *__hcd, struct pt_regs *r);
-static void hc_died (struct usb_hcd *hcd);
-
 /*-------------------------------------------------------------------------*/
 
 /* configure so an HC device and id are always provided */
@@ -1045,7 +1047,7 @@
 #else
        bufp = __irq_itoa(dev->irq);
 #endif
-       if (request_irq (dev->irq, hcd_irq, SA_SHIRQ, hcd->description, hcd)
+       if (request_irq (dev->irq, usb_hcd_irq, SA_SHIRQ, hcd->description, hcd)
                        != 0) {
                err ("request interrupt %s failed", bufp);
                retval = -EBUSY;
@@ -1060,8 +1062,8 @@
                (driver->flags & HCD_MEMORY) ? "pci mem" : "io base",
                base);
 
-       usb_init_bus (&hcd->self);
-       hcd->self.op = &hcd_operations;
+       usb_bus_init (&hcd->self);
+       hcd->self.op = &usb_hcd_operations;
        hcd->self.hcpriv = (void *) hcd;
        hcd->self.bus_name = dev->slot_name;
        hcd->product_desc = dev->name;
@@ -1225,7 +1227,7 @@
        retval = hcd->driver->resume (hcd);
        if (!HCD_IS_RUNNING (hcd->state)) {
                dbg ("resume %s failure, retval %d", hcd->self.bus_name, retval);
-               hc_died (hcd);
+               usb_hc_died (hcd);
 // FIXME:  recover, reset etc.
        } else {
                // FIXME for all connected devices, root-to-leaf:
@@ -1285,7 +1287,15 @@
 
 /*-------------------------------------------------------------------------*/
 
-static void hc_died (struct usb_hcd *hcd)
+/**
+ * usb_hc_died - report abnormal shutdown of a host controller (bus glue)
+ * @hcd: pointer to the HCD representing the controller
+ *
+ * This is called by bus glue to report a USB host controller that died
+ * while operations may still have been pending.  It's called automatically
+ * by the PCI glue, so only glue for non-PCI busses should need to call it. 
+ */
+void usb_hc_died (struct usb_hcd *hcd)
 {
        struct list_head        *devlist, *urblist;
        struct hcd_dev          *dev;
@@ -1313,6 +1323,7 @@
                rh_status_dequeue (hcd, urb);
        hcd->driver->stop (hcd);
 }
+EXPORT_SYMBOL (usb_hc_died);
 
 /*-------------------------------------------------------------------------*/
 
@@ -1337,170 +1348,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.
@@ -1510,9 +1372,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
@@ -1534,18 +1393,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);
+       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;
 }
 
@@ -1751,17 +1607,35 @@
        return 0;
 }
 
-static struct usb_operations hcd_operations = {
+/**
+ * usb_hcd_operations - adapts usb_bus framework to HCD framework (bus glue)
+ *
+ * When registering a USB bus through the HCD framework code, use this
+ * usb_operations vector.  The PCI glue layer does so automatically; only
+ * bus glue for non-PCI system busses will need to use this.
+ */
+struct usb_operations usb_hcd_operations = {
        allocate:               hcd_alloc_dev,
        get_frame_number:       hcd_get_frame_number,
        submit_urb:             hcd_submit_urb,
        unlink_urb:             hcd_unlink_urb,
        deallocate:             hcd_free_dev,
 };
+EXPORT_SYMBOL (usb_hcd_operations);
 
 /*-------------------------------------------------------------------------*/
 
-static void hcd_irq (int irq, void *__hcd, struct pt_regs * r)
+/**
+ * usb_hcd_irq - hook IRQs to HCD framework (bus glue)
+ * @irq: the IRQ being raised
+ * @__hcd: pointer to the HCD whose IRQ is beinng signaled
+ * @r: saved hardware registers (not passed to HCD)
+ *
+ * When registering a USB bus through the HCD framework code, use this
+ * to handle interrupts.  The PCI glue layer does so automatically; only
+ * bus glue for non-PCI system busses will need to use this.
+ */
+void usb_hcd_irq (int irq, void *__hcd, struct pt_regs * r)
 {
        struct usb_hcd          *hcd = __hcd;
        int                     start = hcd->state;
@@ -1771,8 +1645,9 @@
 
        hcd->driver->irq (hcd);
        if (hcd->state != start && hcd->state == USB_STATE_HALT)
-               hc_died (hcd);
+               usb_hc_died (hcd);
 }
+EXPORT_SYMBOL (usb_hcd_irq);
 
 /*-------------------------------------------------------------------------*/
 
--- ./drivers/usb-dist/core/urb.c       Mon Jun  3 11:11:11 2002
+++ ./drivers/usb/core/urb.c    Wed Jun  5 09:37:53 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);
 }
 
 /*-------------------------------------------------------------------*/
--- ./drivers/usb-dist/host/uhci-hcd.c  Mon Jun  3 11:11:11 2002
+++ ./drivers/usb/host/uhci-hcd.c       Wed Jun  5 09:51:56 2002
@@ -1502,10 +1502,6 @@
                break;
        case PIPE_ISOCHRONOUS:
                if (urb->bandwidth == 0) {      /* not yet checked/allocated */
-                       if (urb->number_of_packets <= 0) {
-                               ret = -EINVAL;
-                               break;
-                       }
                        bustime = usb_check_bandwidth(urb->dev, urb);
                        if (bustime < 0) {
                                ret = bustime;
--- ./drivers/usb-dist/host/uhci-hub.c  Mon Jun  3 11:11:11 2002
+++ ./drivers/usb/host/uhci-hub.c       Wed Jun  5 09:57:47 2002
@@ -52,7 +52,7 @@
        outw(status, io_addr + USBPORTSC1 + 2 * (wIndex-1))
 
 
-// FIXME: Shouldn't this return the length of the data too?
+/* size of returned buffer is part of USB spec */
 static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
                        u16 wIndex, char *buf, u16 wLength)
 {

Reply via email to