mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-06-25 23:13:26 +00:00
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:
parent
6449950ccc
commit
be4406c8d9
@ -42,6 +42,7 @@
|
|||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <ctype.h>
|
||||||
#include "usb.h"
|
#include "usb.h"
|
||||||
#include "usbdi.h"
|
#include "usbdi.h"
|
||||||
#include "xhcireg.h"
|
#include "xhcireg.h"
|
||||||
@ -266,6 +267,10 @@ struct pci_xhci_vdev {
|
|||||||
#define XHCI_HALTED(xdev) ((xdev)->opregs.usbsts & XHCI_STS_HCH)
|
#define XHCI_HALTED(xdev) ((xdev)->opregs.usbsts & XHCI_STS_HCH)
|
||||||
#define XHCI_GADDR(xdev, a) paddr_guest2host((xdev)->dev->vmctx, (a), \
|
#define XHCI_GADDR(xdev, a) paddr_guest2host((xdev)->dev->vmctx, (a), \
|
||||||
XHCI_PADDR_SZ - ((a) & (XHCI_PADDR_SZ-1)))
|
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;
|
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,
|
struct usb_data_xfer *xfer, uint32_t slot, uint32_t epid,
|
||||||
int *do_intr);
|
int *do_intr);
|
||||||
static inline int pci_xhci_is_valid_portnum(int n);
|
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
|
static int
|
||||||
pci_xhci_native_usb_dev_conn_cb(void *hci_data, void *dev_data)
|
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;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (port = 1; port < XHCI_MAX_DEVS; ++port) {
|
for (port = 1; port <= XHCI_MAX_DEVS; ++port) {
|
||||||
edev = xdev->devices[port];
|
edev = xdev->devices[port];
|
||||||
if (!edev)
|
if (!edev)
|
||||||
continue;
|
continue;
|
||||||
@ -467,7 +477,7 @@ pci_xhci_native_usb_dev_disconn_cb(void *hci_data, void *dev_data)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (port == XHCI_MAX_DEVS) {
|
if (port == XHCI_MAX_DEVS + 1) {
|
||||||
UPRINTF(LFTL, "fail to find physical port %d\r\n", native_port);
|
UPRINTF(LFTL, "fail to find physical port %d\r\n", native_port);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -632,7 +642,7 @@ pci_xhci_dev_destroy(struct pci_xhci_dev_emu *de)
|
|||||||
static inline int
|
static inline int
|
||||||
pci_xhci_is_valid_portnum(int n)
|
pci_xhci_is_valid_portnum(int n)
|
||||||
{
|
{
|
||||||
return n > 0 && n < XHCI_MAX_DEVS;
|
return n > 0 && n <= XHCI_MAX_DEVS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
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])
|
if (dev == xdev->devices[i])
|
||||||
break;
|
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_PORTREG_PTR(xdev, i)->portsc &= ~(XHCI_PS_CSC |
|
||||||
XHCI_PS_CCS | XHCI_PS_PED | XHCI_PS_PP);
|
XHCI_PS_CCS | XHCI_PS_PED | XHCI_PS_PP);
|
||||||
xdev->devices[i] = NULL;
|
xdev->devices[i] = NULL;
|
||||||
@ -3060,152 +3070,223 @@ pci_xhci_dev_event(struct usb_hci *hci, enum hci_usbev evid, void *param)
|
|||||||
static void
|
static void
|
||||||
pci_xhci_device_usage(char *opt)
|
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
|
static int
|
||||||
pci_xhci_parse_opts(struct pci_xhci_vdev *xdev, char *opts)
|
pci_xhci_parse_opts(struct pci_xhci_vdev *xdev, char *opts)
|
||||||
{
|
{
|
||||||
struct pci_xhci_dev_emu **devices;
|
char *s, *t, *n;
|
||||||
struct pci_xhci_dev_emu *dev;
|
int i, rc = 0;
|
||||||
struct usb_devemu *ue;
|
struct pci_xhci_option_elem *elem;
|
||||||
void *devins;
|
int (*f)(struct pci_xhci_vdev *, char *);
|
||||||
char *uopt, *xopts, *config;
|
int elem_cnt;
|
||||||
int usb3_port, usb2_port, i;
|
|
||||||
|
|
||||||
usb3_port = xdev->usb3_port_start - 1;
|
assert(xdev);
|
||||||
usb2_port = xdev->usb2_port_start - 1;
|
if (!opts) {
|
||||||
devices = NULL;
|
rc = -1;
|
||||||
|
goto errout;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
xdev->slots = calloc(XHCI_MAX_SLOTS, sizeof(struct pci_xhci_dev_emu *));
|
/* allocate neccessary resources during parsing*/
|
||||||
if (xdev->slots == NULL) {
|
xdev->devices = calloc(XHCI_MAX_DEVS + 1, sizeof(*xdev->devices));
|
||||||
usb2_port = usb3_port = -1;
|
xdev->slots = calloc(XHCI_MAX_SLOTS, sizeof(*xdev->slots));
|
||||||
UPRINTF(LWRN, "%s:%d fail to allocate memory(slots)\n",
|
xdev->portregs = calloc(XHCI_MAX_DEVS + 1, sizeof(*xdev->portregs));
|
||||||
__func__, __LINE__);
|
if (!xdev->devices || !xdev->slots || !xdev->portregs) {
|
||||||
goto done;
|
rc = -2;
|
||||||
|
goto errout;
|
||||||
}
|
}
|
||||||
|
|
||||||
xdev->devices = devices;
|
s = strdup(opts);
|
||||||
xdev->ndevices = 0;
|
UPRINTF(LDBG, "options: %s\r\n", s);
|
||||||
|
|
||||||
uopt = strdup(opts);
|
elem = xhci_option_table;
|
||||||
for (xopts = strtok(uopt, ",");
|
elem_cnt = sizeof(xhci_option_table) / sizeof(*elem);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* device[=<config>] */
|
for (t = strtok(s, ",:"); t; t = strtok(NULL, ",:")) {
|
||||||
config = strchr(xopts, '=');
|
if (isdigit(t[0])) { /* bus-port */
|
||||||
if (config == NULL)
|
if (pci_xhci_parse_bus_port(xdev, t)) {
|
||||||
config = ""; /* no config */
|
rc = -3;
|
||||||
else
|
goto errout;
|
||||||
*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++;
|
|
||||||
} else {
|
} else {
|
||||||
dev->hci.hci_port = usb3_port + 1;
|
for (i = 0; i < elem_cnt; i++) {
|
||||||
devices[usb3_port] = dev;
|
n = elem[i].parse_opt;
|
||||||
usb3_port++;
|
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:
|
/* do not use the zero index element */
|
||||||
xdev->portregs =
|
for (i = 1; i <= XHCI_MAX_DEVS; i++)
|
||||||
calloc(XHCI_MAX_DEVS, sizeof(struct pci_xhci_portregs));
|
pci_xhci_init_port(xdev, i);
|
||||||
|
|
||||||
if (xdev->ndevices > 0) {
|
errout:
|
||||||
/* port and slot numbering start from 1
|
if (rc) {
|
||||||
*
|
if (xdev->devices) {
|
||||||
* TODO: This code is really dangerous...
|
for (i = 1; i <= XHCI_MAX_DEVS && xdev->devices[i]; i++)
|
||||||
* xdev->devices[0] and xdev->slots[0] are point to one
|
free(xdev->devices[i]);
|
||||||
* invalid address. Only xdev->slots[1] is the real first
|
xdev->ndevices = 0;
|
||||||
* 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) {
|
|
||||||
xdev->devices = NULL;
|
xdev->devices = NULL;
|
||||||
for (i = 0; devices[i] != NULL; i++)
|
free(xdev->devices);
|
||||||
free(devices[i]);
|
|
||||||
xdev->ndevices = -1;
|
|
||||||
|
|
||||||
free(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;
|
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
|
* Will add command line option in subsequent patches for calling
|
||||||
* usb_dev_sys_init if new parameters are used.
|
* 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,
|
||||||
if (usb_dev_sys_init(pci_xhci_native_usb_dev_conn_cb,
|
pci_xhci_native_usb_dev_disconn_cb,
|
||||||
pci_xhci_native_usb_dev_disconn_cb,
|
pci_xhci_usb_dev_notify_cb,
|
||||||
pci_xhci_usb_dev_notify_cb,
|
pci_xhci_usb_dev_intr_cb,
|
||||||
pci_xhci_usb_dev_intr_cb,
|
xdev, usb_get_log_level()) < 0) {
|
||||||
xdev, usb_get_log_level()) < 0)
|
error = -3;
|
||||||
goto done;
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
xdev->caplength = XHCI_SET_CAPLEN(XHCI_CAPLEN) |
|
xdev->caplength = XHCI_SET_CAPLEN(XHCI_CAPLEN) |
|
||||||
XHCI_SET_HCIVERSION(0x0100);
|
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);
|
pthread_mutex_init(&xdev->mtx, NULL);
|
||||||
|
|
||||||
done:
|
done:
|
||||||
if (error)
|
if (error) {
|
||||||
|
UPRINTF(LFTL, "%s fail, error=%d\n", __func__, error);
|
||||||
free(xdev);
|
free(xdev);
|
||||||
|
}
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user