Files
acrn-hypervisor/devicemodel/hw/platform/usb_pmapper.c
Xiaoguang Wu 8317dea64c DM USB: fix guest kernel short packets warning
There are many 'short packet' warnings in the UOS kernel dmesg output,
which are result from bad short packet identification algorithm. This
patch is used to fix it.

Change-Id: Idfa0b87fc96893b80d5c9fe8dab4db35aa5bfe84
Tracked-On:
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>
2018-07-20 10:42:34 +08:00

1015 lines
22 KiB
C

/*
* Copyright (C) 2018 Intel Corporation. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#include <pthread.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include "usb.h"
#include "usbdi.h"
#include "usb_pmapper.h"
#undef LOG_TAG
#define LOG_TAG "USBPM: "
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 void
usb_dev_comp_req(struct libusb_transfer *libusb_xfer)
{
struct usb_dev_req *req;
struct usb_data_xfer *xfer;
struct usb_data_xfer_block *block;
int len, do_intr = 0, short_data = 0;
int i, idx, buf_idx, done;
int bstart, bcount;
assert(libusb_xfer);
/* async request */
req = libusb_xfer->user_data;
len = libusb_xfer->actual_length;
assert(req);
assert(req->udev);
/* async transfer */
xfer = req->xfer;
assert(xfer);
assert(xfer->dev);
bstart = req->blk_start;
bcount = req->blk_count;
UPRINTF(LDBG, "%s: actual_length %d ep%d-transfer (%d-%d %d) request-%d"
" (%d-%d %d) status %d\r\n", __func__, len, xfer->epid,
xfer->head, (xfer->tail - 1) % USB_MAX_XFER_BLOCKS,
xfer->ndata, req->seq, bstart, (bstart + bcount - 1) %
USB_MAX_XFER_BLOCKS, req->buf_length,
libusb_xfer->status);
/* lock for protecting the transfer */
USB_DATA_XFER_LOCK(xfer);
xfer->status = USB_ERR_NORMAL_COMPLETION;
switch (libusb_xfer->status) {
case LIBUSB_TRANSFER_STALL:
xfer->status = USB_ERR_STALLED;
goto out;
case LIBUSB_TRANSFER_NO_DEVICE:
/* avoid short packet warnings when devices are plugged out. */
xfer->status = USB_ERR_SHORT_XFER;
goto out;
case LIBUSB_TRANSFER_ERROR:
xfer->status = USB_ERR_STALLED;
goto out;
case LIBUSB_TRANSFER_CANCELLED:
xfer->status = USB_ERR_IOERROR;
goto out;
case LIBUSB_TRANSFER_TIMED_OUT:
xfer->status = USB_ERR_TIMEOUT;
goto out;
case LIBUSB_TRANSFER_OVERFLOW:
xfer->status = USB_ERR_BAD_BUFSIZE;
goto out;
case LIBUSB_TRANSFER_COMPLETED:
break;
default:
UPRINTF(LWRN, "unknown failure: %x\r\n", libusb_xfer->status);
break;
}
/* in case the xfer is reset by the USB_DATA_XFER_RESET */
if (xfer->reset == 1) {
UPRINTF(LDBG, "ep%d reset detected\r\n", xfer->epid);
xfer->reset = 0;
/* ONLY interrupt transfer needs this.
* The transfer here is an old one before endpoint reset, so it
* should be discarded. But for bulk transfer, the transfer here
* is a new one after reset, so it should be kept.
*/
if (usb_dev_get_ep_type(req->udev, xfer->pid & 1,
xfer->epid / 2) == USB_ENDPOINT_INT) {
UPRINTF(LDBG, "goto reset out\r\n");
goto reset_out;
}
}
/* post process the usb transfer data */
buf_idx = 0;
idx = req->blk_start;
for (i = 0; i < req->blk_count; i++) {
done = 0;
block = &xfer->data[idx % USB_MAX_XFER_BLOCKS];
/* Link TRB need to be skipped */
if (!block->buf || !block->blen) {
idx = (idx + 1) % USB_MAX_XFER_BLOCKS;
continue;
}
if (len > buf_idx) {
done = block->blen;
if (done > len - buf_idx) {
done = len - buf_idx;
short_data = 1;
}
if (req->in)
memcpy(block->buf, &req->buffer[buf_idx], done);
}
assert(block->processed);
buf_idx += done;
block->bdone = done;
block->blen -= done;
idx = (idx + 1) % USB_MAX_XFER_BLOCKS;
}
if (short_data)
xfer->status = USB_ERR_SHORT_XFER;
out:
/* notify the USB core this transfer is over */
if (g_ctx.notify_cb)
do_intr = g_ctx.notify_cb(xfer->dev, xfer);
/* if a interrupt is needed, send it to guest */
if (do_intr && g_ctx.intr_cb)
g_ctx.intr_cb(xfer->dev, NULL);
reset_out:
/* unlock and release memory */
USB_DATA_XFER_UNLOCK(xfer);
libusb_free_transfer(libusb_xfer);
if (req && req->buffer)
free(req->buffer);
free(req);
}
static struct usb_dev_req *
usb_dev_alloc_req(struct usb_dev *udev, struct usb_data_xfer *xfer, int in,
size_t size)
{
struct usb_dev_req *req;
static int seq = 1;
if (!udev || !xfer)
return NULL;
req = calloc(1, sizeof(*req));
if (!req)
return NULL;
req->udev = udev;
req->in = in;
req->xfer = xfer;
req->seq = seq++;
req->libusb_xfer = libusb_alloc_transfer(0);
if (!req->libusb_xfer)
goto errout;
if (size)
req->buffer = malloc(size);
if (!req->buffer)
goto errout;
return req;
errout:
if (req && req->buffer)
free(req->buffer);
if (req && req->libusb_xfer)
libusb_free_transfer(req->libusb_xfer);
if (req)
free(req);
return NULL;
}
static int
usb_dev_prepare_xfer(struct usb_data_xfer *xfer, int *count, int *size)
{
int found, i, idx, c, s, first;
struct usb_data_xfer_block *block = NULL;
assert(xfer);
idx = xfer->head;
found = 0;
first = -1;
c = s = 0;
if (!count || !size || idx < 0 || idx >= USB_MAX_XFER_BLOCKS)
return -1;
for (i = 0; i < xfer->ndata; i++) {
block = &xfer->data[idx];
if (block->processed) {
idx = (idx + 1) % USB_MAX_XFER_BLOCKS;
c++;
continue;
}
if (block->buf && block->blen > 0) {
if (!found) {
found = 1;
first = idx;
}
c++;
s += block->blen;
} else if (found) {
UPRINTF(LWRN, "find a NULL data. %d total %d\n",
i, xfer->ndata);
}
block->processed = 1;
idx = (idx + 1) % USB_MAX_XFER_BLOCKS;
}
*count = c;
*size = s;
return first;
}
static inline int
usb_dev_err_convert(int err)
{
switch (err) {
case LIBUSB_ERROR_TIMEOUT: return USB_ERR_TIMEOUT;
case LIBUSB_ERROR_PIPE: return USB_ERR_STALLED;
case LIBUSB_ERROR_NO_DEVICE: return USB_ERR_INVAL;
case LIBUSB_ERROR_BUSY: return USB_ERR_IN_USE;
case LIBUSB_ERROR_OVERFLOW: return USB_ERR_TOO_DEEP;
default:
break; /* add more when required */
}
return USB_ERR_IOERROR;
}
static inline struct usb_dev_ep *
usb_dev_get_ep(struct usb_dev *udev, int pid, int ep)
{
assert(udev);
if (ep < 0 || ep >= USB_NUM_ENDPOINT) {
UPRINTF(LWRN, "invalid ep %d\r\n", ep);
return NULL;
}
if (ep == 0)
return &udev->epc;
if (pid == TOKEN_IN)
return udev->epi + ep - 1;
else
return udev->epo + ep - 1;
}
static inline void
usb_dev_set_ep_type(struct usb_dev *udev, int pid, int epnum,
uint8_t type)
{
struct usb_dev_ep *ep;
ep = usb_dev_get_ep(udev, pid, epnum);
if (ep)
ep->type = type;
}
static inline uint8_t
usb_dev_get_ep_type(struct usb_dev *udev, int pid, int epnum)
{
struct usb_dev_ep *ep;
ep = usb_dev_get_ep(udev, pid, epnum);
if (!ep)
return USB_EP_ERR_TYPE;
else
return ep->type;
}
static void
usb_dev_reset_ep(struct usb_dev *udev)
{
int ep;
udev->epc.type = USB_ENDPOINT_CONTROL;
for (ep = 0; ep < USB_NUM_ENDPOINT; ep++) {
udev->epi[ep].pid = TOKEN_IN;
udev->epo[ep].pid = TOKEN_OUT;
udev->epi[ep].type = USB_ENDPOINT_INVALID;
udev->epo[ep].type = USB_ENDPOINT_INVALID;
}
}
static void
usb_dev_update_ep(struct usb_dev *udev)
{
struct libusb_config_descriptor *cfg;
const struct libusb_interface_descriptor *_if;
const struct libusb_endpoint_descriptor *desc;
int i, j;
assert(udev);
if (libusb_get_active_config_descriptor(udev->ldev, &cfg))
return;
for (i = 0; i < cfg->bNumInterfaces; i++) {
_if = &cfg->interface[i].altsetting[udev->alts[i]];
for (j = 0; j < _if->bNumEndpoints; j++) {
desc = &_if->endpoint[j];
usb_dev_set_ep_type(udev,
USB_EP_PID(desc),
USB_EP_NR(desc),
USB_EP_TYPE(desc));
}
}
libusb_free_config_descriptor(cfg);
}
static int
usb_dev_native_toggle_if(struct usb_dev *udev, int claim)
{
struct libusb_config_descriptor *config;
uint8_t b, p, c, i;
int rc = 0, r;
assert(udev);
assert(udev->handle);
assert(udev->ldev);
assert(claim == 1 || claim == 0);
b = udev->bus;
p = udev->port;
r = libusb_get_active_config_descriptor(udev->ldev, &config);
if (r) {
UPRINTF(LWRN, "%d-%d: can't get config\r\n", b, p);
return -1;
}
c = config->bConfigurationValue;
for (i = 0; i < config->bNumInterfaces; i++) {
if (claim == 1)
r = libusb_claim_interface(udev->handle, i);
else
r = libusb_release_interface(udev->handle, i);
if (r) {
rc = -1;
UPRINTF(LWRN, "%d-%d:%d.%d can't %s if\r\n", b, p, c, i,
claim == 1 ? "claim" : "release");
}
}
if (rc)
UPRINTF(LWRN, "%d-%d fail to %s rc %d\r\n", b, p,
claim == 1 ? "claim" : "release", rc);
libusb_free_config_descriptor(config);
return rc;
}
static int
usb_dev_native_toggle_if_drivers(struct usb_dev *udev, int attach)
{
struct libusb_config_descriptor *config;
uint8_t b, p, c, i;
int rc = 0, r;
assert(udev);
assert(udev->handle);
assert(udev->ldev);
assert(attach == 1 || attach == 0);
b = udev->bus;
p = udev->port;
r = libusb_get_active_config_descriptor(udev->ldev, &config);
if (r) {
UPRINTF(LWRN, "%d-%d: can't get config\r\n", b, p);
return -1;
}
c = config->bConfigurationValue;
for (i = 0; i < config->bNumInterfaces; i++) {
if (attach == 1)
r = libusb_attach_kernel_driver(udev->handle, i);
else
r = libusb_detach_kernel_driver(udev->handle, i);
if (r) {
rc = -1;
UPRINTF(LWRN, "%d-%d:%d.%d can't %stach if driver\r\n",
b, p, c, i, attach == 1 ? "at" : "de");
}
}
if (rc)
UPRINTF(LWRN, "%d-%d fail to %s rc %d\r\n", b, p,
attach == 1 ? "attach" : "detach", rc);
libusb_free_config_descriptor(config);
return rc;
}
static void
usb_dev_set_config(struct usb_dev *udev, struct usb_data_xfer *xfer, int config)
{
int rc = 0;
struct libusb_config_descriptor *cfg;
assert(udev);
assert(udev->ldev);
assert(udev->handle);
/*
* set configuration
* according to the libusb doc, the detach and release work
* should be done before set configuration.
*/
usb_dev_native_toggle_if_drivers(udev, 0);
usb_dev_native_toggle_if(udev, 0);
rc = libusb_set_configuration(udev->handle, config);
if (rc) {
UPRINTF(LWRN, "fail to set config rc %d\r\n", rc);
goto err2;
}
/* claim all the interfaces of this configuration */
rc = libusb_get_active_config_descriptor(udev->ldev, &cfg);
if (rc) {
UPRINTF(LWRN, "fail to get config rc %d\r\n", rc);
goto err2;
}
rc = usb_dev_native_toggle_if(udev, 1);
if (rc) {
UPRINTF(LWRN, "fail to claim if, rc %d\r\n", rc);
goto err1;
}
udev->if_num = cfg->bNumInterfaces;
udev->configuration = config;
usb_dev_reset_ep(udev);
usb_dev_update_ep(udev);
libusb_free_config_descriptor(cfg);
return;
err1:
usb_dev_native_toggle_if(udev, 0);
libusb_free_config_descriptor(cfg);
err2:
UPRINTF(LWRN, "%d-%d: fail to set config\r\n", udev->bus, udev->port);
xfer->status = USB_ERR_STALLED;
}
static void
usb_dev_set_if(struct usb_dev *udev, int iface, int alt, struct usb_data_xfer
*xfer)
{
assert(udev);
assert(xfer);
assert(udev->handle);
if (iface >= USB_NUM_INTERFACE)
goto errout;
UPRINTF(LDBG, "%d-%d set if, iface %d alt %d\r\n", udev->bus,
udev->port, iface, alt);
if (libusb_set_interface_alt_setting(udev->handle, iface, alt))
goto errout;
udev->alts[iface] = alt;
/*
* FIXME: Only support single interface USB device first. Need fix in
* future to support composite USB device.
*/
usb_dev_reset_ep(udev);
usb_dev_update_ep(udev);
return;
errout:
xfer->status = USB_ERR_STALLED;
UPRINTF(LDBG, "%d-%d fail to set if, iface %d alt %d\r\n",
udev->bus, udev->port, iface, alt);
}
static struct usb_data_xfer_block *
usb_dev_prepare_ctrl_xfer(struct usb_data_xfer *xfer)
{
int i, idx;
struct usb_data_xfer_block *ret = NULL;
struct usb_data_xfer_block *blk = NULL;
idx = xfer->head;
if (idx < 0 || idx >= USB_MAX_XFER_BLOCKS)
return NULL;
for (i = 0; i < xfer->ndata; i++) {
/*
* find out the data block and set every
* block to be processed
*/
blk = &xfer->data[idx];
if (blk->blen > 0 && !ret)
ret = blk;
blk->processed = 1;
idx = (idx + 1) % USB_MAX_XFER_BLOCKS;
}
return ret;
}
int
usb_dev_reset(void *pdata)
{
struct usb_dev *udev;
udev = pdata;
assert(udev);
UPRINTF(LDBG, "reset endpoints\n");
libusb_reset_device(udev->handle);
usb_dev_reset_ep(udev);
usb_dev_update_ep(udev);
return 0;
}
int
usb_dev_data(void *pdata, struct usb_data_xfer *xfer, int dir, int epctx)
{
struct usb_dev *udev;
struct usb_dev_req *req;
int rc = 0, epid;
uint8_t type;
int blk_start, data_size, blk_count;
int retries = 3, i, buf_idx;
struct usb_data_xfer_block *b;
udev = pdata;
assert(udev);
xfer->status = USB_ERR_NORMAL_COMPLETION;
blk_start = usb_dev_prepare_xfer(xfer, &blk_count, &data_size);
if (blk_start < 0)
goto done;
type = usb_dev_get_ep_type(udev, dir ? TOKEN_IN : TOKEN_OUT, epctx);
epid = dir ? (0x80 | epctx) : epctx;
if (data_size <= 0)
goto done;
req = usb_dev_alloc_req(udev, xfer, dir, data_size);
if (!req) {
xfer->status = USB_ERR_IOERROR;
goto done;
}
req->buf_length = data_size;
req->blk_start = blk_start;
req->blk_count = blk_count;
if (!dir) {
for (i = 0, buf_idx = 0; i < blk_count; i++) {
b = &xfer->data[(blk_start + i) % USB_MAX_XFER_BLOCKS];
if (b->buf) {
memcpy(&req->buffer[buf_idx], b->buf, b->blen);
buf_idx += b->blen;
}
}
}
if (type == USB_ENDPOINT_BULK) {
/*
* give data to physical device through libusb.
* This is an asynchronous process, data is sent to libusb.so,
* and it may be not sent to physical device instantly, but
* just return here. After the data is really received by the
* physical device, the callback function usb_dev_comp_req
* will be triggered.
*/
/*
* TODO: Is there any risk of data missing?
*/
libusb_fill_bulk_transfer(req->libusb_xfer,
udev->handle, epid,
req->buffer,
data_size,
usb_dev_comp_req,
req,
0);
do {
rc = libusb_submit_transfer(req->libusb_xfer);
} while (rc && retries--);
} else if (type == USB_ENDPOINT_INT) {
/* give data to physical device through libusb */
libusb_fill_interrupt_transfer(req->libusb_xfer,
udev->handle,
epid,
req->buffer,
data_size,
usb_dev_comp_req,
req,
0);
rc = libusb_submit_transfer(req->libusb_xfer);
} else {
/* TODO isoch transfer is not implemented */
UPRINTF(LWRN, "ISOCH transfer still not supported.\n");
xfer->status = USB_ERR_INVAL;
}
if (rc) {
xfer->status = USB_ERR_IOERROR;
UPRINTF(LDBG, "libusb_submit_transfer fail: %d\n", rc);
}
done:
return xfer->status;
}
int
usb_dev_request(void *pdata, struct usb_data_xfer *xfer)
{
struct usb_dev *udev;
uint8_t request_type;
uint8_t request;
uint16_t value;
uint16_t index;
uint16_t len;
struct usb_data_xfer_block *blk;
uint8_t *data;
int rc;
udev = pdata;
assert(xfer);
assert(udev);
xfer->status = USB_ERR_NORMAL_COMPLETION;
if (!udev->ldev || !xfer->ureq) {
UPRINTF(LWRN, "invalid request\r\n");
xfer->status = USB_ERR_IOERROR;
goto out;
}
request_type = xfer->ureq->bmRequestType;
request = xfer->ureq->bRequest;
value = xfer->ureq->wValue;
index = xfer->ureq->wIndex;
len = xfer->ureq->wLength;
blk = usb_dev_prepare_ctrl_xfer(xfer);
data = blk ? blk->buf : NULL;
UPRINTF(LDBG,
"urb: type 0x%x req 0x%x val 0x%x idx %d len %d data %d\n",
request_type, request, value, index, len,
blk ? blk->blen : 0);
/*
* according to usb spec, control transfer may have no
* DATA STAGE, so the valid situations are:
* a. with DATA STAGE: blk != NULL && len > 0
* b. without DATA STAGE: blk == NULL && len == 0
* any other situations, just skip process
*/
if ((!blk && len > 0) || (blk && len <= 0))
goto out;
switch (UREQ(request, request_type)) {
case UREQ(UR_SET_ADDRESS, UT_WRITE_DEVICE):
UPRINTF(LDBG, "UR_SET_ADDRESS\n");
udev->addr = value;
goto out;
case UREQ(UR_SET_CONFIG, UT_WRITE_DEVICE):
UPRINTF(LDBG, "UR_SET_CONFIG\n");
usb_dev_set_config(udev, xfer, value & 0xff);
goto out;
case UREQ(UR_SET_INTERFACE, UT_WRITE_INTERFACE):
UPRINTF(LDBG, "UR_SET_INTERFACE\n");
usb_dev_set_if(udev, index, value, xfer);
goto out;
case UREQ(UR_CLEAR_FEATURE, UT_WRITE_ENDPOINT):
if (value) {
/* according to usb spec (ch9), this is impossible */
UPRINTF(LWRN, "Clear Feature request with non-zero "
"value %d\r\n", value);
break;
}
UPRINTF(LDBG, "UR_CLEAR_HALT\n");
rc = libusb_clear_halt(udev->handle, index);
if (rc)
UPRINTF(LWRN, "fail to clear halted ep, rc %d\r\n", rc);
goto out;
}
/* send it to physical device */
/* TODO: should this be async operation? */
rc = libusb_control_transfer(udev->handle, request_type, request,
value, index, data, len, 100);
if (rc >= 0 && blk) {
blk->blen = len - rc;
blk->bdone += rc;
xfer->status = blk->blen > 0 ? USB_ERR_SHORT_XFER :
USB_ERR_NORMAL_COMPLETION;
} else if (rc >= 0)
xfer->status = USB_ERR_NORMAL_COMPLETION;
else
xfer->status = usb_dev_err_convert(rc);
UPRINTF(LDBG, "usb rc %d, blk %p, blen %u bdon %u\n", rc, blk,
blk ? blk->blen : 0, blk ? blk->bdone : 0);
out:
return xfer->status;
}
void *
usb_dev_init(void *pdata, char *opt)
{
struct usb_dev *udev = NULL;
struct libusb_device *ldev;
struct libusb_device_descriptor desc;
uint8_t bus, port;
int speed, ver;
assert(pdata);
ldev = pdata;
speed = libusb_get_device_speed(ldev);
port = libusb_get_port_number(ldev);
bus = libusb_get_bus_number(ldev);
libusb_get_device_descriptor(ldev, &desc);
UPRINTF(LINF, "Found USB device: %d-%d\r\nPID(0x%X), VID(0x%X) CLASS"
"(0x%X) SUBCLASS(0x%X) BCD(0x%X) SPEED(%d)\r\n", bus,
port, desc.idProduct, desc.idVendor, desc.bDeviceClass,
desc.bDeviceSubClass, desc.bcdUSB, speed);
/* allocate and populate udev */
udev = calloc(1, sizeof(struct usb_dev));
if (!udev)
goto errout;
/* this is a root hub */
if (port == 0)
goto errout;
switch (desc.bcdUSB) { /* TODO: implemnt USB3.0 */
case 0x300:
ver = 2; break;
case 0x200:
case 0x110:
ver = 2; break;
default:
goto errout;
}
udev->speed = speed;
udev->ldev = ldev;
udev->version = ver;
udev->handle = NULL;
udev->port = port;
udev->bus = bus;
udev->pid = desc.idProduct;
udev->vid = desc.idVendor;
/* configure physical device through libusb library */
if (libusb_open(udev->ldev, &udev->handle)) {
UPRINTF(LWRN, "fail to open device.\r\n");
goto errout;
}
if (usb_dev_native_toggle_if_drivers(udev, 0) < 0) {
UPRINTF(LWRN, "fail to detach interface driver.\r\n");
goto errout;
}
return udev;
errout:
if (udev && udev->handle)
libusb_close(udev->handle);
free(udev);
return NULL;
}
void
usb_dev_deinit(void *pdata)
{
int rc = 0;
struct usb_dev *udev;
udev = pdata;
if (udev) {
if (udev->handle) {
rc = usb_dev_native_toggle_if_drivers(udev, 1);
if (rc)
UPRINTF(LWRN, "fail to attach if drv rc:%d\r\n",
rc);
libusb_close(udev->handle);
}
free(udev);
}
}
int
usb_dev_info(void *pdata, int type, void *value, int size)
{
struct usb_dev *udev;
int sz;
void *pv;
udev = pdata;
assert(udev);
assert(value);
switch (type) {
case USB_INFO_VERSION:
sz = sizeof(udev->version);
pv = &udev->version;
break;
case USB_INFO_SPEED:
sz = sizeof(udev->speed);
pv = &udev->speed;
break;
case USB_INFO_BUS:
sz = sizeof(udev->bus);
pv = &udev->bus;
break;
case USB_INFO_PORT:
sz = sizeof(udev->port);
pv = &udev->port;
break;
case USB_INFO_VID:
sz = sizeof(udev->vid);
pv = &udev->vid;
break;
case USB_INFO_PID:
sz = sizeof(udev->pid);
pv = &udev->pid;
break;
default:
return -1;
}
if (size == sz)
memcpy(value, pv, sz);
return sz == size ? 0 : -1;
}
static void *
usb_dev_sys_thread(void *arg)
{
struct timeval t = {1, 0};
while (g_ctx.thread_exit == 0 &&
libusb_handle_events_timeout(g_ctx.libusb_ctx, &t) >= 0)
; /* nothing */
UPRINTF(LINF, "poll thread exit\n\r");
return NULL;
}
static int
usb_dev_native_sys_conn_cb(struct libusb_context *ctx, struct libusb_device
*ldev, libusb_hotplug_event event, void *pdata)
{
UPRINTF(LDBG, "connect event\r\n");
if (!ctx || !ldev) {
UPRINTF(LFTL, "connect callback fails!\n");
return -1;
}
if (g_ctx.conn_cb)
g_ctx.conn_cb(g_ctx.hci_data, ldev);
return 0;
}
static int
usb_dev_native_sys_disconn_cb(struct libusb_context *ctx, struct libusb_device
*ldev, libusb_hotplug_event event, void *pdata)
{
uint8_t port;
UPRINTF(LDBG, "disconnect event\r\n");
if (!ctx || !ldev) {
UPRINTF(LFTL, "disconnect callback fails!\n");
return -1;
}
port = libusb_get_port_number(ldev);
if (g_ctx.disconn_cb)
g_ctx.disconn_cb(g_ctx.hci_data, &port);
return 0;
}
int
usb_dev_sys_init(usb_dev_sys_cb conn_cb, usb_dev_sys_cb disconn_cb,
usb_dev_sys_cb notify_cb, usb_dev_sys_cb intr_cb,
void *hci_data, int log_level)
{
libusb_hotplug_event native_conn_evt;
libusb_hotplug_event native_disconn_evt;
libusb_hotplug_flag flags;
libusb_hotplug_callback_handle native_conn_handle;
libusb_hotplug_callback_handle native_disconn_handle;
int native_pid, native_vid, native_cls, rc;
assert(conn_cb);
assert(disconn_cb);
usb_set_log_level(log_level);
if (g_ctx.libusb_ctx) {
UPRINTF(LFTL, "port mapper is already initialized.\r\n");
return -1;
}
rc = libusb_init(&g_ctx.libusb_ctx);
if (rc < 0) {
UPRINTF(LFTL, "libusb_init fails, rc:%d\r\n", rc);
return -1;
}
g_ctx.hci_data = hci_data;
g_ctx.conn_cb = conn_cb;
g_ctx.disconn_cb = disconn_cb;
g_ctx.notify_cb = notify_cb;
g_ctx.intr_cb = intr_cb;
native_conn_evt = LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED;
native_disconn_evt = LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT;
native_pid = LIBUSB_HOTPLUG_MATCH_ANY;
native_vid = LIBUSB_HOTPLUG_MATCH_ANY;
native_cls = LIBUSB_HOTPLUG_MATCH_ANY;
flags = 0;
/* register connect callback */
rc = libusb_hotplug_register_callback(g_ctx.libusb_ctx, native_conn_evt,
flags, native_vid, native_pid, native_cls,
usb_dev_native_sys_conn_cb, NULL, &native_conn_handle);
if (rc != LIBUSB_SUCCESS)
goto errout;
/* register disconnect callback */
rc = libusb_hotplug_register_callback(g_ctx.libusb_ctx,
native_disconn_evt, flags, native_vid, native_pid,
native_cls, usb_dev_native_sys_disconn_cb, NULL,
&native_disconn_handle);
if (rc != LIBUSB_SUCCESS) {
libusb_hotplug_deregister_callback(g_ctx.libusb_ctx,
native_conn_handle);
goto errout;
}
/* this is for guest rebooting purpose */
g_ctx.conn_handle = native_conn_handle;
g_ctx.disconn_handle = native_disconn_handle;
g_ctx.thread_exit = 0;
if (pthread_create(&g_ctx.thread, NULL, usb_dev_sys_thread, NULL)) {
libusb_hotplug_deregister_callback(g_ctx.libusb_ctx,
native_conn_handle);
libusb_hotplug_deregister_callback(g_ctx.libusb_ctx,
native_disconn_handle);
goto errout;
}
return 0;
errout:
if (g_ctx.libusb_ctx)
libusb_exit(g_ctx.libusb_ctx);
g_ctx.libusb_ctx = NULL;
return -1;
}
void
usb_dev_sys_deinit(void)
{
if (!g_ctx.libusb_ctx)
return;
UPRINTF(LINF, "port-mapper de-initialization\r\n");
libusb_hotplug_deregister_callback(g_ctx.libusb_ctx, g_ctx.conn_handle);
libusb_hotplug_deregister_callback(g_ctx.libusb_ctx,
g_ctx.disconn_handle);
g_ctx.thread_exit = 1;
pthread_join(g_ctx.thread, NULL);
libusb_exit(g_ctx.libusb_ctx);
g_ctx.libusb_ctx = NULL;
}