Author: thompsa
Date: Sun Nov 22 21:16:43 2009
New Revision: 199672
URL: http://svn.freebsd.org/changeset/base/199672

Log:
  Improve High Speed slot allocation mechanism by moving the computation to the
  endpoint rather than per xfer and provide functions around get/free of 
resources.
  
  Submitted by: Hans Petter Selasky

Modified:
  head/sys/dev/usb/controller/ehci.c
  head/sys/dev/usb/usb_core.h
  head/sys/dev/usb/usb_device.c
  head/sys/dev/usb/usb_hub.c
  head/sys/dev/usb/usb_hub.h
  head/sys/dev/usb/usb_transfer.c
  head/sys/dev/usb/usbdi.h

Modified: head/sys/dev/usb/controller/ehci.c
==============================================================================
--- head/sys/dev/usb/controller/ehci.c  Sun Nov 22 21:16:30 2009        
(r199671)
+++ head/sys/dev/usb/controller/ehci.c  Sun Nov 22 21:16:43 2009        
(r199672)
@@ -2016,8 +2016,8 @@ ehci_setup_standard_chain(struct usb_xfe
 
        qh_endphub =
            (EHCI_QH_SET_MULT(xfer->max_packet_count & 3) |
-           EHCI_QH_SET_CMASK(xfer->usb_cmask) |
-           EHCI_QH_SET_SMASK(xfer->usb_smask) |
+           EHCI_QH_SET_CMASK(xfer->endpoint->usb_cmask) |
+           EHCI_QH_SET_SMASK(xfer->endpoint->usb_smask) |
            EHCI_QH_SET_HUBA(xfer->xroot->udev->hs_hub_addr) |
            EHCI_QH_SET_PORT(xfer->xroot->udev->hs_port_no));
 
@@ -2162,7 +2162,7 @@ ehci_isoc_hs_done(ehci_softc_t *sc, stru
 
                DPRINTFN(2, "status=0x%08x, len=%u\n", status, len);
 
-               if (xfer->usb_smask & (1 << td_no)) {
+               if (xfer->endpoint->usb_smask & (1 << td_no)) {
 
                        if (*plen >= len) {
                                /*
@@ -2348,22 +2348,8 @@ ehci_device_intr_open(struct usb_xfer *x
        uint16_t best;
        uint16_t bit;
        uint16_t x;
-       uint8_t slot;
 
-       /* Allocate a microframe slot first: */
-
-       slot = usb_intr_schedule_adjust
-           (xfer->xroot->udev, xfer->max_frame_size, USB_HS_MICRO_FRAMES_MAX);
-
-       if (usbd_get_speed(xfer->xroot->udev) == USB_SPEED_HIGH) {
-               xfer->usb_uframe = slot;
-               xfer->usb_smask = (1 << slot) & 0xFF;
-               xfer->usb_cmask = 0;
-       } else {
-               xfer->usb_uframe = slot;
-               xfer->usb_smask = (1 << slot) & 0x3F;
-               xfer->usb_cmask = (-(4 << slot)) & 0xFE;
-       }
+       usb_hs_bandwidth_alloc(xfer);
 
        /*
         * Find the best QH position corresponding to the given interval:
@@ -2399,12 +2385,12 @@ ehci_device_intr_close(struct usb_xfer *
 {
        ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus);
 
-       usb_intr_schedule_adjust(xfer->xroot->udev,
-           -(xfer->max_frame_size), xfer->usb_uframe);
-
        sc->sc_intr_stat[xfer->qh_pos]--;
 
        ehci_device_done(xfer, USB_ERR_CANCELLED);
+
+       /* bandwidth must be freed after device done */
+       usb_hs_bandwidth_free(xfer);
 }
 
 static void
@@ -2726,28 +2712,8 @@ ehci_device_isoc_hs_open(struct usb_xfer
        ehci_itd_t *td;
        uint32_t temp;
        uint8_t ds;
-       uint8_t slot;
-
-       slot = usb_intr_schedule_adjust(xfer->xroot->udev, xfer->max_frame_size,
-           USB_HS_MICRO_FRAMES_MAX);
 
-       xfer->usb_uframe = slot;
-       xfer->usb_cmask = 0;
-
-       switch (usbd_xfer_get_fps_shift(xfer)) {
-       case 0:
-               xfer->usb_smask = 0xFF;
-               break;
-       case 1:
-               xfer->usb_smask = 0x55 << (slot & 1);
-               break;
-       case 2:
-               xfer->usb_smask = 0x11 << (slot & 3);
-               break;
-       default:
-               xfer->usb_smask = 0x01 << (slot & 7);
-               break;
-       }
+       usb_hs_bandwidth_alloc(xfer);
 
        /* initialize all TD's */
 
@@ -2791,11 +2757,10 @@ ehci_device_isoc_hs_open(struct usb_xfer
 static void
 ehci_device_isoc_hs_close(struct usb_xfer *xfer)
 {
-
-       usb_intr_schedule_adjust(xfer->xroot->udev,
-           -(xfer->max_frame_size), xfer->usb_uframe);
-
        ehci_device_done(xfer, USB_ERR_CANCELLED);
+
+       /* bandwidth must be freed after device done */
+       usb_hs_bandwidth_free(xfer);
 }
 
 static void
@@ -2905,7 +2870,7 @@ ehci_device_isoc_hs_enter(struct usb_xfe
                        *plen = xfer->max_frame_size;
                }
 
-               if (xfer->usb_smask & (1 << td_no)) {
+               if (xfer->endpoint->usb_smask & (1 << td_no)) {
                        status = (EHCI_ITD_SET_LEN(*plen) |
                            EHCI_ITD_ACTIVE |
                            EHCI_ITD_SET_PG(0));

Modified: head/sys/dev/usb/usb_core.h
==============================================================================
--- head/sys/dev/usb/usb_core.h Sun Nov 22 21:16:30 2009        (r199671)
+++ head/sys/dev/usb/usb_core.h Sun Nov 22 21:16:43 2009        (r199672)
@@ -161,9 +161,6 @@ struct usb_xfer {
        uint8_t address;                /* physical USB address */
        uint8_t endpointno;             /* physical USB endpoint */
        uint8_t max_packet_count;
-       uint8_t usb_smask;
-       uint8_t usb_cmask;
-       uint8_t usb_uframe;
        uint8_t usb_state;
        uint8_t fps_shift;              /* down shift of FPS, 0..3 */
 

Modified: head/sys/dev/usb/usb_device.c
==============================================================================
--- head/sys/dev/usb/usb_device.c       Sun Nov 22 21:16:30 2009        
(r199671)
+++ head/sys/dev/usb/usb_device.c       Sun Nov 22 21:16:43 2009        
(r199672)
@@ -665,7 +665,7 @@ usb_config_parse(struct usb_device *udev
                        /* look for matching endpoints */
                        if ((iface_index == USB_IFACE_INDEX_ANY) ||
                            (iface_index == ep->iface_index)) {
-                               if (ep->refcount != 0) {
+                               if (ep->refcount_alloc != 0) {
                                        /*
                                         * This typically indicates a
                                         * more serious error.

Modified: head/sys/dev/usb/usb_hub.c
==============================================================================
--- head/sys/dev/usb/usb_hub.c  Sun Nov 22 21:16:30 2009        (r199671)
+++ head/sys/dev/usb/usb_hub.c  Sun Nov 22 21:16:43 2009        (r199672)
@@ -1106,43 +1106,62 @@ done:
  *   The best Transaction Translation slot for an interrupt endpoint.
  *------------------------------------------------------------------------*/
 static uint8_t
-usb_intr_find_best_slot(usb_size_t *ptr, uint8_t start, uint8_t end)
+usb_intr_find_best_slot(usb_size_t *ptr, uint8_t start,
+    uint8_t end, uint8_t mask)
 {
-       usb_size_t max = 0 - 1;
+       usb_size_t min = 0 - 1;
+       usb_size_t sum;
        uint8_t x;
        uint8_t y;
+       uint8_t z;
 
        y = 0;
 
        /* find the last slot with lesser used bandwidth */
 
        for (x = start; x < end; x++) {
-               if (max >= ptr[x]) {
-                       max = ptr[x];
+
+               sum = 0;
+
+               /* compute sum of bandwidth */
+               for (z = x; z < end; z++) {
+                       if (mask & (1U << (z - x)))
+                               sum += ptr[z];
+               }
+
+               /* check if the current multi-slot is more optimal */
+               if (min >= sum) {
+                       min = sum;
                        y = x;
                }
+
+               /* check if the mask is about to be shifted out */
+               if (mask & (1U << (end - 1 - x)))
+                       break;
        }
        return (y);
 }
 
 /*------------------------------------------------------------------------*
- *     usb_intr_schedule_adjust
+ *     usb_hs_bandwidth_adjust
  *
  * This function will update the bandwith usage for the microframe
  * having index "slot" by "len" bytes. "len" can be negative.  If the
  * "slot" argument is greater or equal to "USB_HS_MICRO_FRAMES_MAX"
  * the "slot" argument will be replaced by the slot having least used
- * bandwidth.
+ * bandwidth. The "mask" argument is used for multi-slot allocations.
  *
  * Returns:
- *   The slot on which the bandwidth update was done.
+ *    The slot in which the bandwidth update was done: 0..7
  *------------------------------------------------------------------------*/
-uint8_t
-usb_intr_schedule_adjust(struct usb_device *udev, int16_t len, uint8_t slot)
+static uint8_t
+usb_hs_bandwidth_adjust(struct usb_device *udev, int16_t len,
+    uint8_t slot, uint8_t mask)
 {
        struct usb_bus *bus = udev->bus;
        struct usb_hub *hub;
        enum usb_dev_speed speed;
+       uint8_t x;
 
        USB_BUS_LOCK_ASSERT(bus, MA_OWNED);
 
@@ -1164,23 +1183,157 @@ usb_intr_schedule_adjust(struct usb_devi
                hub = udev->parent_hs_hub->hub;
                if (slot >= USB_HS_MICRO_FRAMES_MAX) {
                        slot = usb_intr_find_best_slot(hub->uframe_usage,
-                           USB_FS_ISOC_UFRAME_MAX, 6);
+                           USB_FS_ISOC_UFRAME_MAX, 6, mask);
+               }
+               for (x = slot; x < 8; x++) {
+                       if (mask & (1U << (x - slot))) {
+                               hub->uframe_usage[x] += len;
+                               bus->uframe_usage[x] += len;
+                       }
                }
-               hub->uframe_usage[slot] += len;
-               bus->uframe_usage[slot] += len;
                break;
        default:
                if (slot >= USB_HS_MICRO_FRAMES_MAX) {
                        slot = usb_intr_find_best_slot(bus->uframe_usage, 0,
-                           USB_HS_MICRO_FRAMES_MAX);
+                           USB_HS_MICRO_FRAMES_MAX, mask);
+               }
+               for (x = slot; x < 8; x++) {
+                       if (mask & (1U << (x - slot))) {
+                               bus->uframe_usage[x] += len;
+                       }
                }
-               bus->uframe_usage[slot] += len;
                break;
        }
        return (slot);
 }
 
 /*------------------------------------------------------------------------*
+ *     usb_hs_bandwidth_alloc
+ *
+ * This function is a wrapper function for "usb_hs_bandwidth_adjust()".
+ *------------------------------------------------------------------------*/
+void
+usb_hs_bandwidth_alloc(struct usb_xfer *xfer)
+{
+       struct usb_device *udev;
+       uint8_t slot;
+       uint8_t mask;
+       uint8_t speed;
+
+       udev = xfer->xroot->udev;
+
+       if (udev->flags.usb_mode != USB_MODE_HOST)
+               return;         /* not supported */
+
+       xfer->endpoint->refcount_bw++;
+       if (xfer->endpoint->refcount_bw != 1)
+               return;         /* already allocated */
+
+       speed = usbd_get_speed(udev);
+
+       switch (xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE) {
+       case UE_INTERRUPT:
+               /* allocate a microframe slot */
+
+               mask = 0x01;
+               slot = usb_hs_bandwidth_adjust(udev,
+                   xfer->max_frame_size, USB_HS_MICRO_FRAMES_MAX, mask);
+
+               xfer->endpoint->usb_uframe = slot;
+               xfer->endpoint->usb_smask = mask << slot;
+
+               if ((speed != USB_SPEED_FULL) &&
+                   (speed != USB_SPEED_LOW)) {
+                       xfer->endpoint->usb_cmask = 0x00 ;
+               } else {
+                       xfer->endpoint->usb_cmask = (-(0x04 << slot)) & 0xFE;
+               }
+               break;
+
+       case UE_ISOCHRONOUS:
+               switch (usbd_xfer_get_fps_shift(xfer)) {
+               case 0:
+                       mask = 0xFF;
+                       break;
+               case 1:
+                       mask = 0x55;
+                       break;
+               case 2:
+                       mask = 0x11;
+                       break;
+               default:
+                       mask = 0x01;
+                       break;
+               }
+
+               /* allocate a microframe multi-slot */
+
+               slot = usb_hs_bandwidth_adjust(udev,
+                   xfer->max_frame_size, USB_HS_MICRO_FRAMES_MAX, mask);
+
+               xfer->endpoint->usb_uframe = slot;
+               xfer->endpoint->usb_cmask = 0;
+               xfer->endpoint->usb_smask = mask << slot;
+               break;
+
+       default:
+               xfer->endpoint->usb_uframe = 0;
+               xfer->endpoint->usb_cmask = 0;
+               xfer->endpoint->usb_smask = 0;
+               break;
+       }
+
+       DPRINTFN(11, "slot=%d, mask=0x%02x\n", 
+           xfer->endpoint->usb_uframe, 
+           xfer->endpoint->usb_smask >> xfer->endpoint->usb_uframe);
+}
+
+/*------------------------------------------------------------------------*
+ *     usb_hs_bandwidth_free
+ *
+ * This function is a wrapper function for "usb_hs_bandwidth_adjust()".
+ *------------------------------------------------------------------------*/
+void
+usb_hs_bandwidth_free(struct usb_xfer *xfer)
+{
+       struct usb_device *udev;
+       uint8_t slot;
+       uint8_t mask;
+
+       udev = xfer->xroot->udev;
+
+       if (udev->flags.usb_mode != USB_MODE_HOST)
+               return;         /* not supported */
+
+       xfer->endpoint->refcount_bw--;
+       if (xfer->endpoint->refcount_bw != 0)
+               return;         /* still allocated */
+
+       switch (xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE) {
+       case UE_INTERRUPT:
+       case UE_ISOCHRONOUS:
+
+               slot = xfer->endpoint->usb_uframe;
+               mask = xfer->endpoint->usb_smask;
+
+               /* free microframe slot(s): */    
+               usb_hs_bandwidth_adjust(udev,
+                   -xfer->max_frame_size, slot, mask >> slot);
+
+               DPRINTFN(11, "slot=%d, mask=0x%02x\n", 
+                   slot, mask >> slot);
+
+               xfer->endpoint->usb_uframe = 0;
+               xfer->endpoint->usb_cmask = 0;
+               xfer->endpoint->usb_smask = 0;
+               break;
+
+       default:
+               break;
+       }
+}
+
+/*------------------------------------------------------------------------*
  *     usbd_fs_isoc_schedule_init_sub
  *
  * This function initialises an USB FULL speed isochronous schedule

Modified: head/sys/dev/usb/usb_hub.h
==============================================================================
--- head/sys/dev/usb/usb_hub.h  Sun Nov 22 21:16:30 2009        (r199671)
+++ head/sys/dev/usb/usb_hub.h  Sun Nov 22 21:16:43 2009        (r199672)
@@ -66,8 +66,8 @@ struct usb_hub {
 
 /* function prototypes */
 
-uint8_t        usb_intr_schedule_adjust(struct usb_device *udev, int16_t len,
-           uint8_t slot);
+void   usb_hs_bandwidth_alloc(struct usb_xfer *xfer);
+void   usb_hs_bandwidth_free(struct usb_xfer *xfer);
 void   usbd_fs_isoc_schedule_init_all(struct usb_fs_isoc_schedule *fss);
 void   usb_bus_port_set_device(struct usb_bus *bus, struct usb_port *up,
            struct usb_device *udev, uint8_t device_index);

Modified: head/sys/dev/usb/usb_transfer.c
==============================================================================
--- head/sys/dev/usb/usb_transfer.c     Sun Nov 22 21:16:30 2009        
(r199671)
+++ head/sys/dev/usb/usb_transfer.c     Sun Nov 22 21:16:43 2009        
(r199672)
@@ -942,10 +942,18 @@ usbd_transfer_setup(struct usb_device *u
                                 * configuration and alternate setting
                                 * when USB transfers are in use on
                                 * the given interface. Search the USB
-                                * code for "endpoint->refcount" if you
+                                * code for "endpoint->refcount_alloc" if you
                                 * want more information.
                                 */
-                               xfer->endpoint->refcount++;
+                               USB_BUS_LOCK(info->bus);
+                               if (xfer->endpoint->refcount_alloc >= 
USB_EP_REF_MAX)
+                                       parm.err = USB_ERR_INVAL;
+
+                               xfer->endpoint->refcount_alloc++;
+
+                               if (xfer->endpoint->refcount_alloc == 0)
+                                       panic("usbd_transfer_setup(): Refcount 
wrapped to zero\n");
+                               USB_BUS_UNLOCK(info->bus);
 
                                /*
                                 * Whenever we set ppxfer[] then we
@@ -960,6 +968,10 @@ usbd_transfer_setup(struct usb_device *u
                                 */
                                ppxfer[n] = xfer;
                        }
+
+                       /* check for error */
+                       if (parm.err)
+                               goto done;
                }
 
                if (buf || parm.err) {
@@ -1179,7 +1191,9 @@ usbd_transfer_unsetup(struct usb_xfer **
                 * NOTE: default endpoint does not have an
                 * interface, even if endpoint->iface_index == 0
                 */
-               xfer->endpoint->refcount--;
+               USB_BUS_LOCK(info->bus);
+               xfer->endpoint->refcount_alloc--;
+               USB_BUS_UNLOCK(info->bus);
 
                usb_callout_drain(&xfer->timeout_handle);
 

Modified: head/sys/dev/usb/usbdi.h
==============================================================================
--- head/sys/dev/usb/usbdi.h    Sun Nov 22 21:16:30 2009        (r199671)
+++ head/sys/dev/usb/usbdi.h    Sun Nov 22 21:16:43 2009        (r199672)
@@ -130,13 +130,22 @@ struct usb_endpoint {
        struct usb_pipe_methods *methods;       /* set by HC driver */
 
        uint16_t isoc_next;
-       uint16_t refcount;
 
        uint8_t toggle_next:1;          /* next data toggle value */
        uint8_t is_stalled:1;           /* set if endpoint is stalled */
        uint8_t is_synced:1;            /* set if we a synchronised */
        uint8_t unused:5;
        uint8_t iface_index;            /* not used by "default endpoint" */
+
+       uint8_t refcount_alloc;         /* allocation refcount */
+       uint8_t refcount_bw;            /* bandwidth refcount */
+#define        USB_EP_REF_MAX 0x3f
+
+       /* High-Speed resource allocation (valid if "refcount_bw" > 0) */
+
+       uint8_t usb_smask;              /* USB start mask */
+       uint8_t usb_cmask;              /* USB complete mask */
+       uint8_t usb_uframe;             /* USB microframe */
 };
 
 /*
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to