summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorthompsa <thompsa@FreeBSD.org>2009-11-22 21:16:43 +0000
committerthompsa <thompsa@FreeBSD.org>2009-11-22 21:16:43 +0000
commit87194982c0d82d711579a618a553c5d555599700 (patch)
tree03755e6bb81bc77a4f5a2db3eb4c79f32ac294f7 /sys
parent33b0b9f0e9af6a61858f23dd610353152d5204cd (diff)
downloadFreeBSD-src-87194982c0d82d711579a618a553c5d555599700.zip
FreeBSD-src-87194982c0d82d711579a618a553c5d555599700.tar.gz
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
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/usb/controller/ehci.c59
-rw-r--r--sys/dev/usb/usb_core.h3
-rw-r--r--sys/dev/usb/usb_device.c2
-rw-r--r--sys/dev/usb/usb_hub.c181
-rw-r--r--sys/dev/usb/usb_hub.h4
-rw-r--r--sys/dev/usb/usb_transfer.c20
-rw-r--r--sys/dev/usb/usbdi.h11
7 files changed, 209 insertions, 71 deletions
diff --git a/sys/dev/usb/controller/ehci.c b/sys/dev/usb/controller/ehci.c
index 5cc9b29..d665b89 100644
--- a/sys/dev/usb/controller/ehci.c
+++ b/sys/dev/usb/controller/ehci.c
@@ -2016,8 +2016,8 @@ ehci_setup_standard_chain(struct usb_xfer *xfer, ehci_qh_t **qh_last)
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, struct usb_xfer *xfer)
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 *xfer)
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 *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 *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 *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_xfer *xfer)
*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));
diff --git a/sys/dev/usb/usb_core.h b/sys/dev/usb/usb_core.h
index 7541adb..3dfd0d1 100644
--- a/sys/dev/usb/usb_core.h
+++ b/sys/dev/usb/usb_core.h
@@ -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 */
diff --git a/sys/dev/usb/usb_device.c b/sys/dev/usb/usb_device.c
index 4339cb2..ae71f7d 100644
--- a/sys/dev/usb/usb_device.c
+++ b/sys/dev/usb/usb_device.c
@@ -665,7 +665,7 @@ usb_config_parse(struct usb_device *udev, uint8_t iface_index, uint8_t cmd)
/* 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.
diff --git a/sys/dev/usb/usb_hub.c b/sys/dev/usb/usb_hub.c
index 07a0970..0809086 100644
--- a/sys/dev/usb/usb_hub.c
+++ b/sys/dev/usb/usb_hub.c
@@ -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_device *udev, int16_t len, uint8_t slot)
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
diff --git a/sys/dev/usb/usb_hub.h b/sys/dev/usb/usb_hub.h
index ceb0cc2..5b8dedf 100644
--- a/sys/dev/usb/usb_hub.h
+++ b/sys/dev/usb/usb_hub.h
@@ -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);
diff --git a/sys/dev/usb/usb_transfer.c b/sys/dev/usb/usb_transfer.c
index 4169ac8..c434703 100644
--- a/sys/dev/usb/usb_transfer.c
+++ b/sys/dev/usb/usb_transfer.c
@@ -942,10 +942,18 @@ usbd_transfer_setup(struct usb_device *udev,
* 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 *udev,
*/
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 **pxfer, uint16_t n_setup)
* 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);
diff --git a/sys/dev/usb/usbdi.h b/sys/dev/usb/usbdi.h
index 46747d7..1a66652 100644
--- a/sys/dev/usb/usbdi.h
+++ b/sys/dev/usb/usbdi.h
@@ -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 */
};
/*
OpenPOWER on IntegriCloud