PERFORCE change 182044 for review
Hans Petter Selasky
hselasky at FreeBSD.org
Sat Aug 7 19:38:53 UTC 2010
http://p4web.freebsd.org/@@182044?ac=10
Change 182044 by hselasky at hselasky_laptop001 on 2010/08/07 19:38:13
USB controller (XHCI):
- fix a bunch of misunderstandings about the XHCI chip.
- driver has reached a state where device enumeration
is possible.
- some issues are still left
Affected files ...
.. //depot/projects/usb/src/sys/dev/usb/controller/xhci.c#15 edit
.. //depot/projects/usb/src/sys/dev/usb/controller/xhci.h#17 edit
Differences ...
==== //depot/projects/usb/src/sys/dev/usb/controller/xhci.c#15 (text+ko) ====
@@ -32,6 +32,12 @@
* http://www.usb.org/developers/docs/usb_30_spec_060910.zip
*/
+/*
+ * A few words about the design implementation: This driver emulates
+ * the concept about TDs which is found in EHCI specification. This
+ * way we avoid too much diveration among USB drivers.
+ */
+
#include <sys/cdefs.h>
__FBSDID("$FreeBSD: src/sys/dev/usb/controller/xhci.c $");
@@ -79,7 +85,7 @@
((uint8_t *)&(((struct xhci_softc *)0)->sc_bus))))
#ifdef USB_DEBUG
-static int xhcidebug = 15;
+static int xhcidebug = 17;
SYSCTL_NODE(_hw_usb, OID_AUTO, xhci, CTLFLAG_RW, 0, "USB XHCI");
SYSCTL_INT(_hw_usb_xhci, OID_AUTO, debug, CTLFLAG_RW,
@@ -113,40 +119,50 @@
static void xhci_device_done(struct usb_xfer *, usb_error_t);
static void xhci_root_intr(struct xhci_softc *);
static void xhci_free_device_ext(struct usb_device *udev);
-static struct xhci_endpoint_ext *xhci_get_endpoint_ext(struct usb_xfer *xfer);
+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_reset_endpoint(struct usb_xfer *xfer, uint8_t set_address);
+static usb_error_t xhci_configure_reset_endpoint(struct usb_xfer *);
+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, uint16_t, uint16_t);
+static usb_error_t xhci_configure_endpoint_by_xfer(struct usb_xfer *);
+static usb_error_t xhci_set_address(struct usb_device *, struct mtx *, uint16_t);
+static usb_error_t xhci_configure_mask(struct usb_device *, uint32_t, uint8_t);
extern struct usb_bus_methods xhci_bus_methods;
#ifdef USB_DEBUG
-#if 0
static void
-xhci_dump_device(struct usb_device *udev)
+xhci_dump_trb(struct xhci_trb *trb)
{
- struct xhci_softc *sc = XHCI_BUS2SC(udev->bus);
- struct usb_page_search buf_dev;
- struct usb_page_cache *pcdev;
- struct xhci_dev_ctx *pdev;
- uint8_t index;
+ DPRINTFN(5, "trb = %p\n", trb);
+ DPRINTFN(5, "qwTrb0 = 0x%016llx\n", (long long)le64toh(trb->qwTrb0));
+ DPRINTFN(5, "dwTrb2 = 0x%08x\n", le32toh(trb->dwTrb2));
+ DPRINTFN(5, "dwTrb3 = 0x%08x\n", le32toh(trb->dwTrb3));
+}
- index = udev->controller_slot_id;
+static void
+xhci_dump_endpoint(struct xhci_endp_ctx *pep)
+{
+ DPRINTFN(5, "pep = %p\n", pep);
+ DPRINTFN(5, "dwEpCtx0=0x%08x\n", pep->dwEpCtx0);
+ DPRINTFN(5, "dwEpCtx1=0x%08x\n", pep->dwEpCtx1);
+ DPRINTFN(5, "qwEpCtx2=0x%016llx\n", (long long)pep->qwEpCtx2);
+ DPRINTFN(5, "dwEpCtx4=0x%08x\n", pep->dwEpCtx4);
+ DPRINTFN(5, "dwEpCtx5=0x%08x\n", pep->dwEpCtx5);
+ DPRINTFN(5, "dwEpCtx6=0x%08x\n", pep->dwEpCtx6);
+ DPRINTFN(5, "dwEpCtx7=0x%08x\n", pep->dwEpCtx7);
+}
- pcdev = &sc->sc_hw.devs[index].device_pc;
-
- usbd_get_page(pcdev, 0, &buf_dev);
-
- usb_pc_cpu_invalidate(pcdev);
-
- pdev = buf_dev.buffer;
-
- DPRINTF("SCTX0=0x%08x\n", pdev->ctx_slot.dwSctx0);
- DPRINTF("SCTX1=0x%08x\n", pdev->ctx_slot.dwSctx1);
- DPRINTF("SCTX2=0x%08x\n", pdev->ctx_slot.dwSctx2);
- DPRINTF("SCTX3=0x%08x\n", pdev->ctx_slot.dwSctx3);
+static void
+xhci_dump_device(struct xhci_slot_ctx *psl)
+{
+ DPRINTFN(5, "psl = %p\n", psl);
+ DPRINTFN(5, "dwSctx0=0x%08x\n", psl->dwSctx0);
+ DPRINTFN(5, "dwSctx1=0x%08x\n", psl->dwSctx1);
+ DPRINTFN(5, "dwSctx2=0x%08x\n", psl->dwSctx2);
+ DPRINTFN(5, "dwSctx3=0x%08x\n", psl->dwSctx3);
}
#endif
-#endif
static void
xhci_iterate_hw_softc(struct usb_bus *bus, usb_bus_mem_sub_cb_t *cb)
@@ -208,7 +224,7 @@
for (i = 0; i != 100; i++) {
usb_pause_mtx(NULL, hz / 1000);
- temp = XREAD4(sc, oper, XHCI_USBCMD) & XHCI_CMD_HCRST;
+ temp = XREAD4(sc, oper, XHCI_USBCMD) & (XHCI_CMD_HCRST | XHCI_STS_CNR);
if (!temp)
break;
}
@@ -242,6 +258,9 @@
/* setup number of device slots */
+ DPRINTF("CONFIG=0x%08x -> 0x%08x\n",
+ XREAD4(sc, oper, XHCI_CONFIG), sc->sc_noslot);
+
XWRITE4(sc, oper, XHCI_CONFIG, sc->sc_noslot);
DPRINTF("Max slots: %u\n", sc->sc_noslot);
@@ -279,10 +298,11 @@
memset(pdctxa, 0, sizeof(*pdctxa));
- addr = buf_res.physaddr + (uintptr_t)&((struct xhci_dev_ctx_addr *)0)->qwSpBufPtr[0];
+ addr = buf_res.physaddr;
+ addr += (uintptr_t)&((struct xhci_dev_ctx_addr *)0)->qwSpBufPtr[0];
/* slot 0 points to the table of scratchpad pointers */
- pdctxa->qwBaaDevCtxAddr[0] = htole64(buf_res.physaddr);
+ pdctxa->qwBaaDevCtxAddr[0] = htole64(addr);
for (i = 0; i != sc->sc_noscratch; i++) {
@@ -298,11 +318,26 @@
XWRITE4(sc, oper, XHCI_DCBAAP_LO, (uint32_t)addr);
XWRITE4(sc, oper, XHCI_DCBAAP_HI, (uint32_t)(addr >> 32));
- /* Setup interrupter registers */
+ XWRITE4(sc, oper, XHCI_DCBAAP_LO, (uint32_t)addr);
+ XWRITE4(sc, oper, XHCI_DCBAAP_HI, (uint32_t)(addr >> 32));
+
+ /* Setup event table size */
+
+ temp = XREAD4(sc, capa, XHCI_HCSPARAMS2);
+
+ DPRINTF("HCS2=0x%08x\n", temp);
+
+ temp = XHCI_HCS2_ERST_MAX(temp);
+ temp = 1U << temp;
+ if (temp > XHCI_MAX_RSEG)
+ temp = XHCI_MAX_RSEG;
+
+ sc->sc_erst_max = temp;
+
+ DPRINTF("ERSTSZ=0x%08x -> 0x%08x\n",
+ XREAD4(sc, runt, XHCI_ERSTSZ(0)), temp);
- temp = XREAD4(sc, runt, XHCI_IMAN(0));
- temp |= XHCI_IMAN_INTR_ENA;
- XWRITE4(sc, runt, XHCI_IMAN(0), temp);
+ XWRITE4(sc, runt, XHCI_ERSTSZ(0), XHCI_ERSTS_SET(temp));
/* Setup interrupt rate */
@@ -311,7 +346,8 @@
usbd_get_page(&sc->sc_hw.root_pc, 0, &buf_res);
phwr = buf_res.buffer;
- addr = (uint64_t)buf_res.physaddr + (uintptr_t)&((struct xhci_hw_root *)0)->hwr_events[0];
+ addr = buf_res.physaddr;
+ addr += (uintptr_t)&((struct xhci_hw_root *)0)->hwr_events[0];
/* reset hardware root structure */
@@ -320,21 +356,6 @@
phwr->hwr_ring_seg[0].qwEvrsTablePtr = htole64(addr);
phwr->hwr_ring_seg[0].dwEvrsTableSize = htole32(XHCI_MAX_EVENTS);
- /* Setup event table size */
-
- temp = XREAD4(sc, capa, XHCI_HCSPARAMS2);
-
- DPRINTF("HCS2=0x%08x\n", temp);
-
- temp = XHCI_HCS2_ERST_MAX(temp);
- temp = 1U << temp;
- if (temp > XHCI_MAX_RSEG)
- temp = XHCI_MAX_RSEG;
-
- sc->sc_erst_max = temp;
-
- XWRITE4(sc, runt, XHCI_ERSTSZ(0), XHCI_ERSTS_SET(temp));
-
DPRINTF("ERDP(0)=0x%016llx\n", (unsigned long long)addr);
XWRITE4(sc, runt, XHCI_ERDP_LO(0), (uint32_t)addr);
@@ -347,9 +368,16 @@
XWRITE4(sc, runt, XHCI_ERSTBA_LO(0), (uint32_t)addr);
XWRITE4(sc, runt, XHCI_ERSTBA_HI(0), (uint32_t)(addr >> 32));
+ /* Setup interrupter registers */
+
+ temp = XREAD4(sc, runt, XHCI_IMAN(0));
+ temp |= XHCI_IMAN_INTR_ENA;
+ XWRITE4(sc, runt, XHCI_IMAN(0), temp);
+
/* setup command ring control base address */
- addr = (uint64_t)buf_res.physaddr + (uintptr_t)&((struct xhci_hw_root *)0)->hwr_commands[0];
+ addr = buf_res.physaddr;
+ addr += (uintptr_t)&((struct xhci_hw_root *)0)->hwr_commands[0];
DPRINTF("CRCR=0x%016llx\n", (unsigned long long)addr);
@@ -428,12 +456,6 @@
sc->sc_bus.devices = sc->sc_devices;
sc->sc_bus.devices_max = XHCI_MAX_DEVICES;
- /*
- * The XHCI is very high level at this point and requires
- * special handling.
- */
- sc->sc_bus.no_set_address = 1;
-
/* setup command queue mutex and condition varible */
cv_init(&sc->sc_cmd_cv, "CMDQ");
sx_init(&sc->sc_cmd_sx, "CMDQ lock");
@@ -509,7 +531,7 @@
status = td->status;
len = td->remainder;
- DPRINTFN(4, "xfer=%p[%u/%u] len=%u/%u status=%u\n",
+ DPRINTFN(4, "xfer=%p[%u/%u] rem=%u/%u status=%u\n",
xfer, (unsigned int)xfer->aframes,
(unsigned int)xfer->nframes,
(unsigned int)len, (unsigned int)td->len,
@@ -620,13 +642,23 @@
struct xhci_endpoint_ext *pepext;
uint64_t td_event;
uint32_t temp;
- uint32_t actlen;
+ uint32_t remainder;
uint8_t status;
+ uint8_t halted;
/* decode TRB */
- td_event = trb->qwTrb0;
+ td_event = le64toh(trb->qwTrb0);
temp = le32toh(trb->dwTrb2);
+ remainder = XHCI_TRB_2_REM_GET(temp);
+ status = XHCI_TRB_2_ERROR_GET(temp);
+
+ /* check if error means halted */
+ halted = (status != XHCI_TRB_ERROR_SHORT_PKT) &&
+ (status != XHCI_TRB_ERROR_SUCCESS);
+
+ DPRINTF("remainder=%u status=%u\n", remainder, status);
+
/* try to find the USB transfer that generated the event */
TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) {
@@ -634,7 +666,8 @@
if (xfer->flags_int.did_dma_delay)
continue;
- pepext = xhci_get_endpoint_ext(xfer);
+ pepext = xhci_get_endpoint_ext(xfer->xroot->udev,
+ xfer->endpoint->edesc);
/* check if endpoint is halted */
if (pepext->trb_halted != 0)
@@ -642,19 +675,24 @@
td = xfer->td_transfer_cache;
- if (td_event == td->td_event) {
+ DPRINTF("0x%08llx == (0x%08llx .. 0x%08llx)\n",
+ (long long)td_event,
+ (long long)td->td_self,
+ (long long)td->td_event_last);
+
+ if ((td_event == td->td_event_last) ||
+ (halted && (td_event >= td->td_self) &&
+ (td_event < td->td_event_last))) {
struct xhci_endpoint_ext *pepext;
- pepext = xhci_get_endpoint_ext(xfer);
-
- actlen = XHCI_TRB_2_ACTLEN_GET(temp);
- status = XHCI_TRB_2_ERROR_GET(temp);
+ pepext = xhci_get_endpoint_ext(xfer->xroot->udev,
+ xfer->endpoint->edesc);
usb_pc_cpu_invalidate(td->page_cache);
/* "td->remainder" is verified later */
- td->remainder -= actlen;
+ td->remainder = remainder;
td->status = status;
usb_pc_cpu_flush(td->page_cache);
@@ -664,6 +702,7 @@
* transfer done
*/
if (((void *)td) == xfer->td_transfer_last) {
+ DPRINTF("TD is last\n");
xhci_generic_done(xfer);
break;
}
@@ -672,8 +711,8 @@
* 2) Any kind of error makes the transfer
* done
*/
- if ((status != XHCI_TRB_ERROR_SHORT_PKT) &&
- (status != XHCI_TRB_ERROR_SUCCESS)) {
+ if (halted) {
+ DPRINTF("TD has I/O error\n");
xhci_generic_done(xfer);
break;
}
@@ -683,6 +722,7 @@
* a short packet also makes the transfer done
*/
if (td->remainder > 0) {
+ DPRINTF("TD has short pkt\n");
if (xfer->flags_int.short_frames_ok) {
/* follow alt next */
if (td->alt_next != NULL) {
@@ -697,6 +737,7 @@
/*
* 4) Transfer complete - go to next TD
*/
+ DPRINTF("Following next TD\n");
xfer->td_transfer_cache = td->obj_next;
break; /* there should only be one match */
}
@@ -719,6 +760,7 @@
{
struct usb_page_search buf_res;
struct xhci_hw_root *phwr;
+ uint64_t addr;
uint32_t temp;
uint16_t i;
uint8_t event;
@@ -741,6 +783,7 @@
while (1) {
temp = le32toh(phwr->hwr_events[i].dwTrb3);
+
k = (temp & XHCI_TRB_3_CYCLE_BIT) ? 1 : 0;
if (j != k)
@@ -761,7 +804,7 @@
xhci_check_command(sc, &phwr->hwr_events[i]);
break;
default:
- DPRINTF("Received event = 0x%x\n", event);
+ DPRINTF("Unhandled event = %u\n", event);
break;
}
@@ -777,16 +820,24 @@
}
}
- temp = buf_res.physaddr + (uintptr_t)&((struct xhci_hw_root *)0)->hwr_events[i];
+ sc->sc_event_idx = i;
+ sc->sc_event_ccs = j;
- /* we are within a PAGE - no need to update the high bits */
+ /*
+ * NOTE: The Event Ring Dequeue Pointer Register is 64-bit
+ * latched. That means to activate the register we need to
+ * write both the low and high double word of the 64-bit
+ * register.
+ */
- temp |= XHCI_ERDP_LO_SINDEX(i) | XHCI_ERDP_LO_BUSY;
+ addr = (uint32_t)buf_res.physaddr;
+ addr += (uintptr_t)&((struct xhci_hw_root *)0)->hwr_events[i];
- XWRITE4(sc, runt, XHCI_ERDP_LO(0), temp);
+ /* try to clear busy bit */
+ addr |= XHCI_ERDP_LO_BUSY;
- sc->sc_event_idx = i;
- sc->sc_event_ccs = j;
+ XWRITE4(sc, runt, XHCI_ERDP_LO(0), (uint32_t)addr);
+ XWRITE4(sc, runt, XHCI_ERDP_HI(0), (uint32_t)(addr >> 32));
}
static usb_error_t
@@ -817,7 +868,7 @@
j = sc->sc_command_ccs;
DPRINTFN(10, "command[%u] = %u (0x%016llx, 0x%08lx, 0x%08lx)\n",
- i, XHCI_TRB_3_TYPE_GET(le32toh(trb->qwTrb0)),
+ i, XHCI_TRB_3_TYPE_GET(le32toh(trb->dwTrb3)),
(long long)le64toh(trb->qwTrb0),
(long)le32toh(trb->dwTrb2),
(long)le32toh(trb->dwTrb3));
@@ -840,8 +891,8 @@
usb_pc_cpu_flush(&sc->sc_hw.root_pc);
- addr = (uint64_t)buf_res.physaddr +
- (uintptr_t)&((struct xhci_hw_root *)0)->hwr_commands[i];
+ addr = buf_res.physaddr;
+ addr += (uintptr_t)&((struct xhci_hw_root *)0)->hwr_commands[i];
sc->sc_cmd_addr = htole64(addr);
@@ -850,12 +901,12 @@
if (i == (XHCI_MAX_COMMANDS - 1)) {
if (j) {
- temp |= htole32(XHCI_TRB_3_CYCLE_BIT |
+ temp = htole32(XHCI_TRB_3_TC_BIT |
XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_LINK) |
- XHCI_TRB_3_TC_BIT);
+ XHCI_TRB_3_CYCLE_BIT);
} else {
- temp &= htole32(XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_LINK) |
- XHCI_TRB_3_TC_BIT);
+ temp = htole32(XHCI_TRB_3_TC_BIT |
+ XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_LINK));
}
phwr->hwr_commands[i].dwTrb3 = temp;
@@ -882,7 +933,7 @@
} else {
temp = le32toh(sc->sc_cmd_result[0]);
if (XHCI_TRB_2_ERROR_GET(temp) != XHCI_TRB_ERROR_SUCCESS)
- err = USB_ERR_INVAL;
+ err = USB_ERR_IOERROR;
trb->dwTrb2 = sc->sc_cmd_result[0];
trb->dwTrb3 = sc->sc_cmd_result[1];
@@ -974,9 +1025,117 @@
trb.dwTrb3 = htole32(temp);
- return (xhci_do_command(sc, &trb, 50 /* ms */));
+ return (xhci_do_command(sc, &trb, 500 /* ms */));
+}
+
+static usb_error_t
+xhci_set_address(struct usb_device *udev, struct mtx *mtx, uint16_t address)
+{
+ struct xhci_softc *sc = XHCI_BUS2SC(udev->bus);
+ struct xhci_hw_dev *hdev;
+ usb_error_t err;
+ uint8_t index;
+
+ /* the root HUB case is not handled here */
+ if (udev->parent_hub == NULL)
+ return (USB_ERR_INVAL);
+
+ index = udev->controller_slot_id;
+
+ hdev = &sc->sc_hw.devs[index];
+
+ if (mtx != NULL)
+ mtx_unlock(mtx);
+
+ XHCI_CMD_LOCK(sc);
+
+ switch (hdev->state) {
+ case XHCI_ST_ADDRESSED:
+ err = 0;
+ break;
+
+ case XHCI_ST_ENABLED:
+ if (address == 0) {
+ err = 0;
+ break;
+ }
+ /* FALLTHROUGH */
+
+ case XHCI_ST_DEFAULT:
+
+ /* set configure mask to slot and EP0 */
+ xhci_configure_mask(udev, 3, 0);
+
+ /* configure input slot context structure */
+ err = xhci_configure_device(udev);
+
+ /* configure input endpoint context structure */
+ if (err == 0) {
+ struct xhci_endpoint_ext *pepext;
+ uint16_t mps;
+
+ switch (udev->speed) {
+ case USB_SPEED_LOW:
+ case USB_SPEED_FULL:
+ mps = 8;
+ break;
+ case USB_SPEED_HIGH:
+ mps = 64;
+ break;
+ default:
+ mps = 512;
+ break;
+ }
+
+ pepext = xhci_get_endpoint_ext(udev,
+ &udev->ctrl_ep_desc);
+ err = xhci_configure_endpoint(udev,
+ &udev->ctrl_ep_desc, pepext->physaddr,
+ 0, 1, mps, mps);
+ }
+
+ /* execute set address command */
+ if (err == 0) {
+ struct usb_page_search buf_inp;
+
+ usbd_get_page(&hdev->input_pc, 0, &buf_inp);
+
+ err = xhci_cmd_set_address(sc, buf_inp.physaddr,
+ (address == 0), index);
+ }
+
+ /* update device address and state to new value */
+ if (err == 0) {
+ struct usb_page_search buf_dev;
+ struct xhci_dev_ctx *pdev;
+
+ if (address == 0)
+ hdev->state = XHCI_ST_DEFAULT;
+ else
+ hdev->state = XHCI_ST_ADDRESSED;
+
+ usbd_get_page(&hdev->device_pc, 0, &buf_dev);
+ pdev = buf_dev.buffer;
+ usb_pc_cpu_invalidate(&hdev->device_pc);
+ udev->address = XHCI_SCTX_3_DEV_ADDR_GET(pdev->ctx_slot.dwSctx3);
+ }
+ break;
+
+ default:
+ DPRINTF("Wrong state for set address.\n");
+ err = USB_ERR_IOERROR;
+ break;
+ }
+
+ XHCI_CMD_UNLOCK(sc);
+
+ if (mtx != NULL)
+ mtx_lock(mtx);
+
+ return (err);
}
+#if 0
static usb_error_t
xhci_cmd_configure_ep(struct xhci_softc *sc, uint64_t input_ctx,
uint8_t deconfigure, uint8_t slot_id)
@@ -998,8 +1157,8 @@
return (xhci_do_command(sc, &trb, 50 /* ms */));
}
+#endif
-#if 0
static usb_error_t
xhci_cmd_evaluate_ctx(struct xhci_softc *sc, uint64_t input_ctx,
uint8_t slot_id)
@@ -1017,7 +1176,6 @@
return (xhci_do_command(sc, &trb, 50 /* ms */));
}
-#endif
static usb_error_t
xhci_cmd_reset_ep(struct xhci_softc *sc, uint8_t preserve,
@@ -1042,7 +1200,6 @@
return (xhci_do_command(sc, &trb, 50 /* ms */));
}
-#if 0
static usb_error_t
xhci_cmd_stop_ep(struct xhci_softc *sc, uint8_t suspend,
uint8_t ep_id, uint8_t slot_id)
@@ -1065,7 +1222,6 @@
return (xhci_do_command(sc, &trb, 50 /* ms */));
}
-#endif
static usb_error_t
xhci_cmd_reset_dev(struct xhci_softc *sc, uint8_t slot_id)
@@ -1105,9 +1261,11 @@
temp = XREAD4(sc, runt, XHCI_IMAN(0));
/* acknowledge pending event */
+
XWRITE4(sc, runt, XHCI_IMAN(0), temp);
- DPRINTFN(16, "real interrupt (sts=0x%08x, iman=0x%08x)\n", status, temp);
+ DPRINTFN(16, "real interrupt (sts=0x%08x, "
+ "iman=0x%08x)\n", status, temp);
if (status != 0) {
if (status & XHCI_STS_PCD) {
@@ -1148,7 +1306,8 @@
USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED);
- pepext = xhci_get_endpoint_ext(xfer);
+ pepext = xhci_get_endpoint_ext(xfer->xroot->udev,
+ xfer->endpoint->edesc);
#if 0
/* check if endpoint is halted */
@@ -1177,6 +1336,7 @@
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;
@@ -1241,7 +1401,7 @@
/* fill out current TD */
td->len = average;
- td->remainder = average;
+ td->remainder = 0;
td->status = 0;
/* update remaining length */
@@ -1270,7 +1430,7 @@
td->td_trb[0].dwTrb2 = htole32(dword);
dword = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_SETUP_STAGE) |
- XHCI_TRB_3_IDT_BIT;
+ XHCI_TRB_3_IDT_BIT | XHCI_TRB_3_CYCLE_BIT;
/* check wLength */
if (td->td_trb[0].qwTrb0 & htole64(0xFFFF00000000ULL)) {
@@ -1281,7 +1441,9 @@
}
td->td_trb[0].dwTrb3 = htole32(dword);
-
+#ifdef USB_DEBUG
+ xhci_dump_trb(&td->td_trb[x]);
+#endif
x++;
} else do {
@@ -1294,7 +1456,8 @@
npkt = 1;
memset(&buf_res, 0, sizeof(buf_res));
} else {
- usbd_get_page(temp->pc, temp->offset + buf_offset, &buf_res);
+ usbd_get_page(temp->pc, temp->offset +
+ buf_offset, &buf_res);
/* get length to end of page */
if (buf_res.length > average)
@@ -1305,7 +1468,9 @@
buf_res.length = XHCI_TD_PAGE_SIZE;
/* setup npkt */
- npkt = (average + temp->max_packet_size - 1) / temp->max_packet_size;
+ npkt = (average + temp->max_packet_size - 1) /
+ temp->max_packet_size;
+
if (npkt > 31)
npkt = 31;
}
@@ -1321,42 +1486,50 @@
td->td_trb[x].dwTrb2 = htole32(dword);
- dword = XHCI_TRB_3_CHAIN_BIT |
+ 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);
if (temp->direction == UE_DIR_IN)
dword |= XHCI_TRB_3_DIR_IN;
+ if (average == 0)
+ dword |= XHCI_TRB_3_IDT_BIT;
+
td->td_trb[x].dwTrb3 = htole32(dword);
average -= buf_res.length;
buf_offset += buf_res.length;
+#ifdef USB_DEBUG
+ xhci_dump_trb(&td->td_trb[x]);
+#endif
x++;
} while (average != 0);
+ td->td_trb[x-1].dwTrb3 |= htole32(XHCI_TRB_3_IOC_BIT);
+
/* store number of data TRB's */
td->ntrb = x;
+ DPRINTF("NTRB=%u\n", x);
+
/* compute event pointer */
- if (1) {
- uint64_t td_event;
-
- td_event = le64toh(td->td_self);
- td_event += x * sizeof(td->td_trb[0]);
- td->td_event = htole64(td_event);
- }
+ addr = td->td_self;
+ addr += (x - 1) * sizeof(struct xhci_trb);
+ td->td_event_last = addr;
/* fill out link TRB */
if (td_next != NULL) {
/* link the current TD with the next one */
- td->td_trb[x].qwTrb0 = td_next->td_self;
+ td->td_trb[x].qwTrb0 = htole64((uint64_t)td_next->td_self);
+ DPRINTF("LINK=0x%08llx\n", (long long)td_next->td_self);
} else {
/* this field will get updated later */
+ DPRINTF("NOLINK\n");
}
dword = XHCI_TRB_2_IRQ_SET(0);
@@ -1364,12 +1537,15 @@
td->td_trb[x].dwTrb2 = htole32(dword);
dword = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_LINK) |
- XHCI_TRB_3_IOC_BIT | XHCI_TRB_3_CHAIN_BIT;
+ XHCI_TRB_3_CHAIN_BIT | XHCI_TRB_3_CYCLE_BIT |
+ XHCI_TRB_3_IOC_BIT;
td->td_trb[x].dwTrb3 = htole32(dword);
td->alt_next = td_alt_next;
-
+#ifdef USB_DEBUG
+ xhci_dump_trb(&td->td_trb[x]);
+#endif
usb_pc_cpu_flush(td->page_cache);
}
@@ -1388,15 +1564,15 @@
temp->shortpkt = shortpkt_old;
temp->len = len_old;
goto restart;
- } else {
- 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);
- td->td_trb[td->ntrb].dwTrb2 &= ~htole32(XHCI_TRB_2_TDSZ_SET(15));
- td->td_trb[td->ntrb].dwTrb3 &= ~htole32(XHCI_TRB_3_CHAIN_BIT);
- usb_pc_cpu_flush(td->page_cache);
- }
+ }
+
+ 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);
+ td->td_trb[td->ntrb].dwTrb2 &= ~htole32(XHCI_TRB_2_TDSZ_SET(15));
+ td->td_trb[td->ntrb].dwTrb3 &= ~htole32(XHCI_TRB_3_CHAIN_BIT);
+ usb_pc_cpu_flush(td->page_cache);
}
temp->td = td;
temp->td_next = td_next;
@@ -1605,6 +1781,8 @@
/* must have at least one frame! */
xfer->td_transfer_last = td;
+
+ DPRINTF("first=%p last=%p\n", xfer->td_transfer_first, td);
}
static void
@@ -1617,6 +1795,8 @@
pdctxa = buf_res.buffer;
+ DPRINTF("addr[%u]=0x%016llx\n", index, (long long)dev_addr);
+
pdctxa->qwBaaDevCtxAddr[index] = htole64(dev_addr);
usb_pc_cpu_flush(&sc->sc_hw.ctx_pc);
@@ -1640,31 +1820,49 @@
}
static usb_error_t
-xhci_configure_endpoint(struct usb_xfer *xfer, uint8_t drop)
+xhci_configure_mask(struct usb_device *udev, uint32_t mask, uint8_t drop)
+{
+ struct xhci_softc *sc = XHCI_BUS2SC(udev->bus);
+ struct usb_page_search buf_inp;
+ struct xhci_input_dev_ctx *pinp;
+ uint8_t index;
+
+ index = udev->controller_slot_id;
+
+ usbd_get_page(&sc->sc_hw.devs[index].input_pc, 0, &buf_inp);
+
+ pinp = buf_inp.buffer;
+
+ if (drop) {
+ mask &= 0xFFFFFFFC;
+ pinp->ctx_input.dwInCtx0 = htole32(mask);
+ pinp->ctx_input.dwInCtx1 = 0;
+ } else {
+ pinp->ctx_input.dwInCtx0 = 0;
+ pinp->ctx_input.dwInCtx1 = htole32(mask);
+ }
+ return (0);
+}
+
+static usb_error_t
+xhci_configure_endpoint(struct usb_device *udev,
+ struct usb_endpoint_descriptor *edesc, uint64_t ring_addr,
+ uint16_t interval, uint8_t max_packet_count,
+ uint16_t max_packet_size, uint16_t max_frame_size)
{
struct usb_page_search buf_inp;
- struct usb_page_search buf_ep;
- struct xhci_softc *sc = XHCI_BUS2SC(xfer->xroot->bus);
+ struct xhci_softc *sc = XHCI_BUS2SC(udev->bus);
struct xhci_input_dev_ctx *pinp;
- struct usb_endpoint_descriptor *edesc;
- struct xhci_endpoint_ext *pepext;
- struct usb_device *udev;
- uint64_t addr;
uint32_t temp;
- uint32_t mask;
uint8_t index;
uint8_t epno;
uint8_t k;
- pepext = xhci_get_endpoint_ext(xfer);
-
- udev = xfer->xroot->udev;
-
index = udev->controller_slot_id;
usbd_get_page(&sc->sc_hw.devs[index].input_pc, 0, &buf_inp);
- edesc = xfer->endpoint->edesc;
+ pinp = buf_inp.buffer;
epno = edesc->bEndpointAddress;
@@ -1674,23 +1872,12 @@
epno = XHCI_EPNO2EPID(epno);
if (epno == 0)
- return (USB_ERR_INVAL); /* invalid */
+ return (USB_ERR_NO_PIPE); /* invalid */
- usbd_get_page(&sc->sc_hw.devs[index].endpoint_pc, (XHCI_MAX_TRANSFERS * sizeof(struct xhci_trb)) * epno, &buf_ep);
+ if (max_packet_count == 0)
+ return (USB_ERR_BAD_BUFSIZE);
- pinp = buf_inp.buffer;
-
- mask = 1U << epno;
-
- if (drop) {
- mask &= 0xFFFFFFFC;
- pinp->ctx_input.dwInCtx0 = htole32(mask);
- pinp->ctx_input.dwInCtx1 = 0;
- } else {
- mask |= 1;
- pinp->ctx_input.dwInCtx0 = 0;
- pinp->ctx_input.dwInCtx1 = htole32(mask);
- }
+ max_packet_count --;
temp = XHCI_EPCTX_0_EPSTATE_SET(0) |
XHCI_EPCTX_0_MAXP_STREAMS_SET(0) |
@@ -1698,12 +1885,12 @@
switch (edesc->bmAttributes & UE_XFERTYPE) {
case UE_INTERRUPT:
- k = xhci_log2(xfer->interval) + 3;
+ k = xhci_log2(interval) + 3;
temp |= XHCI_EPCTX_0_IVAL_SET(k);
break;
case UE_ISOCHRONOUS:
if (udev->speed == USB_SPEED_SUPER)
- temp |= XHCI_EPCTX_0_MULT_SET(xfer->max_packet_count - 1);
+ temp |= XHCI_EPCTX_0_MULT_SET(max_packet_count);
break;
default:
break;
@@ -1713,8 +1900,8 @@
temp =
XHCI_EPCTX_1_HID_SET(0) |
- XHCI_EPCTX_1_MAXB_SET(xfer->max_packet_count - 1) |
- XHCI_EPCTX_1_MAXP_SIZE_SET(xfer->max_packet_size);
+ XHCI_EPCTX_1_MAXB_SET(max_packet_count) |
+ XHCI_EPCTX_1_MAXP_SIZE_SET(max_packet_size);
if ((udev->parent_hs_hub != NULL) || (udev->address != 0))
temp |= XHCI_EPCTX_1_CERR_SET(3);
@@ -1739,34 +1926,38 @@
pinp->ctx_ep[epno - 1].dwEpCtx1 = htole32(temp);
- if (drop == 0) {
+ ring_addr |= XHCI_EPCTX_2_DCS_SET(1);
- USB_BUS_LOCK(&sc->sc_bus);
+ pinp->ctx_ep[epno - 1].qwEpCtx2 = htole64(ring_addr);
- pepext->trb_ccs = 1;
- pepext->trb_index = 0;
+ temp = XHCI_EPCTX_4_AVG_TRB_LEN_SET(max_frame_size) |
+ XHCI_EPCTX_4_MAX_ESIT_PAYLOAD_SET(max_frame_size);
- for (k = 0; k != XHCI_MAX_TRANSFERS; k++)
- pepext->trb[k].dwTrb3 = 0;
+ pinp->ctx_ep[epno - 1].dwEpCtx4 = htole32(temp);
- usb_pc_cpu_flush(pepext->page_cache);
+#ifdef USB_DEBUG
+ xhci_dump_endpoint(&pinp->ctx_ep[epno - 1]);
+#endif
+ usb_pc_cpu_flush(&sc->sc_hw.devs[index].input_pc);
- USB_BUS_UNLOCK(&sc->sc_bus);
- }
+ return (0); /* success */
+}
- addr = XHCI_EPCTX_2_DCS_SET(1) |
- (buf_ep.physaddr + (uintptr_t)&((struct xhci_dev_endpoint_trbs *)0)->trb[epno][0]);
+static usb_error_t
+xhci_configure_endpoint_by_xfer(struct usb_xfer *xfer)
+{
+ struct xhci_endpoint_ext *pepext;
- pinp->ctx_ep[epno - 1].qwEpCtx2 = htole64(addr);
+ pepext = xhci_get_endpoint_ext(xfer->xroot->udev,
+ xfer->endpoint->edesc);
- temp = XHCI_EPCTX_4_AVG_TRB_LEN_SET(xfer->max_frame_size) |
- XHCI_EPCTX_4_MAX_ESIT_PAYLOAD_SET(xfer->max_frame_size);
+ pepext->trb[0].dwTrb3 = 0; /* halt any transfers */
+ usb_pc_cpu_flush(pepext->page_cache);
- pinp->ctx_ep[epno - 1].dwEpCtx4 = htole32(temp);
-
- usb_pc_cpu_flush(&sc->sc_hw.devs[index].input_pc);
-
- return (0); /* success */
+ return (xhci_configure_endpoint(xfer->xroot->udev,
+ xfer->endpoint->edesc, pepext->physaddr,
+ xfer->interval, xfer->max_packet_count,
+ xfer->max_packet_size, xfer->max_frame_size));
}
static usb_error_t
@@ -1778,39 +1969,57 @@
struct usb_page_cache *pcdev;
struct usb_page_cache *pcinp;
struct xhci_input_dev_ctx *pinp;
+ struct xhci_dev_ctx *pdev;
struct usb_device *hubdev;
uint32_t temp;
+ uint32_t route;
+ uint8_t is_hub;
uint8_t index;
uint8_t rh_port;
index = udev->controller_slot_id;
+ DPRINTF("index=%u\n", index);
+
pcdev = &sc->sc_hw.devs[index].device_pc;
>>> TRUNCATED FOR MAIL (1000 lines) <<<
More information about the p4-projects
mailing list