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); | ||||