DM USB: xHCI: modify option parsing function to enable USB virtualiztion

Re-write the xHCI option parse function to support port mapper.

The new usage:
  -s <n>,xhci,[bus1-port1,bus2-port2]:[tablet]
  eg: -s 8,xhci,1-2,2-2
  eg: -s 7,xhci,tablet
  eg: -s 7,xhci,1-2,2-2:tablet

Note: please follow the board hardware design, assign the ports
according to the receptacle connection

Change-Id: I3c8392f7e15580cf768c8c4a619d705411da699d
Signed-off-by: Wu, Xiaoguang <xiaoguang.wu@intel.com>
Reviewed-by: Shuo Liu <shuo.a.liu@intel.com>
Reviewed-by: Yu Wang <yu1.wang@intel.com>
Reviewed-by: Zhao Yakui <yakui.zhao@intel.com>
Acked-by: Eddie Dong <eddie.dong@intel.com>
This commit is contained in:
Wu, Xiaoguang 2018-05-10 22:11:29 +08:00 committed by lijinxia
parent 6449950ccc
commit be4406c8d9

View File

@ -42,6 +42,7 @@
#include <pthread.h>
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>
#include "usb.h"
#include "usbdi.h"
#include "xhcireg.h"
@ -266,6 +267,10 @@ struct pci_xhci_vdev {
#define XHCI_HALTED(xdev) ((xdev)->opregs.usbsts & XHCI_STS_HCH)
#define XHCI_GADDR(xdev, a) paddr_guest2host((xdev)->dev->vmctx, (a), \
XHCI_PADDR_SZ - ((a) & (XHCI_PADDR_SZ-1)))
struct pci_xhci_option_elem {
char *parse_opt;
int (*parse_fn)(struct pci_xhci_vdev *, char *);
};
static int xhci_in_use;
@ -328,6 +333,11 @@ static int pci_xhci_xfer_complete(struct pci_xhci_vdev *xdev,
struct usb_data_xfer *xfer, uint32_t slot, uint32_t epid,
int *do_intr);
static inline int pci_xhci_is_valid_portnum(int n);
static int pci_xhci_parse_tablet(struct pci_xhci_vdev *xdev, char *opts);
static struct pci_xhci_option_elem xhci_option_table[] = {
{"tablet", pci_xhci_parse_tablet}
};
static int
pci_xhci_native_usb_dev_conn_cb(void *hci_data, void *dev_data)
@ -457,7 +467,7 @@ pci_xhci_native_usb_dev_disconn_cb(void *hci_data, void *dev_data)
return -1;
}
for (port = 1; port < XHCI_MAX_DEVS; ++port) {
for (port = 1; port <= XHCI_MAX_DEVS; ++port) {
edev = xdev->devices[port];
if (!edev)
continue;
@ -467,7 +477,7 @@ pci_xhci_native_usb_dev_disconn_cb(void *hci_data, void *dev_data)
break;
}
if (port == XHCI_MAX_DEVS) {
if (port == XHCI_MAX_DEVS + 1) {
UPRINTF(LFTL, "fail to find physical port %d\r\n", native_port);
return -1;
}
@ -632,7 +642,7 @@ pci_xhci_dev_destroy(struct pci_xhci_dev_emu *de)
static inline int
pci_xhci_is_valid_portnum(int n)
{
return n > 0 && n < XHCI_MAX_DEVS;
return n > 0 && n <= XHCI_MAX_DEVS;
}
static int
@ -1200,11 +1210,11 @@ pci_xhci_cmd_disable_slot(struct pci_xhci_vdev *xdev, uint32_t slot)
}
}
for (i = 0; i < XHCI_MAX_DEVS; ++i)
for (i = 1; i <= XHCI_MAX_DEVS; ++i)
if (dev == xdev->devices[i])
break;
if (i < XHCI_MAX_DEVS && XHCI_PORTREG_PTR(xdev, i)) {
if (i <= XHCI_MAX_DEVS && XHCI_PORTREG_PTR(xdev, i)) {
XHCI_PORTREG_PTR(xdev, i)->portsc &= ~(XHCI_PS_CSC |
XHCI_PS_CCS | XHCI_PS_PED | XHCI_PS_PP);
xdev->devices[i] = NULL;
@ -3060,152 +3070,223 @@ pci_xhci_dev_event(struct usb_hci *hci, enum hci_usbev evid, void *param)
static void
pci_xhci_device_usage(char *opt)
{
fprintf(stderr, "Invalid USB emulation \"%s\"\r\n", opt);
static const char *usage_str = "usage:\r\n"
" -s <n>,xhci,[bus1-port1,bus2-port2]:[tablet]\r\n"
" eg: -s 8,xhci,1-2,2-2\r\n"
" eg: -s 7,xhci,tablet\r\n"
" eg: -s 7,xhci,1-2,2-2:tablet\r\n"
" Note: please follow the board hardware design, assign the "
" ports according to the receptacle connection\r\n";
UPRINTF(LFTL, "error: invalid options: \"%s\"\r\n", opt);
UPRINTF(LFTL, "%s", usage_str);
}
static int
pci_xhci_parse_bus_port(struct pci_xhci_vdev *xdev, char *opts)
{
int rc = 0, cnt;
uint32_t port, bus;
assert(xdev);
assert(opts);
/* 'bus-port' format */
cnt = sscanf(opts, "%u-%u", &bus, &port);
if (cnt == EOF || cnt < 2) {
rc = -1;
goto errout;
}
if (!usb_native_is_bus_existed(bus) ||
!usb_native_is_port_existed(bus, port)) {
rc = -2;
goto errout;
}
if (!xdev->native_assign_ports[bus]) {
xdev->native_assign_ports[bus] = calloc(USB_NATIVE_NUM_PORT,
sizeof(uint8_t));
if (!xdev->native_assign_ports[bus]) {
rc = -3;
goto errout;
}
}
xdev->native_assign_ports[bus][port] = 1;
errout:
if (rc)
UPRINTF(LWRN, "%s fails, rc=%d\r\n", __func__, rc);
return rc;
}
static int
pci_xhci_parse_tablet(struct pci_xhci_vdev *xdev, char *opts)
{
char *cfg, *str;
void *devins;
struct usb_devemu *ue;
struct pci_xhci_dev_emu *dev = NULL;
uint8_t port_u2, port_u3;
int rc = 0;
assert(xdev);
assert(opts);
if (strncmp(opts, "tablet", sizeof("tablet") - 1)) {
rc = -1;
goto errout;
}
str = opts;
cfg = strchr(str, '=');
cfg = cfg ? cfg + 1 : "";
ue = usb_emu_finddev(opts);
if (ue == NULL) {
rc = -2;
goto errout;
}
dev = calloc(1, sizeof(struct pci_xhci_dev_emu));
if (!dev) {
rc = -3;
goto errout;
}
dev->xdev = xdev;
dev->hci.dev = dev;
dev->hci.hci_intr = pci_xhci_dev_intr;
dev->hci.hci_event = pci_xhci_dev_event;
/*
* This is a safe operation because there is no other
* device created and port_u2/port_u3 definitely points
* to an empty position in xdev->devices
*/
port_u2 = xdev->usb3_port_start - 1;
port_u3 = xdev->usb2_port_start - 1;
if (ue->ue_usbver == 2) {
dev->hci.hci_port = port_u2 + 1;
xdev->devices[port_u2] = dev;
} else {
dev->hci.hci_port = port_u3 + 1;
xdev->devices[port_u3] = dev;
}
dev->hci.hci_address = 0;
devins = ue->ue_init(&dev->hci, cfg);
if (devins == NULL) {
rc = -4;
goto errout;
}
dev->dev_ue = ue;
dev->dev_instance = devins;
/* assign slot number to device */
xdev->ndevices++;
xdev->slots[xdev->ndevices] = dev;
return 0;
errout:
if (dev) {
if (ue) {
if (dev == xdev->devices[port_u2])
xdev->devices[port_u2] = NULL;
if (dev == xdev->devices[port_u3])
xdev->devices[port_u3] = NULL;
}
free(dev);
}
UPRINTF(LFTL, "fail to parse tablet, rc=%d\r\n", rc);
return rc;
}
static int
pci_xhci_parse_opts(struct pci_xhci_vdev *xdev, char *opts)
{
struct pci_xhci_dev_emu **devices;
struct pci_xhci_dev_emu *dev;
struct usb_devemu *ue;
void *devins;
char *uopt, *xopts, *config;
int usb3_port, usb2_port, i;
char *s, *t, *n;
int i, rc = 0;
struct pci_xhci_option_elem *elem;
int (*f)(struct pci_xhci_vdev *, char *);
int elem_cnt;
usb3_port = xdev->usb3_port_start - 1;
usb2_port = xdev->usb2_port_start - 1;
devices = NULL;
if (opts == NULL)
goto portsfinal;
devices = calloc(XHCI_MAX_DEVS, sizeof(struct pci_xhci_dev_emu *));
if (devices == NULL) {
usb2_port = usb3_port = -1;
UPRINTF(LWRN, "%s:%d fail to allocate memory(devices)\n",
__func__, __LINE__);
goto done;
assert(xdev);
if (!opts) {
rc = -1;
goto errout;
}
xdev->slots = calloc(XHCI_MAX_SLOTS, sizeof(struct pci_xhci_dev_emu *));
if (xdev->slots == NULL) {
usb2_port = usb3_port = -1;
UPRINTF(LWRN, "%s:%d fail to allocate memory(slots)\n",
__func__, __LINE__);
goto done;
/* allocate neccessary resources during parsing*/
xdev->devices = calloc(XHCI_MAX_DEVS + 1, sizeof(*xdev->devices));
xdev->slots = calloc(XHCI_MAX_SLOTS, sizeof(*xdev->slots));
xdev->portregs = calloc(XHCI_MAX_DEVS + 1, sizeof(*xdev->portregs));
if (!xdev->devices || !xdev->slots || !xdev->portregs) {
rc = -2;
goto errout;
}
xdev->devices = devices;
xdev->ndevices = 0;
s = strdup(opts);
UPRINTF(LDBG, "options: %s\r\n", s);
uopt = strdup(opts);
for (xopts = strtok(uopt, ",");
xopts != NULL;
xopts = strtok(NULL, ",")) {
if (usb2_port ==
((xdev->usb2_port_start - 1) + XHCI_MAX_DEVS/2) ||
usb3_port ==
((xdev->usb3_port_start - 1) + XHCI_MAX_DEVS/2)) {
UPRINTF(LWRN, "pci_xhci max number of USB 2 or 3 "
"devices reached, max %d\r\n", XHCI_MAX_DEVS/2);
usb2_port = usb3_port = -1;
goto done;
}
elem = xhci_option_table;
elem_cnt = sizeof(xhci_option_table) / sizeof(*elem);
/* device[=<config>] */
config = strchr(xopts, '=');
if (config == NULL)
config = ""; /* no config */
else
*config++ = '\0';
ue = usb_emu_finddev(xopts);
if (ue == NULL) {
pci_xhci_device_usage(xopts);
UPRINTF(LWRN, "device not found %s\r\n", xopts);
usb2_port = usb3_port = -1;
goto done;
}
UPRINTF(LDBG, "adding device %s, opts \"%s\"\r\n",
xopts, config);
dev = calloc(1, sizeof(struct pci_xhci_dev_emu));
if (!dev) {
usb2_port = usb3_port = -1;
UPRINTF(LWRN, "%s:%d fail to allocate memory\n",
__func__, __LINE__);
goto done;
}
dev->xdev = xdev;
dev->hci.dev = dev;
dev->hci.hci_intr = pci_xhci_dev_intr;
dev->hci.hci_event = pci_xhci_dev_event;
if (ue->ue_usbver == 2) {
dev->hci.hci_port = usb2_port + 1;
devices[usb2_port] = dev;
usb2_port++;
for (t = strtok(s, ",:"); t; t = strtok(NULL, ",:")) {
if (isdigit(t[0])) { /* bus-port */
if (pci_xhci_parse_bus_port(xdev, t)) {
rc = -3;
goto errout;
}
} else {
dev->hci.hci_port = usb3_port + 1;
devices[usb3_port] = dev;
usb3_port++;
for (i = 0; i < elem_cnt; i++) {
n = elem[i].parse_opt;
f = elem[i].parse_fn;
if (!n || !f)
continue;
if (!strncmp(t, n, strlen(n))) {
f(xdev, t);
break;
}
}
if (i >= elem_cnt) {
rc = -4;
goto errout;
}
}
dev->hci.hci_address = 0;
devins = ue->ue_init(&dev->hci, config);
if (devins == NULL) {
pci_xhci_device_usage(xopts);
usb2_port = usb3_port = -1;
goto done;
}
dev->dev_ue = ue;
dev->dev_instance = devins;
/* assign slot number to device */
xdev->slots[xdev->ndevices] = dev;
xdev->ndevices++;
}
portsfinal:
xdev->portregs =
calloc(XHCI_MAX_DEVS, sizeof(struct pci_xhci_portregs));
/* do not use the zero index element */
for (i = 1; i <= XHCI_MAX_DEVS; i++)
pci_xhci_init_port(xdev, i);
if (xdev->ndevices > 0) {
/* port and slot numbering start from 1
*
* TODO: This code is really dangerous...
* xdev->devices[0] and xdev->slots[0] are point to one
* invalid address. Only xdev->slots[1] is the real first
* item. Just use this design now due to the original xhci.c
* and the usb_mouse.c depend on it and it will be fixed
* in future.
*/
xdev->devices--;
xdev->portregs--;
xdev->slots--;
for (i = 1; i <= XHCI_MAX_DEVS; i++)
pci_xhci_init_port(xdev, i);
} else {
UPRINTF(LWRN, "no USB devices configured\r\n");
xdev->ndevices = 1;
}
done:
if (devices != NULL) {
if (usb2_port <= 0 && usb3_port <= 0) {
errout:
if (rc) {
if (xdev->devices) {
for (i = 1; i <= XHCI_MAX_DEVS && xdev->devices[i]; i++)
free(xdev->devices[i]);
xdev->ndevices = 0;
xdev->devices = NULL;
for (i = 0; devices[i] != NULL; i++)
free(devices[i]);
xdev->ndevices = -1;
free(devices);
free(xdev->devices);
}
if (xdev->slots) {
free(xdev->slots);
xdev->slots = NULL;
}
if (xdev->portregs) {
free(xdev->portregs);
xdev->portregs = NULL;
}
UPRINTF(LFTL, "fail to parse xHCI options, rc=%d\r\n", rc);
pci_xhci_device_usage(opts);
return rc;
}
free(s);
return xdev->ndevices;
}
@ -3246,13 +3327,14 @@ pci_xhci_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts)
* Will add command line option in subsequent patches for calling
* usb_dev_sys_init if new parameters are used.
*/
if (xdev->ndevices == 0)
if (usb_dev_sys_init(pci_xhci_native_usb_dev_conn_cb,
pci_xhci_native_usb_dev_disconn_cb,
pci_xhci_usb_dev_notify_cb,
pci_xhci_usb_dev_intr_cb,
xdev, usb_get_log_level()) < 0)
goto done;
if (usb_dev_sys_init(pci_xhci_native_usb_dev_conn_cb,
pci_xhci_native_usb_dev_disconn_cb,
pci_xhci_usb_dev_notify_cb,
pci_xhci_usb_dev_intr_cb,
xdev, usb_get_log_level()) < 0) {
error = -3;
goto done;
}
xdev->caplength = XHCI_SET_CAPLEN(XHCI_CAPLEN) |
XHCI_SET_HCIVERSION(0x0100);
@ -3315,8 +3397,10 @@ pci_xhci_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts)
pthread_mutex_init(&xdev->mtx, NULL);
done:
if (error)
if (error) {
UPRINTF(LFTL, "%s fail, error=%d\n", __func__, error);
free(xdev);
}
return error;
}