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
This commit is contained in:
Andrew Thompson 2009-11-22 21:16:43 +00:00
parent ca3f1187f1
commit f12c6c2913
7 changed files with 209 additions and 71 deletions

View File

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

View File

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

View File

@ -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.

View File

@ -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,22 +1183,156 @@ 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
*

View File

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

View File

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

View File

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