summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorhselasky <hselasky@FreeBSD.org>2012-04-29 17:12:33 +0000
committerhselasky <hselasky@FreeBSD.org>2012-04-29 17:12:33 +0000
commit5827c4e6ace287e7d537ca4e7b19b17680693d6a (patch)
tree75a758f6263175281d04ea31332521e60c1a4fc7
parent99428404bed28d421872e61f367292954ab61dc2 (diff)
downloadFreeBSD-src-5827c4e6ace287e7d537ca4e7b19b17680693d6a.zip
FreeBSD-src-5827c4e6ace287e7d537ca4e7b19b17680693d6a.tar.gz
Add support for Multi-TT mode of modern USB HUBs.
This will give you more bandwidth for isochronous FULL speed applications connected through a High Speed HUB. This patch has been tested with XHCI and EHCI. MFC after: 1 week
-rw-r--r--sys/dev/usb/controller/ehci.c41
-rw-r--r--sys/dev/usb/controller/xhci.c49
-rw-r--r--sys/dev/usb/usb_controller.h5
-rw-r--r--sys/dev/usb/usb_hub.c225
-rw-r--r--sys/dev/usb/usb_hub.h14
5 files changed, 161 insertions, 173 deletions
diff --git a/sys/dev/usb/controller/ehci.c b/sys/dev/usb/controller/ehci.c
index da37954..a822077 100644
--- a/sys/dev/usb/controller/ehci.c
+++ b/sys/dev/usb/controller/ehci.c
@@ -2398,9 +2398,9 @@ ehci_device_isoc_fs_open(struct usb_xfer *xfer)
EHCI_SITD_SET_HUBA(xfer->xroot->udev->hs_hub_addr) |
EHCI_SITD_SET_PORT(xfer->xroot->udev->hs_port_no);
- if (UE_GET_DIR(xfer->endpointno) == UE_DIR_IN) {
+ if (UE_GET_DIR(xfer->endpointno) == UE_DIR_IN)
sitd_portaddr |= EHCI_SITD_SET_DIR_IN;
- }
+
sitd_portaddr = htohc32(sc, sitd_portaddr);
/* initialize all TD's */
@@ -2436,9 +2436,6 @@ ehci_device_isoc_fs_enter(struct usb_xfer *xfer)
{
struct usb_page_search buf_res;
ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus);
- struct usb_fs_isoc_schedule *fss_start;
- struct usb_fs_isoc_schedule *fss_end;
- struct usb_fs_isoc_schedule *fss;
ehci_sitd_t *td;
ehci_sitd_t *td_last = NULL;
ehci_sitd_t **pp_last;
@@ -2450,7 +2447,6 @@ ehci_device_isoc_fs_enter(struct usb_xfer *xfer)
uint16_t tlen;
uint8_t sa;
uint8_t sb;
- uint8_t error;
#ifdef USB_DEBUG
uint8_t once = 1;
@@ -2495,9 +2491,8 @@ ehci_device_isoc_fs_enter(struct usb_xfer *xfer)
* pre-compute when the isochronous transfer will be finished:
*/
xfer->isoc_time_complete =
- usbd_fs_isoc_schedule_isoc_time_expand
- (xfer->xroot->udev, &fss_start, &fss_end, nframes) + buf_offset +
- xfer->nframes;
+ usb_isoc_time_expand(&sc->sc_bus, nframes) +
+ buf_offset + xfer->nframes;
/* get the real number of frames */
@@ -2520,19 +2515,14 @@ ehci_device_isoc_fs_enter(struct usb_xfer *xfer)
xfer->qh_pos = xfer->endpoint->isoc_next;
- fss = fss_start + (xfer->qh_pos % USB_ISOC_TIME_MAX);
-
while (nframes--) {
if (td == NULL) {
panic("%s:%d: out of TD's\n",
__FUNCTION__, __LINE__);
}
- if (pp_last >= &sc->sc_isoc_fs_p_last[EHCI_VIRTUAL_FRAMELIST_COUNT]) {
+ if (pp_last >= &sc->sc_isoc_fs_p_last[EHCI_VIRTUAL_FRAMELIST_COUNT])
pp_last = &sc->sc_isoc_fs_p_last[0];
- }
- if (fss >= fss_end) {
- fss = fss_start;
- }
+
/* reuse sitd_portaddr and sitd_back from last transfer */
if (*plen > xfer->max_frame_size) {
@@ -2547,17 +2537,19 @@ ehci_device_isoc_fs_enter(struct usb_xfer *xfer)
#endif
*plen = xfer->max_frame_size;
}
- /*
- * We currently don't care if the ISOCHRONOUS schedule is
- * full!
- */
- error = usbd_fs_isoc_schedule_alloc(fss, &sa, *plen);
- if (error) {
+
+ /* allocate a slot */
+
+ sa = usbd_fs_isoc_schedule_alloc_slot(xfer,
+ xfer->isoc_time_complete - nframes - 1);
+
+ if (sa == 255) {
/*
- * The FULL speed schedule is FULL! Set length
- * to zero.
+ * Schedule is FULL, set length to zero:
*/
+
*plen = 0;
+ sa = USB_FS_ISOC_UFRAME_MAX - 1;
}
if (*plen) {
/*
@@ -2637,7 +2629,6 @@ ehci_device_isoc_fs_enter(struct usb_xfer *xfer)
pp_last++;
plen++;
- fss++;
td_last = td;
td = td->obj_next;
}
diff --git a/sys/dev/usb/controller/xhci.c b/sys/dev/usb/controller/xhci.c
index aed7edf..1a4e432 100644
--- a/sys/dev/usb/controller/xhci.c
+++ b/sys/dev/usb/controller/xhci.c
@@ -115,6 +115,7 @@ struct xhci_std_temp {
uint8_t tbc;
uint8_t tlbpc;
uint8_t step_td;
+ uint8_t do_isoc_sync;
};
static void xhci_do_poll(struct usb_bus *);
@@ -1657,11 +1658,15 @@ restart:
td->td_trb[x].dwTrb2 = htole32(dword);
dword = XHCI_TRB_3_CHAIN_BIT | XHCI_TRB_3_CYCLE_BIT |
- XHCI_TRB_3_TYPE_SET(temp->trb_type) |
- XHCI_TRB_3_FRID_SET(temp->isoc_frame / 8) |
+ XHCI_TRB_3_TYPE_SET(temp->trb_type) |
+ (temp->do_isoc_sync ?
+ XHCI_TRB_3_FRID_SET(temp->isoc_frame / 8) :
+ XHCI_TRB_3_ISO_SIA_BIT) |
XHCI_TRB_3_TBC_SET(temp->tbc) |
XHCI_TRB_3_TLBPC_SET(temp->tlbpc);
+ temp->do_isoc_sync = 0;
+
if (temp->direction == UE_DIR_IN) {
dword |= XHCI_TRB_3_DIR_IN;
@@ -1764,6 +1769,7 @@ xhci_setup_generic_chain(struct usb_xfer *xfer)
uint32_t y;
uint8_t mult;
+ temp.do_isoc_sync = 0;
temp.step_td = 0;
temp.tbc = 0;
temp.tlbpc = 0;
@@ -1841,6 +1847,8 @@ xhci_setup_generic_chain(struct usb_xfer *xfer)
*/
xfer->endpoint->isoc_next = XHCI_MFINDEX_GET(x + (3 * 8));
xfer->endpoint->is_synced = 1;
+ temp.do_isoc_sync = 1;
+
DPRINTFN(3, "start next=%d\n", xfer->endpoint->isoc_next);
}
@@ -1931,7 +1939,10 @@ xhci_setup_generic_chain(struct usb_xfer *xfer)
uint8_t tdpc;
- /* isochronous transfers don't have short packet termination */
+ /*
+ * Isochronous transfers don't have short
+ * packet termination:
+ */
temp.shortpkt = 1;
@@ -2271,12 +2282,29 @@ xhci_configure_device(struct usb_device *udev)
switch (udev->speed) {
case USB_SPEED_LOW:
temp |= XHCI_SCTX_0_SPEED_SET(2);
+ if (udev->parent_hs_hub != NULL &&
+ udev->parent_hs_hub->ddesc.bDeviceProtocol ==
+ UDPROTO_HSHUBMTT) {
+ DPRINTF("Device inherits MTT\n");
+ temp |= XHCI_SCTX_0_MTT_SET(1);
+ }
break;
case USB_SPEED_HIGH:
temp |= XHCI_SCTX_0_SPEED_SET(3);
+ if (sc->sc_hw.devs[index].nports != 0 &&
+ udev->ddesc.bDeviceProtocol == UDPROTO_HSHUBMTT) {
+ DPRINTF("HUB supports MTT\n");
+ temp |= XHCI_SCTX_0_MTT_SET(1);
+ }
break;
case USB_SPEED_FULL:
temp |= XHCI_SCTX_0_SPEED_SET(1);
+ if (udev->parent_hs_hub != NULL &&
+ udev->parent_hs_hub->ddesc.bDeviceProtocol ==
+ UDPROTO_HSHUBMTT) {
+ DPRINTF("Device inherits MTT\n");
+ temp |= XHCI_SCTX_0_MTT_SET(1);
+ }
break;
default:
temp |= XHCI_SCTX_0_SPEED_SET(4);
@@ -2287,15 +2315,8 @@ xhci_configure_device(struct usb_device *udev)
(udev->speed == USB_SPEED_SUPER ||
udev->speed == USB_SPEED_HIGH);
- if (is_hub) {
+ if (is_hub)
temp |= XHCI_SCTX_0_HUB_SET(1);
-#if 0
- if (udev->ddesc.bDeviceProtocol == UDPROTO_HSHUBMTT) {
- DPRINTF("HUB supports MTT\n");
- temp |= XHCI_SCTX_0_MTT_SET(1);
- }
-#endif
- }
xhci_ctx_set_le32(sc, &pinp->ctx_slot.dwSctx0, temp);
@@ -2327,8 +2348,10 @@ xhci_configure_device(struct usb_device *udev)
temp = XHCI_SCTX_2_IRQ_TARGET_SET(0);
- if (is_hub)
- temp |= XHCI_SCTX_2_TT_THINK_TIME_SET(sc->sc_hw.devs[index].tt);
+ if (is_hub) {
+ temp |= XHCI_SCTX_2_TT_THINK_TIME_SET(
+ sc->sc_hw.devs[index].tt);
+ }
hubdev = udev->parent_hs_hub;
diff --git a/sys/dev/usb/usb_controller.h b/sys/dev/usb/usb_controller.h
index 4ffc041..bbbf66b 100644
--- a/sys/dev/usb/usb_controller.h
+++ b/sys/dev/usb/usb_controller.h
@@ -231,7 +231,8 @@ void usb_bus_mem_flush_all(struct usb_bus *bus, usb_bus_mem_cb_t *cb);
uint8_t usb_bus_mem_alloc_all(struct usb_bus *bus, bus_dma_tag_t dmat, usb_bus_mem_cb_t *cb);
void usb_bus_mem_free_all(struct usb_bus *bus, usb_bus_mem_cb_t *cb);
uint16_t usb_isoc_time_expand(struct usb_bus *bus, uint16_t isoc_time_curr);
-uint16_t usbd_fs_isoc_schedule_isoc_time_expand(struct usb_device *udev, struct usb_fs_isoc_schedule **pp_start, struct usb_fs_isoc_schedule **pp_end, uint16_t isoc_time);
-uint8_t usbd_fs_isoc_schedule_alloc(struct usb_fs_isoc_schedule *fss, uint8_t *pstart, uint16_t len);
+#if USB_HAVE_TT_SUPPORT
+uint8_t usbd_fs_isoc_schedule_alloc_slot(struct usb_xfer *isoc_xfer, uint16_t isoc_time);
+#endif
#endif /* _USB_CONTROLLER_H_ */
diff --git a/sys/dev/usb/usb_hub.c b/sys/dev/usb/usb_hub.c
index bc9d98c..85448b9 100644
--- a/sys/dev/usb/usb_hub.c
+++ b/sys/dev/usb/usb_hub.c
@@ -109,6 +109,7 @@ struct uhub_softc {
#define UHUB_PROTO(sc) ((sc)->sc_udev->ddesc.bDeviceProtocol)
#define UHUB_IS_HIGH_SPEED(sc) (UHUB_PROTO(sc) != UDPROTO_FSHUB)
#define UHUB_IS_SINGLE_TT(sc) (UHUB_PROTO(sc) == UDPROTO_HSHUBSTT)
+#define UHUB_IS_MULTI_TT(sc) (UHUB_PROTO(sc) == UDPROTO_HSHUBMTT)
#define UHUB_IS_SUPER_SPEED(sc) (UHUB_PROTO(sc) == UDPROTO_SSHUB)
/* prototypes for type checking: */
@@ -948,6 +949,16 @@ uhub_attach(device_t dev)
"bus powered HUB. HUB ignored\n");
goto error;
}
+
+ if (UHUB_IS_MULTI_TT(sc)) {
+ err = usbd_set_alt_interface_index(udev, 0, 1);
+ if (err) {
+ device_printf(dev, "MTT could not be enabled\n");
+ goto error;
+ }
+ device_printf(dev, "MTT enabled\n");
+ }
+
/* get HUB descriptor */
DPRINTFN(2, "Getting HUB descriptor\n");
@@ -1057,10 +1068,6 @@ uhub_attach(device_t dev)
}
udev->hub = hub;
-#if USB_HAVE_TT_SUPPORT
- /* init FULL-speed ISOCHRONOUS schedule */
- usbd_fs_isoc_schedule_init_all(hub->fs_isoc_schedule);
-#endif
/* initialize HUB structure */
hub->hubsoftc = sc;
hub->explore = &uhub_explore;
@@ -1656,42 +1663,6 @@ usb_hs_bandwidth_free(struct usb_xfer *xfer)
}
/*------------------------------------------------------------------------*
- * usbd_fs_isoc_schedule_init_sub
- *
- * This function initialises an USB FULL speed isochronous schedule
- * entry.
- *------------------------------------------------------------------------*/
-#if USB_HAVE_TT_SUPPORT
-static void
-usbd_fs_isoc_schedule_init_sub(struct usb_fs_isoc_schedule *fss)
-{
- fss->total_bytes = (USB_FS_ISOC_UFRAME_MAX *
- USB_FS_BYTES_PER_HS_UFRAME);
- fss->frame_bytes = (USB_FS_BYTES_PER_HS_UFRAME);
- fss->frame_slot = 0;
-}
-#endif
-
-/*------------------------------------------------------------------------*
- * usbd_fs_isoc_schedule_init_all
- *
- * This function will reset the complete USB FULL speed isochronous
- * bandwidth schedule.
- *------------------------------------------------------------------------*/
-#if USB_HAVE_TT_SUPPORT
-void
-usbd_fs_isoc_schedule_init_all(struct usb_fs_isoc_schedule *fss)
-{
- struct usb_fs_isoc_schedule *fss_end = fss + USB_ISOC_TIME_MAX;
-
- while (fss != fss_end) {
- usbd_fs_isoc_schedule_init_sub(fss);
- fss++;
- }
-}
-#endif
-
-/*------------------------------------------------------------------------*
* usb_isoc_time_expand
*
* This function will expand the time counter from 7-bit to 16-bit.
@@ -1723,114 +1694,130 @@ usb_isoc_time_expand(struct usb_bus *bus, uint16_t isoc_time_curr)
}
/*------------------------------------------------------------------------*
- * usbd_fs_isoc_schedule_isoc_time_expand
+ * usbd_fs_isoc_schedule_alloc_slot
*
- * This function does multiple things. First of all it will expand the
- * passed isochronous time, which is the return value. Then it will
- * store where the current FULL speed isochronous schedule is
- * positioned in time and where the end is. See "pp_start" and
- * "pp_end" arguments.
+ * This function will allocate bandwidth for an isochronous FULL speed
+ * transaction in the FULL speed schedule.
*
* Returns:
- * Expanded version of "isoc_time".
- *
- * NOTE: This function depends on being called regularly with
- * intervals less than "USB_ISOC_TIME_MAX".
+ * <8: Success
+ * Else: Error
*------------------------------------------------------------------------*/
#if USB_HAVE_TT_SUPPORT
-uint16_t
-usbd_fs_isoc_schedule_isoc_time_expand(struct usb_device *udev,
- struct usb_fs_isoc_schedule **pp_start,
- struct usb_fs_isoc_schedule **pp_end,
- uint16_t isoc_time)
+uint8_t
+usbd_fs_isoc_schedule_alloc_slot(struct usb_xfer *isoc_xfer, uint16_t isoc_time)
{
- struct usb_fs_isoc_schedule *fss_end;
- struct usb_fs_isoc_schedule *fss_a;
- struct usb_fs_isoc_schedule *fss_b;
- struct usb_hub *hs_hub;
+ struct usb_xfer *xfer;
+ struct usb_xfer *pipe_xfer;
+ struct usb_bus *bus;
+ usb_frlength_t len;
+ usb_frlength_t data_len;
+ uint16_t delta;
+ uint16_t slot;
+ uint8_t retval;
- isoc_time = usb_isoc_time_expand(udev->bus, isoc_time);
+ data_len = 0;
+ slot = 0;
- hs_hub = udev->parent_hs_hub->hub;
+ bus = isoc_xfer->xroot->bus;
- if (hs_hub != NULL) {
+ TAILQ_FOREACH(xfer, &bus->intr_q.head, wait_entry) {
- fss_a = hs_hub->fs_isoc_schedule +
- (hs_hub->isoc_last_time % USB_ISOC_TIME_MAX);
+ /* skip self, if any */
- hs_hub->isoc_last_time = isoc_time;
+ if (xfer == isoc_xfer)
+ continue;
- fss_b = hs_hub->fs_isoc_schedule +
- (isoc_time % USB_ISOC_TIME_MAX);
+ /* check if this USB transfer is going through the same TT */
- fss_end = hs_hub->fs_isoc_schedule + USB_ISOC_TIME_MAX;
+ if (xfer->xroot->udev->parent_hs_hub !=
+ isoc_xfer->xroot->udev->parent_hs_hub) {
+ continue;
+ }
+ if ((isoc_xfer->xroot->udev->parent_hs_hub->
+ ddesc.bDeviceProtocol == UDPROTO_HSHUBMTT) &&
+ (xfer->xroot->udev->hs_port_no !=
+ isoc_xfer->xroot->udev->hs_port_no)) {
+ continue;
+ }
+ if (xfer->endpoint->methods != isoc_xfer->endpoint->methods)
+ continue;
+
+ /* check if isoc_time is part of this transfer */
+
+ delta = xfer->isoc_time_complete - isoc_time;
+ if (delta > 0 && delta <= xfer->nframes) {
+ delta = xfer->nframes - delta;
+
+ len = xfer->frlengths[delta];
+ len += 8;
+ len *= 7;
+ len /= 6;
+
+ data_len += len;
+ }
+
+ /* check double buffered transfers */
- *pp_start = hs_hub->fs_isoc_schedule;
- *pp_end = fss_end;
+ TAILQ_FOREACH(pipe_xfer, &xfer->endpoint->endpoint_q.head,
+ wait_entry) {
- while (fss_a != fss_b) {
- if (fss_a == fss_end) {
- fss_a = hs_hub->fs_isoc_schedule;
+ /* skip self, if any */
+
+ if (pipe_xfer == isoc_xfer)
continue;
+
+ /* check if isoc_time is part of this transfer */
+
+ delta = pipe_xfer->isoc_time_complete - isoc_time;
+ if (delta > 0 && delta <= pipe_xfer->nframes) {
+ delta = pipe_xfer->nframes - delta;
+
+ len = pipe_xfer->frlengths[delta];
+ len += 8;
+ len *= 7;
+ len /= 6;
+
+ data_len += len;
}
- usbd_fs_isoc_schedule_init_sub(fss_a);
- fss_a++;
}
+ }
- } else {
-
- *pp_start = NULL;
- *pp_end = NULL;
+ while (data_len >= USB_FS_BYTES_PER_HS_UFRAME) {
+ data_len -= USB_FS_BYTES_PER_HS_UFRAME;
+ slot++;
}
- return (isoc_time);
-}
-#endif
-/*------------------------------------------------------------------------*
- * usbd_fs_isoc_schedule_alloc
- *
- * This function will allocate bandwidth for an isochronous FULL speed
- * transaction in the FULL speed schedule. The microframe slot where
- * the transaction should be started is stored in the byte pointed to
- * by "pstart". The "len" argument specifies the length of the
- * transaction in bytes.
- *
- * Returns:
- * 0: Success
- * Else: Error
- *------------------------------------------------------------------------*/
-#if USB_HAVE_TT_SUPPORT
-uint8_t
-usbd_fs_isoc_schedule_alloc(struct usb_fs_isoc_schedule *fss,
- uint8_t *pstart, uint16_t len)
-{
- uint8_t slot = fss->frame_slot;
+ /* check for overflow */
- /* Compute overhead and bit-stuffing */
+ if (slot >= USB_FS_ISOC_UFRAME_MAX)
+ return (255);
- len += 8;
+ retval = slot;
- len *= 7;
- len /= 6;
+ delta = isoc_xfer->isoc_time_complete - isoc_time;
+ if (delta > 0 && delta <= isoc_xfer->nframes) {
+ delta = isoc_xfer->nframes - delta;
- if (len > fss->total_bytes) {
- *pstart = 0; /* set some dummy value */
- return (1); /* error */
+ len = isoc_xfer->frlengths[delta];
+ len += 8;
+ len *= 7;
+ len /= 6;
+
+ data_len += len;
}
- if (len > 0) {
- fss->total_bytes -= len;
+ while (data_len >= USB_FS_BYTES_PER_HS_UFRAME) {
+ data_len -= USB_FS_BYTES_PER_HS_UFRAME;
+ slot++;
+ }
- while (len >= fss->frame_bytes) {
- len -= fss->frame_bytes;
- fss->frame_bytes = USB_FS_BYTES_PER_HS_UFRAME;
- fss->frame_slot++;
- }
+ /* check for overflow */
- fss->frame_bytes -= len;
- }
- *pstart = slot;
- return (0); /* success */
+ if (slot >= USB_FS_ISOC_UFRAME_MAX)
+ return (255);
+
+ return (retval);
}
#endif
diff --git a/sys/dev/usb/usb_hub.h b/sys/dev/usb/usb_hub.h
index 0f59599..23a1fa4 100644
--- a/sys/dev/usb/usb_hub.h
+++ b/sys/dev/usb/usb_hub.h
@@ -38,22 +38,9 @@ struct usb_port {
};
/*
- * The following structure defines how many bytes are
- * left in an 1ms USB time slot.
- */
-struct usb_fs_isoc_schedule {
- uint16_t total_bytes;
- uint8_t frame_bytes;
- uint8_t frame_slot;
-};
-
-/*
* The following structure defines an USB HUB.
*/
struct usb_hub {
-#if USB_HAVE_TT_SUPPORT
- struct usb_fs_isoc_schedule fs_isoc_schedule[USB_ISOC_TIME_MAX];
-#endif
struct usb_device *hubudev; /* the HUB device */
usb_error_t (*explore) (struct usb_device *hub);
void *hubsoftc;
@@ -68,7 +55,6 @@ struct usb_hub {
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);
struct usb_device *usb_bus_port_get_device(struct usb_bus *bus,
OpenPOWER on IntegriCloud