Module Name: src Committed By: skrll Date: Tue Apr 7 06:49:10 UTC 2015
Modified Files: src/sys/dev/usb [nick-nhusb]: xhci.c Log Message: USB 3.0 snapshot from Takahiro HAYASHI To generate a diff of this commit: cvs rdiff -u -r1.28.2.18 -r1.28.2.19 src/sys/dev/usb/xhci.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/usb/xhci.c diff -u src/sys/dev/usb/xhci.c:1.28.2.18 src/sys/dev/usb/xhci.c:1.28.2.19 --- src/sys/dev/usb/xhci.c:1.28.2.18 Mon Apr 6 19:28:40 2015 +++ src/sys/dev/usb/xhci.c Tue Apr 7 06:49:10 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: xhci.c,v 1.28.2.18 2015/04/06 19:28:40 skrll Exp $ */ +/* $NetBSD: xhci.c,v 1.28.2.19 2015/04/07 06:49:10 skrll Exp $ */ /* * Copyright (c) 2013 Jonathan A. Kollasch @@ -27,7 +27,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: xhci.c,v 1.28.2.18 2015/04/06 19:28:40 skrll Exp $"); +__KERNEL_RCSID(0, "$NetBSD: xhci.c,v 1.28.2.19 2015/04/07 06:49:10 skrll Exp $"); #include "opt_usb.h" @@ -50,6 +50,7 @@ __KERNEL_RCSID(0, "$NetBSD: xhci.c,v 1.2 #include <dev/usb/usb.h> #include <dev/usb/usbdi.h> #include <dev/usb/usbdivar.h> +#include <dev/usb/usbdi_util.h> #include <dev/usb/usbhist.h> #include <dev/usb/usb_mem.h> #include <dev/usb/usb_quirks.h> @@ -126,18 +127,22 @@ static int xhci_roothub_ctrl(struct usbd void *, int); static usbd_status xhci_configure_endpoint(struct usbd_pipe *); -static usbd_status xhci_unconfigure_endpoint(struct usbd_pipe *); +//static usbd_status xhci_unconfigure_endpoint(struct usbd_pipe *); static usbd_status xhci_reset_endpoint(struct usbd_pipe *); -//static usbd_status xhci_stop_endpoint(struct usbd_pipe *); +static usbd_status xhci_stop_endpoint(struct usbd_pipe *); static usbd_status xhci_set_dequeue(struct usbd_pipe *); static usbd_status xhci_do_command(struct xhci_softc * const, struct xhci_trb * const, int); -static usbd_status xhci_init_slot(struct xhci_softc * const, uint32_t, - int, int, int, int); +static usbd_status xhci_do_command1(struct xhci_softc * const, + struct xhci_trb * const, int, int); +static usbd_status xhci_do_command_locked(struct xhci_softc * const, + struct xhci_trb * const, int); +static usbd_status xhci_init_slot(struct usbd_device *, uint32_t, int, int); static usbd_status xhci_enable_slot(struct xhci_softc * const, uint8_t * const); +static usbd_status xhci_disable_slot(struct xhci_softc * const, uint8_t); static usbd_status xhci_address_device(struct xhci_softc * const, uint64_t, uint8_t, bool); static usbd_status xhci_update_ep0_mps(struct xhci_softc * const, @@ -228,11 +233,24 @@ static const struct usbd_pipe_methods xh }; static inline uint32_t +xhci_read_1(const struct xhci_softc * const sc, bus_size_t offset) +{ + return bus_space_read_1(sc->sc_iot, sc->sc_ioh, offset); +} + +static inline uint32_t xhci_read_4(const struct xhci_softc * const sc, bus_size_t offset) { return bus_space_read_4(sc->sc_iot, sc->sc_ioh, offset); } +static inline void +xhci_write_1(const struct xhci_softc * const sc, bus_size_t offset, + uint32_t value) +{ + bus_space_write_1(sc->sc_iot, sc->sc_ioh, offset, value); +} + #if 0 /* unused */ static inline void xhci_write_4(const struct xhci_softc * const sc, bus_size_t offset, @@ -374,7 +392,7 @@ xhci_db_write_4(const struct xhci_softc static inline uint8_t xhci_ep_get_type(usb_endpoint_descriptor_t * const ed) { - u_int eptype; + u_int eptype = 0; switch (UE_GET_XFERTYPE(ed->bmAttributes)) { case UE_CONTROL: @@ -517,6 +535,7 @@ xhci_detach(struct xhci_softc *sc, int f mutex_destroy(&sc->sc_lock); mutex_destroy(&sc->sc_intr_lock); + cv_destroy(&sc->sc_softwake_cv); pool_cache_destroy(sc->sc_xferpool); @@ -658,6 +677,28 @@ xhci_init(struct xhci_softc *sc) } break; } + case XHCI_ID_USB_LEGACY: { + uint8_t bios_sem; + + /* Take host controller from BIOS */ + bios_sem = xhci_read_1(sc, ecp + XHCI_XECP_BIOS_SEM); + if (bios_sem) { + /* sets xHCI to be owned by OS */ + xhci_write_1(sc, ecp + XHCI_XECP_OS_SEM, 1); + aprint_debug( + "waiting for BIOS to give up control\n"); + for (i = 0; i < 5000; i++) { + bios_sem = xhci_read_1(sc, ecp + + XHCI_XECP_BIOS_SEM); + if (bios_sem == 0) + break; + DELAY(1000); + } + if (bios_sem) + printf("timed out waiting for BIOS\n"); + } + break; + } default: break; } @@ -732,6 +773,7 @@ xhci_init(struct xhci_softc *sc) aprint_debug_dev(sc->sc_dev, "sc_pgsz 0x%08x\n", (uint32_t)sc->sc_pgsz); aprint_debug_dev(sc->sc_dev, "sc_maxslots 0x%08x\n", (uint32_t)sc->sc_maxslots); + aprint_debug_dev(sc->sc_dev, "sc_maxports %d\n", sc->sc_maxports); usbd_status err; @@ -825,6 +867,16 @@ xhci_init(struct xhci_softc *sc) KM_SLEEP); cv_init(&sc->sc_command_cv, "xhcicmd"); + mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_SOFTUSB); + mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_SCHED); + cv_init(&sc->sc_softwake_cv, "xhciab"); + + sc->sc_xferpool = pool_cache_init(sizeof(struct xhci_xfer), 0, 0, 0, + "xhcixfer", NULL, IPL_USB, NULL, NULL, NULL); + + /* Set up the bus struct. */ + sc->sc_bus.ub_methods = &xhci_bus_methods; + sc->sc_bus.ub_pipesize = sizeof(struct xhci_pipe); struct xhci_erste *erst; erst = KERNADDR(&sc->sc_eventst_dma, 0); @@ -848,23 +900,18 @@ xhci_init(struct xhci_softc *sc) #endif xhci_rt_write_4(sc, XHCI_IMAN(0), XHCI_IMAN_INTR_ENA); - xhci_rt_write_4(sc, XHCI_IMOD(0), 0); +#ifdef XHCI_QUIRK_INTEL + if ((sc->sc_quirks & XHCI_QUIRK_INTEL) != 0) + /* Intel xhci needs interrupt rate moderated. */ + xhci_rt_write_4(sc, XHCI_IMOD(0), XHCI_IMOD_DEFAULT_LP); + else +#endif /* XHCI_QUIRK_INTEL */ + xhci_rt_write_4(sc, XHCI_IMOD(0), 0); xhci_op_write_4(sc, XHCI_USBCMD, XHCI_CMD_INTE|XHCI_CMD_RS); /* Go! */ aprint_debug_dev(sc->sc_dev, "USBCMD %08"PRIx32"\n", xhci_op_read_4(sc, XHCI_USBCMD)); - mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_SOFTUSB); - mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_SCHED); - cv_init(&sc->sc_softwake_cv, "xhciab"); - - sc->sc_xferpool = pool_cache_init(sizeof(struct xhci_xfer), 0, 0, 0, - "xhcixfer", NULL, IPL_USB, NULL, NULL, NULL); - - /* Set up the bus struct. */ - sc->sc_bus.ub_methods = &xhci_bus_methods; - sc->sc_bus.ub_pipesize = sizeof(struct xhci_pipe); - return USBD_NORMAL_COMPLETION; } @@ -920,9 +967,19 @@ xhci_intr1(struct xhci_softc * const sc) iman = xhci_rt_read_4(sc, XHCI_IMAN(0)); DPRINTFN(16, "IMAN0 %08x", iman, 0, 0, 0); +#ifdef XHCI_QUIRK_FORCE_INTR + + if (!(sc->sc_quirks & XHCI_QUIRK_FORCE_INTR)) { + if ((iman & XHCI_IMAN_INTR_PEND) == 0) { + return 0; + } + } + +#else if ((iman & XHCI_IMAN_INTR_PEND) == 0) { return 0; } +#endif /* XHCI_QUIRK_FORCE_INTR */ xhci_rt_write_4(sc, XHCI_IMAN(0), iman); iman = xhci_rt_read_4(sc, XHCI_IMAN(0)); DPRINTFN(16, "IMAN0 %08x", iman, 0, 0, 0); @@ -934,6 +991,138 @@ xhci_intr1(struct xhci_softc * const sc) return 1; } +/* + * 3 port speed types used in USB stack + * + * usbdi speed + * definition: USB_SPEED_* in usb.h + * They are used in struct usbd_device in USB stack. + * ioctl interface uses these values too. + * port_status speed + * definition: UPS_*_SPEED in usb.h + * They are used in usb_port_status_t. + * Some 3.0 values overlap with 2.0 values. + * (e.g. 0x200 means UPS_POER_POWER_SS in SS and + * means UPS_LOW_SPEED in HS.) + * port status sent from hub also uses these values. + * (but I've never seen UPS_SUPER_SPEED in port_status from hub.) + * xspeed: + * definition: Protocol Speed ID (PSI) (xHCI 1.1 7.2.1) + * They are used in only slot context and PORTSC reg of xhci. + * The difference between usbdi speed and them are that + * FS and LS values are swapped. + */ + +static int +xhci_speed2xspeed(int speed) +{ + switch (speed) { + case USB_SPEED_LOW: return 2; + case USB_SPEED_FULL: return 1; + case USB_SPEED_HIGH: return 3; + case USB_SPEED_SUPER: return 4; + default: + break; + } + return 0; +} + +/* construct slot context */ +static void +xhci_setup_sctx(struct usbd_device *dev, uint32_t *cp) +{ + usb_device_descriptor_t * const dd = &dev->ud_ddesc; + int speed = dev->ud_speed; + int tthubslot, ttportnum; + bool ishub; + bool usemtt; + + XHCIHIST_FUNC(); XHCIHIST_CALLED(); + + /* 6.2.2 */ + /* + * tthubslot: + * This is the slot ID of parent HS hub + * if LS/FS device is connected && connected through HS hub. + * This is 0 if device is not LS/FS device || + * parent hub is not HS hub || + * attached to root hub. + * ttportnum: + * This is the downstream facing port of parent HS hub + * if LS/FS device is connected. + * This is 0 if device is not LS/FS device || + * parent hub is not HS hub || + * attached to root hub. + */ + if (dev->ud_myhsport != NULL && + dev->ud_myhub != NULL && dev->ud_myhub->ud_depth != 0 && + (dev->ud_myhub != NULL && + dev->ud_myhub->ud_speed == USB_SPEED_HIGH) && + (speed == USB_SPEED_LOW || speed == USB_SPEED_FULL)) { + ttportnum = dev->ud_myhsport->up_portno; + /* XXX addr == slot ? */ + tthubslot = dev->ud_myhsport->up_parent->ud_addr; + } else { + ttportnum = 0; + tthubslot = 0; + } + DPRINTFN(4, "myhsport %p ttportnum=%d tthubslot=%d", + dev->ud_myhsport, ttportnum, tthubslot, 0); + + /* ishub is valid after reading UDESC_DEVICE */ + ishub = (dd->bDeviceClass == UDCLASS_HUB); + + /* dev->ud_hub is valid after reading UDESC_HUB */ + if (ishub && dev->ud_hub) { + usb_hub_descriptor_t *hd = &dev->ud_hub->uh_hubdesc; + + cp[1] |= htole32(XHCI_SCTX_1_NUM_PORTS_SET(hd->bNbrPorts)); + cp[2] |= htole32(XHCI_SCTX_2_TT_THINK_TIME_SET( + __SHIFTOUT(UGETW(hd->wHubCharacteristics), UHD_TT_THINK))); + DPRINTFN(4, "nports=%d ttt=%d", + hd->bNbrPorts, XHCI_SCTX_2_TT_THINK_TIME_GET(cp[2]), 0, 0); + } + +#define IS_TTHUB(dd) \ + ((dd)->bDeviceProtocol == UDPROTO_HSHUBSTT || \ + (dd)->bDeviceProtocol == UDPROTO_HSHUBMTT) + + /* + * MTT flag is set if + * 1. this is HS hub && MTT is enabled + * or + * 2. this is not hub && this is LS or FS device && + * MTT of parent HS hub (and its parent, too) is enabled + */ + if (ishub && speed == USB_SPEED_HIGH && IS_TTHUB(dd)) + usemtt = true; + else if (!ishub && + (speed == USB_SPEED_LOW || speed == USB_SPEED_FULL) && + dev->ud_myhub != NULL && dev->ud_myhub->ud_depth != 0 && + (dev->ud_myhub != NULL && + dev->ud_myhub->ud_speed == USB_SPEED_HIGH) && + dev->ud_myhsport != NULL && + IS_TTHUB(&dev->ud_myhsport->up_parent->ud_ddesc)) + usemtt = true; + else + usemtt = false; + DPRINTFN(4, "class %u proto %u ishub %d usemtt %d", + dd->bDeviceClass, dd->bDeviceProtocol, ishub, usemtt); + + cp[0] |= htole32( + XHCI_SCTX_0_SPEED_SET(xhci_speed2xspeed(speed)) | + XHCI_SCTX_0_HUB_SET(ishub ? 1 : 0) | + XHCI_SCTX_0_MTT_SET(usemtt ? 1 : 0) + ); + cp[1] |= htole32(0); + cp[2] |= htole32( + XHCI_SCTX_2_IRQ_TARGET_SET(0) | + XHCI_SCTX_2_TT_HUB_SID_SET(tthubslot) | + XHCI_SCTX_2_TT_PORT_NUM_SET(ttportnum) + ); + cp[3] |= htole32(0); +} + static usbd_status xhci_configure_endpoint(struct usbd_pipe *pipe) { @@ -945,10 +1134,14 @@ xhci_configure_endpoint(struct usbd_pipe struct xhci_trb trb; usbd_status err; uint32_t *cp; + uint32_t mps = UGETW(ed->wMaxPacketSize); + uint32_t maxb = 0; + int speed = pipe->up_dev->ud_speed; + uint32_t ival = ed->bInterval; XHCIHIST_FUNC(); XHCIHIST_CALLED(); - DPRINTFN(4, "dci %u epaddr 0x%02x attr 0x%02x", - dci, ed->bEndpointAddress, ed->bmAttributes, 0); + DPRINTFN(4, "slot %u dci %u epaddr 0x%02x attr 0x%02x", + xs->xs_idx, dci, ed->bEndpointAddress, ed->bmAttributes); /* XXX ensure input context is available? */ @@ -960,34 +1153,121 @@ xhci_configure_endpoint(struct usbd_pipe /* set up input slot context */ cp = xhci_slot_get_icv(sc, xs, xhci_dci_to_ici(XHCI_DCI_SLOT)); - cp[0] = htole32(XHCI_SCTX_0_CTX_NUM_SET(dci)); - cp[1] = htole32(0); - cp[2] = htole32(0); - cp[3] = htole32(0); + xhci_setup_sctx(pipe->up_dev, cp); + cp[0] |= htole32(XHCI_SCTX_0_CTX_NUM_SET(dci)); - uint8_t eptype = xhci_ep_get_type(pipe->up_endpoint->ue_edesc); cp = xhci_slot_get_icv(sc, xs, xhci_dci_to_ici(dci)); - if (xfertype == UE_INTERRUPT) { - cp[0] = htole32( - XHCI_EPCTX_0_IVAL_SET(3) /* XXX */ - ); - cp[1] = htole32( - XHCI_EPCTX_1_CERR_SET(3) | - XHCI_EPCTX_1_EPTYPE_SET(eptype) | - XHCI_EPCTX_1_MAXB_SET(0) | - XHCI_EPCTX_1_MAXP_SIZE_SET(8) /* XXX */ - ); + cp[0] = htole32( + XHCI_EPCTX_0_EPSTATE_SET(0) | + XHCI_EPCTX_0_MAXP_STREAMS_SET(0) | + XHCI_EPCTX_0_LSA_SET(0) + ); + cp[1] = htole32( + XHCI_EPCTX_1_EPTYPE_SET(xhci_ep_get_type(ed)) | + XHCI_EPCTX_1_MAXB_SET(0) + ); + if (xfertype != UE_ISOCHRONOUS) + cp[1] |= htole32(XHCI_EPCTX_1_CERR_SET(3)); + + if (speed == USB_SPEED_SUPER) { + usbd_desc_iter_t iter; + const usb_cdc_descriptor_t *cdcd; + const usb_endpoint_ss_comp_descriptor_t * esscd = NULL; + uint8_t ep; + + cdcd = (const usb_cdc_descriptor_t *)usb_find_desc( + pipe->up_dev, UDESC_INTERFACE, USBD_CDCSUBTYPE_ANY); + usb_desc_iter_init(pipe->up_dev, &iter); + iter.cur = (const void *)cdcd; + + /* find endpoint_ss_comp desc for ep of this pipe */ + for(ep = 0;;) { + cdcd = (const usb_cdc_descriptor_t *) + usb_desc_iter_next(&iter); + if (cdcd == NULL) + break; + if (ep == 0 && + cdcd->bDescriptorType == UDESC_ENDPOINT) { + ep = ((const usb_endpoint_descriptor_t *)cdcd)-> + bEndpointAddress; + if (UE_GET_ADDR(ep) == + UE_GET_ADDR(ed->bEndpointAddress)) { + cdcd = (const usb_cdc_descriptor_t *) + usb_desc_iter_next(&iter); + break; + } + ep = 0; + } + } + if (cdcd != NULL && + cdcd->bDescriptorType == UDESC_ENDPOINT_SS_COMP) { + esscd = (const usb_endpoint_ss_comp_descriptor_t *)cdcd; + maxb = esscd->bMaxBurst; + cp[1] |= htole32(XHCI_EPCTX_1_MAXB_SET(maxb)); + DPRINTFN(4, "setting SS MaxBurst %u", maxb, 0, 0, 0); + } + } + if (speed == USB_SPEED_HIGH && + (xfertype == UE_ISOCHRONOUS || xfertype == UE_INTERRUPT)) { + maxb = UE_GET_TRANS(UGETW(ed->wMaxPacketSize)); + cp[1] |= htole32(XHCI_EPCTX_1_MAXB_SET(maxb)); + DPRINTFN(4, "setting HS MaxBurst %u", maxb, 0, 0, 0); + } + + switch (xfertype) { + case UE_INTERRUPT: + /* 6.2.3.6 */ + if (speed == USB_SPEED_LOW || speed == USB_SPEED_FULL) { + ival = ival > 10 ? 10 : ival; + ival = ival < 3 ? 3 : ival; + } else { + ival = ival > 15 ? 15 : ival; + } + if (speed == USB_SPEED_SUPER) { + if (maxb > 0) + mps = 1024; + } else { + mps = mps ? mps : 8; + } + cp[0] |= htole32(XHCI_EPCTX_0_IVAL_SET(ival)); + cp[1] |= htole32(XHCI_EPCTX_1_MAXP_SIZE_SET(mps)); cp[4] = htole32( - XHCI_EPCTX_4_AVG_TRB_LEN_SET(8) - ); - } else { - cp[0] = htole32(0); - cp[1] = htole32( - XHCI_EPCTX_1_CERR_SET(3) | - XHCI_EPCTX_1_EPTYPE_SET(eptype) | - XHCI_EPCTX_1_MAXB_SET(0) | - XHCI_EPCTX_1_MAXP_SIZE_SET(512) /* XXX */ + XHCI_EPCTX_4_AVG_TRB_LEN_SET(8) /* XXX */ ); + break; + case UE_CONTROL: + if (speed == USB_SPEED_SUPER) + mps = 512; + else + mps = mps ? mps : 8; + cp[1] |= htole32(XHCI_EPCTX_1_MAXP_SIZE_SET(mps)); + cp[4] = htole32(XHCI_EPCTX_4_AVG_TRB_LEN_SET(8)); /* XXX */ + break; +#ifdef notyet + case UE_ISOCHRONOUS: + if (speed == USB_SPEED_FULL) { + ival = ival > 18 ? 18 : ival; + ival = ival < 3 ? 3 : ival; + } else { + ival = ival > 15 ? 15 : ival; + } + if (speed == USB_SPEED_SUPER) { + mps = 1024; + } else { + mps = mps ? mps : 1024; + } + cp[1] |= htole32(XHCI_EPCTX_1_MAXP_SIZE_SET(mps)); + cp[4] = htole32(XHCI_EPCTX_4_AVG_TRB_LEN_SET(1024)); /* XXX */ + break; +#endif + default: + if (speed == USB_SPEED_SUPER) + mps = 1024; + else + mps = mps ? mps : 512; + cp[1] |= htole32(XHCI_EPCTX_1_MAXP_SIZE_SET(mps)); + cp[4] = htole32(XHCI_EPCTX_4_AVG_TRB_LEN_SET(1024)); /* XXX */ + break; } *(uint64_t *)(&cp[2]) = htole64( xhci_ring_trbp(&xs->xs_ep[dci].xe_tr, 0) | @@ -1014,6 +1294,7 @@ xhci_configure_endpoint(struct usbd_pipe return err; } +#if 0 static usbd_status xhci_unconfigure_endpoint(struct usbd_pipe *pipe) { @@ -1026,6 +1307,7 @@ xhci_unconfigure_endpoint(struct usbd_pi return USBD_NORMAL_COMPLETION; } +#endif static usbd_status xhci_reset_endpoint(struct usbd_pipe *pipe) @@ -1037,7 +1319,9 @@ xhci_reset_endpoint(struct usbd_pipe *pi usbd_status err; XHCIHIST_FUNC(); XHCIHIST_CALLED(); - DPRINTFN(4, "dci %u", dci, 0, 0, 0); + DPRINTFN(4, "slot %u dci %u", xs->xs_idx, dci, 0, 0); + + KASSERT(!mutex_owned(&sc->sc_lock)); trb.trb_0 = 0; trb.trb_2 = 0; @@ -1050,7 +1334,6 @@ xhci_reset_endpoint(struct usbd_pipe *pi return err; } -#if 0 static usbd_status xhci_stop_endpoint(struct usbd_pipe *pipe) { @@ -1061,7 +1344,9 @@ xhci_stop_endpoint(struct usbd_pipe *pip const u_int dci = xhci_ep_get_dci(pipe->up_endpoint->ue_edesc); XHCIHIST_FUNC(); XHCIHIST_CALLED(); - DPRINTFN(4, "dci %u", dci, 0, 0, 0); + DPRINTFN(4, "slot %u dci %u", xs->xs_idx, dci, 0, 0); + + KASSERT(mutex_owned(&sc->sc_lock)); trb.trb_0 = 0; trb.trb_2 = 0; @@ -1069,11 +1354,10 @@ xhci_stop_endpoint(struct usbd_pipe *pip XHCI_TRB_3_EP_SET(dci) | XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_STOP_EP); - err = xhci_do_command(sc, &trb, USBD_DEFAULT_TIMEOUT); + err = xhci_do_command_locked(sc, &trb, USBD_DEFAULT_TIMEOUT); return err; } -#endif static usbd_status xhci_set_dequeue(struct usbd_pipe *pipe) @@ -1116,14 +1400,14 @@ xhci_open(struct usbd_pipe *pipe) XHCIHIST_FUNC(); XHCIHIST_CALLED(); DPRINTFN(1, "addr %d depth %d port %d speed %d", - dev->ud_addr, dev->ud_depth, dev->ud_powersrc->up_portno, dev->ud_speed); + dev->ud_addr, dev->ud_depth, dev->ud_powersrc->up_portno, + dev->ud_speed); if (sc->sc_dying) return USBD_IOERROR; /* Root Hub */ - if (dev->ud_depth == 0 && dev->ud_powersrc->up_portno == 0 && - dev->ud_speed != USB_SPEED_SUPER) { + if (dev->ud_depth == 0 && dev->ud_powersrc->up_portno == 0) { switch (ed->bEndpointAddress) { case USB_CONTROL_ENDPOINT: pipe->up_methods = &roothub_ctrl_methods; @@ -1160,11 +1444,146 @@ xhci_open(struct usbd_pipe *pipe) } if (ed->bEndpointAddress != USB_CONTROL_ENDPOINT) - xhci_configure_endpoint(pipe); + return xhci_configure_endpoint(pipe); return USBD_NORMAL_COMPLETION; } +static usbd_status +xhci_close_pipe(struct usbd_pipe *pipe) +{ + struct xhci_softc * const sc = pipe->up_dev->ud_bus->ub_hcpriv; + struct xhci_slot * const xs = pipe->up_dev->ud_hcpriv; + usb_endpoint_descriptor_t * const ed = pipe->up_endpoint->ue_edesc; + const u_int dci = xhci_ep_get_dci(ed); + struct xhci_trb trb; + usbd_status err; + uint32_t *cp; + + XHCIHIST_FUNC(); XHCIHIST_CALLED(); + + if (sc->sc_dying) + return USBD_IOERROR; + + if (xs == NULL || xs->xs_idx == 0) + /* xs is uninitialized before xhci_init_slot */ + return USBD_IOERROR; + + DPRINTFN(4, "slot %u dci %u", xs->xs_idx, dci, 0, 0); + + KASSERTMSG(!cpu_intr_p() && !cpu_softintr_p(), "called from intr ctx"); + KASSERT(mutex_owned(&sc->sc_lock)); + + if (pipe->up_dev->ud_depth == 0) + return USBD_NORMAL_COMPLETION; + + if (dci == XHCI_DCI_EP_CONTROL) { + DPRINTFN(4, "closing ep0", 0, 0, 0, 0); + return xhci_disable_slot(sc, xs->xs_idx); + } + + (void)xhci_stop_endpoint(pipe); + + /* + * set appropriate bit to be dropped. + * don't set DC bit to 1, otherwise all endpoints + * would be deconfigured. + */ + cp = xhci_slot_get_icv(sc, xs, XHCI_ICI_INPUT_CONTROL); + cp[0] = htole32(XHCI_INCTX_0_DROP_MASK(dci)); + cp[1] = htole32(0); + + /* XXX should be most significant one, not dci? */ + cp = xhci_slot_get_icv(sc, xs, xhci_dci_to_ici(XHCI_DCI_SLOT)); + cp[0] = htole32(XHCI_SCTX_0_CTX_NUM_SET(dci)); + + /* sync input contexts before they are read from memory */ + usb_syncmem(&xs->xs_ic_dma, 0, sc->sc_pgsz, BUS_DMASYNC_PREWRITE); + + trb.trb_0 = xhci_slot_get_icp(sc, xs, 0); + trb.trb_2 = 0; + trb.trb_3 = XHCI_TRB_3_SLOT_SET(xs->xs_idx) | + XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_CONFIGURE_EP); + + err = xhci_do_command_locked(sc, &trb, USBD_DEFAULT_TIMEOUT); + usb_syncmem(&xs->xs_dc_dma, 0, sc->sc_pgsz, BUS_DMASYNC_POSTREAD); + + return err; +} + +static void +xhci_abort_xfer(struct usbd_xfer *xfer, usbd_status status) +{ + struct xhci_softc * const sc = xfer->ux_pipe->up_dev->ud_bus->ub_hcpriv; + + XHCIHIST_FUNC(); XHCIHIST_CALLED(); + DPRINTFN(4, "xfer %p pipe %p status %d", + xfer, xfer->ux_pipe, status, 0); + + KASSERT(mutex_owned(&sc->sc_lock)); + + if (sc->sc_dying) { + /* If we're dying, just do the software part. */ + DPRINTFN(4, "dying", 0, 0, 0, 0); + xfer->ux_status = status; /* make software ignore it */ + callout_stop(&xfer->ux_callout); + usb_transfer_complete(xfer); + return; + } + + /* XXX need more stuff */ + xfer->ux_status = status; + callout_stop(&xfer->ux_callout); + usb_transfer_complete(xfer); + + KASSERT(mutex_owned(&sc->sc_lock)); +} + +#if 1 /* XXX experimental */ +static void +xhci_clear_endpoint_stall_async_task(void *cookie) +{ + struct usbd_xfer * const xfer = cookie; + struct xhci_softc * const sc = xfer->ux_pipe->up_dev->ud_bus->ub_hcpriv; + struct xhci_slot * const xs = xfer->ux_pipe->up_dev->ud_hcpriv; + const u_int dci = xhci_ep_get_dci(xfer->ux_pipe->up_endpoint->ue_edesc); + struct xhci_ring * const tr = &xs->xs_ep[dci].xe_tr; + + XHCIHIST_FUNC(); XHCIHIST_CALLED(); + DPRINTFN(4, "xfer %p slot %u dci %u", xfer, xs->xs_idx, dci, 0); + + xhci_reset_endpoint(xfer->ux_pipe); + xhci_set_dequeue(xfer->ux_pipe); + + mutex_enter(&sc->sc_lock); + tr->is_halted = false; + usb_transfer_complete(xfer); + mutex_exit(&sc->sc_lock); + DPRINTFN(4, "ends", 0, 0, 0, 0); +} + +static usbd_status +xhci_clear_endpoint_stall_async(struct usbd_xfer *xfer) +{ + struct xhci_softc * const sc = xfer->ux_pipe->up_dev->ud_bus->ub_hcpriv; + + XHCIHIST_FUNC(); XHCIHIST_CALLED(); + DPRINTFN(4, "xfer %p", xfer, 0, 0, 0); + + if (sc->sc_dying) { + return USBD_IOERROR; + } + + usb_init_task(&xfer->ux_pipe->up_async_task, + xhci_clear_endpoint_stall_async_task, xfer, USB_TASKQ_MPSAFE); + usb_add_task(xfer->ux_pipe->up_dev, &xfer->ux_pipe->up_async_task, + USB_TASKQ_HC); + DPRINTFN(4, "ends", 0, 0, 0, 0); + + return USBD_NORMAL_COMPLETION; +} + +#endif /* XXX experimental */ static void xhci_rhpsc(struct xhci_softc * const sc, u_int port) { @@ -1177,14 +1596,6 @@ xhci_rhpsc(struct xhci_softc * const sc, if (xfer == NULL) return; - if (!(port >= sc->sc_hs_port_start && - port < sc->sc_hs_port_start + sc->sc_hs_port_count)) - return; - - port -= sc->sc_hs_port_start; - port += 1; - DPRINTFN(4, "hs port %u status change", port, 0, 0, 0); - p = xfer->ux_buf; memset(p, 0, xfer->ux_length); p[port/NBBY] |= 1 << (port%NBBY); @@ -1199,17 +1610,19 @@ xhci_handle_event(struct xhci_softc * co { uint64_t trb_0; uint32_t trb_2, trb_3; + uint8_t trberr; XHCIHIST_FUNC(); XHCIHIST_CALLED(); trb_0 = le64toh(trb->trb_0); trb_2 = le32toh(trb->trb_2); trb_3 = le32toh(trb->trb_3); + trberr = XHCI_TRB_2_ERROR_GET(trb_2); DPRINTFN(14, "event: %p 0x%016"PRIx64" 0x%08"PRIx32" 0x%08"PRIx32, trb, trb_0, trb_2, trb_3); - switch (XHCI_TRB_3_TYPE_GET(trb_3)){ + switch (XHCI_TRB_3_TYPE_GET(trb_3)) { case XHCI_TRB_EVENT_TRANSFER: { u_int slot, dci; struct xhci_slot *xs; @@ -1223,10 +1636,24 @@ xhci_handle_event(struct xhci_softc * co xs = &sc->sc_slots[slot]; xr = &xs->xs_ep[dci].xe_tr; + /* sanity check */ + KASSERT(xs->xs_idx != 0); if ((trb_3 & XHCI_TRB_3_ED_BIT) == 0) { - xx = xr->xr_cookies[(trb_0 - xhci_ring_trbp(xr, 0))/ - sizeof(struct xhci_trb)]; + bus_addr_t trbp = xhci_ring_trbp(xr, 0); + + /* trb_0 range sanity check */ + if (trb_0 < trbp || + (trb_0 - trbp) % sizeof(struct xhci_trb) != 0 || + (trb_0 - trbp) / sizeof(struct xhci_trb) >= + xr->xr_ntrb) { + DPRINTFN(1, + "invalid trb_0 0x%"PRIx64" trbp 0x%"PRIx64, + trb_0, trbp, 0, 0); + break; + } + int idx = (trb_0 - trbp) / sizeof(struct xhci_trb); + xx = xr->xr_cookies[idx]; } else { xx = (void *)(uintptr_t)(trb_0 & ~0x3); } @@ -1243,21 +1670,38 @@ xhci_handle_event(struct xhci_softc * co } } - if (XHCI_TRB_2_ERROR_GET(trb_2) == - XHCI_TRB_ERROR_SUCCESS) { - xfer->ux_actlen = xfer->ux_length - XHCI_TRB_2_REM_GET(trb_2); - err = USBD_NORMAL_COMPLETION; - } else if (XHCI_TRB_2_ERROR_GET(trb_2) == - XHCI_TRB_ERROR_SHORT_PKT) { - xfer->ux_actlen = xfer->ux_length - XHCI_TRB_2_REM_GET(trb_2); + if (trberr == XHCI_TRB_ERROR_SUCCESS || + trberr == XHCI_TRB_ERROR_SHORT_PKT) { + xfer->ux_actlen = + xfer->ux_length - XHCI_TRB_2_REM_GET(trb_2); err = USBD_NORMAL_COMPLETION; - } else if (XHCI_TRB_2_ERROR_GET(trb_2) == - XHCI_TRB_ERROR_STALL) { + } else if (trberr == XHCI_TRB_ERROR_STALL || + trberr == XHCI_TRB_ERROR_BABBLE) { err = USBD_STALLED; xr->is_halted = true; - DPRINTFN(1, "ev: xfer done: err %u slot %u dci %u", - XHCI_TRB_2_ERROR_GET(trb_2), slot, dci, 0); + DPRINTFN(1, "evh: xfer done: ERR %u slot %u dci %u", + trberr, slot, dci, 0); +#if 1 /* XXX experimental */ + /* + * Stalled endpoints can be recoverd by issuing + * command TRB TYPE_RESET_EP on xHCI instead of + * issuing request CLEAR_PORT_FEATURE UF_ENDPOINT_HALT + * on the endpoint. However, this function may be + * called from softint context (e.g. from umass), + * in that case driver gets KASSERT in cv_timedwait + * in xhci_do_command. + * To avoid this, this runs reset_endpoint and + * usb_transfer_complete in usb task thread + * asynchronously (and then umass issues clear + * UF_ENDPOINT_HALT). + */ + xfer->ux_status = err; + xhci_clear_endpoint_stall_async(xfer); + break; +#endif } else { + DPRINTFN(1, "evh: xfer done: ERR %u slot %u dci %u", + trberr, slot, dci, 0); err = USBD_IOERROR; } xfer->ux_status = err; @@ -1287,7 +1731,7 @@ xhci_handle_event(struct xhci_softc * co } cv_signal(&sc->sc_command_cv); } else { - DPRINTFN(1, "event: %p 0x%016"PRIx64" " + DPRINTFN(1, "spurious event: %p 0x%016"PRIx64" " "0x%08"PRIx32" 0x%08"PRIx32, trb, trb_0, trb_2, trb_3); } @@ -1418,7 +1862,8 @@ xhci_new_device(device_t parent, struct int rhport = 0; struct xhci_slot *xs; uint32_t *cp; - uint8_t slot; + uint32_t route = 0; + uint8_t slot = 0; uint8_t addr; XHCIHIST_FUNC(); XHCIHIST_CALLED(); @@ -1439,11 +1884,21 @@ xhci_new_device(device_t parent, struct dev->ud_ep0desc.bDescriptorType = UDESC_ENDPOINT; dev->ud_ep0desc.bEndpointAddress = USB_CONTROL_ENDPOINT; dev->ud_ep0desc.bmAttributes = UE_CONTROL; - /* XXX */ - if (speed == USB_SPEED_LOW) + /* 4.3, 4.8.2.1 */ + switch (speed) { + case USB_SPEED_SUPER: + USETW(dev->ud_ep0desc.wMaxPacketSize, USB_3_MAX_CTRL_PACKET); + break; + case USB_SPEED_FULL: + /* XXX using 64 as initial mps of ep0 in FS */ + case USB_SPEED_HIGH: + USETW(dev->ud_ep0desc.wMaxPacketSize, USB_2_MAX_CTRL_PACKET); + break; + case USB_SPEED_LOW: + default: USETW(dev->ud_ep0desc.wMaxPacketSize, USB_MAX_IPACKET); - else - USETW(dev->ud_ep0desc.wMaxPacketSize, 64); + break; + } dev->ud_ep0desc.bInterval = 0; /* doesn't matter, just don't let it uninitialized */ @@ -1461,28 +1916,48 @@ xhci_new_device(device_t parent, struct up->up_dev = dev; /* Locate root hub port */ - for (adev = dev, hub = dev; - hub != NULL; - adev = hub, hub = hub->ud_myhub) { - DPRINTFN(4, "hub %p", hub, 0, 0, 0); - } - DPRINTFN(4, "hub %p", hub, 0, 0, 0); + for (hub = dev; hub != NULL; hub = hub->ud_myhub) { + uint32_t dep; + + DPRINTFN(4, "hub %p depth %d upport %p upportno %d", + hub, hub->ud_depth, hub->ud_powersrc, + hub->ud_powersrc ? hub->ud_powersrc->up_portno : -1); + + if (hub->ud_powersrc == NULL) + break; + dep = hub->ud_depth; + if (dep == 0) + break; + rhport = hub->ud_powersrc->up_portno; + if (dep > USB_HUB_MAX_DEPTH) + continue; - if (hub != NULL) { - for (int p = 0; p < hub->ud_hub->uh_hubdesc.bNbrPorts; p++) { + route |= + (rhport > UHD_SS_NPORTS_MAX ? UHD_SS_NPORTS_MAX : rhport) + << ((dep - 1) * 4); + } + route = route >> 4; + DPRINTFN(4, "rhport %d Route %05x hub %p", rhport, route, hub, 0); + + /* Locate port on upstream high speed hub */ + for (adev = dev, hub = up->up_parent; + hub != NULL && hub->ud_speed != USB_SPEED_HIGH; + adev = hub, hub = hub->ud_myhub) + ; + if (hub) { + int p; + for (p = 0; p < hub->ud_hub->uh_hubdesc.bNbrPorts; p++) { if (hub->ud_hub->uh_ports[p].up_dev == adev) { - rhport = p; + dev->ud_myhsport = &hub->ud_hub->uh_ports[p]; + goto found; } } + panic("xhci_new_device: cannot find HS port"); + found: + DPRINTFN(4, "high speed port %d", p, 0, 0, 0); } else { - rhport = port; + dev->ud_myhsport = NULL; } - if (speed == USB_SPEED_SUPER) { - rhport += sc->sc_ss_port_start - 1; - } else { - rhport += sc->sc_hs_port_start - 1; - } - DPRINTFN(4, "rhport %d", rhport, 0, 0, 0); dev->ud_speed = speed; dev->ud_langid = USBD_NOLANG; @@ -1492,8 +1967,7 @@ xhci_new_device(device_t parent, struct err = usbd_setup_pipe(dev, 0, &dev->ud_ep0, USBD_DEFAULT_INTERVAL, &dev->ud_pipe0); if (err) { - usbd_remove_device(dev, up); - return err; + goto bad; } dd = &dev->ud_ddesc; @@ -1503,19 +1977,24 @@ xhci_new_device(device_t parent, struct bus->ub_devices[dev->ud_addr] = dev; err = usbd_get_initial_ddesc(dev, dd); if (err) - return err; + goto bad; err = usbd_reload_device_desc(dev); if (err) - return err; + goto bad; } else { err = xhci_enable_slot(sc, &slot); if (err) - return err; - err = xhci_init_slot(sc, slot, depth, speed, port, rhport); - if (err) - return err; + goto bad; xs = &sc->sc_slots[slot]; dev->ud_hcpriv = xs; + err = xhci_init_slot(dev, slot, route, rhport); + if (err) { + dev->ud_hcpriv = NULL; + goto bad; + } + + /* Allow device time to set new address */ + usbd_delay_ms(dev, USB_SET_ADDRESS_SETTLE); cp = xhci_slot_get_dcv(sc, xs, XHCI_DCI_SLOT); //hexdump("slot context", cp, sc->sc_ctxsz); addr = XHCI_SCTX_3_DEV_ADDR_GET(cp[3]); @@ -1530,12 +2009,19 @@ xhci_new_device(device_t parent, struct err = usbd_get_initial_ddesc(dev, dd); if (err) - return err; + goto bad; /* 4.8.2.1 */ - if (speed == USB_SPEED_SUPER) + if (speed == USB_SPEED_SUPER) { + if (dd->bMaxPacketSize != 9) { + printf("%s: invalid mps 2^%u for SS ep0," + " using 512\n", + device_xname(sc->sc_dev), + dd->bMaxPacketSize); + dd->bMaxPacketSize = 9; + } USETW(dev->ud_ep0desc.wMaxPacketSize, (1 << dd->bMaxPacketSize)); - else + } else USETW(dev->ud_ep0desc.wMaxPacketSize, dd->bMaxPacketSize); DPRINTFN(4, "bMaxPacketSize %u", dd->bMaxPacketSize, 0, 0, 0); @@ -1543,11 +2029,15 @@ xhci_new_device(device_t parent, struct UGETW(dev->ud_ep0desc.wMaxPacketSize)); err = usbd_reload_device_desc(dev); if (err) - return err; + goto bad; +#if 0 + /* Re-establish the default pipe with the new MPS. */ + /* In xhci this is done by xhci_update_ep0_mps. */ usbd_kill_pipe(dev->ud_pipe0); err = usbd_setup_pipe(dev, 0, &dev->ud_ep0, USBD_DEFAULT_INTERVAL, &dev->ud_pipe0); +#endif } DPRINTFN(1, "adding unit addr=%d, rev=%02x,", @@ -1569,12 +2059,12 @@ xhci_new_device(device_t parent, struct err = usbd_probe_and_attach(parent, dev, port, dev->ud_addr); - if (err) { + bad: + if (err != USBD_NORMAL_COMPLETION) { usbd_remove_device(dev, up); - return err; } - return USBD_NORMAL_COMPLETION; + return err; } static usbd_status @@ -1713,8 +2203,8 @@ xhci_ring_put(struct xhci_softc * const } static usbd_status -xhci_do_command(struct xhci_softc * const sc, struct xhci_trb * const trb, - int timeout) +xhci_do_command1(struct xhci_softc * const sc, struct xhci_trb * const trb, + int timeout, int locked) { struct xhci_ring * const cr = &sc->sc_cr; usbd_status err; @@ -1723,7 +2213,10 @@ xhci_do_command(struct xhci_softc * cons DPRINTFN(12, "input: 0x%016"PRIx64" 0x%08"PRIx32" 0x%08"PRIx32, trb->trb_0, trb->trb_2, trb->trb_3, 0); - mutex_enter(&sc->sc_lock); + KASSERTMSG(!cpu_intr_p() && !cpu_softintr_p(), "called from intr ctx"); + + if (!locked) + mutex_enter(&sc->sc_lock); KASSERT(sc->sc_command_addr == 0); sc->sc_command_addr = xhci_ring_trbp(cr, cr->xr_ep); @@ -1762,11 +2255,26 @@ xhci_do_command(struct xhci_softc * cons timedout: sc->sc_command_addr = 0; - mutex_exit(&sc->sc_lock); + if (!locked) + mutex_exit(&sc->sc_lock); return err; } static usbd_status +xhci_do_command(struct xhci_softc * const sc, struct xhci_trb * const trb, + int timeout) +{ + return xhci_do_command1(sc, trb, timeout, 0); +} + +static usbd_status +xhci_do_command_locked(struct xhci_softc * const sc, + struct xhci_trb * const trb, int timeout) +{ + return xhci_do_command1(sc, trb, timeout, 1); +} + +static usbd_status xhci_enable_slot(struct xhci_softc * const sc, uint8_t * const slotp) { struct xhci_trb trb; @@ -1789,6 +2297,36 @@ xhci_enable_slot(struct xhci_softc * con } static usbd_status +xhci_disable_slot(struct xhci_softc * const sc, uint8_t slot) +{ + struct xhci_trb trb; + struct xhci_slot *xs; + + XHCIHIST_FUNC(); XHCIHIST_CALLED(); + + if (sc->sc_dying) + return USBD_IOERROR; + + xs = &sc->sc_slots[slot]; + if (xs->xs_idx != 0) { + for (int i = XHCI_DCI_SLOT + 1; i < 32; i++) { + xhci_ring_free(sc, &xs->xs_ep[i].xe_tr); + memset(&xs->xs_ep[i], 0, sizeof(xs->xs_ep[i])); + } + usb_freemem(&sc->sc_bus, &xs->xs_ic_dma); + usb_freemem(&sc->sc_bus, &xs->xs_dc_dma); + } + + trb.trb_0 = 0; + trb.trb_2 = 0; + trb.trb_3 = htole32( + XHCI_TRB_3_SLOT_SET(slot) | + XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_DISABLE_SLOT)); + + return xhci_do_command_locked(sc, &trb, USBD_DEFAULT_TIMEOUT); +} + +static usbd_status xhci_address_device(struct xhci_softc * const sc, uint64_t icp, uint8_t slot_id, bool bsr) { @@ -1855,46 +2393,20 @@ xhci_set_dcba(struct xhci_softc * const } static usbd_status -xhci_init_slot(struct xhci_softc * const sc, uint32_t slot, int depth, - int speed, int port, int rhport) +xhci_init_slot(struct usbd_device *dev, uint32_t slot, int route, int rhport) { + struct xhci_softc * const sc = dev->ud_bus->ub_hcpriv; struct xhci_slot *xs; usbd_status err; u_int dci; uint32_t *cp; - uint32_t mps; - uint32_t xspeed; + uint32_t mps = UGETW(dev->ud_ep0desc.wMaxPacketSize); XHCIHIST_FUNC(); XHCIHIST_CALLED(); - DPRINTFN(4, "slot %u depth %d speed %d", - slot, depth, speed, 0); - DPRINTFN(4, " port %d rhport %d", - port, rhport, 0, 0); - - switch (speed) { - case USB_SPEED_LOW: - xspeed = 2; - mps = USB_MAX_IPACKET; - break; - case USB_SPEED_FULL: - xspeed = 1; - mps = 64; - break; - case USB_SPEED_HIGH: - xspeed = 3; - mps = USB_2_MAX_CTRL_PACKET; - break; - case USB_SPEED_SUPER: - xspeed = 4; - mps = USB_3_MAX_CTRL_PACKET; - break; - default: - DPRINTFN(0, "impossible speed: %x", speed, 0, 0, 0); - return USBD_INVAL; - } + DPRINTFN(4, "slot %u speed %d rhport %d route %05x", + slot, dev->ud_speed, route, rhport); xs = &sc->sc_slots[slot]; - xs->xs_idx = slot; /* allocate contexts */ err = usb_allocmem(&sc->sc_bus, sc->sc_pgsz, sc->sc_pgsz, @@ -1906,7 +2418,7 @@ xhci_init_slot(struct xhci_softc * const err = usb_allocmem(&sc->sc_bus, sc->sc_pgsz, sc->sc_pgsz, &xs->xs_ic_dma); if (err) - return err; + goto bad1; memset(KERNADDR(&xs->xs_ic_dma, 0), 0, sc->sc_pgsz); for (dci = 0; dci < 32; dci++) { @@ -1918,7 +2430,7 @@ xhci_init_slot(struct xhci_softc * const XHCI_TRANSFER_RING_TRBS, XHCI_TRB_ALIGN); if (err) { DPRINTFN(0, "ring init failure", 0, 0, 0, 0); - return err; + goto bad2; } } @@ -1930,17 +2442,10 @@ xhci_init_slot(struct xhci_softc * const /* set up input slot context */ cp = xhci_slot_get_icv(sc, xs, xhci_dci_to_ici(XHCI_DCI_SLOT)); - cp[0] = htole32( - XHCI_SCTX_0_CTX_NUM_SET(1) | - XHCI_SCTX_0_SPEED_SET(xspeed) - ); - cp[1] = htole32( - XHCI_SCTX_1_RH_PORT_SET(rhport) - ); - cp[2] = htole32( - XHCI_SCTX_2_IRQ_TARGET_SET(0) - ); - cp[3] = htole32(0); + xhci_setup_sctx(dev, cp); + cp[0] |= htole32(XHCI_SCTX_0_CTX_NUM_SET(1)); + cp[0] |= htole32(XHCI_SCTX_0_ROUTE_SET(route)); + cp[1] |= htole32(XHCI_SCTX_1_RH_PORT_SET(rhport)); /* set up input EP0 context */ cp = xhci_slot_get_icv(sc, xs, xhci_dci_to_ici(XHCI_DCI_EP_CONTROL)); @@ -1972,6 +2477,20 @@ xhci_init_slot(struct xhci_softc * const hexdump("output context", xhci_slot_get_dcv(sc, xs, 0), sc->sc_ctxsz * 2); + bad2: + if (err == USBD_NORMAL_COMPLETION) { + xs->xs_idx = slot; + } else { + for (int i = 1; i < dci; i++) { + xhci_ring_free(sc, &xs->xs_ep[i].xe_tr); + memset(&xs->xs_ep[i], 0, sizeof(xs->xs_ep[i])); + } + usb_freemem(&sc->sc_bus, &xs->xs_ic_dma); + bad1: + usb_freemem(&sc->sc_bus, &xs->xs_dc_dma); + xs->xs_idx = 0; + } + return err; } @@ -2031,10 +2550,10 @@ xhci_roothub_ctrl(struct usbd_bus *bus, case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_OTHER): DPRINTFN(4, "UR_CLEAR_PORT_FEATURE port=%d feature=%d", index, value, 0, 0); - if (index < 1 || index > sc->sc_hs_port_count) { + if (index < 1 || index > sc->sc_maxports) { return -1; } - port = XHCI_PORTSC(sc->sc_hs_port_start - 1 + index); + port = XHCI_PORTSC(index); v = xhci_op_read_4(sc, port); DPRINTFN(4, "portsc=0x%08x", v, 0, 0, 0); v &= ~XHCI_PS_CLEAR; @@ -2056,9 +2575,18 @@ xhci_roothub_ctrl(struct usbd_bus *bus, case UHF_C_PORT_SUSPEND: case UHF_C_PORT_OVER_CURRENT: return -1; + case UHF_C_BH_PORT_RESET: + xhci_op_write_4(sc, port, v | XHCI_PS_WRC); + break; case UHF_C_PORT_RESET: xhci_op_write_4(sc, port, v | XHCI_PS_PRC); break; + case UHF_C_PORT_LINK_STATE: + xhci_op_write_4(sc, port, v | XHCI_PS_PLC); + break; + case UHF_C_PORT_CONFIG_ERROR: + xhci_op_write_4(sc, port, v | XHCI_PS_CEC); + break; default: return -1; } @@ -2073,7 +2601,7 @@ xhci_roothub_ctrl(struct usbd_bus *bus, totlen = min(buflen, sizeof(hubd)); memcpy(&hubd, buf, totlen); - hubd.bNbrPorts = sc->sc_hs_port_count; + hubd.bNbrPorts = sc->sc_maxports; USETW(hubd.wHubCharacteristics, UHD_PWR_NO_SWITCH); hubd.bPwrOn2PwrGood = 200; for (i = 0, l = sc->sc_maxports; l > 0; i++, l -= 8) @@ -2097,10 +2625,8 @@ xhci_roothub_ctrl(struct usbd_bus *bus, if (len != 4) { return -1; } - v = xhci_op_read_4(sc, XHCI_PORTSC(sc->sc_hs_port_start - 1 + - index)); - DPRINTFN(4, "READ_CLASS_OTHER GET_STATUS PORTSC %d (%d) %08x", - index, sc->sc_hs_port_start - 1 + index, v, 0); + v = xhci_op_read_4(sc, XHCI_PORTSC(index)); + DPRINTFN(4, "getrhportsc %d %08x", index, v, 0, 0); switch (XHCI_PS_SPEED_GET(v)) { case 1: i = UPS_FULL_SPEED; @@ -2111,6 +2637,9 @@ xhci_roothub_ctrl(struct usbd_bus *bus, case 3: i = UPS_HIGH_SPEED; break; + case 4: + i = UPS_SUPER_SPEED; + break; default: i = 0; break; @@ -2120,26 +2649,39 @@ xhci_roothub_ctrl(struct usbd_bus *bus, if (v & XHCI_PS_OCA) i |= UPS_OVERCURRENT_INDICATOR; //if (v & XHCI_PS_SUSP) i |= UPS_SUSPEND; if (v & XHCI_PS_PR) i |= UPS_RESET; - if (v & XHCI_PS_PP) i |= UPS_PORT_POWER; + if (v & XHCI_PS_PP) { + if (i & UPS_SUPER_SPEED) + i |= UPS_PORT_POWER_SS; + else + i |= UPS_PORT_POWER; + } USETW(ps.wPortStatus, i); i = 0; if (v & XHCI_PS_CSC) i |= UPS_C_CONNECT_STATUS; if (v & XHCI_PS_PEC) i |= UPS_C_PORT_ENABLED; if (v & XHCI_PS_OCC) i |= UPS_C_OVERCURRENT_INDICATOR; if (v & XHCI_PS_PRC) i |= UPS_C_PORT_RESET; + if (v & XHCI_PS_WRC) i |= UPS_C_BH_PORT_RESET; + if (v & XHCI_PS_PLC) i |= UPS_C_PORT_LINK_STATE; + if (v & XHCI_PS_CEC) i |= UPS_C_PORT_CONFIG_ERROR; USETW(ps.wPortChange, i); totlen = min(len, sizeof(ps)); memcpy(buf, &ps, totlen); break; case C(UR_SET_DESCRIPTOR, UT_WRITE_CLASS_DEVICE): return -1; + case C(UR_SET_HUB_DEPTH, UT_WRITE_CLASS_DEVICE): + break; case C(UR_SET_FEATURE, UT_WRITE_CLASS_DEVICE): break; case C(UR_SET_FEATURE, UT_WRITE_CLASS_OTHER): - if (index < 1 || index > sc->sc_hs_port_count) { + { + int optval = (index >> 8) & 0xff; + index &= 0xff; + if (index < 1 || index > sc->sc_maxports) { return -1; } - port = XHCI_PORTSC(sc->sc_hs_port_start - 1 + index); + port = XHCI_PORTSC(index); v = xhci_op_read_4(sc, port); DPRINTFN(4, "portsc=0x%08x", v, 0, 0, 0); v &= ~XHCI_PS_CLEAR; @@ -2172,9 +2714,30 @@ xhci_roothub_ctrl(struct usbd_bus *bus, case UHF_C_PORT_RESET: xhci_op_write_4(sc, port, v | XHCI_PS_PRC); break; + case UHF_PORT_U1_TIMEOUT: + if (XHCI_PS_SPEED_GET(v) != 4) { + return -1; + } + port = XHCI_PORTPMSC(index); + v = xhci_op_read_4(sc, port); + v &= ~XHCI_PM3_U1TO_SET(0xff); + v |= XHCI_PM3_U1TO_SET(optval); + xhci_op_write_4(sc, port, v); + break; + case UHF_PORT_U2_TIMEOUT: + if (XHCI_PS_SPEED_GET(v) != 4) { + return -1; + } + port = XHCI_PORTPMSC(index); + v = xhci_op_read_4(sc, port); + v &= ~XHCI_PM3_U2TO_SET(0xff); + v |= XHCI_PM3_U2TO_SET(optval); + xhci_op_write_4(sc, port, v); + break; default: return -1; } + } break; case C(UR_CLEAR_TT_BUFFER, UT_WRITE_CLASS_OTHER): case C(UR_RESET_TT, UT_WRITE_CLASS_OTHER): @@ -2237,8 +2800,6 @@ xhci_root_intr_abort(struct usbd_xfer *x KASSERT(mutex_owned(&sc->sc_lock)); KASSERT(xfer->ux_pipe->up_intrxfer == xfer); - DPRINTFN(1, "remove", 0, 0, 0, 0); - sc->sc_intrxfer = NULL; xfer->ux_status = USBD_CANCELLED; @@ -2311,6 +2872,8 @@ xhci_device_ctrl_start(struct usbd_xfer /* XXX */ if (tr->is_halted) { + DPRINTFN(1, "ctrl xfer %p halted: slot %u dci %u", + xfer, xs->xs_idx, dci, 0); xhci_reset_endpoint(xfer->ux_pipe); tr->is_halted = false; xhci_set_dequeue(xfer->ux_pipe); @@ -2400,12 +2963,16 @@ static void xhci_device_ctrl_abort(struct usbd_xfer *xfer) { XHCIHIST_FUNC(); XHCIHIST_CALLED(); + + xhci_abort_xfer(xfer, USBD_CANCELLED); } static void xhci_device_ctrl_close(struct usbd_pipe *pipe) { XHCIHIST_FUNC(); XHCIHIST_CALLED(); + + (void)xhci_close_pipe(pipe); } /* ------------------ */ @@ -2518,12 +3085,16 @@ static void xhci_device_bulk_abort(struct usbd_xfer *xfer) { XHCIHIST_FUNC(); XHCIHIST_CALLED(); + + xhci_abort_xfer(xfer, USBD_CANCELLED); } static void xhci_device_bulk_close(struct usbd_pipe *pipe) { XHCIHIST_FUNC(); XHCIHIST_CALLED(); + + (void)xhci_close_pipe(pipe); } /* ---------------- */ @@ -2646,8 +3217,7 @@ xhci_device_intr_abort(struct usbd_xfer KASSERT(mutex_owned(&sc->sc_lock)); DPRINTFN(15, "%p", xfer, 0, 0, 0); KASSERT(xfer->ux_pipe->up_intrxfer == xfer); - xfer->ux_status = USBD_CANCELLED; - usb_transfer_complete(xfer); + xhci_abort_xfer(xfer, USBD_CANCELLED); } static void @@ -2658,7 +3228,7 @@ xhci_device_intr_close(struct usbd_pipe XHCIHIST_FUNC(); XHCIHIST_CALLED(); DPRINTFN(15, "%p", pipe, 0, 0, 0); - xhci_unconfigure_endpoint(pipe); + (void)xhci_close_pipe(pipe); } /* ------------ */