Changeset View
Changeset View
Standalone View
Standalone View
usr.sbin/bhyve/pci_xhci.c
Show First 20 Lines • Show All 50 Lines • ▼ Show 20 Lines | |||||
#include <machine/vmm_snapshot.h> | #include <machine/vmm_snapshot.h> | ||||
#include <dev/usb/usbdi.h> | #include <dev/usb/usbdi.h> | ||||
#include <dev/usb/usb.h> | #include <dev/usb/usb.h> | ||||
#include <dev/usb/usb_freebsd.h> | #include <dev/usb/usb_freebsd.h> | ||||
#include <xhcireg.h> | #include <xhcireg.h> | ||||
#include "bhyverun.h" | #include "bhyverun.h" | ||||
#include "config.h" | |||||
#include "debug.h" | #include "debug.h" | ||||
#include "pci_emul.h" | #include "pci_emul.h" | ||||
#include "pci_xhci.h" | #include "pci_xhci.h" | ||||
#include "usb_emul.h" | #include "usb_emul.h" | ||||
static int xhci_debug = 0; | static int xhci_debug = 0; | ||||
#define DPRINTF(params) if (xhci_debug) PRINTLN params | #define DPRINTF(params) if (xhci_debug) PRINTLN params | ||||
▲ Show 20 Lines • Show All 2,572 Lines • ▼ Show 20 Lines | |||||
static int | static int | ||||
pci_xhci_dev_event(struct usb_hci *hci, enum hci_usbev evid, void *param) | pci_xhci_dev_event(struct usb_hci *hci, enum hci_usbev evid, void *param) | ||||
{ | { | ||||
DPRINTF(("xhci device event port %d", hci->hci_port)); | DPRINTF(("xhci device event port %d", hci->hci_port)); | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | |||||
* Each controller contains a "devices" node which contains a list of | |||||
* child nodes each of which is a device. The legacy config names | |||||
* these nodes as integers starting at 0. Each USB device node | |||||
* contains a "device" variable identifying the device model of the | |||||
* USB device. For example: | |||||
* | |||||
* pci.0.1.0 | |||||
* .device="xhci" | |||||
* .devices | |||||
* .0 | |||||
* .device="tablet" | |||||
*/ | |||||
static int | |||||
pci_xhci_legacy_config(nvlist_t *nvl, const char *opts) | |||||
{ | |||||
char node_name[16]; | |||||
nvlist_t *devices_nvl, *device_nvl; | |||||
char *cp, *opt, *str, *tofree; | |||||
int count; | |||||
devices_nvl = create_relative_config_node(nvl, "devices"); | |||||
count = 0; | |||||
tofree = str = strdup(opts); | |||||
while ((opt = strsep(&str, ",")) != NULL) { | |||||
/* device[=<config>] */ | |||||
cp = strchr(opt, '='); | |||||
if (cp != NULL) { | |||||
*cp = '\0'; | |||||
cp++; | |||||
} | |||||
static void | snprintf(node_name, sizeof(node_name), "%d", count); | ||||
pci_xhci_device_usage(char *opt) | count++; | ||||
{ | device_nvl = create_relative_config_node(devices_nvl, | ||||
node_name); | |||||
set_config_value_node(device_nvl, "device", opt); | |||||
EPRINTLN("Invalid USB emulation \"%s\"", opt); | /* | ||||
* NB: Given that we split on commas above, the legacy | |||||
* format only supports a single option. | |||||
*/ | |||||
if (cp != NULL && *cp != '\0') | |||||
pci_parse_legacy_config(device_nvl, cp); | |||||
} | } | ||||
free(tofree); | |||||
return (0); | |||||
} | |||||
static int | static int | ||||
pci_xhci_parse_opts(struct pci_xhci_softc *sc, char *opts) | pci_xhci_parse_devices(struct pci_xhci_softc *sc, nvlist_t *nvl) | ||||
{ | { | ||||
struct pci_xhci_dev_emu **devices; | struct pci_xhci_dev_emu **devices; | ||||
struct pci_xhci_dev_emu *dev; | struct pci_xhci_dev_emu *dev; | ||||
struct usb_devemu *ue; | struct usb_devemu *ue; | ||||
void *devsc; | const nvlist_t *devices_nvl, *device_nvl; | ||||
char *uopt, *xopts, *config; | const char *name, *device; | ||||
int usb3_port, usb2_port, i; | void *devsc, *cookie; | ||||
int type, usb3_port, usb2_port, i; | |||||
uopt = NULL; | |||||
usb3_port = sc->usb3_port_start - 1; | usb3_port = sc->usb3_port_start - 1; | ||||
usb2_port = sc->usb2_port_start - 1; | usb2_port = sc->usb2_port_start - 1; | ||||
devices = NULL; | devices = NULL; | ||||
if (opts == NULL) | devices_nvl = find_relative_config_node(nvl, "devices"); | ||||
if (devices_nvl == NULL) | |||||
goto portsfinal; | goto portsfinal; | ||||
devices = calloc(XHCI_MAX_DEVS, sizeof(struct pci_xhci_dev_emu *)); | devices = calloc(XHCI_MAX_DEVS, sizeof(struct pci_xhci_dev_emu *)); | ||||
sc->slots = calloc(XHCI_MAX_SLOTS, sizeof(struct pci_xhci_dev_emu *)); | sc->slots = calloc(XHCI_MAX_SLOTS, sizeof(struct pci_xhci_dev_emu *)); | ||||
sc->devices = devices; | sc->devices = devices; | ||||
sc->ndevices = 0; | sc->ndevices = 0; | ||||
uopt = strdup(opts); | cookie = NULL; | ||||
for (xopts = strtok(uopt, ","); | while ((name = nvlist_next(devices_nvl, &type, &cookie)) != NULL) { | ||||
xopts != NULL; | |||||
xopts = strtok(NULL, ",")) { | |||||
if (usb2_port == ((sc->usb2_port_start-1) + XHCI_MAX_DEVS/2) || | if (usb2_port == ((sc->usb2_port_start-1) + XHCI_MAX_DEVS/2) || | ||||
usb3_port == ((sc->usb3_port_start-1) + XHCI_MAX_DEVS/2)) { | usb3_port == ((sc->usb3_port_start-1) + XHCI_MAX_DEVS/2)) { | ||||
WPRINTF(("pci_xhci max number of USB 2 or 3 " | WPRINTF(("pci_xhci max number of USB 2 or 3 " | ||||
"devices reached, max %d", XHCI_MAX_DEVS/2)); | "devices reached, max %d", XHCI_MAX_DEVS/2)); | ||||
usb2_port = usb3_port = -1; | usb2_port = usb3_port = -1; | ||||
goto done; | goto done; | ||||
} | } | ||||
/* device[=<config>] */ | if (type != NV_TYPE_NVLIST) { | ||||
if ((config = strchr(xopts, '=')) == NULL) | EPRINTLN( | ||||
config = ""; /* no config */ | "pci_xhci: config variable '%s' under devices node", | ||||
else | name); | ||||
*config++ = '\0'; | return (-1); | ||||
} | |||||
ue = usb_emu_finddev(xopts); | device_nvl = nvlist_get_nvlist(devices_nvl, name); | ||||
device = get_config_value_node(device_nvl, "device"); | |||||
if (device == NULL) { | |||||
EPRINTLN( | |||||
"pci_xhci: missing \"device\" value for device '%s'", | |||||
name); | |||||
return (-1); | |||||
} | |||||
ue = usb_emu_finddev(device); | |||||
if (ue == NULL) { | if (ue == NULL) { | ||||
pci_xhci_device_usage(xopts); | EPRINTLN("pci_xhci: unknown device model \"%s\"", | ||||
DPRINTF(("pci_xhci device not found %s", xopts)); | device); | ||||
usb2_port = usb3_port = -1; | usb2_port = usb3_port = -1; | ||||
goto done; | return (-1); | ||||
} | } | ||||
DPRINTF(("pci_xhci adding device %s, opts \"%s\"", | DPRINTF(("pci_xhci adding device %s", device)); | ||||
xopts, config)); | |||||
dev = calloc(1, sizeof(struct pci_xhci_dev_emu)); | dev = calloc(1, sizeof(struct pci_xhci_dev_emu)); | ||||
dev->xsc = sc; | dev->xsc = sc; | ||||
dev->hci.hci_sc = dev; | dev->hci.hci_sc = dev; | ||||
dev->hci.hci_intr = pci_xhci_dev_intr; | dev->hci.hci_intr = pci_xhci_dev_intr; | ||||
dev->hci.hci_event = pci_xhci_dev_event; | dev->hci.hci_event = pci_xhci_dev_event; | ||||
/* | |||||
* XXX: This seems broken if you mix USB 2 and 3 | |||||
* devices on a single controller in that the | |||||
* 'devices' array won't be consistent. Perhaps we | |||||
* need separate USB 2 and USB 3 port arrays instead? | |||||
*/ | |||||
if (ue->ue_usbver == 2) { | if (ue->ue_usbver == 2) { | ||||
dev->hci.hci_port = usb2_port + 1; | dev->hci.hci_port = usb2_port + 1; | ||||
devices[usb2_port] = dev; | devices[usb2_port] = dev; | ||||
usb2_port++; | usb2_port++; | ||||
} else { | } else { | ||||
dev->hci.hci_port = usb3_port + 1; | dev->hci.hci_port = usb3_port + 1; | ||||
devices[usb3_port] = dev; | devices[usb3_port] = dev; | ||||
usb3_port++; | usb3_port++; | ||||
} | } | ||||
dev->hci.hci_address = 0; | dev->hci.hci_address = 0; | ||||
devsc = ue->ue_init(&dev->hci, config); | devsc = ue->ue_init(&dev->hci, nvl); | ||||
if (devsc == NULL) { | if (devsc == NULL) { | ||||
pci_xhci_device_usage(xopts); | |||||
usb2_port = usb3_port = -1; | usb2_port = usb3_port = -1; | ||||
goto done; | goto done; | ||||
} | } | ||||
dev->dev_ue = ue; | dev->dev_ue = ue; | ||||
dev->dev_sc = devsc; | dev->dev_sc = devsc; | ||||
/* assign slot number to device */ | /* assign slot number to device */ | ||||
Show All 25 Lines | if (usb2_port <= 0 && usb3_port <= 0) { | ||||
sc->devices = NULL; | sc->devices = NULL; | ||||
for (i = 0; devices[i] != NULL; i++) | for (i = 0; devices[i] != NULL; i++) | ||||
free(devices[i]); | free(devices[i]); | ||||
sc->ndevices = -1; | sc->ndevices = -1; | ||||
free(devices); | free(devices); | ||||
} | } | ||||
} | } | ||||
free(uopt); | |||||
return (sc->ndevices); | return (sc->ndevices); | ||||
} | } | ||||
static int | static int | ||||
pci_xhci_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) | pci_xhci_init(struct vmctx *ctx, struct pci_devinst *pi, nvlist_t *nvl) | ||||
{ | { | ||||
struct pci_xhci_softc *sc; | struct pci_xhci_softc *sc; | ||||
int error; | int error; | ||||
if (xhci_in_use) { | if (xhci_in_use) { | ||||
WPRINTF(("pci_xhci controller already defined")); | WPRINTF(("pci_xhci controller already defined")); | ||||
return (-1); | return (-1); | ||||
} | } | ||||
xhci_in_use = 1; | xhci_in_use = 1; | ||||
sc = calloc(1, sizeof(struct pci_xhci_softc)); | sc = calloc(1, sizeof(struct pci_xhci_softc)); | ||||
pi->pi_arg = sc; | pi->pi_arg = sc; | ||||
sc->xsc_pi = pi; | sc->xsc_pi = pi; | ||||
sc->usb2_port_start = (XHCI_MAX_DEVS/2) + 1; | sc->usb2_port_start = (XHCI_MAX_DEVS/2) + 1; | ||||
sc->usb3_port_start = 1; | sc->usb3_port_start = 1; | ||||
/* discover devices */ | /* discover devices */ | ||||
error = pci_xhci_parse_opts(sc, opts); | error = pci_xhci_parse_devices(sc, nvl); | ||||
if (error < 0) | if (error < 0) | ||||
goto done; | goto done; | ||||
else | else | ||||
error = 0; | error = 0; | ||||
sc->caplength = XHCI_SET_CAPLEN(XHCI_CAPLEN) | | sc->caplength = XHCI_SET_CAPLEN(XHCI_CAPLEN) | | ||||
XHCI_SET_HCIVERSION(0x0100); | XHCI_SET_HCIVERSION(0x0100); | ||||
sc->hcsparams1 = XHCI_SET_HCSP1_MAXPORTS(XHCI_MAX_DEVS) | | sc->hcsparams1 = XHCI_SET_HCSP1_MAXPORTS(XHCI_MAX_DEVS) | | ||||
▲ Show 20 Lines • Show All 312 Lines • ▼ Show 20 Lines | |||||
done: | done: | ||||
return (ret); | return (ret); | ||||
} | } | ||||
#endif | #endif | ||||
struct pci_devemu pci_de_xhci = { | struct pci_devemu pci_de_xhci = { | ||||
.pe_emu = "xhci", | .pe_emu = "xhci", | ||||
.pe_init = pci_xhci_init, | .pe_init = pci_xhci_init, | ||||
.pe_legacy_config = pci_xhci_legacy_config, | |||||
.pe_barwrite = pci_xhci_write, | .pe_barwrite = pci_xhci_write, | ||||
.pe_barread = pci_xhci_read, | .pe_barread = pci_xhci_read, | ||||
#ifdef BHYVE_SNAPSHOT | #ifdef BHYVE_SNAPSHOT | ||||
.pe_snapshot = pci_xhci_snapshot, | .pe_snapshot = pci_xhci_snapshot, | ||||
#endif | #endif | ||||
}; | }; | ||||
PCI_EMUL_SET(pci_de_xhci); | PCI_EMUL_SET(pci_de_xhci); |