PERFORCE change 182149 for review
Hans Petter Selasky
hselasky at skunkworks.freebsd.org
Wed Aug 11 18:26:06 UTC 2010
http://p4web.freebsd.org/@@182149?ac=10
Change 182149 by hselasky at hselasky_laptop001 on 2010/08/09 19:48:33
USB controller (XHCI):
- fix problem about short USB transfers (compute correct remainder).
- correctly initialise USB HUB fields in slot context
- correct interrupt endpoint intervals (we round down the intervals).
- add support for USB 2.0 device suspend and resume
- add new callback function to correctly update the current XHCI device state.
- at this point the XHCI driver works with any kind of USB device (HS/LS/FS and SS)
- have tested ISOC, BULK, CONTROL and INTERRUPT transfer types.
Affected files ...
.. //depot/projects/usb/src/sys/dev/usb/controller/xhci.c#23 edit
.. //depot/projects/usb/src/sys/dev/usb/controller/xhci.h#20 edit
Differences ...
==== //depot/projects/usb/src/sys/dev/usb/controller/xhci.c#23 (text+ko) ====
@@ -122,10 +122,9 @@
static struct xhci_endpoint_ext *xhci_get_endpoint_ext(struct usb_device *, struct usb_endpoint_descriptor *);
static usb_proc_callback_t xhci_configure_msg;
static usb_error_t xhci_configure_device(struct usb_device *);
-static usb_error_t xhci_configure_endpoint(struct usb_device *, struct usb_endpoint_descriptor *, uint64_t, uint16_t, uint8_t, uint8_t, uint16_t, uint16_t);
+static usb_error_t xhci_configure_endpoint(struct usb_device *, struct usb_endpoint_descriptor *, uint64_t, uint16_t, uint8_t, uint8_t, uint8_t, uint16_t, uint16_t);
static usb_error_t xhci_configure_mask(struct usb_device *, uint32_t, uint8_t);
static usb_error_t xhci_cmd_evaluate_ctx(struct xhci_softc *, uint64_t, uint8_t);
-static usb_error_t xhci_cmd_reset_dev(struct xhci_softc *, uint8_t);
extern struct usb_bus_methods xhci_bus_methods;
@@ -637,6 +636,7 @@
static void
xhci_check_transfer(struct xhci_softc *sc, struct xhci_trb *trb)
{
+ int64_t offset;
uint64_t td_event;
uint32_t temp;
uint32_t remainder;
@@ -688,18 +688,37 @@
td = xfer->td_transfer_cache;
- DPRINTFN(5, "Checking if 0x%016llx == (0x%016llx .. 0x%016llx +/- 16)\n",
+ DPRINTFN(5, "Checking if 0x%016llx == (0x%016llx .. 0x%016llx)\n",
(long long)td_event,
(long long)td->td_self,
- (long long)td->td_event_last);
+ (long long)td->td_self + sizeof(td->td_trb));
+
+ /*
+ * NOTE: Some XHCI implementations might not trigger
+ * an event on the last LINK TRB so we need to
+ * consider both the last and second last event
+ * address as conditions for a successful transfer.
+ *
+ * NOTE: We assume that the XHCI will only trigger one
+ * event per chain of TRBs.
+ */
+
+ offset = td_event - td->td_self;
- if ((td_event == td->td_event_last) ||
- (td_event == (td->td_event_last - 16)) ||
- (halted && (td_event >= td->td_self) &&
- (td_event <= td->td_event_last))) {
+ if ((offset >= 0) &&
+ (offset < sizeof(td->td_trb))) {
usb_pc_cpu_invalidate(td->page_cache);
+ /* compute rest of remainder, if any */
+ for (i = (offset / 16) + 1; i < td->ntrb; i++) {
+ temp = le32toh(td->td_trb[i].dwTrb2);
+ remainder += XHCI_TRB_2_BYTES_GET(temp);
+ }
+
+ DPRINTFN(5, "New remainder: %u\n", remainder);
+
+ /* clear isochronous transfer errors */
if (xfer->flags_int.isochronous_xfr) {
if (halted) {
halted = 0;
@@ -1073,27 +1092,11 @@
XHCI_CMD_LOCK(sc);
switch (hdev->state) {
- case XHCI_ST_ADDRESSED:
- case XHCI_ST_CONFIGURED:
- if (udev->address == 0) {
- err = xhci_cmd_reset_dev(sc, index);
-
- if (err != 0) {
- DPRINTF("Device reset failed\n");
- }
- } else {
- err = 0;
- break;
- }
-
case XHCI_ST_DEFAULT:
+ case XHCI_ST_ENABLED:
hdev->state = XHCI_ST_ENABLED;
- /* FALLTHROUGH */
-
- case XHCI_ST_ENABLED:
-
/* set configure mask to slot and EP0 */
xhci_configure_mask(udev, 3, 0);
@@ -1124,7 +1127,7 @@
&udev->ctrl_ep_desc);
err = xhci_configure_endpoint(udev,
&udev->ctrl_ep_desc, pepext->physaddr,
- 0, 1, 1, mps, mps);
+ 0, 1, 1, 0, mps, mps);
if (err != 0) {
DPRINTF("Could not configure default endpoint\n");
@@ -1138,7 +1141,8 @@
(address == 0), index);
if (err != 0) {
- DPRINTF("Could not set address\n");
+ DPRINTF("Could not set address "
+ "for slot %u.\n", index);
if (address != 0)
break;
}
@@ -1152,27 +1156,10 @@
/* update device state to new value */
- if (address != 0) {
-
- /* we skip the addressed state */
- hdev->state = XHCI_ST_CONFIGURED;
-
- xhci_configure_mask(udev, 1, 0);
-
- err = xhci_configure_device(udev);
- if (err != 0) {
- DPRINTF("Could not configure device\n");
- break;
- }
-
- err = xhci_cmd_evaluate_ctx(sc, buf_inp.physaddr, index);
- if (err != 0) {
- DPRINTF("Could not evaluate device context\n");
- break;
- }
- } else {
+ if (address != 0)
+ hdev->state = XHCI_ST_ADDRESSED;
+ else
hdev->state = XHCI_ST_DEFAULT;
- }
break;
default:
@@ -1399,7 +1386,6 @@
struct xhci_td *td;
struct xhci_td *td_next;
struct xhci_td *td_alt_next;
- uint64_t addr;
uint32_t buf_offset;
uint32_t average;
uint32_t len_old;
@@ -1553,11 +1539,20 @@
XHCI_TRB_3_TYPE_SET(temp->trb_type) |
XHCI_TRB_3_FRID_SET(temp->isoc_frame);
- if (temp->direction == UE_DIR_IN)
+ if (temp->direction == UE_DIR_IN) {
dword |= XHCI_TRB_3_DIR_IN;
- if (average == 0)
- dword |= XHCI_TRB_3_IDT_BIT;
+ /*
+ * NOTE: Only the SETUP stage should
+ * use the IDT bit. Else transactions
+ * can be sent using the wrong data
+ * toggle value.
+ */
+
+ if ((temp->trb_type != XHCI_TRB_TYPE_SETUP_STAGE) &&
+ (temp->trb_type != XHCI_TRB_TYPE_STATUS_STAGE))
+ dword |= XHCI_TRB_3_ISP_BIT;
+ }
td->td_trb[x].dwTrb3 = htole32(dword);
@@ -1578,12 +1573,6 @@
DPRINTF("NTRB=%u\n", x);
- /* compute event pointer */
-
- addr = td->td_self;
- addr += x * sizeof(struct xhci_trb);
- td->td_event_last = addr;
-
/* fill out link TRB */
if (td_next != NULL) {
@@ -1629,7 +1618,7 @@
goto restart;
}
- if (temp->multishort == 0) {
+ if (temp->multishort != 0) {
/* remove chain bit and clear TD SIZE - end of frame */
td->td_trb[td->ntrb - 1].dwTrb2 &= ~htole32(XHCI_TRB_2_TDSZ_SET(15));
td->td_trb[td->ntrb - 1].dwTrb3 &= ~htole32(XHCI_TRB_3_CHAIN_BIT);
@@ -1656,6 +1645,7 @@
temp.last_frame = 0;
temp.offset = 0;
temp.multishort = xfer->flags_int.isochronous_xfr ||
+ xfer->flags_int.control_xfr ||
xfer->flags_int.short_frames_ok;
/* toggle the DMA set we are using */
@@ -1867,23 +1857,6 @@
usb_pc_cpu_flush(&sc->sc_hw.ctx_pc);
}
-static uint8_t
-xhci_log2(uint32_t x)
-{
- uint8_t retval;
-
- retval = 0;
- while (1) {
- if (x == 1)
- break;
- x >>= 2;
- if (x == 0)
- break;
- retval++;
- }
- return (retval);
-}
-
static usb_error_t
xhci_configure_mask(struct usb_device *udev, uint32_t mask, uint8_t drop)
{
@@ -1913,7 +1886,7 @@
xhci_configure_endpoint(struct usb_device *udev,
struct usb_endpoint_descriptor *edesc, uint64_t ring_addr,
uint16_t interval, uint8_t max_packet_count, uint8_t mult,
- uint16_t max_packet_size, uint16_t max_frame_size)
+ uint8_t fps_shift, uint16_t max_packet_size, uint16_t max_frame_size)
{
struct usb_page_search buf_inp;
struct xhci_softc *sc = XHCI_BUS2SC(udev->bus);
@@ -1921,7 +1894,7 @@
uint32_t temp;
uint8_t index;
uint8_t epno;
- uint8_t k;
+ uint8_t type;
index = udev->controller_slot_id;
@@ -1930,8 +1903,9 @@
pinp = buf_inp.buffer;
epno = edesc->bEndpointAddress;
+ type = edesc->bmAttributes & UE_XFERTYPE;
- if ((edesc->bmAttributes & UE_XFERTYPE) == UE_CONTROL)
+ if (type == UE_CONTROL)
epno |= UE_DIR_IN;
epno = XHCI_EPNO2EPID(epno);
@@ -1947,21 +1921,38 @@
if (mult == 0)
return (USB_ERR_BAD_BUFSIZE);
- mult--;
-
temp = XHCI_EPCTX_0_EPSTATE_SET(0) |
XHCI_EPCTX_0_MAXP_STREAMS_SET(0) |
XHCI_EPCTX_0_LSA_SET(0);
- switch (edesc->bmAttributes & UE_XFERTYPE) {
+ switch (udev->speed) {
+ case USB_SPEED_FULL:
+ case USB_SPEED_LOW:
+ /* 1ms -> 125us */
+ fps_shift += 3;
+ break;
+ default:
+ break;
+ }
+
+ switch (type) {
case UE_INTERRUPT:
- k = xhci_log2(interval) + 3;
- temp |= XHCI_EPCTX_0_IVAL_SET(k);
+ if (fps_shift > 3)
+ fps_shift--;
+ temp |= XHCI_EPCTX_0_IVAL_SET(fps_shift);
break;
case UE_ISOCHRONOUS:
- if (udev->speed == USB_SPEED_SUPER) {
- temp |= XHCI_EPCTX_0_MULT_SET(mult);
+ temp |= XHCI_EPCTX_0_IVAL_SET(fps_shift);
+
+ switch (udev->speed) {
+ case USB_SPEED_SUPER:
+ if (mult > 3)
+ mult = 3;
+ temp |= XHCI_EPCTX_0_MULT_SET(mult - 1);
max_packet_count /= mult;
+ break;
+ default:
+ break;
}
break;
default:
@@ -1976,11 +1967,11 @@
XHCI_EPCTX_1_MAXP_SIZE_SET(max_packet_size);
if ((udev->parent_hs_hub != NULL) || (udev->address != 0)) {
- if ((edesc->bmAttributes & UE_XFERTYPE) != UE_ISOCHRONOUS)
+ if (type != UE_ISOCHRONOUS)
temp |= XHCI_EPCTX_1_CERR_SET(3);
}
- switch (edesc->bmAttributes & UE_XFERTYPE) {
+ switch (type) {
case UE_CONTROL:
temp |= XHCI_EPCTX_1_EPTYPE_SET(4);
break;
@@ -1995,7 +1986,8 @@
break;
}
- if (edesc->bEndpointAddress & UE_DIR_IN)
+ /* check for IN direction */
+ if (epno & 1)
temp |= XHCI_EPCTX_1_EPTYPE_SET(4);
pinp->ctx_ep[epno - 1].dwEpCtx1 = htole32(temp);
@@ -2004,19 +1996,17 @@
pinp->ctx_ep[epno - 1].qwEpCtx2 = htole64(ring_addr);
- temp = 0;
-
switch (edesc->bmAttributes & UE_XFERTYPE) {
case UE_INTERRUPT:
case UE_ISOCHRONOUS:
- temp |= XHCI_EPCTX_4_MAX_ESIT_PAYLOAD_SET(max_frame_size);
- temp |= XHCI_EPCTX_4_AVG_TRB_LEN_SET(XHCI_TD_PAYLOAD_MAX);
+ temp = XHCI_EPCTX_4_MAX_ESIT_PAYLOAD_SET(max_frame_size);
+ temp = XHCI_EPCTX_4_AVG_TRB_LEN_SET(XHCI_TD_PAYLOAD_MAX);
break;
case UE_CONTROL:
- temp |= XHCI_EPCTX_4_AVG_TRB_LEN_SET(8);
+ temp = XHCI_EPCTX_4_AVG_TRB_LEN_SET(8);
break;
default:
- temp |= XHCI_EPCTX_4_AVG_TRB_LEN_SET(XHCI_TD_PAYLOAD_MAX);
+ temp = XHCI_EPCTX_4_AVG_TRB_LEN_SET(XHCI_TD_PAYLOAD_MAX);
break;
}
@@ -2048,7 +2038,8 @@
xfer->endpoint->edesc, pepext->physaddr,
xfer->interval, xfer->max_packet_count,
(ecomp != NULL) ? (ecomp->bmAttributes & 3) + 1 : 1,
- xfer->max_packet_size, xfer->max_frame_size));
+ usbd_xfer_get_fps_shift(xfer), xfer->max_packet_size,
+ xfer->max_frame_size));
}
static usb_error_t
@@ -2135,7 +2126,7 @@
break;
}
- is_hub = (udev->hub != NULL) &&
+ is_hub = (sc->sc_hw.devs[index].nports != 0) &&
((udev->speed == USB_SPEED_SUPER) ||
(udev->speed = USB_SPEED_HIGH));
@@ -2147,7 +2138,7 @@
temp = XHCI_SCTX_1_RH_PORT_SET(rh_port);
if (is_hub)
- temp |= XHCI_SCTX_1_NUM_PORTS_SET(udev->hub->nports);
+ temp |= XHCI_SCTX_1_NUM_PORTS_SET(sc->sc_hw.devs[index].nports);
switch (udev->speed) {
case USB_SPEED_SUPER:
@@ -2170,16 +2161,24 @@
temp = XHCI_SCTX_2_IRQ_TARGET_SET(0);
- if (is_hub) {
- if (udev->speed == USB_SPEED_HIGH)
- temp |= XHCI_SCTX_2_TT_THINK_TIME_SET(3);
- }
+ if (is_hub)
+ temp |= XHCI_SCTX_2_TT_THINK_TIME_SET(sc->sc_hw.devs[index].tt);
hubdev = udev->parent_hs_hub;
- if (hubdev != NULL) {
- temp |= XHCI_SCTX_2_TT_HUB_SID_SET(hubdev->controller_slot_id);
- temp |= XHCI_SCTX_2_TT_PORT_NUM_SET(hubdev->port_no);
+ /* check if we should activate the transaction translator */
+ switch (udev->speed) {
+ case USB_SPEED_FULL:
+ case USB_SPEED_LOW:
+ if (hubdev != NULL) {
+ temp |= XHCI_SCTX_2_TT_HUB_SID_SET(
+ hubdev->controller_slot_id);
+ temp |= XHCI_SCTX_2_TT_PORT_NUM_SET(
+ udev->hs_port_no);
+ }
+ break;
+ default:
+ break;
}
pinp->ctx_slot.dwSctx2 = htole32(temp);
@@ -2342,7 +2341,8 @@
epno = XHCI_EPNO2EPID(epno);
index = xfer->xroot->udev->controller_slot_id;
- XWRITE4(sc, door, XHCI_DOORBELL(index), epno | XHCI_DB_SID_SET(0));
+ if (xfer->xroot->udev->flags.self_suspended == 0)
+ XWRITE4(sc, door, XHCI_DOORBELL(index), epno | XHCI_DB_SID_SET(0));
}
static void
@@ -2536,8 +2536,15 @@
static void
xhci_device_generic_open(struct usb_xfer *xfer)
{
- if (xfer->flags_int.isochronous_xfr)
- usb_hs_bandwidth_alloc(xfer);
+ if (xfer->flags_int.isochronous_xfr) {
+ switch (xfer->xroot->udev->speed) {
+ case USB_SPEED_FULL:
+ break;
+ default:
+ usb_hs_bandwidth_alloc(xfer);
+ break;
+ }
+ }
}
static void
@@ -2547,8 +2554,15 @@
xhci_device_done(xfer, USB_ERR_CANCELLED);
- if (xfer->flags_int.isochronous_xfr)
- usb_hs_bandwidth_free(xfer);
+ if (xfer->flags_int.isochronous_xfr) {
+ switch (xfer->xroot->udev->speed) {
+ case USB_SPEED_FULL:
+ break;
+ default:
+ usb_hs_bandwidth_free(xfer);
+ break;
+ }
+ }
}
static void
@@ -3237,10 +3251,6 @@
xfer->flags_int.curr_dma_set = 1;
goto alloc_dma_set;
}
-
- /* make sure we catch any set address updates */
- if (parm->buf != NULL)
- xhci_set_address(xfer->xroot->udev, NULL, 0);
}
static usb_error_t
@@ -3249,7 +3259,6 @@
struct xhci_softc *sc = XHCI_BUS2SC(xfer->xroot->bus);
struct usb_page_search buf_dev;
struct usb_page_search buf_inp;
- struct usb_device *hub;
struct usb_device *udev;
struct xhci_endpoint_ext *pepext;
struct usb_endpoint_descriptor *edesc;
@@ -3288,46 +3297,6 @@
XHCI_CMD_LOCK(sc);
- /* figure out if a parent HUB should be configured */
-
- hub = udev->parent_hs_hub;
- if (hub == NULL) {
- hub = udev->parent_hub;
- if (hub == NULL)
- hub = NULL;
- else if (hub->parent_hub == NULL)
- hub = NULL;
- else if (hub->speed != USB_SPEED_SUPER)
- hub = NULL;
- } else {
- /* check for root HUB */
- if (hub->parent_hub == NULL)
- hub = NULL;
- }
-
- if (hub != NULL) {
-
- struct usb_page_search buf_hub;
-
- DPRINTF("Configure HUB\n");
-
- usbd_get_page(&sc->sc_hw.devs[
- hub->controller_slot_id].input_pc, 0, &buf_hub);
-
- /* set configure mask to slot only */
- xhci_configure_mask(hub, 1, 0);
-
- /* configure input slot context structure */
- err = xhci_configure_device(hub);
-
- /* update context */
- if (err == 0)
- err = xhci_cmd_evaluate_ctx(sc, buf_hub.physaddr, index);
-
- if (err)
- DPRINTF("Could not update parent HS HUB context.\n");
- }
-
/* configure endpoint */
err = xhci_configure_endpoint_by_xfer(xfer);
@@ -3625,25 +3594,61 @@
static void
xhci_device_resume(struct usb_device *udev)
{
+ struct xhci_softc *sc = XHCI_BUS2SC(udev->bus);
+ uint8_t index;
+ uint8_t n;
+
DPRINTF("\n");
+ /* check for root HUB */
+ if (udev->parent_hub == NULL)
+ return;
+
+ index = udev->controller_slot_id;
+
+ XHCI_CMD_LOCK(sc);
+
+ /* blindly resume all endpoints */
+
USB_BUS_LOCK(udev->bus);
- /* start endpoint */
+ for (n = 1; n != XHCI_MAX_ENDPOINTS; n++)
+ XWRITE4(sc, door, XHCI_DOORBELL(index), n | XHCI_DB_SID_SET(0));
USB_BUS_UNLOCK(udev->bus);
+
+ XHCI_CMD_UNLOCK(sc);
}
static void
xhci_device_suspend(struct usb_device *udev)
{
+ struct xhci_softc *sc = XHCI_BUS2SC(udev->bus);
+ uint8_t index;
+ uint8_t n;
+ usb_error_t err;
+
DPRINTF("\n");
- USB_BUS_LOCK(udev->bus);
+ /* check for root HUB */
+ if (udev->parent_hub == NULL)
+ return;
+
+ index = udev->controller_slot_id;
+
+ XHCI_CMD_LOCK(sc);
+
+ /* blindly suspend all endpoints */
- /* stop endpoint */
+ for (n = 1; n != XHCI_MAX_ENDPOINTS; n++) {
+ err = xhci_cmd_stop_ep(sc, 1, n, index);
+ if (err != 0) {
+ DPRINTF("Failed to suspend endpoint "
+ "%u on slot %u (ignored).\n", n, index);
+ }
+ }
- USB_BUS_UNLOCK(udev->bus);
+ XHCI_CMD_UNLOCK(sc);
}
static void
@@ -3652,6 +3657,89 @@
DPRINTF("\n");
}
+static void
+xhci_device_state_change(struct usb_device *udev)
+{
+ struct xhci_softc *sc = XHCI_BUS2SC(udev->bus);
+ struct usb_page_search buf_inp;
+ usb_error_t err;
+ uint8_t index;
+
+ /* check for root HUB */
+ if (udev->parent_hub == NULL)
+ return;
+
+ index = udev->controller_slot_id;
+
+ DPRINTF("\n");
+
+ if (usb_get_device_state(udev) == USB_STATE_CONFIGURED) {
+ err = uhub_query_info(udev, &sc->sc_hw.devs[index].nports,
+ &sc->sc_hw.devs[index].tt);
+ if (err != 0)
+ sc->sc_hw.devs[index].nports = 0;
+ }
+
+ XHCI_CMD_LOCK(sc);
+
+ switch (usb_get_device_state(udev)) {
+ case USB_STATE_POWERED:
+ if (sc->sc_hw.devs[index].state == XHCI_ST_DEFAULT)
+ break;
+
+ sc->sc_hw.devs[index].state = XHCI_ST_DEFAULT;
+
+ err = xhci_cmd_reset_dev(sc, index);
+
+ if (err != 0) {
+ DPRINTF("Device reset failed "
+ "for slot %u.\n", index);
+ }
+ break;
+
+ case USB_STATE_ADDRESSED:
+ if (sc->sc_hw.devs[index].state == XHCI_ST_ADDRESSED)
+ break;
+
+ sc->sc_hw.devs[index].state = XHCI_ST_ADDRESSED;
+
+ err = xhci_cmd_configure_ep(sc, 0, 1, index);
+
+ if (err) {
+ DPRINTF("Failed to deconfigure "
+ "slot %u.\n", index);
+ }
+ break;
+
+ case USB_STATE_CONFIGURED:
+ if (sc->sc_hw.devs[index].state == XHCI_ST_CONFIGURED)
+ break;
+
+ sc->sc_hw.devs[index].state = XHCI_ST_CONFIGURED;
+
+ usbd_get_page(&sc->sc_hw.devs[index].input_pc, 0, &buf_inp);
+
+ xhci_configure_mask(udev, 1, 0);
+
+ err = xhci_configure_device(udev);
+ if (err != 0) {
+ DPRINTF("Could not configure device "
+ "at slot %u.\n", index);
+ }
+
+ err = xhci_cmd_evaluate_ctx(sc, buf_inp.physaddr, index);
+ if (err != 0) {
+ DPRINTF("Could not evaluate device "
+ "context at slot %u.\n", index);
+ }
+ break;
+
+ default:
+ break;
+ }
+ XHCI_CMD_UNLOCK(sc);
+}
+
struct usb_bus_methods xhci_bus_methods = {
.endpoint_init = xhci_ep_init,
.endpoint_uninit = xhci_ep_uninit,
@@ -3668,4 +3756,5 @@
.start_dma_delay = xhci_start_dma_delay,
.set_address = xhci_set_address,
.clear_stall = xhci_ep_clear_stall,
+ .device_state_change = xhci_device_state_change,
};
==== //depot/projects/usb/src/sys/dev/usb/controller/xhci.h#20 (text+ko) ====
@@ -314,7 +314,6 @@
* Extra information needed:
*/
uint64_t td_self;
- uint64_t td_event_last;
struct xhci_td *next;
struct xhci_td *alt_next;
struct xhci_td *obj_next;
@@ -378,6 +377,9 @@
struct xhci_endpoint_ext endp[XHCI_MAX_ENDPOINTS];
uint8_t state;
+ uint8_t nports;
+ uint8_t tt;
+ uint8_t reserved;
};
struct xhci_hw_softc {
More information about the p4-projects
mailing list