tech-kern archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
xhci patch for nick-nhusb branch
Hello,
These are usb 3.0 patches for nick-nhusb branch.
Mostly same as patches I posted previously for current.
nh-xhci-cosmetic.diff
whitespace
nh-xhci-fixtypo.diff
fix typo in comment
nh-xhci-main.diff
main patch
(mostly same as I posted previously for -current)
nh-xhci-comments.diff
sprinkle comments
nh-xhci-compat.diff
share sources with other branches
nh-uhub-usbhistify.diff
convert to use usbhist
nh-uhub-usb3.diff
add support for usb3 hub
nh-misc.diff
+ usb.h
fix SS and LS speed definition was swapped
rename usb_usb2ext_descriptor_t to usb_devcap_usb2ext_descriptor_t
add SS current definition
add comments
+ usb_subr.c
add UPS_PORT_POWER_SS treatment
+ xhcireg.h
add definitions of port routing and capability ids
+ xhcivar.h
add sc_quirks and definitions for it
--
t-hash
--- src/sys/dev/usb/xhci.c.orig 2015-03-27 15:05:03.000000000 +0900
+++ src/sys/dev/usb/xhci.c 2015-04-04 16:43:18.000000000 +0900
@@ -62,7 +62,7 @@ __KERNEL_RCSID(0, "$NetBSD: xhci.c,v 1.2
#ifdef USB_DEBUG
#ifndef XHCI_DEBUG
#define xhcidebug 0
-#else
+#else /* !XHCI_DEBUG */
static int xhcidebug = 0;
SYSCTL_SETUP(sysctl_hw_xhci_setup, "sysctl hw.xhci setup")
@@ -92,7 +92,7 @@ fail:
aprint_error("%s: sysctl_createv failed (err = %d)\n", __func__, err);
}
-#endif /* XHCI_DEBUG */
+#endif /* !XHCI_DEBUG */
#endif /* USB_DEBUG */
#define DPRINTFN(N,FMT,A,B,C,D) USBHIST_LOGN(xhcidebug,N,FMT,A,B,C,D)
@@ -1109,7 +1109,7 @@ xhci_set_dequeue(struct usbd_pipe *pipe)
static usbd_status
xhci_open(struct usbd_pipe *pipe)
{
- struct usbd_device *const dev = pipe->up_dev;
+ struct usbd_device * const dev = pipe->up_dev;
struct xhci_softc * const sc = dev->ud_bus->ub_hcpriv;
usb_endpoint_descriptor_t * const ed = pipe->up_endpoint->ue_edesc;
const uint8_t xfertype = UE_GET_XFERTYPE(ed->bmAttributes);
@@ -1168,7 +1168,7 @@ xhci_open(struct usbd_pipe *pipe)
static void
xhci_rhpsc(struct xhci_softc * const sc, u_int port)
{
- struct usbd_xfer *const xfer = sc->sc_intrxfer;
+ struct usbd_xfer * const xfer = sc->sc_intrxfer;
uint8_t *p;
XHCIHIST_FUNC(); XHCIHIST_CALLED();
@@ -1303,7 +1303,7 @@ xhci_handle_event(struct xhci_softc * co
static void
xhci_softintr(void *v)
{
- struct usbd_bus *const bus = v;
+ struct usbd_bus * const bus = v;
struct xhci_softc * const sc = bus->ub_hcpriv;
struct xhci_ring * const er = &sc->sc_er;
struct xhci_trb *trb;
@@ -1983,7 +1983,8 @@ xhci_noop(struct usbd_pipe *pipe)
XHCIHIST_FUNC(); XHCIHIST_CALLED();
}
-static int xhci_roothub_ctrl(struct usbd_bus *bus, usb_device_request_t *req,
+static int
+xhci_roothub_ctrl(struct usbd_bus *bus, usb_device_request_t *req,
void *buf, int buflen)
{
struct xhci_softc * const sc = bus->ub_hcpriv;
@@ -2666,7 +2667,7 @@ static void
xhci_timeout(void *addr)
{
struct xhci_xfer * const xx = addr;
- struct usbd_xfer *const xfer = &xx->xx_xfer;
+ struct usbd_xfer * const xfer = &xx->xx_xfer;
struct xhci_softc * const sc = xfer->ux_pipe->up_dev->ud_bus->ub_hcpriv;
XHCIHIST_FUNC(); XHCIHIST_CALLED();
@@ -2684,7 +2685,7 @@ xhci_timeout(void *addr)
static void
xhci_timeout_task(void *addr)
{
- struct usbd_xfer *const xfer = addr;
+ struct usbd_xfer * const xfer = addr;
struct xhci_softc * const sc = xfer->ux_pipe->up_dev->ud_bus->ub_hcpriv;
XHCIHIST_FUNC(); XHCIHIST_CALLED();
--- src/sys/dev/usb/xhci.c.orig 2015-04-04 16:43:18.000000000 +0900
+++ src/sys/dev/usb/xhci.c 2015-04-04 16:47:39.000000000 +0900
@@ -2189,7 +2189,7 @@ xhci_roothub_ctrl(struct usbd_bus *bus,
return totlen;
}
-/* root hub intrerrupt */
+/* root hub interrupt */
static usbd_status
xhci_root_intr_transfer(struct usbd_xfer *xfer)
--- src/sys/dev/usb/xhci.c.orig 2015-04-04 17:01:37.000000000 +0900
+++ src/sys/dev/usb/xhci.c 2015-04-05 05:53:30.000000000 +0900
@@ -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);
}
/* ------------ */
--- src/sys/dev/usb/xhci.c.orig 2015-04-05 05:53:30.000000000 +0900
+++ src/sys/dev/usb/xhci.c 2015-04-05 07:10:10.000000000 +0900
@@ -1123,6 +1123,12 @@ xhci_setup_sctx(struct usbd_device *dev,
cp[3] |= htole32(0);
}
+/*
+ * called
+ * from xhci_open
+ * from usbd_setup_pipe_flags
+ * from usbd_open_pipe_ival
+ */
static usbd_status
xhci_configure_endpoint(struct usbd_pipe *pipe)
{
@@ -1309,6 +1315,7 @@ xhci_unconfigure_endpoint(struct usbd_pi
}
#endif
+/* 4.6.8, 6.4.3.7 */
static usbd_status
xhci_reset_endpoint(struct usbd_pipe *pipe)
{
@@ -1334,6 +1341,11 @@ xhci_reset_endpoint(struct usbd_pipe *pi
return err;
}
+/*
+ * 4.6.9, 6.4.3.8
+ * Stop execution of TDs on xfer ring.
+ * Should be called with sc_lock held.
+ */
static usbd_status
xhci_stop_endpoint(struct usbd_pipe *pipe)
{
@@ -1359,6 +1371,12 @@ xhci_stop_endpoint(struct usbd_pipe *pip
return err;
}
+/*
+ * Set TR Dequeue Pointer.
+ * xCHI 1.1 4.6.10 6.4.3.9
+ * Purge all of transfer requests in ring.
+ * EPSTATE of endpoint must be ERROR or STOPPED, or CONTEXT_STATE error.
+ */
static usbd_status
xhci_set_dequeue(struct usbd_pipe *pipe)
{
@@ -1379,6 +1397,7 @@ xhci_set_dequeue(struct usbd_pipe *pipe)
xr->xr_ep = 0;
xr->xr_cs = 1;
+ /* set DCS */
trb.trb_0 = xhci_ring_trbp(xr, 0) | 1; /* XXX */
trb.trb_2 = 0;
trb.trb_3 = XHCI_TRB_3_SLOT_SET(xs->xs_idx) |
@@ -1390,6 +1409,11 @@ xhci_set_dequeue(struct usbd_pipe *pipe)
return err;
}
+/*
+ * Open new pipe: called from usbd_setup_pipe_flags.
+ * Fills methods of pipe.
+ * If pipe is not for ep0, calls configure_endpoint.
+ */
static usbd_status
xhci_open(struct usbd_pipe *pipe)
{
@@ -1449,6 +1473,11 @@ xhci_open(struct usbd_pipe *pipe)
return USBD_NORMAL_COMPLETION;
}
+/*
+ * Closes pipe, called from usbd_kill_pipe via close methods.
+ * If the endpoint to be closed is ep0, disable_slot.
+ * Should be called with sc_lock held.
+ */
static usbd_status
xhci_close_pipe(struct usbd_pipe *pipe)
{
@@ -1482,6 +1511,10 @@ xhci_close_pipe(struct usbd_pipe *pipe)
return xhci_disable_slot(sc, xs->xs_idx);
}
+ /*
+ * This may fail in the case that xhci_close_pipe is called after
+ * xhci_abort_xfer e.g. usbd_kill_pipe.
+ */
(void)xhci_stop_endpoint(pipe);
/*
@@ -1511,6 +1544,11 @@ xhci_close_pipe(struct usbd_pipe *pipe)
return err;
}
+/*
+ * Abort transfer.
+ * Called with sc_lock held.
+ * May be called from softintr context.
+ */
static void
xhci_abort_xfer(struct usbd_xfer *xfer, usbd_status status)
{
@@ -1540,6 +1578,7 @@ xhci_abort_xfer(struct usbd_xfer *xfer,
}
#if 1 /* XXX experimental */
+/* issue reset_ep and set_dequeue in thread context */
static void
xhci_clear_endpoint_stall_async_task(void *cookie)
{
@@ -1574,6 +1613,7 @@ xhci_clear_endpoint_stall_async(struct u
return USBD_IOERROR;
}
+ /* XXX never use up_async_task for incompatible type of function */
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,
@@ -1584,6 +1624,9 @@ xhci_clear_endpoint_stall_async(struct u
}
#endif /* XXX experimental */
+/*
+ * Notify roothub port status/change to uhub_intr.
+ */
static void
xhci_rhpsc(struct xhci_softc * const sc, u_int port)
{
@@ -1604,6 +1647,12 @@ xhci_rhpsc(struct xhci_softc * const sc,
usb_transfer_complete(xfer);
}
+/*
+ * Process events:
+ * + Transfer comeplete
+ * + Command complete
+ * + Roothub Port status/change
+ */
static void
xhci_handle_event(struct xhci_softc * const sc,
const struct xhci_trb * const trb)
@@ -1849,6 +1898,16 @@ xhci_get_lock(struct usbd_bus *bus, kmut
extern uint32_t usb_cookie_no;
+/*
+ * Called if uhub_explore find new device (via usbd_new_device).
+ * Allocate and construct dev structure of default endpoint (ep0).
+ * Determine initial MaxPacketSize (mps) by speed.
+ * Determine route string and roothub port for slot of dev.
+ * Allocate pipe of ep0.
+ * Enable and initialize slot and Set Address.
+ * Read device descriptor.
+ * Register this device.
+ */
static usbd_status
xhci_new_device(device_t parent, struct usbd_bus *bus, int depth,
int speed, int port, struct usbd_port *up)
@@ -2202,6 +2261,13 @@ xhci_ring_put(struct xhci_softc * const
DPRINTFN(12, "%p xr_ep 0x%x xr_cs %u", xr, xr->xr_ep, xr->xr_cs, 0);
}
+/*
+ * Put a command on command ring, ring bell, set timer, and cv_timedwait.
+ * Command completion is notified by cv_signal from xhci_handle_event
+ * (called from interrupt from xHCI), or timed-out.
+ * Command validation is performed in xhci_handle_event by checking if
+ * trb_0 in CMD_COMPLETE TRB and sc->sc_command_addr are identical.
+ */
static usbd_status
xhci_do_command1(struct xhci_softc * const sc, struct xhci_trb * const trb,
int timeout, int locked)
@@ -2218,6 +2284,7 @@ xhci_do_command1(struct xhci_softc * con
if (!locked)
mutex_enter(&sc->sc_lock);
+ /* XXX KASSERT may fire when cv_timedwait unlocks sc_lock */
KASSERT(sc->sc_command_addr == 0);
sc->sc_command_addr = xhci_ring_trbp(cr, cr->xr_ep);
@@ -2267,6 +2334,11 @@ xhci_do_command(struct xhci_softc * cons
return xhci_do_command1(sc, trb, timeout, 0);
}
+/*
+ * This allows xhci_do_command with already sc_lock held.
+ * This is needed as USB stack calls close methods with sc_lock_held.
+ * (see usbdivar.h)
+ */
static usbd_status
xhci_do_command_locked(struct xhci_softc * const sc,
struct xhci_trb * const trb, int timeout)
@@ -2296,6 +2368,10 @@ xhci_enable_slot(struct xhci_softc * con
return err;
}
+/*
+ * Deallocate DMA buffer and ring buffer, and disable_slot.
+ * Should be called with sc_lock held.
+ */
static usbd_status
xhci_disable_slot(struct xhci_softc * const sc, uint8_t slot)
{
@@ -2326,6 +2402,12 @@ xhci_disable_slot(struct xhci_softc * co
return xhci_do_command_locked(sc, &trb, USBD_DEFAULT_TIMEOUT);
}
+/*
+ * Change slot state.
+ * bsr=0: ENABLED -> ADDRESSED
+ * bsr=1: ENABLED -> DEFAULT
+ * see xHCI 1.1 4.5.3, 3.3.4
+ */
static usbd_status
xhci_address_device(struct xhci_softc * const sc,
uint64_t icp, uint8_t slot_id, bool bsr)
@@ -2392,6 +2474,11 @@ xhci_set_dcba(struct xhci_softc * const
BUS_DMASYNC_PREWRITE);
}
+/*
+ * Allocate DMA buffer and ring buffer for specified slot
+ * and set Device Context Base Address
+ * and issue Set Address device command.
+ */
static usbd_status
xhci_init_slot(struct usbd_device *dev, uint32_t slot, int route, int rhport)
{
@@ -2502,6 +2589,9 @@ xhci_noop(struct usbd_pipe *pipe)
XHCIHIST_FUNC(); XHCIHIST_CALLED();
}
+/*
+ * Process root hub request.
+ */
static int
xhci_roothub_ctrl(struct usbd_bus *bus, usb_device_request_t *req,
void *buf, int buflen)
@@ -2773,6 +2863,7 @@ xhci_root_intr_transfer(struct usbd_xfer
return xhci_root_intr_start(SIMPLEQ_FIRST(&xfer->ux_pipe->up_queue));
}
+/* Wait for roothub port status/change */
static usbd_status
xhci_root_intr_start(struct usbd_xfer *xfer)
{
--- src/sys/dev/usb/xhci.c.orig 2015-04-05 07:10:10.000000000 +0900
+++ src/sys/dev/usb/xhci.c 2015-04-05 08:46:34.000000000 +0900
@@ -32,9 +32,17 @@ __KERNEL_RCSID(0, "$NetBSD: xhci.c,v 1.2
#include "opt_usb.h"
#include <sys/param.h>
+
+#if __NetBSD_Version__ >= 799000200
+#define USE_USBHIST
+#endif
+
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/kmem.h>
+#ifndef NHUSB
+#include <sys/malloc.h>
+#endif
#include <sys/device.h>
#include <sys/select.h>
#include <sys/proc.h>
@@ -50,16 +58,96 @@ __KERNEL_RCSID(0, "$NetBSD: xhci.c,v 1.2
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usbdivar.h>
+#undef NHUSB
+/* XXX any other better flag.. */
+#ifndef URQ_AUTO_DMABUF
+#define NHUSB
+#endif
#include <dev/usb/usbdi_util.h>
+#ifdef USE_USBHIST
#include <dev/usb/usbhist.h>
+#endif
#include <dev/usb/usb_mem.h>
#include <dev/usb/usb_quirks.h>
#include <dev/usb/xhcireg.h>
#include <dev/usb/xhcivar.h>
+#ifndef NHUSB
+#include <dev/usb/usbroothub_subr.h>
+#else
#include <dev/usb/usbroothub.h>
+#endif
+
+#ifndef NHUSB
+/* usbd_xfer */
+#define ux_pipe pipe
+#define ux_priv priv
+#define ux_buffer buffer
+#define ux_length length
+#define ux_actlen actlen
+#define ux_flags flags
+#define ux_timeout timeout
+#define ux_status status
+#define ux_callback callback
+#define ux_done done
+#define ux_state busy_free
+#define ux_request request
+#define ux_frlengths frlengths
+#define ux_nframes nframes
+#define ux_dmabuf dmabuf
+#define ux_rqflags rqflags
+#define ux_hcpriv hcpriv
+#define ux_hcflags hcflags
+#define ux_callout timeout_handle
+#define ux_hccv hccv
+/* usbd_pipe */
+#define up_dev device
+#define up_endpoint endpoint
+#define up_queue queue
+#define up_async_task async_task
+#define up_intrxfer intrxfer
+#define up_repeat repeat
+#define up_interval interval
+#define up_methods methods
+/* usbd_device */
+#define ud_bus bus
+#define ud_pipe0 default_pipe
+#define ud_addr address
+#define ud_depth depth
+#define ud_speed speed
+#define ud_langid langid
+#define ud_cookie cookie
+#define ud_powersrc powersrc
+#define ud_myhub myhub
+#define ud_myhsport myhsport
+#define ud_ep0desc def_ep_desc
+#define ud_ep0 def_ep
+#define ud_ddesc ddesc
+#define ud_quirks quirks
+#define ud_hub hub
+#define ud_hcpriv hci_private
+/* usbd_endpoint */
+#define ue_edesc edesc
+#define ue_toggle datatoggle
+/* usbd_bus */
+#define ub_hcpriv hci_private
+#define ub_revision usbrev
+#define ub_methods methods
+#define ub_pipesize pipe_size
+#define ub_roothub root_hub
+#define ub_devices devices
+#define ub_usepolling use_polling
+/* usbd_hub */
+#define uh_hubdesc hubdesc
+#define uh_ports ports
+/* usbd_port */
+#define up_portno portno
+#define up_parent parent
+/* usb_dma_block */
+#define udma_block block
+#endif
#ifdef USB_DEBUG
#ifndef XHCI_DEBUG
#define xhcidebug 0
@@ -96,9 +184,34 @@ fail:
#endif /* !XHCI_DEBUG */
#endif /* USB_DEBUG */
+#ifdef USE_USBHIST
#define DPRINTFN(N,FMT,A,B,C,D) USBHIST_LOGN(xhcidebug,N,FMT,A,B,C,D)
#define XHCIHIST_FUNC() USBHIST_FUNC()
#define XHCIHIST_CALLED(name) USBHIST_CALLED(xhcidebug)
+#else /* USE_USBHIST */
+# if defined(USB_DEBUG) && defined(XHCI_DEBUG)
+/* fake USBHIST_LOGN() for netbsd-7 */
+static void
+xhci_histprint(const char *fmt, u_long a0, u_long a1, u_long a2, u_long a3)
+{
+ printf(fmt, a0, a1, a2, a3);
+}
+# define DPRINTFN(N,FMT,A,B,C,D) do { \
+ if (xhcidebug >= (N)) { \
+ printf("%s: ", __func__); \
+ xhci_histprint((FMT), \
+ (u_long)(A), (u_long)(B), (u_long)(C), (u_long)(D));\
+ printf("\n"); \
+ } \
+ } while(0)
+# define XHCIHIST_FUNC() do {} while(0)
+# define XHCIHIST_CALLED(name) do {} while(0)
+# else /* defined(USB_DEBUG) && defined(XHCI_DEBUG) */
+# define DPRINTFN(N,FMT,A,B,C,D) do {} while(0)
+# define XHCIHIST_FUNC() do {} while(0)
+# define XHCIHIST_CALLED(name) do {} while(0)
+# endif /* defined(USB_DEBUG) && defined(XHCI_DEBUG) */
+#endif /* USE_USBHIST */
#define XHCI_DCI_SLOT 0
#define XHCI_DCI_EP_CONTROL 1
@@ -109,6 +222,9 @@ struct xhci_pipe {
struct usbd_pipe xp_pipe;
};
+#ifndef NHUSB
+#define USBROOTHUB_INTR_ENDPT 1
+#endif
#define XHCI_COMMAND_RING_TRBS 256
#define XHCI_EVENT_RING_TRBS 256
#define XHCI_EVENT_RING_SEGMENTS 1
@@ -118,13 +234,19 @@ static usbd_status xhci_open(struct usbd
static int xhci_intr1(struct xhci_softc * const);
static void xhci_softintr(void *);
static void xhci_poll(struct usbd_bus *);
+#ifndef NHUSB
+static usbd_status xhci_allocm(struct usbd_bus *, usb_dma_t *, uint32_t);
+static void xhci_freem(struct usbd_bus *, usb_dma_t *);
+#endif
static struct usbd_xfer *xhci_allocx(struct usbd_bus *);
static void xhci_freex(struct usbd_bus *, struct usbd_xfer *);
static void xhci_get_lock(struct usbd_bus *, kmutex_t **);
static usbd_status xhci_new_device(device_t, struct usbd_bus *, int, int, int,
struct usbd_port *);
+#ifdef NHUSB
static int xhci_roothub_ctrl(struct usbd_bus *, usb_device_request_t *,
void *, int);
+#endif
static usbd_status xhci_configure_endpoint(struct usbd_pipe *);
//static usbd_status xhci_unconfigure_endpoint(struct usbd_pipe *);
@@ -153,6 +275,14 @@ static void xhci_ring_free(struct xhci_s
static void xhci_noop(struct usbd_pipe *);
+#ifndef NHUSB
+static usbd_status xhci_root_ctrl_transfer(struct usbd_xfer *);
+static usbd_status xhci_root_ctrl_start(struct usbd_xfer *);
+static void xhci_root_ctrl_abort(struct usbd_xfer *);
+static void xhci_root_ctrl_close(struct usbd_pipe *);
+static void xhci_root_ctrl_done(struct usbd_xfer *);
+
+#endif
static usbd_status xhci_root_intr_transfer(struct usbd_xfer *);
static usbd_status xhci_root_intr_start(struct usbd_xfer *);
static void xhci_root_intr_abort(struct usbd_xfer *);
@@ -181,6 +311,17 @@ static void xhci_timeout(void *);
static void xhci_timeout_task(void *);
static const struct usbd_bus_methods xhci_bus_methods = {
+#ifndef NHUSB
+ .open_pipe = xhci_open,
+ .soft_intr = xhci_softintr,
+ .do_poll = xhci_poll,
+ .allocm = xhci_allocm,
+ .freem = xhci_freem,
+ .allocx = xhci_allocx,
+ .freex = xhci_freex,
+ .get_lock = xhci_get_lock,
+ .new_device = xhci_new_device,
+#else
.ubm_open = xhci_open,
.ubm_softint = xhci_softintr,
.ubm_dopoll = xhci_poll,
@@ -189,47 +330,99 @@ static const struct usbd_bus_methods xhc
.ubm_getlock = xhci_get_lock,
.ubm_newdev = xhci_new_device,
.ubm_rhctrl = xhci_roothub_ctrl,
+#endif
+};
+
+#ifndef NHUSB
+static const struct usbd_pipe_methods xhci_root_ctrl_methods = {
+ .transfer = xhci_root_ctrl_transfer,
+ .start = xhci_root_ctrl_start,
+ .abort = xhci_root_ctrl_abort,
+ .close = xhci_root_ctrl_close,
+ .cleartoggle = xhci_noop,
+ .done = xhci_root_ctrl_done,
};
+#endif
static const struct usbd_pipe_methods xhci_root_intr_methods = {
+#ifndef NHUSB
+ .transfer = xhci_root_intr_transfer,
+ .start = xhci_root_intr_start,
+ .abort = xhci_root_intr_abort,
+ .close = xhci_root_intr_close,
+ .cleartoggle = xhci_noop,
+ .done = xhci_root_intr_done,
+#else
.upm_transfer = xhci_root_intr_transfer,
.upm_start = xhci_root_intr_start,
.upm_abort = xhci_root_intr_abort,
.upm_close = xhci_root_intr_close,
.upm_cleartoggle = xhci_noop,
.upm_done = xhci_root_intr_done,
+#endif
};
static const struct usbd_pipe_methods xhci_device_ctrl_methods = {
+#ifndef NHUSB
+ .transfer = xhci_device_ctrl_transfer,
+ .start = xhci_device_ctrl_start,
+ .abort = xhci_device_ctrl_abort,
+ .close = xhci_device_ctrl_close,
+ .cleartoggle = xhci_noop,
+ .done = xhci_device_ctrl_done,
+#else
.upm_transfer = xhci_device_ctrl_transfer,
.upm_start = xhci_device_ctrl_start,
.upm_abort = xhci_device_ctrl_abort,
.upm_close = xhci_device_ctrl_close,
.upm_cleartoggle = xhci_noop,
.upm_done = xhci_device_ctrl_done,
+#endif
};
static const struct usbd_pipe_methods xhci_device_isoc_methods = {
+#ifndef NHUSB
+ .cleartoggle = xhci_noop,
+#else
.upm_cleartoggle = xhci_noop,
+#endif
};
static const struct usbd_pipe_methods xhci_device_bulk_methods = {
+#ifndef NHUSB
+ .transfer = xhci_device_bulk_transfer,
+ .start = xhci_device_bulk_start,
+ .abort = xhci_device_bulk_abort,
+ .close = xhci_device_bulk_close,
+ .cleartoggle = xhci_noop,
+ .done = xhci_device_bulk_done,
+#else
.upm_transfer = xhci_device_bulk_transfer,
.upm_start = xhci_device_bulk_start,
.upm_abort = xhci_device_bulk_abort,
.upm_close = xhci_device_bulk_close,
.upm_cleartoggle = xhci_noop,
.upm_done = xhci_device_bulk_done,
+#endif
};
static const struct usbd_pipe_methods xhci_device_intr_methods = {
+#ifndef NHUSB
+ .transfer = xhci_device_intr_transfer,
+ .start = xhci_device_intr_start,
+ .abort = xhci_device_intr_abort,
+ .close = xhci_device_intr_close,
+ .cleartoggle = xhci_noop,
+ .done = xhci_device_intr_done,
+#else
.upm_transfer = xhci_device_intr_transfer,
.upm_start = xhci_device_intr_start,
.upm_abort = xhci_device_intr_abort,
.upm_close = xhci_device_intr_close,
.upm_cleartoggle = xhci_noop,
.upm_done = xhci_device_intr_done,
+#endif
};
static inline uint32_t
@@ -618,7 +811,9 @@ xhci_init(struct xhci_softc *sc)
/* XXX Low/Full/High speeds for now */
sc->sc_bus.ub_revision = USBREV_2_0;
+#ifdef NHUSB
sc->sc_bus.ub_usedma = true;
+#endif
cap = xhci_read_4(sc, XHCI_CAPLENGTH);
caplength = XHCI_CAP_CAPLENGTH(cap);
@@ -1434,7 +1629,11 @@ xhci_open(struct usbd_pipe *pipe)
if (dev->ud_depth == 0 && dev->ud_powersrc->up_portno == 0) {
switch (ed->bEndpointAddress) {
case USB_CONTROL_ENDPOINT:
+#ifndef NHUSB
+ pipe->up_methods = &xhci_root_ctrl_methods;
+#else
pipe->up_methods = &roothub_ctrl_methods;
+#endif
break;
case UE_DIR_IN | USBROOTHUB_INTR_ENDPT:
pipe->up_methods = &xhci_root_intr_methods;
@@ -1639,7 +1838,11 @@ xhci_rhpsc(struct xhci_softc * const sc,
if (xfer == NULL)
return;
+#ifndef NHUSB
+ p = KERNADDR(&xfer->ux_dmabuf, 0);
+#else
p = xfer->ux_buf;
+#endif
memset(p, 0, xfer->ux_length);
p[port/NBBY] |= 1 << (port%NBBY);
xfer->ux_actlen = xfer->ux_length;
@@ -1852,6 +2055,45 @@ xhci_poll(struct usbd_bus *bus)
return;
}
+#ifndef NHUSB
+static usbd_status
+xhci_allocm(struct usbd_bus *bus, usb_dma_t *dma, uint32_t size)
+{
+ struct xhci_softc * const sc = bus->ub_hcpriv;
+ usbd_status err;
+
+ XHCIHIST_FUNC(); XHCIHIST_CALLED();
+
+ err = usb_allocmem(&sc->sc_bus, size, 0, dma);
+#if 0
+ if (err == USBD_NOMEM)
+ err = usb_reserve_allocm(&sc->sc_dma_reserve, dma, size);
+#endif
+#ifdef XHCI_DEBUG
+ if (err)
+ DPRINTFN(1, "usb_allocmem(%u)=%d", err, size, 0, 0);
+#endif
+
+ return err;
+}
+
+static void
+xhci_freem(struct usbd_bus *bus, usb_dma_t *dma)
+{
+ struct xhci_softc * const sc = bus->ub_hcpriv;
+
+ XHCIHIST_FUNC(); XHCIHIST_CALLED();
+
+#if 0
+ if (dma->udma_block->flags & USB_DMA_RESERVE) {
+ usb_reserve_freem(&sc->sc_dma_reserve, dma);
+ return;
+ }
+#endif
+ usb_freemem(&sc->sc_bus, dma);
+}
+
+#endif /* !NHUSB */
static struct usbd_xfer *
xhci_allocx(struct usbd_bus *bus)
{
@@ -1929,7 +2171,11 @@ xhci_new_device(device_t parent, struct
DPRINTFN(4, "port=%d depth=%d speed=%d upport %d",
port, depth, speed, up->up_portno);
+#ifndef NHUSB
+ dev = malloc(sizeof *dev, M_USB, M_NOWAIT|M_ZERO);
+#else
dev = kmem_zalloc(sizeof(*dev), KM_SLEEP);
+#endif
if (dev == NULL)
return USBD_NOMEM;
@@ -2589,6 +2835,489 @@ xhci_noop(struct usbd_pipe *pipe)
XHCIHIST_FUNC(); XHCIHIST_CALLED();
}
+#ifndef NHUSB
+/* root hub descriptors */
+
+static const usb_device_descriptor_t xhci_devd = {
+ USB_DEVICE_DESCRIPTOR_SIZE,
+ UDESC_DEVICE, /* type */
+ {0x00, 0x02}, /* USB version */
+ UDCLASS_HUB, /* class */
+ UDSUBCLASS_HUB, /* subclass */
+ UDPROTO_HSHUBSTT, /* protocol */
+ 64, /* max packet */
+ {0},{0},{0x00,0x01}, /* device id */
+ 1,2,0, /* string indexes */
+ 1 /* # of configurations */
+};
+
+static const usb_device_qualifier_t xhci_odevd = {
+ USB_DEVICE_DESCRIPTOR_SIZE,
+ UDESC_DEVICE_QUALIFIER, /* type */
+ {0x00, 0x02}, /* USB version */
+ UDCLASS_HUB, /* class */
+ UDSUBCLASS_HUB, /* subclass */
+ UDPROTO_FSHUB, /* protocol */
+ 64, /* max packet */
+ 1, /* # of configurations */
+ 0
+};
+
+static const usb_config_descriptor_t xhci_confd = {
+ USB_CONFIG_DESCRIPTOR_SIZE,
+ UDESC_CONFIG,
+ {USB_CONFIG_DESCRIPTOR_SIZE +
+ USB_INTERFACE_DESCRIPTOR_SIZE +
+ USB_ENDPOINT_DESCRIPTOR_SIZE},
+ 1,
+ 1,
+ 0,
+ UC_ATTR_MBO | UC_SELF_POWERED,
+ 0 /* max power */
+};
+
+static const usb_interface_descriptor_t xhci_ifcd = {
+ USB_INTERFACE_DESCRIPTOR_SIZE,
+ UDESC_INTERFACE,
+ 0,
+ 0,
+ 1,
+ UICLASS_HUB,
+ UISUBCLASS_HUB,
+ UIPROTO_HSHUBSTT,
+ 0
+};
+
+static const usb_endpoint_descriptor_t xhci_endpd = {
+ USB_ENDPOINT_DESCRIPTOR_SIZE,
+ UDESC_ENDPOINT,
+ UE_DIR_IN | USBROOTHUB_INTR_ENDPT,
+ UE_INTERRUPT,
+ {8, 0}, /* max packet */
+ 12
+};
+
+static const usb_hub_descriptor_t xhci_hubd = {
+ USB_HUB_DESCRIPTOR_SIZE,
+ UDESC_HUB,
+ 0,
+ {0,0},
+ 0,
+ 0,
+ {""},
+ {""},
+};
+
+/* root hub control */
+
+static usbd_status
+xhci_root_ctrl_transfer(struct usbd_xfer *xfer)
+{
+ struct xhci_softc * const sc = xfer->ux_pipe->up_dev->ud_bus->ub_hcpriv;
+ usbd_status err;
+
+ XHCIHIST_FUNC(); XHCIHIST_CALLED();
+
+ /* Insert last in queue. */
+ mutex_enter(&sc->sc_lock);
+ err = usb_insert_transfer(xfer);
+ mutex_exit(&sc->sc_lock);
+ if (err)
+ return err;
+
+ /* Pipe isn't running, start first */
+ return xhci_root_ctrl_start(SIMPLEQ_FIRST(&xfer->ux_pipe->up_queue));
+}
+
+/*
+ * Process root hub request.
+ */
+static usbd_status
+xhci_root_ctrl_start(struct usbd_xfer *xfer)
+{
+ struct xhci_softc * const sc = xfer->ux_pipe->up_dev->ud_bus->ub_hcpriv;
+ usb_port_status_t ps;
+ usb_device_request_t *req;
+ void *buf = NULL;
+ usbd_status err;
+ int len, value, index;
+ int l, totlen = 0;
+ int port, i;
+ uint32_t v;
+
+ XHCIHIST_FUNC(); XHCIHIST_CALLED();
+
+ if (sc->sc_dying)
+ return USBD_IOERROR;
+
+ req = &xfer->ux_request;
+
+ len = UGETW(req->wLength);
+ value = UGETW(req->wValue);
+ index = UGETW(req->wIndex);
+
+ if (len != 0)
+ buf = KERNADDR(&xfer->ux_dmabuf, 0);
+
+ DPRINTFN(12, "rhreq: %04x %04x %04x %04x",
+ req->bmRequestType | (req->bRequest << 8), value, index, len);
+
+#define C(x,y) ((x) | ((y) << 8))
+ switch (C(req->bRequest, req->bmRequestType)) {
+ case C(UR_CLEAR_FEATURE, UT_WRITE_DEVICE):
+ case C(UR_CLEAR_FEATURE, UT_WRITE_INTERFACE):
+ case C(UR_CLEAR_FEATURE, UT_WRITE_ENDPOINT):
+ /*
+ * DEVICE_REMOTE_WAKEUP and ENDPOINT_HALT are no-ops
+ * for the integrated root hub.
+ */
+ break;
+ case C(UR_GET_CONFIG, UT_READ_DEVICE):
+ if (len > 0) {
+ *(uint8_t *)buf = sc->sc_conf;
+ totlen = 1;
+ }
+ break;
+ case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE):
+ DPRINTFN(8, "getdesc: wValue=0x%04x", value, 0, 0, 0);
+ if (len == 0)
+ break;
+ switch (value >> 8) {
+ case UDESC_DEVICE:
+ if ((value & 0xff) != 0) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ totlen = l = min(len, USB_DEVICE_DESCRIPTOR_SIZE);
+ memcpy(buf, &xhci_devd, min(l, sizeof(xhci_devd)));
+ break;
+ case UDESC_DEVICE_QUALIFIER:
+ if ((value & 0xff) != 0) {
+ }
+ totlen = l = min(len, USB_DEVICE_DESCRIPTOR_SIZE);
+ memcpy(buf, &xhci_odevd, min(l, sizeof(xhci_odevd)));
+ break;
+ case UDESC_OTHER_SPEED_CONFIGURATION:
+ case UDESC_CONFIG:
+ if ((value & 0xff) != 0) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ totlen = l = min(len, USB_CONFIG_DESCRIPTOR_SIZE);
+ memcpy(buf, &xhci_confd, min(l, sizeof(xhci_confd)));
+ ((usb_config_descriptor_t *)buf)->bDescriptorType =
+ value >> 8;
+ buf = (char *)buf + l;
+ len -= l;
+ l = min(len, USB_INTERFACE_DESCRIPTOR_SIZE);
+ totlen += l;
+ memcpy(buf, &xhci_ifcd, min(l, sizeof(xhci_ifcd)));
+ buf = (char *)buf + l;
+ len -= l;
+ l = min(len, USB_ENDPOINT_DESCRIPTOR_SIZE);
+ totlen += l;
+ memcpy(buf, &xhci_endpd, min(l, sizeof(xhci_endpd)));
+ break;
+ case UDESC_STRING:
+#define sd ((usb_string_descriptor_t *)buf)
+ switch (value & 0xff) {
+ case 0: /* Language table */
+ totlen = usb_makelangtbl(sd, len);
+ break;
+ case 1: /* Vendor */
+ totlen = usb_makestrdesc(sd, len, "NetBSD");
+ break;
+ case 2: /* Product */
+ totlen = usb_makestrdesc(sd, len,
+ "xHCI Root Hub");
+ break;
+ }
+#undef sd
+ break;
+ default:
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ break;
+ case C(UR_GET_INTERFACE, UT_READ_INTERFACE):
+ if (len > 0) {
+ *(uint8_t *)buf = 0;
+ totlen = 1;
+ }
+ break;
+ case C(UR_GET_STATUS, UT_READ_DEVICE):
+ if (len > 1) {
+ USETW(((usb_status_t *)buf)->wStatus,UDS_SELF_POWERED);
+ totlen = 2;
+ }
+ break;
+ case C(UR_GET_STATUS, UT_READ_INTERFACE):
+ case C(UR_GET_STATUS, UT_READ_ENDPOINT):
+ if (len > 1) {
+ USETW(((usb_status_t *)buf)->wStatus, 0);
+ totlen = 2;
+ }
+ break;
+ case C(UR_SET_ADDRESS, UT_WRITE_DEVICE):
+ if (value >= USB_MAX_DEVICES) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ //sc->sc_addr = value;
+ break;
+ case C(UR_SET_CONFIG, UT_WRITE_DEVICE):
+ if (value != 0 && value != 1) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ sc->sc_conf = value;
+ break;
+ case C(UR_SET_DESCRIPTOR, UT_WRITE_DEVICE):
+ break;
+ case C(UR_SET_FEATURE, UT_WRITE_DEVICE):
+ case C(UR_SET_FEATURE, UT_WRITE_INTERFACE):
+ case C(UR_SET_FEATURE, UT_WRITE_ENDPOINT):
+ err = USBD_IOERROR;
+ goto ret;
+ case C(UR_SET_INTERFACE, UT_WRITE_INTERFACE):
+ break;
+ case C(UR_SYNCH_FRAME, UT_WRITE_ENDPOINT):
+ break;
+ /* Hub requests */
+ case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_DEVICE):
+ break;
+ /* Hub Port request */
+ 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_maxports) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ port = XHCI_PORTSC(index);
+ v = xhci_op_read_4(sc, port);
+ DPRINTFN(4, "portsc=0x%08x", v, 0, 0, 0);
+ v &= ~XHCI_PS_CLEAR;
+ switch (value) {
+ case UHF_PORT_ENABLE:
+ xhci_op_write_4(sc, port, v &~ XHCI_PS_PED);
+ break;
+ case UHF_PORT_SUSPEND:
+ err = USBD_IOERROR;
+ goto ret;
+ case UHF_PORT_POWER:
+ break;
+ case UHF_PORT_TEST:
+ case UHF_PORT_INDICATOR:
+ err = USBD_IOERROR;
+ goto ret;
+ case UHF_C_PORT_CONNECTION:
+ xhci_op_write_4(sc, port, v | XHCI_PS_CSC);
+ break;
+ case UHF_C_PORT_ENABLE:
+ case UHF_C_PORT_SUSPEND:
+ case UHF_C_PORT_OVER_CURRENT:
+ err = USBD_IOERROR;
+ goto ret;
+ 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:
+ err = USBD_IOERROR;
+ goto ret;
+ }
+
+ break;
+ case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE):
+ if (len == 0)
+ break;
+ if ((value & 0xff) != 0) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ usb_hub_descriptor_t hubd = xhci_hubd;
+
+ 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)
+ hubd.DeviceRemovable[i++] = 0; /* XXX can't find out? */
+ hubd.bDescLength = USB_HUB_DESCRIPTOR_SIZE + i;
+ l = min(len, hubd.bDescLength);
+ totlen = l;
+ memcpy(buf, &hubd, l);
+ break;
+ case C(UR_GET_STATUS, UT_READ_CLASS_DEVICE):
+ if (len != 4) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ memset(buf, 0, len); /* ? XXX */
+ totlen = len;
+ break;
+ case C(UR_GET_STATUS, UT_READ_CLASS_OTHER):
+ DPRINTFN(8, "get port status i=%d", index, 0, 0, 0);
+ if (index < 1 || index > sc->sc_maxports) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ if (len != 4) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ v = xhci_op_read_4(sc, XHCI_PORTSC(index));
+ DPRINTFN(4, "getrhportsc %d %08x", index, v, 0, 0);
+ /* xspeed -> port_status value */
+ switch (XHCI_PS_SPEED_GET(v)) {
+ case 1:
+ i = UPS_FULL_SPEED;
+ break;
+ case 2:
+ i = UPS_LOW_SPEED;
+ break;
+ case 3:
+ i = UPS_HIGH_SPEED;
+ break;
+ case 4:
+ i = UPS_SUPER_SPEED;
+ break;
+ default:
+ i = 0;
+ break;
+ }
+ if (v & XHCI_PS_CCS) i |= UPS_CURRENT_CONNECT_STATUS;
+ if (v & XHCI_PS_PED) i |= UPS_PORT_ENABLED;
+ 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) {
+ 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);
+ l = min(len, sizeof ps);
+ memcpy(buf, &ps, l);
+ totlen = l;
+ break;
+ case C(UR_SET_DESCRIPTOR, UT_WRITE_CLASS_DEVICE):
+ err = USBD_IOERROR;
+ goto ret;
+ case C(UR_SET_HUB_DEPTH, UT_WRITE_CLASS_DEVICE):
+ break;
+ case C(UR_SET_FEATURE, UT_WRITE_CLASS_DEVICE):
+ break;
+ /* Hub Port request */
+ case C(UR_SET_FEATURE, UT_WRITE_CLASS_OTHER):
+ {
+ int optval = (index >> 8) & 0xff;
+ index &= 0xff;
+ if (index < 1 || index > sc->sc_maxports) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ port = XHCI_PORTSC(index);
+ v = xhci_op_read_4(sc, port);
+ DPRINTFN(4, "portsc=0x%08x", v, 0, 0, 0);
+ v &= ~XHCI_PS_CLEAR;
+ switch (value) {
+ case UHF_PORT_ENABLE:
+ xhci_op_write_4(sc, port, v | XHCI_PS_PED);
+ break;
+ case UHF_PORT_SUSPEND:
+ /* XXX suspend */
+ break;
+ case UHF_PORT_RESET:
+ v &= ~ (XHCI_PS_PED | XHCI_PS_PR);
+ xhci_op_write_4(sc, port, v | XHCI_PS_PR);
+ /* Wait for reset to complete. */
+ usb_delay_ms(&sc->sc_bus, USB_PORT_ROOT_RESET_DELAY);
+ if (sc->sc_dying) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ v = xhci_op_read_4(sc, port);
+ if (v & XHCI_PS_PR) {
+ xhci_op_write_4(sc, port, v & ~XHCI_PS_PR);
+ usb_delay_ms(&sc->sc_bus, 10);
+ /* XXX */
+ }
+ break;
+ case UHF_PORT_POWER:
+ /* XXX power control */
+ break;
+ /* XXX more */
+ 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) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ 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) {
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ 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:
+ err = USBD_IOERROR;
+ goto ret;
+ }
+
+ }
+ break;
+ case C(UR_CLEAR_TT_BUFFER, UT_WRITE_CLASS_OTHER):
+ case C(UR_RESET_TT, UT_WRITE_CLASS_OTHER):
+ case C(UR_GET_TT_STATE, UT_READ_CLASS_OTHER):
+ case C(UR_STOP_TT, UT_WRITE_CLASS_OTHER):
+ break;
+ default:
+ err = USBD_IOERROR;
+ goto ret;
+ }
+ xfer->ux_actlen = totlen;
+ err = USBD_NORMAL_COMPLETION;
+ret:
+ xfer->ux_status = err;
+ mutex_enter(&sc->sc_lock);
+ usb_transfer_complete(xfer);
+ mutex_exit(&sc->sc_lock);
+ return USBD_IN_PROGRESS;
+}
+
+#else /* !NHUSB */
/*
* Process root hub request.
*/
@@ -2842,6 +3571,32 @@ xhci_roothub_ctrl(struct usbd_bus *bus,
return totlen;
}
+#endif /* !NHUSB */
+#ifndef NHUSB
+static void
+xhci_root_ctrl_abort(struct usbd_xfer *xfer)
+{
+ XHCIHIST_FUNC(); XHCIHIST_CALLED();
+ /* Nothing to do, all transfers are synchronous. */
+}
+
+
+static void
+xhci_root_ctrl_close(struct usbd_pipe *pipe)
+{
+ XHCIHIST_FUNC(); XHCIHIST_CALLED();
+ /* Nothing to do. */
+}
+
+static void
+xhci_root_ctrl_done(struct usbd_xfer *xfer)
+{
+ XHCIHIST_FUNC(); XHCIHIST_CALLED();
+
+ xfer->ux_hcpriv = NULL;
+}
+
+#endif /* !NHUSB */
/* root hub interrupt */
static usbd_status
--- src/sys/dev/usb/uhub.c.orig 2015-03-27 15:05:01.000000000 +0900
+++ src/sys/dev/usb/uhub.c 2015-04-05 16:51:55.000000000 +0900
@@ -39,11 +39,13 @@
__KERNEL_RCSID(0, "$NetBSD: uhub.c,v 1.126.2.8 2015/03/21 11:33:37 skrll Exp $");
#include <sys/param.h>
+
#include <sys/systm.h>
+#include <sys/device.h>
#include <sys/kernel.h>
#include <sys/kmem.h>
-#include <sys/device.h>
#include <sys/proc.h>
+#include <sys/sysctl.h>
#include <sys/bus.h>
@@ -51,15 +53,48 @@ __KERNEL_RCSID(0, "$NetBSD: uhub.c,v 1.1
#include <dev/usb/usbdi.h>
#include <dev/usb/usbdi_util.h>
#include <dev/usb/usbdivar.h>
+#include <dev/usb/usbhist.h>
-#ifdef UHUB_DEBUG
-#define DPRINTF(x) if (uhubdebug) printf x
-#define DPRINTFN(n,x) if (uhubdebug>(n)) printf x
-int uhubdebug = 0;
+#ifdef USB_DEBUG
+#ifndef UHUB_DEBUG
+#define uhubdebug 0
#else
-#define DPRINTF(x)
-#define DPRINTFN(n,x)
-#endif
+static int uhubdebug = 0;
+
+SYSCTL_SETUP(sysctl_hw_uhub_setup, "sysctl hw.uhub setup")
+{
+ int err;
+ const struct sysctlnode *rnode;
+ const struct sysctlnode *cnode;
+
+ err = sysctl_createv(clog, 0, NULL, &rnode,
+ CTLFLAG_PERMANENT, CTLTYPE_NODE, "uhub",
+ SYSCTL_DESCR("uhub global controls"),
+ NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL);
+
+ if (err)
+ goto fail;
+
+ /* control debugging printfs */
+ err = sysctl_createv(clog, 0, &rnode, &cnode,
+ CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT,
+ "debug", SYSCTL_DESCR("Enable debugging output"),
+ NULL, 0, &uhubdebug, sizeof(uhubdebug), CTL_CREATE, CTL_EOL);
+ if (err)
+ goto fail;
+
+ return;
+fail:
+ aprint_error("%s: sysctl_createv failed (err = %d)\n", __func__, err);
+}
+
+#endif /* UHUB_DEBUG */
+#endif /* USB_DEBUG */
+
+#define DPRINTF(FMT,A,B,C,D) USBHIST_LOGN(uhubdebug,1,FMT,A,B,C,D)
+#define DPRINTFN(N,FMT,A,B,C,D) USBHIST_LOGN(uhubdebug,N,FMT,A,B,C,D)
+#define UHUBHIST_FUNC() USBHIST_FUNC()
+#define UHUBHIST_CALLED(name) USBHIST_CALLED(uhubdebug)
struct uhub_softc {
device_t sc_dev; /* base device */
@@ -118,12 +153,14 @@ uhub_match(device_t parent, cfdata_t mat
struct usb_attach_arg *uaa = aux;
int matchvalue;
+ UHUBHIST_FUNC(); UHUBHIST_CALLED();
+
if (uhub_ubermatch)
matchvalue = UMATCH_HIGHEST+1;
else
matchvalue = UMATCH_DEVCLASS_DEVSUBCLASS;
- DPRINTFN(5,("uhub_match, uaa=%p\n", uaa));
+ DPRINTFN(5, "uaa=%p", uaa, 0, 0, 0);
/*
* The subclass for hubs seems to be 0 for some and 1 for others,
* so we just ignore the subclass.
@@ -151,7 +188,8 @@ uhub_attach(device_t parent, device_t se
struct usbd_tt *tts = NULL;
#endif
- DPRINTFN(1,("uhub_attach\n"));
+ UHUBHIST_FUNC(); UHUBHIST_CALLED();
+
sc->sc_dev = self;
sc->sc_hub = dev;
sc->sc_proto = uaa->uaa_proto;
@@ -169,8 +207,7 @@ uhub_attach(device_t parent, device_t se
err = usbd_set_config_index(dev, 0, 1);
if (err) {
- DPRINTF(("%s: configuration failed, error=%s\n",
- device_xname(sc->sc_dev), usbd_errstr(err)));
+ DPRINTF("configuration failed, sc %p error %d", sc, err, 0, 0);
return;
}
@@ -187,7 +224,7 @@ uhub_attach(device_t parent, device_t se
USETW2(req.wValue, UDESC_HUB, 0);
USETW(req.wIndex, 0);
USETW(req.wLength, USB_HUB_DESCRIPTOR_SIZE);
- DPRINTFN(1,("%s: getting hub descriptor\n", __func__));
+ DPRINTF("uhub %d getting hub descriptor", device_unit(self), 0, 0, 0);
err = usbd_do_request(dev, &req, &hubdesc);
nports = hubdesc.bNbrPorts;
if (!err && nports > 7) {
@@ -195,8 +232,8 @@ uhub_attach(device_t parent, device_t se
err = usbd_do_request(dev, &req, &hubdesc);
}
if (err) {
- DPRINTF(("%s: getting hub descriptor failed, error=%s\n",
- device_xname(sc->sc_dev), usbd_errstr(err)));
+ DPRINTF("getting hub descriptor failed, uhub %d error %d",
+ device_unit(self), err, 0, 0);
return;
}
@@ -338,7 +375,8 @@ uhub_attach(device_t parent, device_t se
if (err)
aprint_error_dev(self, "port %d power on failed, %s\n",
port, usbd_errstr(err));
- DPRINTF(("usb_init_port: turn on port %d power\n", port));
+ DPRINTF("uhub %d turn on port %d power", device_unit(self),
+ port, 0, 0);
}
/* Wait for stable power if we are not a root hub */
@@ -377,7 +415,10 @@ uhub_explore(struct usbd_device *dev)
int port;
int change, status, reconnect;
- DPRINTFN(10, ("uhub_explore dev=%p addr=%d\n", dev, dev->ud_addr));
+ UHUBHIST_FUNC(); UHUBHIST_CALLED();
+
+ DPRINTFN(10, "uhub %d dev=%p addr=%d speed=%u",
+ device_unit(sc->sc_dev), dev, dev->ud_addr, dev->ud_speed);
if (!sc->sc_running)
return USBD_NOT_STARTED;
@@ -391,12 +432,15 @@ uhub_explore(struct usbd_device *dev)
err = usbd_get_hub_status(dev, &hs);
if (err) {
- DPRINTF(("%s: get hub status failed, err=%d\n",
- device_xname(sc->sc_dev), err));
+ DPRINTF("uhub %d get hub status failed, err %d",
+ device_unit(sc->sc_dev), err, 0, 0);
} else {
/* just acknowledge */
status = UGETW(hs.wHubStatus);
change = UGETW(hs.wHubChange);
+ DPRINTF("uhub %d s/c=%x/%x", device_unit(sc->sc_dev),
+ status, change, 0);
+
if (change & UHS_LOCAL_POWER)
usbd_clear_hub_feature(dev,
UHF_C_HUB_LOCAL_POWER);
@@ -419,16 +463,15 @@ uhub_explore(struct usbd_device *dev)
if (PORTSTAT_ISSET(sc, port) || reconnect) {
err = usbd_get_port_status(dev, port, &up->up_status);
if (err) {
- DPRINTF(("uhub_explore: get port stat failed, "
- "error=%s\n", usbd_errstr(err)));
+ DPRINTF("uhub %d get port stat failed, err %d",
+ device_unit(sc->sc_dev), err, 0, 0);
continue;
}
status = UGETW(up->up_status.wPortStatus);
change = UGETW(up->up_status.wPortChange);
-#if 0
- printf("%s port %d: s/c=%x/%x\n",
- device_xname(sc->sc_dev), port, status, change);
-#endif
+
+ DPRINTF("uhub %d port %d: s/c=%x/%x",
+ device_unit(sc->sc_dev), port, status, change);
}
if (!change && !reconnect) {
/* No status change, just do recursive explore. */
@@ -438,7 +481,8 @@ uhub_explore(struct usbd_device *dev)
}
if (change & UPS_C_PORT_ENABLED) {
- DPRINTF(("uhub_explore: C_PORT_ENABLED\n"));
+ DPRINTF("uhub %d port %d C_PORT_ENABLED",
+ device_unit(sc->sc_dev), port, 0, 0);
usbd_clear_port_feature(dev, port, UHF_C_PORT_ENABLE);
if (change & UPS_C_CONNECT_STATUS) {
/* Ignore the port error if the device
@@ -473,8 +517,8 @@ uhub_explore(struct usbd_device *dev)
/* We have a connect status change, handle it. */
- DPRINTF(("uhub_explore: status change hub=%d port=%d\n",
- dev->ud_addr, port));
+ DPRINTF("status change hub=%d port=%d", dev->ud_addr, port, 0,
+ 0);
usbd_clear_port_feature(dev, port, UHF_C_PORT_CONNECTION);
/*
* If there is already a device on the port the change status
@@ -486,20 +530,23 @@ uhub_explore(struct usbd_device *dev)
disco:
if (up->up_dev != NULL) {
/* Disconnected */
- DPRINTF(("uhub_explore: device addr=%d disappeared "
- "on port %d\n", up->up_dev->ud_addr, port));
+ DPRINTF("uhub %d device addr=%d disappeared on port %d",
+ device_unit(sc->sc_dev), up->up_dev->ud_addr, port,
+ 0);
usb_disconnect_port(up, sc->sc_dev, DETACH_FORCE);
usbd_clear_port_feature(dev, port,
UHF_C_PORT_CONNECTION);
}
if (!(status & UPS_CURRENT_CONNECT_STATUS)) {
/* Nothing connected, just ignore it. */
- DPRINTFN(3,("uhub_explore: port=%d !CURRENT_CONNECT"
- "_STATUS\n", port));
+ DPRINTFN(3, "uhub %d port=%d !CURRENT_CONNECT_STATUS",
+ device_unit(sc->sc_dev), port, 0, 0);
continue;
}
/* Connected */
+ DPRINTF("unit %d dev->ud_speed=%u dev->ud_depth=%u",
+ device_unit(sc->sc_dev), dev->ud_speed, dev->ud_depth, 0);
if (!(status & UPS_PORT_POWER))
aprint_normal_dev(sc->sc_dev,
@@ -517,12 +564,15 @@ uhub_explore(struct usbd_device *dev)
/* Get port status again, it might have changed during reset */
err = usbd_get_port_status(dev, port, &up->up_status);
if (err) {
- DPRINTF(("uhub_explore: get port status failed, "
- "error=%s\n", usbd_errstr(err)));
+ DPRINTF("uhub %d port=%d get port status failed, "
+ "err %d", device_unit(sc->sc_dev), port, err, 0);
continue;
}
status = UGETW(up->up_status.wPortStatus);
change = UGETW(up->up_status.wPortChange);
+ DPRINTF("hub %d port %d reset: s/c=%x/%x",
+ device_unit(sc->sc_dev), port, status, change);
+
if (!(status & UPS_CURRENT_CONNECT_STATUS)) {
/* Nothing connected, just ignore it. */
#ifdef DIAGNOSTIC
@@ -557,8 +607,8 @@ uhub_explore(struct usbd_device *dev)
dev->ud_depth + 1, speed, port, up);
/* XXX retry a few times? */
if (err) {
- DPRINTFN(-1,("uhub_explore: usbd_new_device failed, "
- "error=%s\n", usbd_errstr(err)));
+ DPRINTF("usbd_new_device failed, error %d", err, 0, 0,
+ 0);
/* Avoid addressing problems by disabling. */
/* usbd_reset_port(dev, port, &up->status); */
@@ -597,7 +647,9 @@ uhub_detach(device_t self, int flags)
struct usbd_port *rup;
int nports, port, rc;
- DPRINTF(("uhub_detach: sc=%p flags=%d\n", sc, flags));
+ UHUBHIST_FUNC(); UHUBHIST_CALLED();
+
+ DPRINTF("uhub %d sc=%p flags=%d", device_unit(self), sc, flags, 0);
if (hub == NULL) /* Must be partially working */
return 0;
@@ -708,7 +760,9 @@ uhub_intr(struct usbd_xfer *xfer, void *
{
struct uhub_softc *sc = addr;
- DPRINTFN(5,("uhub_intr: sc=%p\n", sc));
+ UHUBHIST_FUNC(); UHUBHIST_CALLED();
+
+ DPRINTFN(5, "uhub %d sc=%p", device_unit(sc->sc_dev), sc, 0, 0);
if (status == USBD_STALLED)
usbd_clear_endpoint_stall_async(sc->sc_ipipe);
--- ../nick-nhusb/src/sys/dev/usb/uhub.c.orig 2015-04-05 16:51:55.000000000 +0900
+++ ../nick-nhusb/src/sys/dev/usb/uhub.c 2015-04-05 17:21:44.000000000 +0900
@@ -108,11 +108,13 @@ struct uhub_softc {
size_t sc_statuslen;
int sc_explorepending;
int sc_isehciroothub; /* see comment in uhub_intr() */
+ int sc_isxhciroothub;
u_char sc_running;
};
-#define UHUB_IS_HIGH_SPEED(sc) ((sc)->sc_proto != UDPROTO_FSHUB)
+#define UHUB_IS_HIGH_SPEED(sc) \
+ ((sc)->sc_proto == UDPROTO_HSHUBSTT || (sc)->sc_proto == UDPROTO_HSHUBMTT)
#define UHUB_IS_SINGLE_TT(sc) ((sc)->sc_proto == UDPROTO_HSHUBSTT)
#define PORTSTAT_ISSET(sc, port) \
@@ -147,6 +149,76 @@ CFATTACH_DECL2_NEW(uroothub, sizeof(stru
*/
int uhub_ubermatch = 0;
+static usbd_status
+usbd_get_hub_desc(struct usbd_device *dev, usb_hub_descriptor_t *hd, int speed)
+{
+ usb_device_request_t req;
+ usbd_status err;
+ int nports;
+
+ UHUBHIST_FUNC(); UHUBHIST_CALLED();
+
+ /* don't issue UDESC_HUB to SS hub, or it would stall */
+ if (dev->ud_depth != 0 && dev->ud_speed == USB_SPEED_SUPER) {
+ usb_hub_ss_descriptor_t hssd;
+ int rmvlen;
+
+ memset(&hssd, 0, sizeof(hssd));
+ req.bmRequestType = UT_READ_CLASS_DEVICE;
+ req.bRequest = UR_GET_DESCRIPTOR;
+ USETW2(req.wValue, UDESC_SS_HUB, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, USB_HUB_SS_DESCRIPTOR_SIZE);
+ DPRINTFN(1, "getting sshub descriptor", 0, 0, 0, 0);
+ err = usbd_do_request(dev, &req, &hssd);
+ nports = hssd.bNbrPorts;
+ if (dev->ud_depth != 0 && nports > UHD_SS_NPORTS_MAX) {
+ DPRINTF("num of ports %d exceeds maxports %d",
+ nports, UHD_SS_NPORTS_MAX, 0, 0);
+ nports = hd->bNbrPorts = UHD_SS_NPORTS_MAX;
+ }
+ rmvlen = (nports + 7) / 8;
+ hd->bDescLength = USB_HUB_DESCRIPTOR_SIZE +
+ (rmvlen > 1 ? rmvlen : 1) - 1;
+ memcpy(hd->DeviceRemovable, hssd.DeviceRemovable, rmvlen);
+ hd->bDescriptorType = hssd.bDescriptorType;
+ hd->bNbrPorts = hssd.bNbrPorts;
+ hd->wHubCharacteristics[0] = hssd.wHubCharacteristics[0];
+ hd->wHubCharacteristics[1] = hssd.wHubCharacteristics[1];
+ hd->bPwrOn2PwrGood = hssd.bPwrOn2PwrGood;
+ hd->bHubContrCurrent = hssd.bHubContrCurrent;
+ } else {
+ req.bmRequestType = UT_READ_CLASS_DEVICE;
+ req.bRequest = UR_GET_DESCRIPTOR;
+ USETW2(req.wValue, UDESC_HUB, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, USB_HUB_DESCRIPTOR_SIZE);
+ DPRINTFN(1, "getting hub descriptor", 0, 0, 0, 0);
+ err = usbd_do_request(dev, &req, hd);
+ nports = hd->bNbrPorts;
+ if (!err && nports > 7) {
+ USETW(req.wLength,
+ USB_HUB_DESCRIPTOR_SIZE + (nports+1) / 8);
+ err = usbd_do_request(dev, &req, hd);
+ }
+ }
+
+ return err;
+}
+
+static usbd_status
+usbd_set_hub_depth(struct usbd_device *dev, int depth)
+{
+ usb_device_request_t req;
+
+ req.bmRequestType = UT_WRITE_CLASS_DEVICE;
+ req.bRequest = UR_SET_HUB_DEPTH;
+ USETW(req.wValue, depth);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 0);
+ return usbd_do_request(dev, &req, 0);
+}
+
int
uhub_match(device_t parent, cfdata_t match, void *aux)
{
@@ -179,7 +251,6 @@ uhub_attach(device_t parent, device_t se
char *devinfop;
usbd_status err;
struct usbd_hub *hub = NULL;
- usb_device_request_t req;
usb_hub_descriptor_t hubdesc;
int p, port, nports, nremov, pwrdly;
struct usbd_interface *iface;
@@ -219,18 +290,9 @@ uhub_attach(device_t parent, device_t se
}
/* Get hub descriptor. */
- req.bmRequestType = UT_READ_CLASS_DEVICE;
- req.bRequest = UR_GET_DESCRIPTOR;
- USETW2(req.wValue, UDESC_HUB, 0);
- USETW(req.wIndex, 0);
- USETW(req.wLength, USB_HUB_DESCRIPTOR_SIZE);
- DPRINTF("uhub %d getting hub descriptor", device_unit(self), 0, 0, 0);
- err = usbd_do_request(dev, &req, &hubdesc);
+ memset(&hubdesc, 0, sizeof(hubdesc));
+ err = usbd_get_hub_desc(dev, &hubdesc, dev->ud_speed);
nports = hubdesc.bNbrPorts;
- if (!err && nports > 7) {
- USETW(req.wLength, USB_HUB_DESCRIPTOR_SIZE + (nports+1) / 8);
- err = usbd_do_request(dev, &req, &hubdesc);
- }
if (err) {
DPRINTF("getting hub descriptor failed, uhub %d error %d",
device_unit(self), err, 0, 0);
@@ -258,6 +320,16 @@ uhub_attach(device_t parent, device_t se
hub->uh_explore = uhub_explore;
hub->uh_hubdesc = hubdesc;
+ if (dev->ud_speed == USB_SPEED_SUPER && dev->ud_depth != 0) {
+ aprint_debug_dev(self, "setting hub depth %u\n",
+ dev->ud_depth-1);
+ err = usbd_set_hub_depth(dev, dev->ud_depth - 1);
+ if (err) {
+ aprint_error_dev(self, "can't set depth\n");
+ goto bad;
+ }
+ }
+
/* Set up interrupt pipe. */
err = usbd_device2interface_handle(dev, 0, &iface);
if (err) {
@@ -290,6 +362,8 @@ uhub_attach(device_t parent, device_t se
goto bad;
if (device_is_a(device_parent(device_parent(sc->sc_dev)), "ehci"))
sc->sc_isehciroothub = 1;
+ if (device_is_a(device_parent(device_parent(sc->sc_dev)), "xhci"))
+ sc->sc_isxhciroothub = 1;
/* force initial scan */
memset(sc->sc_status, 0xff, sc->sc_statuslen);
@@ -317,9 +391,10 @@ uhub_attach(device_t parent, device_t se
* These are the events on the bus when a hub is attached:
* Get device and config descriptors (see attach code)
* Get hub descriptor (see above)
+ * Set hub depth (if super speed)
* For all ports
* turn on power
- * wait for power to become stable
+ * Wait for power to become stable
* (all below happens in explore code)
* For all ports
* clear C_PORT_CONNECTION
@@ -505,6 +580,19 @@ uhub_explore(struct usbd_device *dev)
port);
}
}
+#if 1
+ if (change & UPS_C_PORT_RESET)
+ usbd_clear_port_feature(dev, port, UHF_C_PORT_RESET);
+#endif
+ if (change & UPS_C_BH_PORT_RESET)
+ usbd_clear_port_feature(dev, port,
+ UHF_C_BH_PORT_RESET);
+ if (change & UPS_C_PORT_LINK_STATE)
+ usbd_clear_port_feature(dev, port,
+ UHF_C_PORT_LINK_STATE);
+ if (change & UPS_C_PORT_CONFIG_ERROR)
+ usbd_clear_port_feature(dev, port,
+ UHF_C_PORT_CONFIG_ERROR);
/* XXX handle overcurrent and resume events! */
@@ -548,7 +636,14 @@ uhub_explore(struct usbd_device *dev)
DPRINTF("unit %d dev->ud_speed=%u dev->ud_depth=%u",
device_unit(sc->sc_dev), dev->ud_speed, dev->ud_depth, 0);
- if (!(status & UPS_PORT_POWER))
+ if ((!(status & UPS_PORT_POWER) &&
+ /* check POWER for root/non-root hub if non-SS */
+ ((dev->ud_depth == 0 && !(status & UPS_SUPER_SPEED)) ||
+ (dev->ud_depth != 0 && dev->ud_speed != USB_SPEED_SUPER)))||
+ /* check POWER_SS for root/non-root hub if SS */
+ (!(status & UPS_PORT_POWER_SS) &&
+ ((dev->ud_depth == 0 && (status & UPS_SUPER_SPEED)) ||
+ (dev->ud_depth != 0 && dev->ud_speed == USB_SPEED_SUPER))))
aprint_normal_dev(sc->sc_dev,
"strange, connected port %d has no power\n", port);
@@ -591,8 +686,9 @@ uhub_explore(struct usbd_device *dev)
}
/* Figure out device speed */
-#if 0
- if (status & UPS_SUPER_SPEED)
+#if 1
+ if ((status & UPS_SUPER_SPEED) ||
+ (dev->ud_speed == USB_SPEED_SUPER && dev->ud_depth != 0))
speed = USB_SPEED_SUPER;
else
#endif
@@ -602,6 +698,8 @@ uhub_explore(struct usbd_device *dev)
speed = USB_SPEED_LOW;
else
speed = USB_SPEED_FULL;
+ DPRINTF("unit %d: speed %u\n", device_unit(sc->sc_dev), speed,
+ 0, 0);
/* Get device info and set its address. */
err = usbd_new_device(sc->sc_dev, dev->ud_bus,
dev->ud_depth + 1, speed, port, up);
@@ -629,7 +727,7 @@ uhub_explore(struct usbd_device *dev)
}
}
/* enable status change notifications again */
- if (!sc->sc_isehciroothub)
+ if (!sc->sc_isehciroothub && !sc->sc_isxhciroothub)
memset(sc->sc_status, 0, sc->sc_statuslen);
sc->sc_explorepending = 0;
return USBD_NORMAL_COMPLETION;
@@ -785,4 +883,12 @@ uhub_intr(struct usbd_xfer *xfer, void *
if (status == USBD_NORMAL_COMPLETION && sc->sc_explorepending &&
sc->sc_isehciroothub)
usb_needs_explore(sc->sc_hub);
+ /*
+ * XXX force re-scan all ports of xhci root hub
+ */
+ if (status == USBD_NORMAL_COMPLETION && sc->sc_explorepending &&
+ sc->sc_isxhciroothub) {
+ memset(sc->sc_status, 0xff, sc->sc_statuslen);
+ usb_needs_explore(sc->sc_hub);
+ }
}
--- src/sys/dev/usb/usb.h.orig 2014-12-04 07:43:25.000000000 +0900
+++ src/sys/dev/usb/usb.h 2014-12-04 08:54:56.000000000 +0900
@@ -244,6 +247,7 @@ typedef struct {
#define UC_REMOTE_WAKEUP 0x20
uByte bMaxPower; /* max current in 2 mA units */
#define UC_POWER_FACTOR 2
+#define UC_POWER_FACTOR_SS 8
} UPACKED usb_config_descriptor_t;
#define USB_CONFIG_DESCRIPTOR_SIZE 9
@@ -301,6 +305,7 @@ typedef struct {
} UPACKED usb_endpoint_descriptor_t;
#define USB_ENDPOINT_DESCRIPTOR_SIZE 7
+/* USB 3.0 9.6.2, Table 9-12 */
typedef struct {
uByte bLength;
uByte bDescriptorType;
@@ -314,6 +319,7 @@ typedef struct {
} UPACKED usb_endpoint_ss_comp_descriptor_t;
#define USB_ENDPOINT_SS_COMP_DESCRIPTOR_SIZE 6
+/* USB 3.0 9.6.2, Table 9-12 */
typedef struct {
uByte bLength;
uByte bDescriptorType;
@@ -322,10 +328,12 @@ typedef struct {
} UPACKED usb_bos_descriptor_t;
#define USB_BOS_DESCRIPTOR_SIZE 5
+/* common members of device capability descriptors */
typedef struct {
uByte bLength;
uByte bDescriptorType;
uByte bDevCapabilityType;
+/* Table 9-14 */
#define USB_DEVCAP_RESERVED 0x00
#define USB_DEVCAP_WUSB 0x01
#define USB_DEVCAP_USB2EXT 0x02
@@ -341,17 +349,19 @@ typedef struct {
#define USB_DEVCAP_WUSB_EXT 0x0c
/* data ... */
} UPACKED usb_device_capability_descriptor_t;
-#define USB_DEVICE_CAPABILITY_DESCRIPTOR_SIZE 3 /* variable length */
+#define USB_DEVICE_CAPABILITY_DESCRIPTOR_SIZE 3 /* at least */
+/* 9.6.2.1 */
typedef struct {
uByte bLength;
uByte bDescriptorType;
uByte bDevCapabilityType;
uDWord bmAttributes;
#define USB_DEVCAP_USB2EXT_LPM __BIT(1)
-} UPACKED usb_usb2ext_descriptor_t;
+} UPACKED usb_devcap_usb2ext_descriptor_t;
#define USB_DEVCAP_USB2EXT_DESCRIPTOR_SIZE 7
+/* 9.6.2.2 */
typedef struct {
uByte bLength;
uByte bDescriptorType;
@@ -359,16 +369,17 @@ typedef struct {
uByte bmAttributes;
#define USB_DEVCAP_SS_LTM __BIT(1)
uWord wSpeedsSupported;
-#define USB_DEVCAP_SS_SPEED_SS __BIT(0)
+#define USB_DEVCAP_SS_SPEED_LS __BIT(0)
#define USB_DEVCAP_SS_SPEED_FS __BIT(1)
#define USB_DEVCAP_SS_SPEED_HS __BIT(2)
-#define USB_DEVCAP_SS_SPEED_LS __BIT(3)
+#define USB_DEVCAP_SS_SPEED_SS __BIT(3)
uByte bFunctionalitySupport;
uByte bU1DevExitLat;
uWord wU2DevExitLat;
} UPACKED usb_devcap_ss_descriptor_t;
#define USB_DEVCAP_SS_DESCRIPTOR_SIZE 10
+/* 9.6.2.4 */
typedef struct {
uByte bLength;
uByte bDescriptorType;
@@ -733,7 +744,9 @@ typedef struct {
#endif
#define USB_MIN_POWER 100 /* mA */
+#define USB_MIN_POWER_SS 150 /* mA */
#define USB_MAX_POWER 500 /* mA */
+#define USB_MAX_POWER_SS 900 /* mA */
#define USB_BUS_RESET_DELAY 100 /* ms XXX?*/
--- src/sys/dev/usb/usb_subr.c.orig 2015-03-27 15:05:02.000000000 +0900
+++ src/sys/dev/usb/usb_subr.c 2015-03-27 15:11:01.000000000 +0900
@@ -1411,6 +1411,13 @@ usbd_fill_deviceinfo(struct usbd_device
err = USB_PORT_ENABLED;
else if (s & UPS_SUSPEND)
err = USB_PORT_SUSPENDED;
+ /*
+ * UPS_PORT_POWER_SS is available only
+ * if SS, otherwise it means UPS_LOW_SPEED.
+ */
+ else if ((s & UPS_PORT_POWER_SS) &&
+ dev->ud_speed == USB_SPEED_SUPER)
+ err = USB_PORT_POWERED;
else if (s & UPS_PORT_POWER)
err = USB_PORT_POWERED;
else
--- src/sys/dev/usb/xhcireg.h.orig 2014-11-18 21:02:04.000000000 +0900
+++ src/sys/dev/usb/xhcireg.h 2015-04-05 09:01:54.000000000 +0900
@@ -38,6 +38,16 @@
#define PCI_USBREV_3_0 0x30 /* USB 3.0 */
#define PCI_XHCI_FLADJ 0x61 /* RW frame length adjust */
+#define PCI_XHCI_INTEL_XUSB2PR 0xD0 /* Intel USB2 Port Routing */
+#define PCI_XHCI_INTEL_USB2PRM 0xD4 /* Intel USB2 Port Routing Mask */
+#define PCI_XHCI_INTEL_USB3_PSSEN 0xD8 /* Intel USB3 Port SuperSpeed Enable */
+#define PCI_XHCI_INTEL_USB3PRM 0xDC /* Intel USB3 Port Routing Mask */
+
+#define PCI_XHCI_INTEL_XUSB2PR 0xD0 /* Intel USB2 Port Routing */
+#define PCI_XHCI_INTEL_USB2PRM 0xD4 /* Intel USB2 Port Routing Mask */
+#define PCI_XHCI_INTEL_USB3_PSSEN 0xD8 /* Intel USB3 Port SuperSpeed Enable */
+#define PCI_XHCI_INTEL_USB3PRM 0xDC /* Intel USB3 Port Routing Mask */
+
/* XHCI capability registers */
#define XHCI_CAPLENGTH 0x00 /* RO capability */
#define XHCI_CAP_CAPLENGTH(x) ((x) & 0xFF)
@@ -168,6 +178,7 @@
#define XHCI_IMOD_ICNT_GET(x) (((x) >> 16) & 0xFFFF) /* 250ns unit */
#define XHCI_IMOD_ICNT_SET(x) (((x) & 0xFFFF) << 16) /* 250ns unit */
#define XHCI_IMOD_DEFAULT 0x000001F4U /* 8000 IRQ/second */
+#define XHCI_IMOD_DEFAULT_LP 0x000003E8U /* 4000 IRQ/sec for LynxPoint */
#define XHCI_ERSTSZ(n) (0x0028 + (0x20 * (n))) /* XHCI event ring segment table size */
#define XHCI_ERSTS_GET(x) ((x) & 0xFFFF)
#define XHCI_ERSTS_SET(x) ((x) & 0xFFFF)
@@ -188,18 +199,20 @@
/* XHCI legacy support */
#define XHCI_XECP_ID(x) ((x) & 0xFF)
#define XHCI_XECP_NEXT(x) (((x) >> 8) & 0xFF)
-#if 0
#define XHCI_XECP_BIOS_SEM 0x0002
#define XHCI_XECP_OS_SEM 0x0003
-#endif
-/* XHCI capability ID's */
-#define XHCI_ID_USB_LEGACY 0x0001
-#define XHCI_ID_PROTOCOLS 0x0002
-#define XHCI_ID_POWER_MGMT 0x0003
-#define XHCI_ID_VIRTUALIZATION 0x0004
-#define XHCI_ID_MSG_IRQ 0x0005
-#define XHCI_ID_USB_LOCAL_MEM 0x0006
+/* XHCI extended capability ID's */
+#define XHCI_ID_USB_LEGACY 0x0001 /* USB Legacy Support */
+#define XHCI_XECP_USBLESUP 0x0000 /* Legacy Support Capability Reg */
+#define XHCI_XECP_USBLEGCTLSTS 0x0004 /* Legacy Support Ctrl & Status Reg */
+#define XHCI_ID_PROTOCOLS 0x0002 /* Supported Protocol */
+#define XHCI_ID_POWER_MGMT 0x0003 /* Extended Power Management */
+#define XHCI_ID_VIRTUALIZATION 0x0004 /* I/O Virtualization */
+#define XHCI_ID_MSG_IRQ 0x0005 /* Message Interrupt */
+#define XHCI_ID_USB_LOCAL_MEM 0x0006 /* Local Memory */
+#define XHCI_ID_USB_DEBUG 0x000A /* USB Debug Capability */
+#define XHCI_ID_XMSG_IRQ 0x0011 /* Extended Message Interrupt */
#define XHCI_PAGE_SIZE(sc) ((sc)->sc_pgsz)
@@ -266,6 +279,8 @@ struct xhci_trb {
#define XHCI_TRB_3_FRID_SET(x) (((x) & 0x7FF) << 20)
#define XHCI_TRB_3_ISO_SIA_BIT (1U << 31)
#define XHCI_TRB_3_SUSP_EP_BIT (1U << 23)
+#define XHCI_TRB_3_VFID_GET(x) (((x) >> 16) & 0xFF)
+#define XHCI_TRB_3_VFID_SET(x) (((x) & 0xFF) << 16)
#define XHCI_TRB_3_SLOT_GET(x) (((x) >> 24) & 0xFF)
#define XHCI_TRB_3_SLOT_SET(x) (((x) & 0xFF) << 24)
@@ -375,10 +390,20 @@ struct xhci_trb {
#define XHCI_SCTX_3_DEV_ADDR_GET(x) ((x) & 0xFF)
#define XHCI_SCTX_3_SLOT_STATE_SET(x) (((x) & 0x1F) << 27)
#define XHCI_SCTX_3_SLOT_STATE_GET(x) (((x) >> 27) & 0x1F)
+#define XHCI_SLOTSTATE_DISABLED 0 /* disabled or enabled */
+#define XHCI_SLOTSTATE_ENABLED 0
+#define XHCI_SLOTSTATE_DEFAULT 1
+#define XHCI_SLOTSTATE_ADDRESSED 2
+#define XHCI_SLOTSTATE_CONFIGURED 3
#define XHCI_EPCTX_0_EPSTATE_SET(x) ((x) & 0x7)
#define XHCI_EPCTX_0_EPSTATE_GET(x) ((x) & 0x7)
+#define XHCI_EPSTATE_DISABLED 0
+#define XHCI_EPSTATE_RUNNING 1
+#define XHCI_EPSTATE_HALTED 2
+#define XHCI_EPSTATE_STOPPED 3
+#define XHCI_EPSTATE_ERROR 4
#define XHCI_EPCTX_0_MULT_SET(x) (((x) & 0x3) << 8)
#define XHCI_EPCTX_0_MULT_GET(x) (((x) >> 8) & 0x3)
#define XHCI_EPCTX_0_MAXP_STREAMS_SET(x) (((x) & 0x1F) << 10)
--- src/sys/dev/usb/xhcivar.h.orig 2015-03-27 15:05:03.000000000 +0900
+++ src/sys/dev/usb/xhcivar.h 2015-03-27 15:53:58.000000000 +0900
@@ -109,6 +109,10 @@ struct xhci_softc {
bool sc_ac64;
bool sc_dying;
+
+ int sc_quirks;
+#define XHCI_QUIRK_FORCE_INTR __BIT(0) /* force interrupt reading */
+#define XHCI_QUIRK_INTEL __BIT(1) /* Intel xhci chip */
};
int xhci_init(struct xhci_softc *);
Home |
Main Index |
Thread Index |
Old Index