Page MenuHomeFreeBSD

D52166.id161041.diff
No OneTemporary

D52166.id161041.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
@@ -406,7 +406,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 +625,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;
@@ -1163,8 +1164,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)
@@ -1173,8 +1175,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 */
@@ -1187,8 +1187,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);
@@ -1212,6 +1210,9 @@
goto done;
}
+ if (devep->ep_xfer != NULL)
+ USB_DATA_XFER_RESET(devep->ep_xfer);
+
done:
return (cmderr);
}
@@ -1622,10 +1623,12 @@
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);
@@ -1633,6 +1636,12 @@
pci_xhci_update_ep_ring(sc, dev, devep, ep_ctx,
xfer->data[i].streamid, xfer->data[i].trbnext,
xfer->data[i].ccs);
+ if (xfer->data[i].blen > 0)
+ err = XHCI_TRB_ERROR_SHORT_PKT;
+ if (xfer->data[i].status == USB_NEXT_DATA) {
+ i = (i + 1) % USB_MAX_XFER_BLOCKS;
+ continue;
+ }
/* Only interrupt if IOC or short packet */
if (!(trb->dwTrb3 & XHCI_TRB_3_IOC_BIT) &&
@@ -1742,13 +1751,11 @@
} 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);
}
}
@@ -1768,6 +1775,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;
@@ -1810,6 +1818,8 @@
xfer_block = usb_data_xfer_append(xfer, NULL, 0,
(void *)addr, ccs);
xfer_block->processed = 1;
+ if (trb->dwTrb3 & XHCI_TRB_3_TC_BIT)
+ xfer_block->ccs = ccs;
break;
case XHCI_TRB_TYPE_SETUP_STAGE:
@@ -1829,8 +1839,8 @@
sizeof(struct usb_device_request));
xfer_block = usb_data_xfer_append(xfer, NULL, 0,
- (void *)addr, ccs);
- xfer_block->processed = 1;
+ (void *)addr, ccs);
+ xfer_block->status = USB_NO_DATA;
break;
case XHCI_TRB_TYPE_NORMAL:
@@ -1848,6 +1858,14 @@
(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:
@@ -1867,6 +1885,11 @@
if ((epid > 1) && (trbflags & XHCI_TRB_3_IOC_BIT)) {
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;
default:
@@ -1886,16 +1909,21 @@
xfer_block->streamid = streamid;
}
+ 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;
}
+ if (XHCI_TRB_3_TYPE_GET(trbflags) == XHCI_TRB_TYPE_EVENT_DATA)
+ continue;
+
/* 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;
+ do_retry = 1;
break;
}
}
@@ -2598,6 +2626,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);
@@ -2632,7 +2661,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 {
@@ -2648,6 +2677,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)
{
@@ -2706,7 +2763,7 @@
DPRINTF(("xhci device interrupt on endpoint %d", epid));
- pci_xhci_device_doorbell(sc, hci->hci_port, epid, 0);
+ pci_xhci_device_doorbell(sc, hci->hci_slot, epid, 0);
done:
return (error);
@@ -2716,7 +2773,34 @@
pci_xhci_dev_event(struct usb_hci *hci, enum hci_usbev evid __unused,
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;
+
+ 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);
+ return (pci_xhci_insert_event(xsc, &evtrb, 1) !=
+ XHCI_TRB_ERROR_SUCCESS);
+ 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);
+ return (pci_xhci_insert_event(xsc, &evtrb, 1) !=
+ XHCI_TRB_ERROR_SUCCESS);
+ break;
+ default:
+ break;
+ }
return (0);
}
@@ -2738,7 +2822,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)
@@ -2758,7 +2842,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
@@ -2846,9 +2939,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;
@@ -2858,7 +2952,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 "
@@ -3258,6 +3352,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
@@ -52,7 +52,7 @@
int ue_usbspeed; /* usb device speed */
/* 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 */
@@ -77,6 +77,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 +97,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 +116,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 +125,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;
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,854 @@
+#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 <stdlib.h>
+#include <string.h>
+
+#include "debug.h"
+
+#include "config.h"
+#include "usb_emul.h"
+
+static int upassthru_debug = 0;
+static bool libusb_is_init = false;
+#define DPRINTF(params) if (upassthru_debug) PRINTLN params
+#define WPRINTF(params) PRINTLN params
+
+#define OUT 0
+#define IN 1
+
+struct upassthru_libusb_xfer;
+
+struct upassthru_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 */
+ int *active_altsettings; /* alternative settings for record total number
+ of interfaces */
+ int num_interfaces; /* number of interfaces */
+ uint8_t *endpoint_types[2]; /* type of endpoints[in, out] */
+ uint8_t ep_cnts[2]; /* number of endpoints for each type[in,out] */
+ LIST_ENTRY(upassthru_softc) next;
+};
+
+struct upassthru_libusb_xfer {
+ struct libusb_transfer *lusb_xfer;
+ struct usb_data_xfer *usb_xfer;
+ struct upassthru_softc *sc;
+ uint8_t *buffer;
+ bool in;
+ int epid;
+ int size;
+};
+
+static LIST_HEAD(, upassthru_softc) devices;
+
+static int upassthru_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_INVAL);
+ 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
+upassthru_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];
+ 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 = xfer->tail;
+ *size = length;
+}
+
+static void
+upassthru_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 upassthru_libusb_xfer *
+upassthru_xfer_alloc(struct upassthru_softc *sc, int in,
+ struct usb_data_xfer *usb_xfer, int size, int ep, int iso)
+{
+ struct upassthru_libusb_xfer *xfer = calloc(1,
+ sizeof(struct upassthru_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
+upassthru_xfer_free(struct upassthru_libusb_xfer *xfer)
+{
+ free(xfer);
+}
+
+static int
+upassthru_parse_ep_types(struct upassthru_softc *sc)
+{
+ struct libusb_config_descriptor *desc;
+ struct libusb_device *dev;
+ struct libusb_device_handle *handle;
+ struct libusb_interface_descriptor *inf_desc;
+ struct libusb_endpoint_descriptor *ep_desc;
+ int res, intf, ep, addr, dir;
+
+ handle = sc->handle;
+ dev = libusb_get_device(handle);
+ res = libusb_get_active_config_descriptor(dev, &desc);
+ if (res != LIBUSB_SUCCESS)
+ return (libusb_error_to_usb_error(res));
+ assert(desc->bNumInterfaces == sc->num_interfaces);
+
+ free(sc->endpoint_types[0]);
+ free(sc->endpoint_types[1]);
+ sc->ep_cnts[0] = sc->ep_cnts[1] = 0;
+ for (intf = 0; intf < desc->bNumInterfaces; ++intf) {
+ inf_desc = &desc->interface[intf]
+ .altsetting[sc->active_altsettings[intf]];
+ for (ep = 0; ep < inf_desc->bNumEndpoints; ++ep) {
+ addr = inf_desc->endpoint[ep].bEndpointAddress;
+ dir = !!(addr & LIBUSB_ENDPOINT_DIR_MASK);
+ addr = addr & LIBUSB_ENDPOINT_ADDRESS_MASK;
+ sc->ep_cnts[dir] = (addr > sc->ep_cnts[dir] ?
+ addr :
+ sc->ep_cnts[dir]);
+ }
+ }
+
+ sc->endpoint_types[OUT] = calloc(sc->ep_cnts[OUT] + 1, sizeof(uint8_t));
+ sc->endpoint_types[IN] = calloc(sc->ep_cnts[IN] + 1, sizeof(uint8_t));
+ for (intf = 0; intf < desc->bNumInterfaces; ++intf) {
+ inf_desc = &desc->interface[intf]
+ .altsetting[sc->active_altsettings[intf]];
+ for (ep = 0; ep < inf_desc->bNumEndpoints; ++ep) {
+ ep_desc = &inf_desc->endpoint[ep];
+ addr = ep_desc->bEndpointAddress;
+ sc->endpoint_types[!!(addr & LIBUSB_ENDPOINT_DIR_MASK)]
+ [addr &
+ LIBUSB_ENDPOINT_ADDRESS_MASK] =
+ ep_desc->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK;
+ }
+ }
+
+ libusb_free_config_descriptor(desc);
+ return (0);
+}
+
+static int
+upassthru_guest_attach_device(struct upassthru_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)
+ return (libusb_error_to_usb_error(res));
+
+ free(sc->active_altsettings);
+ sc->num_interfaces = desc->bNumInterfaces;
+ sc->active_altsettings = calloc(sc->num_interfaces, sizeof(int));
+
+ 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;
+ }
+
+ libusb_free_config_descriptor(desc);
+ upassthru_parse_ep_types(sc);
+ return (res);
+}
+
+static int
+upassthru_guest_detach_device_on_host(struct upassthru_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);
+ free(sc->active_altsettings);
+ free(sc->endpoint_types[0]);
+ free(sc->endpoint_types[1]);
+ sc->endpoint_types[0] = NULL;
+ sc->endpoint_types[1] = NULL;
+ sc->active_altsettings = NULL;
+
+ return (libusb_error_to_usb_error(res));
+}
+
+static int
+upassthru_guest_detach_device(struct upassthru_softc *sc __unused)
+{
+ return (0);
+}
+
+static int
+upassthru_hotplug_callback(struct libusb_context *ctx __unused,
+ struct libusb_device *dev, libusb_hotplug_event event, void *user_data)
+{
+ struct upassthru_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;
+ upassthru_guest_attach_device(sc);
+ assert(sc->hci->hci_event(sc->hci, USBDEV_ATTACH, sc) == 0);
+ }
+
+done:
+ pthread_mutex_unlock(&sc->mtx);
+
+ return (err);
+}
+
+static void
+upassthru_data_callback(struct libusb_transfer *lusb_xfer)
+{
+ struct upassthru_libusb_xfer *up_xfer = lusb_xfer->user_data;
+ struct usb_data_xfer *uxfer = up_xfer->usb_xfer;
+ struct upassthru_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;
+
+ USB_DATA_XFER_LOCK(uxfer);
+ if (lusb_xfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS)
+ upassthru_data_calculate_num_isos(uxfer, &head, &nframe, &size);
+ else
+ upassthru_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));
+
+ pthread_mutex_lock(&up_xfer->sc->mtx);
+
+ 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;
+ cur = (cur + 1) % USB_MAX_XFER_BLOCKS)
+ uxfer->data[cur].processed = 1;
+
+ USB_DATA_SET_ERRCODE(&uxfer->data[head],
+ lusb_xfer->status == LIBUSB_TRANSFER_COMPLETED ?
+ USB_ERR_NORMAL_COMPLETION :
+ USB_ERR_IOERROR);
+ }
+ pthread_mutex_unlock(&sc->mtx);
+
+done:
+ uxfer->tr_softc = NULL;
+ USB_DATA_XFER_UNLOCK(up_xfer->usb_xfer);
+ hci->hci_intr(hci, up_xfer->epid);
+ upassthru_xfer_free(up_xfer);
+}
+
+static void
+upassthru_exit(void)
+{
+ struct upassthru_softc *sc, *tmp;
+
+ LIST_FOREACH_SAFE(sc, &devices, next, tmp) {
+ LIST_REMOVE(sc, next);
+ upassthru_remove(sc);
+ }
+
+ event_thread_exit = true;
+}
+
+static void *
+upassthru_probe(struct usb_hci *hci, const nvlist_t *nvl)
+{
+ struct upassthru_softc *sc;
+ struct libusb_device_descriptor dev_desc;
+ struct libusb_device *dev;
+ struct libusb_init_option opts[] = {
+ { .option = LIBUSB_OPTION_CAPSICUMIZE }
+ };
+ const char *param;
+ 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 capsicumize libusb");
+ return (NULL);
+ }
+ LIST_INIT(&devices);
+ }
+ sc = calloc(1, sizeof(struct upassthru_softc));
+
+ param = get_config_value_node(nvl, "param1");
+ if (param == NULL) {
+ free(sc);
+ return (NULL);
+ }
+ sc->vid = strtoul(param, NULL, 16);
+ param = get_config_value_node(nvl, "param2");
+ if (param == NULL) {
+ free(sc);
+ return (NULL);
+ }
+ sc->pid = strtoul(param, NULL, 16);
+
+ 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 %0lx %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;
+ }
+ LIST_INSERT_HEAD(&devices, sc, next);
+
+ return (sc);
+}
+
+static int
+upassthru_init(void *scarg)
+{
+ struct upassthru_softc *sc = (struct upassthru_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 = upassthru_guest_attach_device(sc)) != LIBUSB_SUCCESS) {
+ EPRINTLN("failed to attach device to guest %s",
+ libusb_error_name(error));
+ 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,
+ upassthru_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;
+ }
+
+ atexit(upassthru_exit);
+ return (error);
+
+failed:
+ pthread_mutex_destroy(&sc->mtx);
+ free(sc);
+ return (1);
+}
+
+#define UREQ(x,y) ((x) | ((y) << 8))
+
+static int
+upassthru_request(void *scarg, struct usb_data_xfer *xfer)
+{
+ struct upassthru_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 (data == NULL)
+ goto done;
+
+ if (!xfer->ureq) {
+ DPRINTF(("upassthru_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);
+ DPRINTF((
+ "upassthru_request: bRequest: %x bmRequestType: %x wValue: %x wIndex: %d wLength: %x",
+ xfer->ureq->bRequest, xfer->ureq->bmRequestType, value, index,
+ len));
+
+ pthread_mutex_lock(&sc->mtx);
+ if (sc->handle == NULL) {
+ err = USB_ERR_STALLED;
+ goto done;
+ }
+
+ switch (UREQ(xfer->ureq->bRequest, xfer->ureq->bmRequestType)) {
+ case UREQ(UR_SET_ADDRESS, UT_WRITE_DEVICE):
+ err = 0;
+ goto done;
+ case UREQ(UR_SET_CONFIG, UT_WRITE_DEVICE):
+ err = upassthru_guest_detach_device_on_host(sc);
+ if (err)
+ goto done;
+ err = libusb_set_configuration(sc->handle, value & 0xff);
+ if (err)
+ goto done;
+ err = upassthru_guest_attach_device(sc);
+ if (err)
+ goto done;
+ goto done;
+ case UREQ(UR_SET_INTERFACE, UT_WRITE_INTERFACE):
+ if (sc->num_interfaces <= index) {
+ err = -1;
+ goto done;
+ }
+ err = libusb_set_interface_alt_setting(sc->handle, index,
+ value);
+ if (err == LIBUSB_SUCCESS)
+ sc->active_altsettings[index] = value;
+ upassthru_parse_ep_types(sc);
+ goto done;
+ case UREQ(UR_CLEAR_FEATURE, UT_WRITE_ENDPOINT):
+ err = libusb_clear_halt(sc->handle, index);
+ goto done;
+ }
+
+ data->blen = len;
+ data->bdone = 0;
+
+ err = libusb_control_transfer(sc->handle, xfer->ureq->bmRequestType,
+ xfer->ureq->bRequest, UGETW(xfer->ureq->wValue),
+ UGETW(xfer->ureq->wIndex), data->buf, data->blen, 1000);
+ if (err < 0)
+ goto done;
+ data->blen -= err;
+ data->bdone = err;
+ err = 0;
+done:
+ pthread_mutex_unlock(&sc->mtx);
+ if (xfer->ureq && (xfer->ureq->bmRequestType & UT_WRITE) &&
+ (err == USB_ERR_NORMAL_COMPLETION) && (data != NULL))
+ data->blen = 0;
+
+ if (data)
+ DPRINTF(
+ ("upassthru request error code %d (0=ok), blen %u txlen %u",
+ err, (data ? data->blen : 0),
+ (data ? data->bdone : 0)));
+
+ return (libusb_error_to_usb_error(err));
+}
+
+static void
+upassthru_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
+upassthru_data_handler(void *scarg, struct usb_data_xfer *xfer, int dir,
+ int epctx)
+{
+ struct upassthru_softc *sc = scarg;
+ struct upassthru_libusb_xfer *up_xfer;
+ int err, cur, len, ep, head, tail, offset, ep_type, nframe;
+
+ assert(sc->ep_cnts[dir] >= epctx);
+
+ ep_type = sc->endpoint_types[dir][epctx];
+
+ err = USB_ERR_NORMAL_COMPLETION;
+ ep = (dir ? LIBUSB_ENDPOINT_IN : 0) | epctx;
+
+ if (ep_type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS)
+ upassthru_data_calculate_num_isos(xfer, &head, &nframe, &len);
+ else
+ upassthru_calculate_xfer_ptr(xfer, &head, &tail, &len);
+
+ if (len == 0)
+ goto done;
+
+ DPRINTF((
+ "upassthru handle data - DIR=%s|EP=%d, blen %d, transfer_type: %d, ndata: %d",
+ dir ? "IN" : "OUT", epctx, len, sc->endpoint_types[dir][epctx],
+ xfer->ndata));
+
+ pthread_mutex_lock(&sc->mtx);
+ if (sc->handle == NULL)
+ goto done;
+
+ up_xfer = upassthru_xfer_alloc(sc, dir, xfer, len, ep,
+ ep_type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS ? nframe : 0);
+
+ if (ep_type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS)
+ upassthru_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;
+ xfer->data[cur].bdone = xfer->data[cur].blen = 0;
+ xfer->data[cur].blen = 0;
+ }
+ }
+
+ switch (ep_type) {
+ case LIBUSB_TRANSFER_TYPE_BULK:
+ libusb_fill_bulk_transfer(up_xfer->lusb_xfer, sc->handle, ep,
+ up_xfer->buffer, len, upassthru_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, upassthru_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, upassthru_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;
+ xfer->tr_softc = up_xfer;
+done:
+ pthread_mutex_unlock(&sc->mtx);
+ return (libusb_error_to_usb_error(err));
+}
+
+static int
+upassthru_reset(void *scarg __unused)
+{
+ struct upassthru_softc *sc = scarg;
+ int err = 0;
+
+ DPRINTF(("%s", __FUNCTION__));
+
+ if (sc->handle) {
+ err = libusb_reset_device(sc->handle);
+ if (err != LIBUSB_SUCCESS)
+ goto done;
+ err = upassthru_guest_attach_device(sc);
+ }
+done:
+ return (libusb_error_to_usb_error(err));
+}
+
+static int
+upassthru_remove(void *scarg)
+{
+ struct upassthru_softc *sc = scarg;
+ int err;
+
+ pthread_mutex_lock(&sc->mtx);
+ err = upassthru_guest_detach_device(sc);
+ if (err)
+ return (err);
+ libusb_hotplug_deregister_callback(NULL, sc->cb);
+ if ((err = upassthru_guest_detach_device_on_host(sc)) !=
+ LIBUSB_SUCCESS) {
+ return (err);
+ }
+ libusb_close(sc->handle);
+ pthread_mutex_unlock(&sc->mtx);
+
+ return (0);
+}
+
+static int
+upassthru_stop(void *scarg __unused)
+{
+ return (0);
+}
+
+static void
+upassthru_cancel(struct usb_data_xfer *xfer)
+{
+ struct upassthru_libusb_xfer *up_xfer = xfer->tr_softc;
+ struct upassthru_softc *sc;
+
+ if (up_xfer == NULL)
+ return;
+
+ sc = up_xfer->sc;
+ DPRINTF(("%s", __FUNCTION__));
+ pthread_mutex_lock(&sc->mtx);
+ libusb_cancel_transfer(up_xfer->lusb_xfer);
+ xfer->tr_softc = NULL;
+ pthread_mutex_unlock(&sc->mtx);
+}
+
+static struct usb_devemu ue_passthru = {
+ .ue_emu = "passthru",
+ .ue_usbver = 3,
+ .ue_usbspeed = USB_SPEED_HIGH,
+ .ue_probe = upassthru_probe,
+ .ue_init = upassthru_init,
+ .ue_request = upassthru_request,
+ .ue_data = upassthru_data_handler,
+ .ue_reset = upassthru_reset,
+ .ue_remove = upassthru_remove,
+ .ue_stop = upassthru_stop,
+ .ue_cancel = upassthru_cancel,
+#ifdef BHYVE_SNAPSHOT
+ .ue_snapshot = upassthru_snapshot,
+#endif
+};
+
+USB_EMUL_SET(ue_passthru);

File Metadata

Mime Type
text/plain
Expires
Thu, Apr 2, 6:31 AM (18 h, 10 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
30711972
Default Alt Text
D52166.id161041.diff (33 KB)

Event Timeline