Skip to content

Commit 0b405ee

Browse files
xiaoguangwulijinxia
authored andcommitted
DM USB: xHCI: change flow of creation of virtual USB device
The xHCI emulation greatly depends on the user space library libusb which is based on the usbfs module in Linux kernel. The libusb will bind usbfs to physical USB device which makes hardware control over libusb in user space possible. The pci_xhci_dev_create is called in pci_xhci_native_usb_dev_conn_cb which is a callback function triggered by physical USB device plugging. This function will bind the physical USB device to usbfs in SOS, which we depend to create the communication between UOS xHCI driver with physical USB device. This design will fail if the reconnection happened in the SOS, which will bind class driver to the physical USB device instead of usbfs, hence the libusb device handle in DM is invalid. Currently, the native S3 will disable the vbus for all xHCI ports and re-drive during S3 resume. This behavior cause native USB driver unbind the usbfs and bind to related class driver, then made the DM lost control and failed to continue emulation. To fix this issue, place the pci_xhci_dev_create in the function pci_xhci_cmd_enable_slot. According to the xHCI spec 4.5.3 Figure 10, the UOS always send Enable Slot command when a device is attached or recovered from errors (by Disable Slot command). So every time the SOS can't resuming normally or some unexpected disconnection happens, this desigen will always survive by Disable Slot and Enable Slot command series from UOS xHCI driver. 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>
1 parent b359dc3 commit 0b405ee

File tree

1 file changed

+129
-67
lines changed

1 file changed

+129
-67
lines changed

devicemodel/hw/pci/xhci.c

Lines changed: 129 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,10 @@ struct pci_xhci_vdev {
373373
int (*excap_write)(struct pci_xhci_vdev *, uint64_t, uint64_t);
374374
int usb2_port_start;
375375
int usb3_port_start;
376-
uint8_t port_map_tbl[USB_NATIVE_NUM_BUS][USB_NATIVE_NUM_PORT];
376+
377+
uint16_t port_map_tbl[USB_NATIVE_NUM_BUS][USB_NATIVE_NUM_PORT];
378+
struct usb_native_devinfo
379+
native_dev_info[USB_NATIVE_NUM_BUS][USB_NATIVE_NUM_PORT];
377380
struct timespec mf_prev_time; /* previous time of accessing MFINDEX */
378381
};
379382

@@ -385,8 +388,16 @@ struct pci_xhci_vdev {
385388
#define XHCI_GADDR(xdev, a) paddr_guest2host((xdev)->dev->vmctx, (a), \
386389
XHCI_PADDR_SZ - ((a) & (XHCI_PADDR_SZ-1)))
387390

391+
/* port mapping status */
388392
#define VPORT_FREE (0)
389-
#define VPORT_ASSIGNED (-1)
393+
#define VPORT_ASSIGNED (1)
394+
#define VPORT_CONNECTED (2)
395+
#define VPORT_EMULATED (3)
396+
397+
/* helpers for get port mapping information */
398+
#define VPORT_NUM(state) (state & 0xFF)
399+
#define VPORT_STATE(state) ((state >> 8) & 0xFF)
400+
#define VPORT_NUM_STATE(status, num) (((status & 0xFF) << 8) | (num & 0xFF))
390401

391402
struct pci_xhci_option_elem {
392403
char *parse_opt;
@@ -470,91 +481,65 @@ static struct pci_xhci_option_elem xhci_option_table[] = {
470481
static int
471482
pci_xhci_native_usb_dev_conn_cb(void *hci_data, void *dev_data)
472483
{
473-
struct pci_xhci_dev_emu *de;
474484
struct pci_xhci_vdev *xdev;
475-
struct usb_devemu *ue;
476485
struct usb_native_devinfo *di;
477-
int port_start, port_end;
478-
int slot_start, slot_end;
479-
int port, slot;
480-
void *ud;
486+
int vport_start, vport_end;
487+
int port;
481488

482489
xdev = hci_data;
483-
di = dev_data;
484490

485491
assert(xdev);
486492
assert(dev_data);
487493
assert(xdev->devices);
488494
assert(xdev->slots);
489495

490-
de = pci_xhci_dev_create(xdev, di);
491-
if (!de) {
492-
UPRINTF(LFTL, "fail to create device\r\n");
493-
return -1;
494-
}
495-
496-
/* find free port and slot for the new usb device */
497-
ud = de->dev_instance;
498-
ue = de->dev_ue;
499-
500-
assert(ud);
501-
assert(ue);
496+
di = dev_data;
502497

503498
/* print physical information about new device */
504499
UPRINTF(LDBG, "%04x:%04x %d-%d connecting.\r\n",
505500
di->vid, di->pid, di->bus, di->port);
506501

507-
if (xdev->port_map_tbl[di->bus][di->port] == VPORT_FREE) {
502+
if (VPORT_STATE(xdev->port_map_tbl[di->bus][di->port]) ==
503+
VPORT_FREE) {
508504
UPRINTF(LDBG, "%04x:%04x %d-%d doesn't belong to this vm, bye."
509505
"\r\n", di->vid, di->pid, di->bus, di->port);
510506
goto errout;
511507
}
512-
513508
UPRINTF(LDBG, "%04x:%04x %d-%d belong to this vm.\r\n", di->vid,
514509
di->pid, di->bus, di->port);
515510

516-
if (di->bcd < 0x300)
517-
port_start = xdev->usb2_port_start;
518-
else
519-
port_start = xdev->usb3_port_start;
520-
521-
slot_start = 1;
522-
port_end = port_start + (XHCI_MAX_DEVS / 2);
523-
slot_end = XHCI_MAX_SLOTS;
511+
if (di->bcd < 0x300) {
512+
vport_start = xdev->usb2_port_start;
513+
vport_end = vport_start + (XHCI_MAX_DEVS / 2);
514+
} else {
515+
vport_start = xdev->usb3_port_start;
516+
vport_end = vport_start + (XHCI_MAX_DEVS / 2);
517+
}
524518

525519
/* find free port */
526-
for (port = port_start; port < port_end; port++)
520+
for (port = vport_start; port < vport_end; port++)
527521
if (!xdev->devices[port])
528522
break;
529523

530-
/* find free slot */
531-
for (slot = slot_start; slot < slot_end; slot++)
532-
if (!xdev->slots[slot])
533-
break;
534-
535-
if (port >= port_end || slot >= slot_end) {
536-
UPRINTF(LFTL, "no free resource: port %d slot %d\r\n",
537-
port, slot);
524+
if (port >= vport_end) {
525+
UPRINTF(LFTL, "no free virtual port for native device %d-%d"
526+
"\r\n", di->bus, di->port);
538527
goto errout;
539528
}
540529

541-
/* use index of devices as port number */
542-
xdev->devices[port] = de;
543-
xdev->slots[slot] = de;
544-
xdev->ndevices++;
530+
UPRINTF(LDBG, "%04X:%04X %d-%d is attached to virtual port %d.\r\n",
531+
di->vid, di->pid, di->bus, di->port, port);
545532

546-
pci_xhci_reset_slot(xdev, slot);
547-
UPRINTF(LDBG, "%X:%X %d-%d locates in slot %d port %d.\r\n",
548-
di->vid, di->pid, di->bus, di->port,
549-
slot, port);
533+
xdev->native_dev_info[di->bus][di->port] = *di;
534+
xdev->port_map_tbl[di->bus][di->port] =
535+
VPORT_NUM_STATE(VPORT_CONNECTED, port);
550536

551537
/* Trigger port change event for the arriving device */
552538
if (pci_xhci_connect_port(xdev, port, di->speed, 1))
553539
UPRINTF(LFTL, "fail to report port event\n");
554540

555541
return 0;
556542
errout:
557-
pci_xhci_dev_destroy(de);
558543
return -1;
559544
}
560545

@@ -563,8 +548,10 @@ pci_xhci_native_usb_dev_disconn_cb(void *hci_data, void *dev_data)
563548
{
564549
struct pci_xhci_vdev *xdev;
565550
struct pci_xhci_dev_emu *edev;
551+
struct usb_native_devinfo di;
566552
struct usb_dev *udev;
567-
uint8_t port, native_port;
553+
uint8_t port, slot, native_port;
554+
uint8_t status;
568555

569556
assert(hci_data);
570557
assert(dev_data);
@@ -584,15 +571,28 @@ pci_xhci_native_usb_dev_disconn_cb(void *hci_data, void *dev_data)
584571
continue;
585572

586573
udev = edev->dev_instance;
587-
if (udev->info.port == native_port)
574+
if (udev->info.port == native_port) {
575+
di = udev->info;
588576
break;
577+
}
589578
}
590579

591580
if (port == XHCI_MAX_DEVS + 1) {
592581
UPRINTF(LFTL, "fail to find physical port %d\r\n", native_port);
593582
return -1;
594583
}
595584

585+
for (slot = 1; slot < XHCI_MAX_SLOTS; ++slot)
586+
if (xdev->slots[slot] == edev)
587+
break;
588+
589+
assert(slot < USB_NATIVE_NUM_BUS);
590+
591+
status = VPORT_STATE(xdev->port_map_tbl[di.bus][di.port]);
592+
assert(status == VPORT_EMULATED || status == VPORT_CONNECTED);
593+
xdev->port_map_tbl[di.bus][di.port] = VPORT_NUM_STATE(VPORT_ASSIGNED,
594+
0);
595+
596596
UPRINTF(LDBG, "report virtual port %d status\r\n", port);
597597
if (pci_xhci_disconnect_port(xdev, port, 1)) {
598598
UPRINTF(LFTL, "fail to report event\r\n");
@@ -1393,28 +1393,67 @@ pci_xhci_insert_event(struct pci_xhci_vdev *xdev,
13931393
return err;
13941394
}
13951395

1396+
static struct usb_native_devinfo *
1397+
pci_xhci_find_native_devinfo(struct pci_xhci_vdev *xdev)
1398+
{
1399+
int i, j;
1400+
1401+
assert(xdev);
1402+
for (i = 0; i < USB_NATIVE_NUM_BUS; ++i)
1403+
for (j = 0; j < USB_NATIVE_NUM_PORT; ++j)
1404+
if (VPORT_STATE(xdev->port_map_tbl[i][j]) ==
1405+
VPORT_CONNECTED)
1406+
return &xdev->native_dev_info[i][j];
1407+
1408+
return NULL;
1409+
}
1410+
13961411
static uint32_t
13971412
pci_xhci_cmd_enable_slot(struct pci_xhci_vdev *xdev, uint32_t *slot)
13981413
{
13991414
struct pci_xhci_dev_emu *dev;
1400-
uint32_t cmderr;
1401-
int i;
1415+
uint32_t cmderr;
1416+
struct usb_native_devinfo *di;
1417+
int i, vport;
14021418

14031419
cmderr = XHCI_TRB_ERROR_NO_SLOTS;
1404-
if (xdev->portregs != NULL)
1405-
for (i = 1; i <= XHCI_MAX_SLOTS; i++) {
1406-
dev = XHCI_SLOTDEV_PTR(xdev, i);
1407-
if (dev && dev->dev_slotstate == XHCI_ST_DISABLED) {
1408-
*slot = i;
1409-
dev->dev_slotstate = XHCI_ST_ENABLED;
1410-
cmderr = XHCI_TRB_ERROR_SUCCESS;
1411-
dev->hci.hci_address = i;
1412-
break;
1413-
}
1420+
1421+
di = pci_xhci_find_native_devinfo(xdev);
1422+
if (!di) {
1423+
UPRINTF(LWRN, "unexpected Enable Slot commnad\r\n");
1424+
return -1;
1425+
}
1426+
1427+
assert(di->priv_data);
1428+
dev = pci_xhci_dev_create(xdev, di);
1429+
if (!dev) {
1430+
UPRINTF(LFTL, "fail to create device\r\n");
1431+
return -1;
1432+
}
1433+
1434+
vport = VPORT_NUM(xdev->port_map_tbl[di->bus][di->port]);
1435+
assert(vport > 0);
1436+
assert(!xdev->devices[vport]);
1437+
1438+
xdev->devices[vport] = dev;
1439+
xdev->ndevices++;
1440+
1441+
for (i = 1; i <= XHCI_MAX_SLOTS; i++) {
1442+
if (XHCI_SLOTDEV_PTR(xdev, i) == NULL) {
1443+
xdev->slots[i] = dev;
1444+
*slot = i;
1445+
dev->dev_slotstate = XHCI_ST_ENABLED;
1446+
cmderr = XHCI_TRB_ERROR_SUCCESS;
1447+
dev->hci.hci_address = i;
1448+
xdev->port_map_tbl[di->bus][di->port] =
1449+
VPORT_NUM_STATE(VPORT_EMULATED, vport);
1450+
break;
14141451
}
1452+
}
14151453

1416-
UPRINTF(LDBG, "enable slot (error=%d) slot %u\r\n",
1417-
cmderr != XHCI_TRB_ERROR_SUCCESS, *slot);
1454+
UPRINTF(LDBG, "enable slot (error=%d) slot %u for native device "
1455+
"%d-%d\r\n", cmderr != XHCI_TRB_ERROR_SUCCESS, *slot,
1456+
di->bus, di->port);
14181457

14191458
return cmderr;
14201459
}
@@ -1423,6 +1462,8 @@ static uint32_t
14231462
pci_xhci_cmd_disable_slot(struct pci_xhci_vdev *xdev, uint32_t slot)
14241463
{
14251464
struct pci_xhci_dev_emu *dev;
1465+
struct usb_dev *udev;
1466+
struct usb_native_devinfo *di;
14261467
uint32_t cmderr;
14271468
int i;
14281469

@@ -1446,6 +1487,9 @@ pci_xhci_cmd_disable_slot(struct pci_xhci_vdev *xdev, uint32_t slot)
14461487
cmderr = XHCI_TRB_ERROR_SUCCESS;
14471488
/* TODO: reset events and endpoints */
14481489
}
1490+
} else {
1491+
UPRINTF(LDBG, "disable NULL device, slot %d\r\n", slot);
1492+
goto done;
14491493
}
14501494

14511495
for (i = 1; i <= XHCI_MAX_DEVS; ++i)
@@ -1455,8 +1499,20 @@ pci_xhci_cmd_disable_slot(struct pci_xhci_vdev *xdev, uint32_t slot)
14551499
if (i <= XHCI_MAX_DEVS && XHCI_PORTREG_PTR(xdev, i)) {
14561500
XHCI_PORTREG_PTR(xdev, i)->portsc &= ~(XHCI_PS_CSC |
14571501
XHCI_PS_CCS | XHCI_PS_PED | XHCI_PS_PP);
1502+
1503+
udev = dev->dev_instance;
1504+
assert(udev);
1505+
14581506
xdev->devices[i] = NULL;
14591507
xdev->slots[slot] = NULL;
1508+
1509+
di = &udev->info;
1510+
xdev->port_map_tbl[di->bus][di->port] =
1511+
VPORT_NUM_STATE(VPORT_ASSIGNED, 0);
1512+
1513+
UPRINTF(LINF, "disable slot %d for native device %d-%d"
1514+
"\r\n", slot, di->bus, di->port);
1515+
14601516
pci_xhci_dev_destroy(dev);
14611517
} else
14621518
UPRINTF(LWRN, "invalid slot %d\r\n", slot);
@@ -1624,7 +1680,10 @@ pci_xhci_cmd_config_ep(struct pci_xhci_vdev *xdev,
16241680
UPRINTF(LDBG, "config_ep slot %u\r\n", slot);
16251681

16261682
dev = XHCI_SLOTDEV_PTR(xdev, slot);
1627-
assert(dev != NULL);
1683+
if (dev == NULL) {
1684+
cmderr = XHCI_TRB_ERROR_SLOT_NOT_ON;
1685+
goto done;
1686+
}
16281687

16291688
if ((trb->dwTrb3 & XHCI_TRB_3_DCEP_BIT) != 0) {
16301689
UPRINTF(LDBG, "config_ep - deconfigure ep slot %u\r\n", slot);
@@ -3364,7 +3423,10 @@ pci_xhci_parse_bus_port(struct pci_xhci_vdev *xdev, char *opts)
33643423
goto errout;
33653424
}
33663425

3367-
xdev->port_map_tbl[bus][port] = VPORT_ASSIGNED;
3426+
xdev->port_map_tbl[bus][port] =
3427+
VPORT_NUM_STATE(VPORT_ASSIGNED, 0);
3428+
return 0;
3429+
33683430
errout:
33693431
if (rc)
33703432
UPRINTF(LWRN, "%s fails, rc=%d\r\n", __func__, rc);

0 commit comments

Comments
 (0)