Page MenuHomeFreeBSD

D52166.diff
No OneTemporary

D52166.diff

diff --git a/usr.sbin/bhyve/Makefile b/usr.sbin/bhyve/Makefile
--- a/usr.sbin/bhyve/Makefile
+++ b/usr.sbin/bhyve/Makefile
@@ -67,6 +67,7 @@
uart_emul.c \
usb_emul.c \
usb_mouse.c \
+ usb_passthru.c \
virtio.c \
vmexit.c \
vmgenc.c
@@ -95,7 +96,7 @@
-I${.CURDIR}/../../contrib/lib9p \
-I${SRCTOP}/sys
-LIBADD+= vmmapi md nv pthread z util sbuf cam 9p
+LIBADD+= vmmapi md nv pthread z util sbuf cam 9p usb
.if ${MK_BHYVE_SNAPSHOT} != "no"
LIBADD+= ucl xo
diff --git a/usr.sbin/bhyve/Makefile.depend b/usr.sbin/bhyve/Makefile.depend
--- a/usr.sbin/bhyve/Makefile.depend
+++ b/usr.sbin/bhyve/Makefile.depend
@@ -16,6 +16,7 @@
lib/libsbuf \
lib/libthr \
lib/libutil \
+ lib/libusb \
lib/libvmmapi \
lib/libz \
diff --git a/usr.sbin/bhyve/pci_xhci.c b/usr.sbin/bhyve/pci_xhci.c
--- a/usr.sbin/bhyve/pci_xhci.c
+++ b/usr.sbin/bhyve/pci_xhci.c
@@ -61,8 +61,7 @@
#endif
#include "usb_emul.h"
-
-static int xhci_debug = 0;
+static int xhci_debug = 1;
#define DPRINTF(params) if (xhci_debug) PRINTLN params
#define WPRINTF(params) PRINTLN params
@@ -85,7 +84,7 @@
#define XHCI_PORTREGS_START 0x400
#define XHCI_DOORBELL_MAX 256
-#define XHCI_STREAMS_MAX 1 /* 4-15 in XHCI spec */
+#define XHCI_STREAMS_MAX 65536 /* 4-15 in XHCI spec */
/* caplength and hci-version registers */
#define XHCI_SET_CAPLEN(x) ((x) & 0xFF)
@@ -406,7 +405,7 @@
* XHCI 4.19.3 USB2 RxDetect->Polling,
* USB3 Polling->U0
*/
- if (dev->hci.hci_usbver == 2)
+ if (dev->hci.hci_usbver <= 2)
port->portsc |=
XHCI_PS_PLS_SET(UPS_PORT_LS_POLL);
else
@@ -625,6 +624,7 @@
static void
pci_xhci_assert_interrupt(struct pci_xhci_softc *sc)
{
+ DPRINTF(("%s", __func__));
sc->rtsregs.intrreg.erdp |= XHCI_ERDP_LO_BUSY;
sc->rtsregs.intrreg.iman |= XHCI_IMAN_INTR_PEND;
@@ -661,6 +661,8 @@
devep = &dev->eps[epid];
pstreams = XHCI_EPCTX_0_MAXP_STREAMS_GET(ep_ctx->dwEpCtx0);
if (pstreams > 0) {
+ assert(pstreams <= 15);
+ pstreams = 1 << (pstreams + 1);
DPRINTF(("init_ep %d with pstreams %u", epid, pstreams));
assert(devep->ep_sctx_trbs == NULL);
@@ -689,6 +691,9 @@
devep->ep_xfer = malloc(sizeof(struct usb_data_xfer));
USB_DATA_XFER_INIT(devep->ep_xfer);
}
+ if (dev->dev_ue->ue_configure_ep)
+ (void)dev->dev_ue->ue_configure_ep(dev->dev_sc, epid, ep_ctx,
+ 1);
}
static void
@@ -726,6 +731,8 @@
free(devep->ep_xfer);
devep->ep_xfer = NULL;
}
+ if (dev->dev_ue->ue_configure_ep)
+ dev->dev_ue->ue_configure_ep(dev->dev_sc, epid, ep_ctx, 0);
memset(devep, 0, sizeof(struct pci_xhci_dev_ep));
}
@@ -1179,8 +1186,9 @@
uint32_t type;
epid = XHCI_TRB_3_EP_GET(trb->dwTrb3);
+ type = XHCI_TRB_3_TYPE_GET(trb->dwTrb3);
- DPRINTF(("pci_xhci: reset ep %u: slot %u", epid, slot));
+ DPRINTF(("pci_xhci: reset ep %u: slot %u: type %u", epid, slot, type));
cmderr = pci_xhci_validate_slot(slot);
if (cmderr != XHCI_TRB_ERROR_SUCCESS)
@@ -1189,8 +1197,6 @@
dev = XHCI_SLOTDEV_PTR(sc, slot);
assert(dev != NULL);
- type = XHCI_TRB_3_TYPE_GET(trb->dwTrb3);
-
if (type == XHCI_TRB_TYPE_STOP_EP &&
(trb->dwTrb3 & XHCI_TRB_3_SUSP_EP_BIT) != 0) {
/* XXX suspend endpoint for 10ms */
@@ -1203,8 +1209,6 @@
}
devep = &dev->eps[epid];
- if (devep->ep_xfer != NULL)
- USB_DATA_XFER_RESET(devep->ep_xfer);
dev_ctx = dev->dev_ctx;
assert(dev_ctx != NULL);
@@ -1229,6 +1233,9 @@
USB_DATA_XFER_UNLOCK(devep->ep_xfer);
}
+ if (devep->ep_xfer != NULL)
+ USB_DATA_XFER_RESET(devep->ep_xfer);
+
done:
return (cmderr);
}
@@ -1546,8 +1553,8 @@
evtrb.qwTrb0 = crcr;
evtrb.dwTrb2 |= XHCI_TRB_2_ERROR_SET(cmderr);
evtrb.dwTrb3 |= XHCI_TRB_3_SLOT_SET(slot);
- DPRINTF(("pci_xhci: command 0x%x result: 0x%x",
- type, cmderr));
+ DPRINTF(("pci_xhci: command 0x%x result: 0x%x", type,
+ cmderr));
pci_xhci_insert_event(sc, &evtrb, 1);
}
@@ -1601,25 +1608,19 @@
pci_xhci_xfer_complete(struct pci_xhci_softc *sc, struct usb_data_xfer *xfer,
uint32_t slot, uint32_t epid, int *do_intr)
{
- struct pci_xhci_dev_emu *dev;
- struct pci_xhci_dev_ep *devep;
- struct xhci_dev_ctx *dev_ctx;
- struct xhci_endp_ctx *ep_ctx;
+ struct xhci_dev_ctx *dev_ctx;
struct xhci_trb *trb;
struct xhci_trb evtrb;
uint32_t trbflags;
uint32_t edtla;
+ uint32_t remain = 0;
int i, err;
- dev = XHCI_SLOTDEV_PTR(sc, slot);
- devep = &dev->eps[epid];
dev_ctx = pci_xhci_get_dev_ctx(sc, slot);
if (dev_ctx == NULL) {
return XHCI_TRB_ERROR_PARAMETER;
}
- ep_ctx = &dev_ctx->ctx_ep[epid];
-
err = XHCI_TRB_ERROR_SUCCESS;
*do_intr = 0;
edtla = 0;
@@ -1639,17 +1640,24 @@
if (!xfer->data[i].processed) {
xfer->head = i;
+ err = XHCI_TRB_ERROR_INVALID;
break;
}
xfer->ndata--;
+ xfer->head = (xfer->head + 1) % USB_MAX_XFER_BLOCKS;
edtla += xfer->data[i].bdone;
trb->dwTrb3 = (trb->dwTrb3 & ~0x1) | (xfer->data[i].ccs);
- pci_xhci_update_ep_ring(sc, dev, devep, ep_ctx,
- xfer->data[i].streamid, xfer->data[i].trbnext,
- xfer->data[i].ccs);
+ remain += xfer->data[i].blen;
+
+ if (USB_DATA_GET_ERRCODE(&xfer->data[i]))
+ err = USB_TO_XHCI_ERR(
+ USB_DATA_GET_ERRCODE(&xfer->data[i]));
+
+ if (xfer->data[i].blen > 0)
+ err = XHCI_TRB_ERROR_SHORT_PKT;
/* Only interrupt if IOC or short packet */
if (!(trb->dwTrb3 & XHCI_TRB_3_IOC_BIT) &&
@@ -1661,7 +1669,7 @@
}
evtrb.dwTrb2 = XHCI_TRB_2_ERROR_SET(err) |
- XHCI_TRB_2_REM_SET(xfer->data[i].blen);
+ XHCI_TRB_2_REM_SET(remain);
evtrb.dwTrb3 = XHCI_TRB_3_TYPE_SET(XHCI_TRB_EVENT_TRANSFER) |
XHCI_TRB_3_SLOT_SET(slot) | XHCI_TRB_3_EP_SET(epid);
@@ -1681,6 +1689,7 @@
if (err != XHCI_TRB_ERROR_SUCCESS) {
break;
}
+ err = XHCI_TRB_ERROR_SUCCESS;
i = (i + 1) % USB_MAX_XFER_BLOCKS;
}
@@ -1746,7 +1755,6 @@
do_intr = 0;
xfer = devep->ep_xfer;
- USB_DATA_XFER_LOCK(xfer);
/* outstanding requests queued up */
if (dev->dev_ue->ue_data != NULL) {
@@ -1759,19 +1767,14 @@
} else {
err = pci_xhci_xfer_complete(sc, xfer, slot, epid,
&do_intr);
- if (err == XHCI_TRB_ERROR_SUCCESS && do_intr) {
- pci_xhci_assert_interrupt(sc);
+ if (err == XHCI_TRB_ERROR_SUCCESS) {
+ if (do_intr)
+ pci_xhci_assert_interrupt(sc);
+ USB_DATA_XFER_RESET(xfer);
}
-
-
- /* XXX should not do it if error? */
- USB_DATA_XFER_RESET(xfer);
}
}
- USB_DATA_XFER_UNLOCK(xfer);
-
-
return (err);
}
@@ -1785,6 +1788,7 @@
struct xhci_trb *setup_trb;
struct usb_data_xfer *xfer;
struct usb_data_xfer_block *xfer_block;
+ struct usb_data_xfer_block *prev_xfer_block = NULL;
uint64_t val;
uint32_t trbflags;
int do_intr, err;
@@ -1808,10 +1812,11 @@
pci_xhci_dump_trb(trb);
trbflags = trb->dwTrb3;
-
if (XHCI_TRB_3_TYPE_GET(trbflags) != XHCI_TRB_TYPE_LINK &&
+ XHCI_TRB_3_TYPE_GET(trbflags) !=
+ XHCI_TRB_TYPE_STATUS_STAGE &&
(trbflags & XHCI_TRB_3_CYCLE_BIT) !=
- (ccs & XHCI_TRB_3_CYCLE_BIT)) {
+ (ccs & XHCI_TRB_3_CYCLE_BIT)) {
DPRINTF(("Cycle-bit changed trbflags %x, ccs %x",
trbflags & XHCI_TRB_3_CYCLE_BIT, ccs));
break;
@@ -1823,6 +1828,7 @@
case XHCI_TRB_TYPE_LINK:
if (trb->dwTrb3 & XHCI_TRB_3_TC_BIT)
ccs ^= 0x1;
+ trb->dwTrb3 &= ~XHCI_TRB_3_IOC_BIT;
xfer_block = usb_data_xfer_append(xfer, NULL, 0,
(void *)addr, ccs);
@@ -1839,14 +1845,20 @@
setup_trb = trb;
val = trb->qwTrb0;
- if (!xfer->ureq)
+ if (xfer->ureq == NULL)
xfer->ureq = malloc(
sizeof(struct usb_device_request));
+ if (xfer->ureq == NULL) {
+ err = XHCI_TRB_ERROR_STALL;
+ goto errout;
+ }
+
memcpy(xfer->ureq, &val,
sizeof(struct usb_device_request));
xfer_block = usb_data_xfer_append(xfer, NULL, 0,
- (void *)addr, ccs);
+ (void *)addr, ccs);
+ xfer_block->status = USB_NO_DATA;
xfer_block->processed = 1;
break;
@@ -1865,24 +1877,42 @@
(void *)(trbflags & XHCI_TRB_3_IDT_BIT ?
&trb->qwTrb0 : XHCI_GADDR(sc, trb->qwTrb0)),
trb->dwTrb2 & 0x1FFFF, (void *)addr, ccs);
+ if (xfer_block == NULL) {
+ err = USB_ERR_STALLED;
+ break;
+ }
+ xfer_block->status = trbflags & XHCI_TRB_3_CHAIN_BIT ?
+ USB_NEXT_DATA :
+ USB_LAST_DATA;
+ prev_xfer_block = xfer_block;
break;
case XHCI_TRB_TYPE_STATUS_STAGE:
xfer_block = usb_data_xfer_append(xfer, NULL, 0,
(void *)addr, ccs);
+ setup_trb = NULL;
break;
case XHCI_TRB_TYPE_NOOP:
- xfer_block = usb_data_xfer_append(xfer, NULL, 0,
- (void *)addr, ccs);
+ if (!xfer_block) {
+ err = XHCI_TRB_ERROR_STALL;
+ goto errout;
+ }
xfer_block->processed = 1;
break;
case XHCI_TRB_TYPE_EVENT_DATA:
xfer_block = usb_data_xfer_append(xfer, NULL, 0,
(void *)addr, ccs);
- if ((epid > 1) && (trbflags & XHCI_TRB_3_IOC_BIT)) {
- xfer_block->processed = 1;
+ if (!xfer_block) {
+ err = XHCI_TRB_ERROR_TRB;
+ goto errout;
+ }
+ xfer_block->processed = 1;
+ if (prev_xfer_block != NULL &&
+ prev_xfer_block->status == USB_NEXT_DATA) {
+ prev_xfer_block->status = USB_LAST_DATA;
+ prev_xfer_block = NULL;
}
break;
@@ -1901,23 +1931,21 @@
if (xfer_block) {
xfer_block->trbnext = addr;
xfer_block->streamid = streamid;
+ pci_xhci_update_ep_ring(sc, dev, devep, ep_ctx,
+ streamid, addr, ccs);
}
+ if (trbflags & XHCI_TRB_3_BEI_BIT)
+ continue;
+
if (!setup_trb && !(trbflags & XHCI_TRB_3_CHAIN_BIT) &&
XHCI_TRB_3_TYPE_GET(trbflags) != XHCI_TRB_TYPE_LINK) {
break;
}
-
- /* handle current batch that requires interrupt on complete */
- if (trbflags & XHCI_TRB_3_IOC_BIT) {
- DPRINTF(("pci_xhci: trb IOC bit set"));
- if (epid == 1)
- do_retry = 1;
- break;
- }
}
- DPRINTF(("pci_xhci[%d]: xfer->ndata %u", __LINE__, xfer->ndata));
+ DPRINTF(
+ ("pci_xhci[%d]: xfer->ndata %u %d", __LINE__, xfer->ndata, epid));
if (xfer->ndata <= 0)
goto errout;
@@ -1952,13 +1980,16 @@
if (!do_retry)
USB_DATA_XFER_UNLOCK(xfer);
- if (do_intr)
+ if (do_intr) {
pci_xhci_assert_interrupt(sc);
+ do_intr = 0;
+ }
if (do_retry) {
USB_DATA_XFER_RESET(xfer);
DPRINTF(("pci_xhci[%d]: retry:continuing with next TRBs",
__LINE__));
+ do_retry = 0;
goto retry;
}
@@ -2013,8 +2044,10 @@
return;
/* handle pending transfers */
- if (devep->ep_xfer->ndata > 0) {
+ if (dev->dev_ue->ue_static && devep->ep_xfer->ndata > 0) {
+ USB_DATA_XFER_LOCK(devep->ep_xfer);
pci_xhci_try_usb_xfer(sc, dev, devep, ep_ctx, slot, epid);
+ USB_DATA_XFER_UNLOCK(devep->ep_xfer);
return;
}
@@ -2615,6 +2648,7 @@
port = XHCI_PORTREG_PTR(sc, portn);
dev = XHCI_DEVINST_PTR(sc, portn);
if (dev) {
+ dev->dev_ue->ue_reset(dev->dev_sc);
port->portsc &= ~(XHCI_PS_PLS_MASK | XHCI_PS_PR | XHCI_PS_PRC);
port->portsc |= XHCI_PS_PED |
XHCI_PS_SPEED_SET(dev->hci.hci_speed);
@@ -2649,7 +2683,7 @@
port->portsc = XHCI_PS_CCS | /* connected */
XHCI_PS_PP; /* port power */
- if (dev->hci.hci_usbver == 2) {
+ if (dev->hci.hci_usbver <= 2) {
port->portsc |= XHCI_PS_PLS_SET(UPS_PORT_LS_POLL) |
XHCI_PS_SPEED_SET(dev->hci.hci_speed);
} else {
@@ -2665,6 +2699,34 @@
}
}
+static void
+pci_xhci_deinit_port(struct pci_xhci_softc *sc, int portn)
+{
+ struct pci_xhci_portregs *port;
+ struct pci_xhci_dev_emu *dev;
+
+ port = XHCI_PORTREG_PTR(sc, portn);
+ dev = XHCI_DEVINST_PTR(sc, portn);
+ if (dev) {
+ port->portsc &= ~(XHCI_PS_CCS | /* connected */
+ XHCI_PS_PP); /* port power */
+
+ if (dev->hci.hci_usbver <= 2) {
+ port->portsc &= ~(XHCI_PS_PLS_SET(UPS_PORT_LS_POLL) |
+ XHCI_PS_SPEED_SET(dev->hci.hci_speed));
+ } else {
+ port->portsc &= ~(XHCI_PS_PLS_SET(UPS_PORT_LS_U0) |
+ XHCI_PS_PED | /* enabled */
+ XHCI_PS_SPEED_SET(dev->hci.hci_speed));
+ }
+
+ DPRINTF(("Deinit port %d 0x%x", portn, port->portsc));
+ } else {
+ port->portsc = XHCI_PS_PLS_SET(UPS_PORT_LS_RX_DET) | XHCI_PS_PP;
+ DPRINTF(("Deinit empty port %d 0x%x", portn, port->portsc));
+ }
+}
+
static int
pci_xhci_dev_intr(struct usb_hci *hci, int epctx)
{
@@ -2674,8 +2736,10 @@
struct pci_xhci_softc *sc;
struct pci_xhci_portregs *p;
struct xhci_endp_ctx *ep_ctx;
+ struct usb_data_xfer *xfer;
int error = 0;
int dir_in;
+ int do_intr;
int epid;
dir_in = epctx & 0x80;
@@ -2723,17 +2787,67 @@
DPRINTF(("xhci device interrupt on endpoint %d", epid));
- pci_xhci_device_doorbell(sc, hci->hci_port, epid, 0);
+ /*
+ * When the device is dynamic, we need to complete the previous TD then
+ * assert all interrupt from previous TD
+ */
+ if (!dev->dev_ue->ue_static) {
+ xfer = dev->eps[epid].ep_xfer;
+ error = pci_xhci_xfer_complete(sc, xfer, hci->hci_slot, epid,
+ &do_intr);
+ if (error == XHCI_TRB_ERROR_SUCCESS) {
+ if (do_intr)
+ pci_xhci_assert_interrupt(sc);
+ USB_DATA_XFER_RESET(xfer);
+ }
+ }
+ pci_xhci_device_doorbell(sc, hci->hci_slot, epid, 0);
done:
return (error);
}
static int
-pci_xhci_dev_event(struct usb_hci *hci, enum hci_usbev evid __unused,
+pci_xhci_dev_event(struct usb_hci *hci, enum hci_usbev evid,
void *param __unused)
{
+ struct xhci_trb evtrb;
+ struct pci_xhci_dev_emu *dev = hci->hci_sc;
+ struct pci_xhci_softc *xsc = dev->xsc;
+ struct pci_xhci_portregs *port;
+ int err;
+
+ port = XHCI_PORTREG_PTR(xsc, hci->hci_port);
DPRINTF(("xhci device event port %d", hci->hci_port));
+
+ switch (evid) {
+ case USBDEV_ATTACH:
+ pci_xhci_init_port(xsc, hci->hci_port);
+ port->portsc |= XHCI_PS_CSC;
+ pci_xhci_set_evtrb(&evtrb, hci->hci_port,
+ XHCI_TRB_ERROR_SUCCESS, XHCI_TRB_EVENT_PORT_STS_CHANGE);
+ if ((err = pci_xhci_insert_event(xsc, &evtrb, 1)) !=
+ XHCI_TRB_ERROR_SUCCESS)
+ return (err);
+ pci_xhci_assert_interrupt(xsc);
+ xsc->opregs.usbsts |= XHCI_STS_PCD;
+ return (0);
+ break;
+ case USBDEV_REMOVE:
+ pci_xhci_deinit_port(xsc, hci->hci_port);
+ port->portsc |= XHCI_PS_CSC;
+ pci_xhci_set_evtrb(&evtrb, hci->hci_port,
+ XHCI_TRB_ERROR_SUCCESS, XHCI_TRB_EVENT_PORT_STS_CHANGE);
+ if ((err = pci_xhci_insert_event(xsc, &evtrb, 1)) !=
+ XHCI_TRB_ERROR_SUCCESS)
+ return (err);
+ xsc->opregs.usbsts |= XHCI_STS_PCD;
+ pci_xhci_assert_interrupt(xsc);
+ return (0);
+ break;
+ default:
+ break;
+ }
return (0);
}
@@ -2755,7 +2869,7 @@
{
char node_name[16];
nvlist_t *slots_nvl, *slot_nvl;
- char *cp, *opt, *str, *tofree;
+ char *cp, *opt, *str, *tofree, *subopt;
int slot;
if (opts == NULL)
@@ -2775,7 +2889,16 @@
snprintf(node_name, sizeof(node_name), "%d", slot);
slot++;
slot_nvl = create_relative_config_node(slots_nvl, node_name);
- set_config_value_node(slot_nvl, "device", opt);
+ subopt = strsep(&opt, ".");
+ set_config_value_node(slot_nvl, "device", subopt);
+ subopt = strsep(&opt, ".");
+ if (subopt == NULL)
+ continue;
+ set_config_value_node(slot_nvl, "param1", subopt);
+ subopt = strsep(&opt, ".");
+ if (subopt == NULL)
+ continue;
+ set_config_value_node(slot_nvl, "param2", subopt);
/*
* NB: Given that we split on commas above, the legacy
@@ -2803,8 +2926,10 @@
usb3_port = sc->usb3_port_start;
usb2_port = sc->usb2_port_start;
- sc->devices = calloc(XHCI_MAX_DEVS, sizeof(struct pci_xhci_dev_emu *));
- sc->slots = calloc(XHCI_MAX_SLOTS, sizeof(struct pci_xhci_dev_emu *));
+ sc->devices = calloc(XHCI_MAX_DEVS + 1,
+ sizeof(struct pci_xhci_dev_emu *));
+ sc->slots = calloc(XHCI_MAX_SLOTS + 1,
+ sizeof(struct pci_xhci_dev_emu *));
ndevices = 0;
@@ -2863,9 +2988,10 @@
dev->hci.hci_intr = pci_xhci_dev_intr;
dev->hci.hci_event = pci_xhci_dev_event;
dev->hci.hci_speed = USB_SPEED_MAX;
+ dev->hci.hci_slot = slot;
dev->hci.hci_usbver = -1;
- devsc = ue->ue_probe(&dev->hci, nvl);
+ devsc = ue->ue_probe(&dev->hci, slot_nvl);
if (devsc == NULL) {
free(dev);
goto bad;
@@ -2875,7 +3001,7 @@
if (dev->hci.hci_usbver == -1)
dev->hci.hci_usbver = ue->ue_usbver;
- if (dev->hci.hci_usbver == 2) {
+ if (dev->hci.hci_usbver <= 2) {
if (usb2_port == sc->usb2_port_start +
XHCI_MAX_DEVS / 2) {
WPRINTF(("pci_xhci max number of USB 2 devices "
@@ -2913,7 +3039,8 @@
}
portsfinal:
- sc->portregs = calloc(XHCI_MAX_DEVS, sizeof(struct pci_xhci_portregs));
+ sc->portregs = calloc(XHCI_MAX_DEVS + 1,
+ sizeof(struct pci_xhci_portregs));
if (ndevices > 0) {
for (i = 1; i <= XHCI_MAX_DEVS; i++) {
@@ -2926,8 +3053,11 @@
bad:
for (i = 1; i <= XHCI_MAX_DEVS; i++) {
- if (XHCI_DEVINST_PTR(sc, i) != NULL)
+ if (XHCI_DEVINST_PTR(sc, i) != NULL) {
+ XHCI_DEVINST_PTR(sc, i)->dev_ue->ue_remove(
+ XHCI_DEVINST_PTR(sc, i)->dev_sc);
free(XHCI_DEVINST_PTR(sc, i)->dev_sc);
+ }
free(XHCI_DEVINST_PTR(sc, i));
}
@@ -2975,8 +3105,7 @@
XHCI_SET_HCCP1_NSS(1) | /* no 2nd-streams */
XHCI_SET_HCCP1_SPC(1) | /* short packet */
XHCI_SET_HCCP1_MAXPSA(XHCI_STREAMS_MAX);
- sc->hccparams2 = XHCI_SET_HCCP2_LEC(1) |
- XHCI_SET_HCCP2_U3C(1);
+ sc->hccparams2 = XHCI_SET_HCCP2_LEC(1) | XHCI_SET_HCCP2_U3C(1);
sc->dboff = XHCI_SET_DOORBELL(XHCI_CAPLEN + XHCI_PORTREGS_START +
XHCI_MAX_DEVS * sizeof(struct pci_xhci_portregs));
@@ -3275,6 +3404,7 @@
SNAPSHOT_VAR_OR_LEAVE(dev->hci.hci_address, meta, ret, done);
SNAPSHOT_VAR_OR_LEAVE(dev->hci.hci_port, meta, ret, done);
SNAPSHOT_VAR_OR_LEAVE(dev->hci.hci_speed, meta, ret, done);
+ SNAPSHOT_VAR_OR_LEAVE(dev->hci.hci_slot, meta, ret, done);
SNAPSHOT_VAR_OR_LEAVE(dev->hci.hci_usbver, meta, ret, done);
}
diff --git a/usr.sbin/bhyve/usb_emul.h b/usr.sbin/bhyve/usb_emul.h
--- a/usr.sbin/bhyve/usb_emul.h
+++ b/usr.sbin/bhyve/usb_emul.h
@@ -34,7 +34,7 @@
#include <sys/linker_set.h>
#include <pthread.h>
-#define USB_MAX_XFER_BLOCKS 8
+#define USB_MAX_XFER_BLOCKS 256
#define USB_XFER_OUT 0
#define USB_XFER_IN 1
@@ -44,15 +44,17 @@
struct usb_device_request;
struct usb_data_xfer;
struct vm_snapshot_meta;
+struct xhci_endp_ctx;
/* Device emulation handlers */
struct usb_devemu {
const char *ue_emu; /* name of device emulation */
int ue_usbver; /* usb version: 2 or 3 */
int ue_usbspeed; /* usb device speed */
+ bool ue_static; /* for static emulated device like mouse */
/* instance creation */
- void *(*ue_probe)(struct usb_hci *hci, nvlist_t *nvl);
+ void *(*ue_probe)(struct usb_hci *hci, const nvlist_t *nvl);
int (*ue_init)(void *sc);
/* handlers */
@@ -64,6 +66,8 @@
int (*ue_stop)(void *sc);
int (*ue_snapshot)(void *scarg, struct vm_snapshot_meta *meta);
int (*ue_cancel)(struct usb_data_xfer *xfer);
+ int (*ue_configure_ep)(void *sc, int epid, struct xhci_endp_ctx *epctx,
+ int configure);
};
#define USB_EMUL_SET(x) DATA_SET(usb_emu_set, x)
@@ -77,6 +81,15 @@
USBDEV_REMOVE,
};
+/*
+ * USB data status for TRB
+ */
+enum usb_xfer_data_status {
+ USB_NO_DATA,
+ USB_NEXT_DATA,
+ USB_LAST_DATA,
+};
+
/* usb controller, ie xhci, ehci */
struct usb_hci {
int (*hci_intr)(struct usb_hci *hci, int epctx);
@@ -88,7 +101,8 @@
int hci_address;
int hci_port;
int hci_speed;
- int hci_usbver;
+ int hci_slot;
+ int hci_usbver; /* Can be modified by the backend in the probe step */
};
/*
@@ -106,6 +120,7 @@
int ccs;
uint32_t streamid;
uint64_t trbnext; /* next TRB guest address */
+ enum usb_xfer_data_status status;
};
struct usb_data_xfer {
@@ -114,6 +129,7 @@
int ndata; /* # of data items */
int head;
int tail;
+ void *tr_softc;
pthread_mutex_t mtx;
};
diff --git a/usr.sbin/bhyve/usb_emul.c b/usr.sbin/bhyve/usb_emul.c
--- a/usr.sbin/bhyve/usb_emul.c
+++ b/usr.sbin/bhyve/usb_emul.c
@@ -69,6 +69,7 @@
xb->ccs = ccs;
xb->processed = 0;
xb->bdone = 0;
+ xb->status = USB_NO_DATA;
xfer->ndata++;
xfer->tail = (xfer->tail + 1) % USB_MAX_XFER_BLOCKS;
return (xb);
diff --git a/usr.sbin/bhyve/usb_mouse.c b/usr.sbin/bhyve/usb_mouse.c
--- a/usr.sbin/bhyve/usb_mouse.c
+++ b/usr.sbin/bhyve/usb_mouse.c
@@ -295,7 +295,7 @@
}
static void *
-umouse_probe(struct usb_hci *hci, nvlist_t *nvl __unused)
+umouse_probe(struct usb_hci *hci, const nvlist_t *nvl __unused)
{
struct umouse_softc *sc;
@@ -816,18 +816,19 @@
#endif
static struct usb_devemu ue_mouse = {
- .ue_emu = "tablet",
- .ue_usbver = 3,
- .ue_usbspeed = USB_SPEED_HIGH,
- .ue_probe = umouse_probe,
- .ue_init = umouse_init,
- .ue_request = umouse_request,
- .ue_data = umouse_data_handler,
- .ue_reset = umouse_reset,
- .ue_remove = umouse_remove,
- .ue_stop = umouse_stop,
+ .ue_emu = "tablet",
+ .ue_static = 1,
+ .ue_usbver = 3,
+ .ue_usbspeed = USB_SPEED_HIGH,
+ .ue_probe = umouse_probe,
+ .ue_init = umouse_init,
+ .ue_request = umouse_request,
+ .ue_data = umouse_data_handler,
+ .ue_reset = umouse_reset,
+ .ue_remove = umouse_remove,
+ .ue_stop = umouse_stop,
#ifdef BHYVE_SNAPSHOT
- .ue_snapshot = umouse_snapshot,
+ .ue_snapshot = umouse_snapshot,
#endif
};
USB_EMUL_SET(ue_mouse);
diff --git a/usr.sbin/bhyve/usb_passthru.c b/usr.sbin/bhyve/usb_passthru.c
new file mode 100644
--- /dev/null
+++ b/usr.sbin/bhyve/usb_passthru.c
@@ -0,0 +1,871 @@
+#include <sys/cdefs.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+
+#include <assert.h>
+#include <libusb.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "config.h"
+#include "debug.h"
+#include "pci_xhci.h"
+#include "usb_emul.h"
+
+static int usb_passthru_debug = 1;
+static bool libusb_is_init = false;
+#define DPRINTF(params) if (usb_passthru_debug) PRINTLN params
+#define WPRINTF(params) PRINTLN params
+
+#define OUT 0
+#define IN 1
+
+struct usb_passthru_libusb_xfer;
+
+struct usb_passthru_ep_types {
+ bool inout;
+ int type;
+};
+
+struct usb_passthru_softc {
+ struct usb_hci *hci; /* hci structure for issue xhci interrupt */
+ pthread_mutex_t mtx; /* mutex for locking */
+ unsigned long vid; /* product vid */
+ unsigned long pid; /* product pid */
+ struct libusb_device_handle *handle; /* libusb handler */
+ libusb_hotplug_callback_handle cb; /* callback handler */
+ struct usb_passthru_ep_types endpoint_types[32];
+ LIST_ENTRY(usb_passthru_softc) next;
+};
+
+struct usb_passthru_libusb_xfer {
+ struct libusb_transfer *lusb_xfer;
+ struct usb_data_xfer *usb_xfer;
+ struct usb_passthru_softc *sc;
+ uint8_t *buffer;
+ bool in;
+ int epid;
+ int size;
+};
+
+static LIST_HEAD(, usb_passthru_softc) devices = LIST_HEAD_INITIALIZER(
+ &devices);
+
+static int usb_passthru_remove(void *scarg);
+static pthread_t libusb_thread = NULL;
+static int event_thread_exit = false;
+
+static int
+libusb_error_to_usb_error(enum libusb_error error)
+{
+ switch (error) {
+ case LIBUSB_SUCCESS:
+ return (USB_ERR_NORMAL_COMPLETION);
+ case LIBUSB_ERROR_IO:
+ return (USB_ERR_IOERROR);
+ case LIBUSB_ERROR_INVALID_PARAM:
+ return (USB_ERR_INVAL);
+ case LIBUSB_ERROR_ACCESS:
+ return (USB_ERR_INVAL);
+ case LIBUSB_ERROR_NO_DEVICE:
+ return (USB_ERR_NOT_CONFIGURED);
+ case LIBUSB_ERROR_NOT_FOUND:
+ return (USB_ERR_STALLED);
+ case LIBUSB_ERROR_BUSY:
+ return (USB_ERR_IN_USE);
+ case LIBUSB_ERROR_TIMEOUT:
+ return (USB_ERR_TIMEOUT);
+ case LIBUSB_ERROR_OVERFLOW:
+ return (USB_ERR_BAD_BUFSIZE);
+ case LIBUSB_ERROR_PIPE:
+ return (USB_ERR_NO_PIPE);
+ case LIBUSB_ERROR_INTERRUPTED:
+ return (USB_ERR_INTERRUPTED);
+ case LIBUSB_ERROR_NO_MEM:
+ return (USB_ERR_NOMEM);
+ case LIBUSB_ERROR_NOT_SUPPORTED:
+ return (USB_ERR_INVAL);
+ case LIBUSB_ERROR_OTHER:
+ return (USB_ERR_INVAL);
+ }
+}
+
+static void *
+libusb_pull_thread(void *arg __unused)
+{
+ struct timeval tv;
+ int err;
+
+ DPRINTF(("start libusb pullthread"));
+ while (!event_thread_exit) {
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ err = libusb_handle_events_timeout(NULL, &tv);
+ if (err && err != LIBUSB_ERROR_TIMEOUT)
+ break;
+ }
+
+ DPRINTF(("stop libusb pullthread"));
+ return (NULL);
+}
+
+static void
+usb_passthru_calculate_xfer_ptr(struct usb_data_xfer *xfer, int *head,
+ int *tail, int *size)
+{
+ int cur, i;
+ int first = -1, length = 0;
+ struct usb_data_xfer_block *blk;
+
+ for (i = 0, cur = xfer->head; i < xfer->ndata;
+ ++i, cur = (cur + 1) % USB_MAX_XFER_BLOCKS) {
+ blk = &xfer->data[cur];
+ DPRINTF(("%s processed: %d len: %d, status: %d", __func__,
+ blk->processed, blk->blen, blk->status));
+ if (blk->processed)
+ continue;
+ switch (blk->status) {
+ case USB_NEXT_DATA:
+ case USB_LAST_DATA:
+ first = first == -1 ? cur : first;
+ length += blk->blen;
+ break;
+ case USB_NO_DATA:
+ blk->processed = 1;
+ continue;
+ }
+
+ if (blk->status == USB_LAST_DATA)
+ break;
+ }
+ *head = first;
+ *tail = i;
+ *size = length;
+}
+
+static void
+usb_passthru_data_calculate_num_isos(struct usb_data_xfer *xfer, int *head,
+ int *nframe, int *len)
+{
+ int i, cur, first = -1, nf = 0, length = 0;
+
+ for (i = 0, cur = xfer->head; i < xfer->ndata;
+ i = (i + 1) % USB_MAX_XFER_BLOCKS) {
+ if (xfer->data[i].processed)
+ continue;
+ switch (xfer->data[i].status) {
+ case USB_LAST_DATA:
+ ++nf;
+ if (first == -1)
+ first = i;
+ /* Fallthrough */
+ case USB_NEXT_DATA:
+ length += xfer->data[i].blen;
+ break;
+ default:
+ break;
+ }
+ }
+
+ *nframe = nf;
+ *head = first;
+ *len = length;
+}
+
+static struct usb_passthru_libusb_xfer *
+usb_passthru_xfer_alloc(struct usb_passthru_softc *sc, int in,
+ struct usb_data_xfer *usb_xfer, int size, int ep, int iso)
+{
+ struct usb_passthru_libusb_xfer *xfer = calloc(1,
+ sizeof(struct usb_passthru_libusb_xfer));
+ xfer->lusb_xfer = libusb_alloc_transfer(iso);
+ xfer->usb_xfer = usb_xfer;
+ xfer->buffer = calloc(1, size);
+ xfer->size = size;
+ xfer->in = in;
+ xfer->sc = sc;
+ xfer->epid = ep;
+ return (xfer);
+}
+
+static void
+usb_passthru_xfer_free(struct usb_passthru_libusb_xfer *xfer)
+{
+ free(xfer);
+}
+
+static int
+usb_passthru_guest_attach_device(struct usb_passthru_softc *sc)
+{
+ struct libusb_config_descriptor *desc;
+ struct libusb_device *dev;
+ struct libusb_device_handle *handle;
+ int intf, res;
+
+ handle = sc->handle;
+ dev = libusb_get_device(handle);
+ res = libusb_get_active_config_descriptor(dev, &desc);
+ if (res != LIBUSB_SUCCESS)
+ goto done;
+
+ for (intf = 0; intf < desc->bNumInterfaces; ++intf) {
+ res = libusb_detach_kernel_driver(handle, intf);
+ if (res != LIBUSB_SUCCESS)
+ break;
+ res = libusb_claim_interface(handle, intf);
+ if (res != LIBUSB_SUCCESS)
+ break;
+ }
+
+done:
+ libusb_free_config_descriptor(desc);
+ return (res);
+}
+
+static int
+usb_passthru_guest_detach_device_on_host(struct usb_passthru_softc *sc)
+{
+ struct libusb_config_descriptor *desc;
+ struct libusb_device *dev;
+ struct libusb_device_handle *handle;
+ int intf, res;
+
+ handle = sc->handle;
+ if (handle == NULL)
+ return (libusb_error_to_usb_error(LIBUSB_SUCCESS));
+ dev = libusb_get_device(handle);
+
+ res = libusb_get_active_config_descriptor(dev, &desc);
+ if (res != LIBUSB_SUCCESS)
+ return (libusb_error_to_usb_error(res));
+
+ for (intf = 0; intf < desc->bNumInterfaces; ++intf) {
+ res = libusb_release_interface(handle, intf);
+ if (res != LIBUSB_SUCCESS)
+ break;
+ res = libusb_attach_kernel_driver(handle, intf);
+ if (res != LIBUSB_SUCCESS)
+ break;
+ }
+
+ libusb_free_config_descriptor(desc);
+
+ return (libusb_error_to_usb_error(res));
+}
+
+static int
+usb_passthru_guest_detach_device(struct usb_passthru_softc *sc __unused)
+{
+ return (USB_ERR_NORMAL_COMPLETION);
+}
+
+static int
+usb_passthru_hotplug_callback(struct libusb_context *ctx __unused,
+ struct libusb_device *dev, libusb_hotplug_event event, void *user_data)
+{
+ struct usb_passthru_softc *sc = user_data;
+ int err = 0;
+
+ DPRINTF(("%s: enter hotplug handler event: %d", __func__, event));
+
+ pthread_mutex_lock(&sc->mtx);
+
+ if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) {
+ assert(sc->hci->hci_event(sc->hci, USBDEV_REMOVE, sc) == 0);
+ libusb_close(sc->handle);
+ sc->handle = NULL;
+ } else if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) {
+ if (sc->handle != NULL)
+ goto done;
+ err = libusb_open(dev, &sc->handle);
+ if (err)
+ goto done;
+ if ((err = usb_passthru_guest_attach_device(sc)) !=
+ LIBUSB_SUCCESS)
+ goto done;
+ assert(sc->hci->hci_event(sc->hci, USBDEV_ATTACH, sc) == 0);
+ }
+
+done:
+ pthread_mutex_unlock(&sc->mtx);
+
+ return (err);
+}
+
+static void
+usb_passthru_data_callback(struct libusb_transfer *lusb_xfer)
+{
+ struct usb_passthru_libusb_xfer *up_xfer = lusb_xfer->user_data;
+ struct usb_data_xfer *uxfer = up_xfer->usb_xfer;
+ struct usb_passthru_softc *sc = up_xfer->sc;
+ struct usb_hci *hci = sc->hci;
+ int act_len = lusb_xfer->actual_length;
+ int head, tail, size, cur_len, cur, offset, nframe;
+ char *buffer;
+ int cur_iso = 0, i;
+
+ /*
+ * uxfer == NULL means the guest is cancelled by guest
+ * like machine shutdown.
+ */
+ if (lusb_xfer->status == LIBUSB_TRANSFER_CANCELLED && uxfer == NULL) {
+ usb_passthru_xfer_free(up_xfer);
+ return;
+ }
+
+ USB_DATA_XFER_LOCK(uxfer);
+
+ if (lusb_xfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS)
+ usb_passthru_data_calculate_num_isos(uxfer, &head, &nframe,
+ &size);
+ else
+ usb_passthru_calculate_xfer_ptr(uxfer, &head, &tail, &size);
+
+ if (lusb_xfer->status == LIBUSB_TRANSFER_STALL ||
+ lusb_xfer->status == LIBUSB_TRANSFER_NO_DEVICE ||
+ lusb_xfer->status == LIBUSB_TRANSFER_CANCELLED) {
+ USB_DATA_SET_ERRCODE(&uxfer->data[head], USB_STALL);
+ goto done;
+ }
+
+ DPRINTF(("%s: act_len: %d blen:%d in:%d epid:%d status: %d", __func__,
+ act_len, size, up_xfer->in, up_xfer->epid, lusb_xfer->status));
+
+ if (up_xfer->in) {
+ if (lusb_xfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS)
+ act_len = lusb_xfer->iso_packet_desc[0].actual_length;
+
+ if (act_len > size)
+ act_len = size;
+
+ for (cur = head, offset = 0, i = 0; i < uxfer->ndata;
+ cur = (cur + 1) % USB_MAX_XFER_BLOCKS, ++i) {
+ cur_len = uxfer->data[cur].blen;
+ if (cur_len > act_len) {
+ cur_len = act_len;
+ USB_DATA_SET_ERRCODE(&uxfer->data[cur],
+ USB_SHORT);
+ }
+ if (lusb_xfer->type != LIBUSB_TRANSFER_TYPE_ISOCHRONOUS)
+ buffer = &lusb_xfer->buffer[offset];
+ else
+ buffer = libusb_get_iso_packet_buffer_simple(
+ lusb_xfer, cur_iso) +
+ offset;
+ if (cur_len)
+ memcpy(uxfer->data[cur].buf, buffer, cur_len);
+ act_len -= cur_len;
+ offset += cur_len;
+ if (act_len <= 0 &&
+ lusb_xfer->type ==
+ LIBUSB_TRANSFER_TYPE_ISOCHRONOUS) {
+ ++cur_iso;
+ act_len = lusb_xfer->iso_packet_desc[cur_iso]
+ .actual_length;
+ offset = 0;
+ }
+ uxfer->data[cur].blen -= cur_len;
+ uxfer->data[cur].bdone = cur_len;
+ uxfer->data[cur].processed = 1;
+ }
+ } else {
+ for (cur = head; uxfer->data[cur].buf && act_len;
+ cur = (cur + 1) % USB_MAX_XFER_BLOCKS) {
+ uxfer->data[cur].processed = 1;
+ cur_len = act_len;
+ if (cur_len > uxfer->data[cur].blen)
+ cur_len = uxfer->data[cur].blen;
+ act_len -= cur_len;
+ uxfer->data[cur].blen -= cur_len;
+ uxfer->data[cur].bdone += cur_len;
+ }
+
+ USB_DATA_SET_ERRCODE(&uxfer->data[head],
+ lusb_xfer->status == LIBUSB_TRANSFER_COMPLETED ?
+ USB_ERR_NORMAL_COMPLETION :
+ USB_ERR_IOERROR);
+ }
+
+done:
+ uxfer->tr_softc = NULL;
+ USB_DATA_XFER_UNLOCK(up_xfer->usb_xfer);
+ hci->hci_intr(hci, up_xfer->epid);
+ usb_passthru_xfer_free(up_xfer);
+}
+
+static void
+usb_passthru_exit(void)
+{
+ struct usb_passthru_softc *sc, *tmp;
+
+ LIST_FOREACH_SAFE(sc, &devices, next, tmp) {
+ usb_passthru_remove(sc);
+ free(sc);
+ }
+
+ event_thread_exit = true;
+}
+
+static void *
+usb_passthru_probe(struct usb_hci *hci, const nvlist_t *nvl)
+{
+ struct usb_passthru_softc *sc;
+ struct libusb_device_descriptor dev_desc;
+ struct libusb_device *dev;
+ struct libusb_init_option opts[] = {
+ { .option = LIBUSB_OPTION_CAPSICUMIZE }
+ };
+ const char *param;
+ char *endp;
+ int error;
+
+ if (!libusb_is_init) {
+ libusb_is_init = true;
+ error = libusb_init_context(NULL, opts,
+ sizeof(opts) / sizeof(struct libusb_init_option));
+ if (error) {
+ EPRINTLN("failed to init libusb: %s",
+ libusb_error_name(error));
+ return (NULL);
+ }
+ }
+ sc = calloc(1, sizeof(struct usb_passthru_softc));
+
+ param = get_config_value_node(nvl, "param1");
+ if (param == NULL) {
+ free(sc);
+ return (NULL);
+ }
+ sc->vid = strtoul(param, &endp, 16);
+ if (*endp != '\0') {
+ free(sc);
+ return (NULL);
+ }
+ param = get_config_value_node(nvl, "param2");
+ if (param == NULL) {
+ free(sc);
+ return (NULL);
+ }
+ sc->pid = strtoul(param, &endp, 16);
+ if (*endp != '\0') {
+ free(sc);
+ return (NULL);
+ }
+
+ sc->hci = hci;
+ sc->handle = libusb_open_device_with_vid_pid(NULL, sc->vid, sc->pid);
+ if (sc->handle == NULL) {
+ EPRINTLN("failed to open usb device with vid: %0lx pid: %0lx",
+ sc->vid, sc->pid);
+ free(sc);
+ return (NULL);
+ }
+ dev = libusb_get_device(sc->handle);
+ if (libusb_get_device_descriptor(dev, &dev_desc) != LIBUSB_SUCCESS) {
+ EPRINTLN("failed to get usb device descriptor with %0lx %0lx",
+ sc->vid, sc->pid);
+ free(sc);
+ return (NULL);
+ }
+ if (dev_desc.bcdUSB >= 0x0300) {
+ hci->hci_usbver = 3;
+ } else {
+ hci->hci_usbver = 2;
+ }
+ memset(sc->endpoint_types, -1, sizeof(sc->endpoint_types));
+
+ return (sc);
+}
+
+static int
+usb_passthru_init(void *scarg)
+{
+ struct usb_passthru_softc *sc = (struct usb_passthru_softc *)scarg;
+ struct usb_hci *hci;
+ enum libusb_speed speed;
+ int error;
+
+ hci = sc->hci;
+ pthread_mutex_init(&sc->mtx, NULL);
+
+ speed = libusb_get_device_speed(libusb_get_device(sc->handle));
+ switch (speed) {
+ case LIBUSB_SPEED_LOW:
+ hci->hci_speed = 2;
+ break;
+ case LIBUSB_SPEED_FULL:
+ hci->hci_speed = 1;
+ break;
+ case LIBUSB_SPEED_HIGH:
+ hci->hci_speed = 3;
+ break;
+ default:
+ break;
+ }
+
+ if ((error = usb_passthru_guest_attach_device(sc)) != LIBUSB_SUCCESS)
+ goto failed;
+
+ error = libusb_hotplug_register_callback(NULL,
+ LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED |
+ LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT,
+ 0, sc->vid, sc->pid, LIBUSB_HOTPLUG_MATCH_ANY,
+ usb_passthru_hotplug_callback, (void *)sc, &sc->cb);
+ if (error != LIBUSB_SUCCESS) {
+ EPRINTLN("failed to create callback for usb device %s",
+ libusb_error_name(error));
+ goto failed;
+ }
+
+ if (libusb_thread == NULL) {
+ error = pthread_create(&libusb_thread, NULL, libusb_pull_thread,
+ NULL);
+ if (error)
+ goto failed;
+ }
+
+ LIST_INSERT_HEAD(&devices, sc, next);
+ atexit(usb_passthru_exit);
+
+ return (error);
+
+failed:
+ usb_passthru_remove(&sc);
+ pthread_mutex_destroy(&sc->mtx);
+ free(sc);
+ return (1);
+}
+
+#define UREQ(x,y) ((x) | ((y) << 8))
+
+static int
+usb_passthru_request(void *scarg, struct usb_data_xfer *xfer)
+{
+ struct usb_passthru_softc *sc = scarg;
+ struct usb_data_xfer_block *data;
+ int i, cur, err, value, index, len;
+
+ data = NULL;
+ cur = xfer->head;
+ err = USB_ERR_NORMAL_COMPLETION;
+
+ for (i = 0; i < xfer->ndata; i++) {
+ xfer->data[cur].bdone = 0;
+ if (data == NULL && USB_DATA_OK(xfer, i)) {
+ data = &xfer->data[cur];
+ }
+ xfer->data[cur].processed = 1;
+ cur = (cur + 1) % USB_MAX_XFER_BLOCKS;
+ }
+
+ if (!xfer->ureq) {
+ DPRINTF(("usb_passthru_request not found: port %d",
+ sc->hci->hci_port));
+ goto done;
+ }
+
+ value = UGETW(xfer->ureq->wValue);
+ index = UGETW(xfer->ureq->wIndex);
+ len = UGETW(xfer->ureq->wLength);
+ if (data == NULL && len != 0) {
+ err = LIBUSB_ERROR_NO_DEVICE;
+ goto done;
+ }
+
+ pthread_mutex_lock(&sc->mtx);
+
+ if (sc->handle == NULL) {
+ err = LIBUSB_ERROR_INVALID_PARAM;
+ goto done_locked;
+ }
+
+ switch (UREQ(xfer->ureq->bRequest, xfer->ureq->bmRequestType)) {
+ case UREQ(UR_SET_ADDRESS, UT_WRITE_DEVICE):
+ err = 0;
+ goto done_locked;
+ case UREQ(UR_SET_CONFIG, UT_WRITE_DEVICE):
+ err = usb_passthru_guest_detach_device_on_host(sc);
+ if (err)
+ goto done_locked;
+ err = libusb_set_configuration(sc->handle, value & 0xff);
+ if (err)
+ goto done_locked;
+ err = usb_passthru_guest_attach_device(sc);
+ if (err)
+ goto done_locked;
+ goto done_locked;
+ case UREQ(UR_SET_INTERFACE, UT_WRITE_INTERFACE):
+ err = libusb_set_interface_alt_setting(sc->handle, index,
+ value);
+ goto done_locked;
+ case UREQ(UR_CLEAR_FEATURE, UT_WRITE_ENDPOINT):
+ err = libusb_clear_halt(sc->handle, index);
+ goto done_locked;
+ }
+
+ if (data) {
+ data->blen = len;
+ data->bdone = 0;
+ }
+ DPRINTF((
+ "usb_passthru_request: bRequest: %x bmRequestType: %x wValue: %x wIndex: %d wLength: %x",
+ xfer->ureq->bRequest, xfer->ureq->bmRequestType, value, index,
+ len));
+
+ err = libusb_control_transfer(sc->handle, xfer->ureq->bmRequestType,
+ xfer->ureq->bRequest, UGETW(xfer->ureq->wValue),
+ UGETW(xfer->ureq->wIndex), data ? data->buf : NULL, len, 1000);
+
+ if (err < 0) {
+ if (err == LIBUSB_ERROR_INTERRUPTED ||
+ err == LIBUSB_ERROR_PIPE) {
+ if (data)
+ USB_DATA_SET_ERRCODE(data, USB_STALL);
+ err = LIBUSB_ERROR_NOT_FOUND;
+ } else {
+ if (data)
+ USB_DATA_SET_ERRCODE(data, USB_ERR);
+ err = LIBUSB_ERROR_NOT_FOUND;
+ }
+ goto done_locked;
+ }
+ if (data) {
+ data->blen -= err;
+ data->bdone = err;
+ }
+ err = 0;
+
+done_locked:
+ pthread_mutex_unlock(&sc->mtx);
+done:
+ err = libusb_error_to_usb_error(err);
+
+ if (xfer->ureq && (xfer->ureq->bmRequestType & UT_WRITE) &&
+ (err == USB_ERR_NORMAL_COMPLETION) && (data != NULL))
+ data->blen = 0;
+
+ DPRINTF(("usb_passthru request error code %d (0=ok), blen %u txlen %u",
+ err, (data ? data->blen : 0), (data ? data->bdone : 0)));
+
+ return (err);
+}
+
+static void
+usb_passthru_data_fill_xfer_iso(struct usb_data_xfer *xfer,
+ struct libusb_transfer *lusb_xfer)
+{
+ int i, cur, idx;
+
+ for (i = 0, cur = xfer->head, idx = 0; i < xfer->ndata;
+ i = (i + 1) % USB_MAX_XFER_BLOCKS) {
+ if (xfer->data[cur].status == USB_LAST_DATA) {
+ lusb_xfer->iso_packet_desc[idx].length +=
+ xfer->data[cur].blen;
+ } else if (xfer->data[cur].status == USB_NEXT_DATA) {
+ lusb_xfer->iso_packet_desc[idx].length +=
+ xfer->data[cur].blen;
+ continue;
+ } else {
+ continue;
+ }
+ ++idx;
+ }
+}
+
+static int
+usb_passthru_data_handler(void *scarg, struct usb_data_xfer *xfer, int dir,
+ int epctx)
+{
+ struct usb_passthru_softc *sc = scarg;
+ struct usb_passthru_libusb_xfer *up_xfer;
+ int err, cur, len, ep, head, tail, offset, ep_type, nframe;
+
+ ep_type = sc->endpoint_types[epctx << 1 | dir].type;
+ dir = sc->endpoint_types[epctx << 1 | dir].inout;
+ assert(ep_type != -1);
+
+ err = USB_ERR_NORMAL_COMPLETION;
+ ep = (dir ? LIBUSB_ENDPOINT_IN : 0) | epctx;
+
+ if (ep_type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS)
+ usb_passthru_data_calculate_num_isos(xfer, &head, &nframe,
+ &len);
+ else
+ usb_passthru_calculate_xfer_ptr(xfer, &head, &tail, &len);
+
+ if (len == 0)
+ goto done;
+
+ DPRINTF((
+ "usb_passthru handle data - DIR=%s|EP=%d, blen %d, transfer_type: %d, ndata: %d",
+ dir ? "IN" : "OUT", epctx, len,
+ sc->endpoint_types[epctx << 1 | dir].type, xfer->ndata));
+
+ pthread_mutex_lock(&sc->mtx);
+ if (sc->handle == NULL || xfer->tr_softc)
+ goto done_locked;
+ pthread_mutex_unlock(&sc->mtx);
+
+ up_xfer = usb_passthru_xfer_alloc(sc, dir, xfer, len, ep,
+ ep_type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS ? nframe : 0);
+
+ if (ep_type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS)
+ usb_passthru_data_fill_xfer_iso(xfer, up_xfer->lusb_xfer);
+
+ if (!dir) {
+ for (cur = head, offset = 0; offset < len;
+ cur = (cur + 1) % USB_MAX_XFER_BLOCKS) {
+ memcpy(&up_xfer->buffer[offset], xfer->data[cur].buf,
+ xfer->data[cur].blen);
+ offset += xfer->data[cur].blen;
+ }
+ }
+
+ pthread_mutex_lock(&sc->mtx);
+ switch (ep_type) {
+ case LIBUSB_TRANSFER_TYPE_BULK:
+ libusb_fill_bulk_transfer(up_xfer->lusb_xfer, sc->handle, ep,
+ up_xfer->buffer, len, usb_passthru_data_callback, up_xfer,
+ 0);
+ break;
+ case LIBUSB_TRANSFER_TYPE_INTERRUPT:
+ libusb_fill_interrupt_transfer(up_xfer->lusb_xfer, sc->handle,
+ ep, up_xfer->buffer, len, usb_passthru_data_callback,
+ up_xfer, 0);
+ break;
+ case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
+ libusb_fill_iso_transfer(up_xfer->lusb_xfer, sc->handle, ep,
+ up_xfer->buffer, len, nframe, usb_passthru_data_callback,
+ up_xfer, 0);
+ break;
+ }
+ up_xfer->lusb_xfer->flags = LIBUSB_TRANSFER_FREE_TRANSFER |
+ LIBUSB_TRANSFER_FREE_BUFFER;
+ err = libusb_submit_transfer(up_xfer->lusb_xfer);
+ if (err)
+ goto done_locked;
+ xfer->tr_softc = up_xfer;
+done_locked:
+ pthread_mutex_unlock(&sc->mtx);
+done:
+ return (libusb_error_to_usb_error(err));
+}
+
+static int
+usb_passthru_reset(void *scarg __unused)
+{
+ struct usb_passthru_softc *sc = scarg;
+ int err = 0;
+
+ DPRINTF(("%s", __FUNCTION__));
+
+ pthread_mutex_lock(&sc->mtx);
+
+ if (sc->handle) {
+ err = libusb_reset_device(sc->handle);
+ if (err != LIBUSB_SUCCESS)
+ goto done;
+ err = usb_passthru_guest_attach_device(sc);
+ }
+done:
+ pthread_mutex_unlock(&sc->mtx);
+ return (libusb_error_to_usb_error(err));
+}
+
+static int
+usb_passthru_remove(void *scarg)
+{
+ struct usb_passthru_softc *sc = scarg;
+ int err;
+ if (sc == NULL)
+ return (USB_ERR_NORMAL_COMPLETION);
+
+ LIST_REMOVE(sc, next);
+ pthread_mutex_lock(&sc->mtx);
+ err = usb_passthru_guest_detach_device(sc);
+ if (err)
+ goto done;
+ libusb_hotplug_deregister_callback(NULL, sc->cb);
+ if ((err = usb_passthru_guest_detach_device_on_host(sc)) !=
+ LIBUSB_SUCCESS) {
+ goto done;
+ }
+ libusb_close(sc->handle);
+
+done:
+ pthread_mutex_unlock(&sc->mtx);
+
+ return (err);
+}
+
+static int
+usb_passthru_stop(void *scarg __unused)
+{
+ return (0);
+}
+
+static int
+usb_passthru_configure_ep(void *scarg, int epid, struct xhci_endp_ctx *ctx,
+ int configure)
+{
+ struct usb_passthru_softc *sc = scarg;
+ int type;
+
+ if (configure) {
+ type = XHCI_EPCTX_1_EPTYPE_GET(ctx->dwEpCtx1);
+ sc->endpoint_types[epid].inout = type >= 5;
+ type &= 3;
+ sc->endpoint_types[epid].type = type;
+ } else {
+ sc->endpoint_types[epid].inout = sc->endpoint_types[epid].type =
+ -1;
+ }
+
+ return (0);
+}
+
+static int
+usb_passthru_cancel(struct usb_data_xfer *xfer)
+{
+ struct usb_passthru_libusb_xfer *up_xfer;
+ int err;
+
+ up_xfer = xfer->tr_softc;
+ if (up_xfer == NULL) {
+ return (0);
+ }
+ up_xfer->usb_xfer = NULL;
+
+ DPRINTF(("%s", __FUNCTION__));
+
+ err = libusb_cancel_transfer(up_xfer->lusb_xfer);
+ xfer->tr_softc = NULL;
+
+ return (libusb_error_to_usb_error(err));
+}
+
+static struct usb_devemu ue_passthru = {
+ .ue_emu = "passthru",
+ .ue_static = 0,
+ .ue_usbver = 3,
+ .ue_usbspeed = USB_SPEED_HIGH,
+ .ue_probe = usb_passthru_probe,
+ .ue_init = usb_passthru_init,
+ .ue_request = usb_passthru_request,
+ .ue_data = usb_passthru_data_handler,
+ .ue_reset = usb_passthru_reset,
+ .ue_remove = usb_passthru_remove,
+ .ue_stop = usb_passthru_stop,
+ .ue_cancel = usb_passthru_cancel,
+ .ue_configure_ep = usb_passthru_configure_ep,
+#ifdef BHYVE_SNAPSHOT
+ .ue_snapshot = usb_passthru_snapshot,
+#endif
+};
+
+USB_EMUL_SET(ue_passthru);

File Metadata

Mime Type
text/plain
Expires
Sat, Dec 6, 2:46 AM (15 h, 43 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
26707054
Default Alt Text
D52166.diff (42 KB)

Event Timeline