diff --git a/devicemodel/hw/pci/xhci.c b/devicemodel/hw/pci/xhci.c index dd9f5215e..1cf3296ff 100644 --- a/devicemodel/hw/pci/xhci.c +++ b/devicemodel/hw/pci/xhci.c @@ -501,12 +501,46 @@ pci_xhci_get_dev_type(struct pci_xhci_vdev *xdev, void *dev_data) return USB_DEV; } +static int +pci_xhci_get_free_rh_port(struct pci_xhci_vdev *xdev, struct usb_native_devinfo + *di) +{ + volatile int bus; + volatile int ports, porte; + volatile int i, j; + int used; + + assert(xdev); + assert(di); + + if (di->bcd < 0x300) { + bus = 1; + ports = xdev->usb2_port_start; + } else { + bus = 2; + ports = xdev->usb3_port_start; + } + porte = ports + (XHCI_MAX_DEVS / 2); + + for (i = ports; i < porte; i++) { + used = 0; + for (j = 1; j < USB_NATIVE_NUM_PORT; ++j) { + if (VPORT_NUM(xdev->port_map_tbl[bus][j]) == i) { + used = 1; + break; + } + } + if (!used) + return i; + } + return -1; +} + static int pci_xhci_native_usb_dev_conn_cb(void *hci_data, void *dev_data) { struct pci_xhci_vdev *xdev; struct usb_native_devinfo *di; - int vport_start, vport_end; int port; int need_intr = 1; enum usb_native_dev_type type; @@ -560,20 +594,8 @@ pci_xhci_native_usb_dev_conn_cb(void *hci_data, void *dev_data) UPRINTF(LDBG, "%04x:%04x %d-%d belong to this vm.\r\n", di->vid, di->pid, di->bus, di->port); - if (di->bcd < 0x300) { - vport_start = xdev->usb2_port_start; - vport_end = vport_start + (XHCI_MAX_DEVS / 2); - } else { - vport_start = xdev->usb3_port_start; - vport_end = vport_start + (XHCI_MAX_DEVS / 2); - } - - /* find free port */ - for (port = vport_start; port < vport_end; port++) - if (!xdev->devices[port]) - break; - - if (port >= vport_end) { + port = pci_xhci_get_free_rh_port(xdev, di); + if (port < 0) { UPRINTF(LFTL, "no free virtual port for native device %d-%d" "\r\n", di->bus, di->port); goto errout; @@ -587,7 +609,7 @@ pci_xhci_native_usb_dev_conn_cb(void *hci_data, void *dev_data) VPORT_NUM_STATE(VPORT_CONNECTED, port); /* TODO: should revisit in deeper level */ - if (vm_get_suspend_mode() != VM_SUSPEND_NONE) + if (vm_get_suspend_mode() != VM_SUSPEND_NONE || xhci_in_use == 0) need_intr = 0; /* Trigger port change event for the arriving device */ @@ -1462,16 +1484,34 @@ done: static struct usb_native_devinfo * pci_xhci_find_native_devinfo(struct pci_xhci_vdev *xdev) { - int i, j; + int i, j, x, y; + int minp = XHCI_MAX_DEVS; + int temp; assert(xdev); + + /* FIXME + * Use the device with minimum port number to do the 'Enable Slot' + * command. This is ok with Linux, but not 100% compatible with + * xHCI spec. Will fix this in future. Need follow xHCI spec to bind + * slot to device in address device command. + */ + x = y = -1; for (i = 0; i < USB_NATIVE_NUM_BUS; ++i) for (j = 0; j < USB_NATIVE_NUM_PORT; ++j) if (VPORT_STATE(xdev->port_map_tbl[i][j]) == - VPORT_CONNECTED) - return &xdev->native_dev_info[i][j]; - - return NULL; + VPORT_CONNECTED) { + temp = VPORT_NUM(xdev->port_map_tbl[i][j]); + if (minp > temp) { + x = i; + y = j; + minp = temp; + } + } + if (x == -1 || y == -1) + return NULL; + else + return &xdev->native_dev_info[x][y]; } static uint32_t @@ -3726,7 +3766,6 @@ pci_xhci_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts) UPRINTF(LWRN, "controller already defined\r\n"); return -1; } - xhci_in_use = 1; xdev = calloc(1, sizeof(struct pci_xhci_vdev)); if (!xdev) { @@ -3839,6 +3878,7 @@ pci_xhci_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts) pthread_mutex_init(&xdev->mtx, NULL); + xhci_in_use = 1; done: if (error) { UPRINTF(LFTL, "%s fail, error=%d\n", __func__, error); diff --git a/devicemodel/hw/platform/usb_pmapper.c b/devicemodel/hw/platform/usb_pmapper.c index 2ef05734a..fa7d195fe 100644 --- a/devicemodel/hw/platform/usb_pmapper.c +++ b/devicemodel/hw/platform/usb_pmapper.c @@ -21,6 +21,85 @@ static struct usb_dev_sys_ctx_info g_ctx; static inline uint8_t usb_dev_get_ep_type(struct usb_dev *udev, int pid, int epnum); +static int +usb_dev_scan_dev() +{ + int i, num_devs; + struct libusb_device **devlist; + struct libusb_device *ldev; + struct usb_native_devinfo di; + struct libusb_device_descriptor d; + int rc; + + if (!g_ctx.libusb_ctx) + return -1; + + num_devs = libusb_get_device_list(g_ctx.libusb_ctx, &devlist); + if (num_devs < 0) + return -1; + + for (i = 0; i < num_devs; ++i) { + ldev = devlist[i]; + + memset(&di, 0, sizeof(di)); + + di.bus = libusb_get_bus_number(ldev); + di.port = libusb_get_port_number(ldev); + di.speed = libusb_get_device_speed(ldev); + + rc = libusb_get_device_descriptor(ldev, &d); + if (rc) { + UPRINTF(LWRN, "fail to get descriptor for %d-%d\r\n", + di.bus, di.port); + continue; + } + + di.pid = d.idProduct; + di.vid = d.idVendor; + di.bcd = d.bcdUSB; + di.priv_data = ldev; + + if (di.port == 0) + continue; + if (d.bDeviceClass != LIBUSB_CLASS_HUB) + continue; + + if (g_ctx.conn_cb) + g_ctx.conn_cb(g_ctx.hci_data, &di); + } + + for (i = 0; i < num_devs; ++i) { + ldev = devlist[i]; + + memset(&di, 0, sizeof(di)); + di.bus = libusb_get_bus_number(ldev); + di.port = libusb_get_port_number(ldev); + di.speed = libusb_get_device_speed(ldev); + + rc = libusb_get_device_descriptor(ldev, &d); + if (rc) { + UPRINTF(LWRN, "fail to get descriptor for %d-%d\r\n", + di.bus, di.port); + continue; + } + + di.pid = d.idProduct; + di.vid = d.idVendor; + di.bcd = d.bcdUSB; + di.priv_data = ldev; + + if (di.port == 0) + continue; + if (d.bDeviceClass == LIBUSB_CLASS_HUB) + continue; + + if (g_ctx.conn_cb) + g_ctx.conn_cb(g_ctx.hci_data, &di); + } + + return num_devs; +} + static int libusb_speed_to_usb_speed(int libusb_speed) { @@ -1134,6 +1213,7 @@ usb_dev_sys_init(usb_dev_sys_cb conn_cb, usb_dev_sys_cb disconn_cb, libusb_hotplug_callback_handle native_conn_handle; libusb_hotplug_callback_handle native_disconn_handle; int native_pid, native_vid, native_cls, rc; + int num_devs; assert(conn_cb); assert(disconn_cb); @@ -1155,6 +1235,10 @@ usb_dev_sys_init(usb_dev_sys_cb conn_cb, usb_dev_sys_cb disconn_cb, g_ctx.disconn_cb = disconn_cb; g_ctx.notify_cb = notify_cb; g_ctx.intr_cb = intr_cb; + + num_devs = usb_dev_scan_dev(); + UPRINTF(LINF, "found %d devices before Guest OS booted\r\n", num_devs); + native_conn_evt = LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED; native_disconn_evt = LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT; native_pid = LIBUSB_HOTPLUG_MATCH_ANY;