diff --git a/devicemodel/hw/pci/xhci.c b/devicemodel/hw/pci/xhci.c index 4379c8014..dd9f5215e 100644 --- a/devicemodel/hw/pci/xhci.c +++ b/devicemodel/hw/pci/xhci.c @@ -95,7 +95,7 @@ #undef LOG_TAG #define LOG_TAG "xHCI: " -#define XHCI_MAX_DEVS 8 /* 4 USB3 + 4 USB2 devs */ +#define XHCI_MAX_DEVS 20 /* 10 root hub + 10 external hub */ #define XHCI_MAX_SLOTS 64 /* min allowed by Windows drivers */ /* @@ -394,6 +394,7 @@ struct pci_xhci_vdev { #define VPORT_ASSIGNED (1) #define VPORT_CONNECTED (2) #define VPORT_EMULATED (3) +#define VPORT_HUB_CONNECTED (4) /* helpers for get port mapping information */ #define VPORT_NUM(state) (state & 0xFF) @@ -479,6 +480,27 @@ static struct pci_xhci_option_elem xhci_option_table[] = { {"cap", pci_xhci_parse_extcap} }; +static enum usb_native_dev_type +pci_xhci_get_dev_type(struct pci_xhci_vdev *xdev, void *dev_data) +{ + uint16_t port, bus; + struct usb_native_devinfo *di; + + assert(dev_data); + + di = dev_data; + if (usb_get_parent_dev_type(di->priv_data, &bus, &port) == USB_HUB) { + if (VPORT_STATE(xdev->port_map_tbl[bus][port]) == + VPORT_HUB_CONNECTED) { + di->port += PORT_HUB_BASE; + return USB_VALID_SUB_DEV; + } else + return USB_INVALID_SUB_DEV; + } + + return USB_DEV; +} + static int pci_xhci_native_usb_dev_conn_cb(void *hci_data, void *dev_data) { @@ -487,6 +509,9 @@ pci_xhci_native_usb_dev_conn_cb(void *hci_data, void *dev_data) int vport_start, vport_end; int port; int need_intr = 1; + enum usb_native_dev_type type; + int state; + int rc; xdev = hci_data; @@ -501,12 +526,37 @@ pci_xhci_native_usb_dev_conn_cb(void *hci_data, void *dev_data) UPRINTF(LDBG, "%04x:%04x %d-%d connecting.\r\n", di->vid, di->pid, di->bus, di->port); - if (VPORT_STATE(xdev->port_map_tbl[di->bus][di->port]) == - VPORT_FREE) { - UPRINTF(LDBG, "%04x:%04x %d-%d doesn't belong to this vm, bye." - "\r\n", di->vid, di->pid, di->bus, di->port); + type = pci_xhci_get_dev_type(xdev, di); + if (type == USB_DEV) { + if (VPORT_STATE(xdev->port_map_tbl[di->bus][di->port]) == + VPORT_FREE) { + UPRINTF(LDBG, "%04x:%04x %d-%d doesn't belong to this" + " vm, bye.\r\n", di->vid, di->pid, + di->bus, di->port); + goto errout; + } + } else if (type == USB_INVALID_SUB_DEV) + return 0; + + state = VPORT_STATE(xdev->port_map_tbl[di->bus][di->port]); + if (state == VPORT_CONNECTED || state == VPORT_EMULATED || + state == VPORT_HUB_CONNECTED) { + UPRINTF(LFTL, "do not support multiple hubs currently, reject " + "device %d-%d\r\n", di->bus, di->port); goto errout; } + + rc = usb_dev_is_hub(di->priv_data); + if (rc == USB_HUB) { + xdev->port_map_tbl[di->bus][di->port] = + VPORT_NUM_STATE(VPORT_HUB_CONNECTED, 0); + return 0; + } else if (rc == USB_TYPE_INVALID) { + UPRINTF(LWRN, "usb_dev_is_hub failed\r\n"); + goto errout; + } + + UPRINTF(LDBG, "%04x:%04x %d-%d belong to this vm.\r\n", di->vid, di->pid, di->bus, di->port); @@ -593,8 +643,6 @@ pci_xhci_native_usb_dev_disconn_cb(void *hci_data, void *dev_data) if (xdev->slots[slot] == edev) break; - assert(slot < XHCI_MAX_SLOTS); - status = VPORT_STATE(xdev->port_map_tbl[di.bus][di.port]); assert(status == VPORT_EMULATED || status == VPORT_CONNECTED); xdev->port_map_tbl[di.bus][di.port] = VPORT_NUM_STATE(VPORT_ASSIGNED, diff --git a/devicemodel/hw/platform/usb_pmapper.c b/devicemodel/hw/platform/usb_pmapper.c index 2aedf28f7..2ef05734a 100644 --- a/devicemodel/hw/platform/usb_pmapper.c +++ b/devicemodel/hw/platform/usb_pmapper.c @@ -855,6 +855,65 @@ out: return xfer->status; } +int +usb_dev_is_hub(void *pdata) +{ + struct libusb_device *ldev; + struct libusb_device_descriptor desc; + int rc; + + assert(pdata); + + ldev = pdata; + rc = libusb_get_device_descriptor(ldev, &desc); + + if (rc) + return USB_TYPE_INVALID; + + if (desc.bDeviceClass == LIBUSB_CLASS_HUB) + return USB_HUB; + else + return USB_DEV; + +} + +enum usb_native_dev_type +usb_get_parent_dev_type(void *pdata, uint16_t *bus, uint16_t *port) +{ + struct libusb_device *ldev; + struct libusb_device *libdev; + struct libusb_device_descriptor desc; + int rc; + + assert(pdata); + assert(bus); + assert(port); + + ldev = pdata; + libdev = libusb_get_parent(ldev); + + if (libdev == NULL) { + UPRINTF(LWRN, "libusb_get_parent return NULL\r\n"); + return USB_TYPE_INVALID; + } + + *bus = libusb_get_bus_number(libdev); + *port = libusb_get_port_number(libdev); + + rc = libusb_get_device_descriptor(libdev, &desc); + if (rc) { + UPRINTF(LWRN, "libusb_get_device_descriptor error %d\r\n", rc); + return USB_TYPE_INVALID; + } + + if (*port == 0) + return ROOT_HUB; + if (desc.bDeviceClass == LIBUSB_CLASS_HUB) + return USB_HUB; + + return USB_TYPE_INVALID; +} + void * usb_dev_init(void *pdata, char *opt) { @@ -1039,6 +1098,9 @@ usb_dev_native_sys_disconn_cb(struct libusb_context *ctx, struct libusb_device *ldev, libusb_hotplug_event event, void *pdata) { uint8_t port; + uint16_t pport; + uint16_t pbus; + int rc; UPRINTF(LDBG, "disconnect event\r\n"); @@ -1048,6 +1110,13 @@ usb_dev_native_sys_disconn_cb(struct libusb_context *ctx, struct libusb_device } port = libusb_get_port_number(ldev); + rc = usb_get_parent_dev_type(ldev, &pbus, &pport); + if (rc == USB_TYPE_INVALID) { + UPRINTF(LWRN, "usb_get_parent_dev_type return %d\r\n", rc); + return 0; + } else if (rc == USB_HUB) + port += PORT_HUB_BASE; + if (g_ctx.disconn_cb) g_ctx.disconn_cb(g_ctx.hci_data, &port); diff --git a/devicemodel/include/usb_pmapper.h b/devicemodel/include/usb_pmapper.h index 43cb09fd8..434a353e9 100644 --- a/devicemodel/include/usb_pmapper.h +++ b/devicemodel/include/usb_pmapper.h @@ -20,6 +20,9 @@ #define USB_EP_NR(d) (USB_EP_ADDR(d) & 0xF) #define USB_EP_ERR_TYPE 0xFF +/* hub port start address */ +#define PORT_HUB_BASE 0x0A + enum { USB_INFO_VERSION, USB_INFO_SPEED, @@ -29,6 +32,15 @@ enum { USB_INFO_PID }; +enum usb_native_dev_type { + ROOT_HUB, + USB_HUB, + USB_DEV, + USB_VALID_SUB_DEV, + USB_INVALID_SUB_DEV, + USB_TYPE_INVALID +}; + struct usb_dev_ep { uint8_t pid; uint8_t type; @@ -119,4 +131,7 @@ int usb_dev_info(void *pdata, int type, void *value, int size); int usb_dev_request(void *pdata, struct usb_data_xfer *xfer); int usb_dev_reset(void *pdata); int usb_dev_data(void *pdata, struct usb_data_xfer *xfer, int dir, int epctx); +enum usb_native_dev_type usb_get_parent_dev_type(void *pdata, uint16_t *bus, + uint16_t *port); +int usb_dev_is_hub(void *pdata); #endif