1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-24 11:29:10 +00:00

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
This commit is contained in:
Hans Petter Selasky 2012-04-29 17:12:33 +00:00
parent d7af8cf14b
commit 0a4cc48fe5
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=234803
5 changed files with 172 additions and 184 deletions

View File

@ -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;
}

View File

@ -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 @@ xhci_setup_generic_chain_sub(struct xhci_std_temp *temp)
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;

View File

@ -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_ */

View File

@ -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;
@ -1655,42 +1662,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
*
@ -1723,114 +1694,130 @@ usb_isoc_time_expand(struct usb_bus *bus, uint16_t isoc_time_curr)
}
/*------------------------------------------------------------------------*
* usbd_fs_isoc_schedule_isoc_time_expand
*
* 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.
*
* Returns:
* Expanded version of "isoc_time".
*
* NOTE: This function depends on being called regularly with
* intervals less than "USB_ISOC_TIME_MAX".
*------------------------------------------------------------------------*/
#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)
{
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;
isoc_time = usb_isoc_time_expand(udev->bus, isoc_time);
hs_hub = udev->parent_hs_hub->hub;
if (hs_hub != NULL) {
fss_a = hs_hub->fs_isoc_schedule +
(hs_hub->isoc_last_time % USB_ISOC_TIME_MAX);
hs_hub->isoc_last_time = isoc_time;
fss_b = hs_hub->fs_isoc_schedule +
(isoc_time % USB_ISOC_TIME_MAX);
fss_end = hs_hub->fs_isoc_schedule + USB_ISOC_TIME_MAX;
*pp_start = hs_hub->fs_isoc_schedule;
*pp_end = fss_end;
while (fss_a != fss_b) {
if (fss_a == fss_end) {
fss_a = hs_hub->fs_isoc_schedule;
continue;
}
usbd_fs_isoc_schedule_init_sub(fss_a);
fss_a++;
}
} else {
*pp_start = NULL;
*pp_end = NULL;
}
return (isoc_time);
}
#endif
/*------------------------------------------------------------------------*
* usbd_fs_isoc_schedule_alloc
* usbd_fs_isoc_schedule_alloc_slot
*
* 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.
* transaction in the FULL speed schedule.
*
* Returns:
* 0: Success
* <8: 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)
usbd_fs_isoc_schedule_alloc_slot(struct usb_xfer *isoc_xfer, uint16_t isoc_time)
{
uint8_t slot = fss->frame_slot;
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;
/* Compute overhead and bit-stuffing */
data_len = 0;
slot = 0;
len += 8;
bus = isoc_xfer->xroot->bus;
len *= 7;
len /= 6;
TAILQ_FOREACH(xfer, &bus->intr_q.head, wait_entry) {
if (len > fss->total_bytes) {
*pstart = 0; /* set some dummy value */
return (1); /* error */
}
if (len > 0) {
/* skip self, if any */
fss->total_bytes -= len;
if (xfer == isoc_xfer)
continue;
while (len >= fss->frame_bytes) {
len -= fss->frame_bytes;
fss->frame_bytes = USB_FS_BYTES_PER_HS_UFRAME;
fss->frame_slot++;
/* check if this USB transfer is going through the same TT */
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;
}
fss->frame_bytes -= len;
/* check double buffered transfers */
TAILQ_FOREACH(pipe_xfer, &xfer->endpoint->endpoint_q.head,
wait_entry) {
/* 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;
}
}
}
*pstart = slot;
return (0); /* success */
while (data_len >= USB_FS_BYTES_PER_HS_UFRAME) {
data_len -= USB_FS_BYTES_PER_HS_UFRAME;
slot++;
}
/* check for overflow */
if (slot >= USB_FS_ISOC_UFRAME_MAX)
return (255);
retval = slot;
delta = isoc_xfer->isoc_time_complete - isoc_time;
if (delta > 0 && delta <= isoc_xfer->nframes) {
delta = isoc_xfer->nframes - delta;
len = isoc_xfer->frlengths[delta];
len += 8;
len *= 7;
len /= 6;
data_len += len;
}
while (data_len >= USB_FS_BYTES_PER_HS_UFRAME) {
data_len -= USB_FS_BYTES_PER_HS_UFRAME;
slot++;
}
/* check for overflow */
if (slot >= USB_FS_ISOC_UFRAME_MAX)
return (255);
return (retval);
}
#endif

View File

@ -37,23 +37,10 @@ struct usb_port {
enum usb_hc_mode usb_mode; /* host or device mode */
};
/*
* 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,