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 <xiaoguang.wu@intel.com>
Reviewed-by: Liang Yang <liang3.yang@intel.com>
Acked-by: Yu Wang <yu1.wang@intel.com>
Tracked-On: #1242
This commit is contained in:
Xiaoguang Wu 2018-09-10 22:01:43 +08:00 committed by lijinxia
parent 612037e138
commit b4755cdc52
2 changed files with 146 additions and 22 deletions

View File

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

View File

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