From b4755cdc52c9a530859064943e9ef1187a6ba894 Mon Sep 17 00:00:00 2001 From: Xiaoguang Wu Date: Mon, 10 Sep 2018 22:01:43 +0800 Subject: [PATCH] DM USB: xHCI: enable 'cold plug' mode USB device is connected before UOS is booted up, this scenario is called 'cold plug' for easy to refer. Under 'cold plug' situation, the libusb will not report 'connect' event to device model, hence UOS will not discover 'cold plugged' device. This patch add support to fix this issue. Signed-off-by: Xiaoguang Wu Reviewed-by: Liang Yang Acked-by: Yu Wang Tracked-On: #1242 --- devicemodel/hw/pci/xhci.c | 84 ++++++++++++++++++++------- devicemodel/hw/platform/usb_pmapper.c | 84 +++++++++++++++++++++++++++ 2 files changed, 146 insertions(+), 22 deletions(-) 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;