mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-08-05 18:25:05 +00:00
DM USB: xHCI: support bulk and interrupt transfer for port mapper
Support USB mouse, USB keyboard and USB flash drive by enabling the USB bulk and interrupt transfer for port mapper. Change-Id: Ia202729e0cfb26fb44a6b278cf4306f2b0b6fa36 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
3b6392740e
commit
7687a3d0d7
@ -322,6 +322,9 @@ static void pci_xhci_dev_destroy(struct pci_xhci_dev_emu *de);
|
|||||||
static int pci_xhci_port_chg(struct pci_xhci_vdev *xdev, int port, int conn);
|
static int pci_xhci_port_chg(struct pci_xhci_vdev *xdev, int port, int conn);
|
||||||
static void pci_xhci_set_evtrb(struct xhci_trb *evtrb, uint64_t port,
|
static void pci_xhci_set_evtrb(struct xhci_trb *evtrb, uint64_t port,
|
||||||
uint32_t errcode, uint32_t evtype);
|
uint32_t errcode, uint32_t evtype);
|
||||||
|
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 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)
|
||||||
@ -417,6 +420,56 @@ pci_xhci_native_usb_dev_disconn_cb(void *hci_data, void *dev_data)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* return value:
|
||||||
|
* = 0: succeed without interrupt
|
||||||
|
* > 0: succeed with interrupt
|
||||||
|
* < 0: failure
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
pci_xhci_usb_dev_notify_cb(void *hci_data, void *udev_data)
|
||||||
|
{
|
||||||
|
int slot, epid, intr, rc;
|
||||||
|
struct usb_data_xfer *xfer;
|
||||||
|
struct pci_xhci_dev_emu *edev;
|
||||||
|
struct pci_xhci_vdev *xdev;
|
||||||
|
|
||||||
|
xfer = udev_data;
|
||||||
|
if (!xfer)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
epid = xfer->epid;
|
||||||
|
edev = xfer->dev;
|
||||||
|
if (!edev)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
xdev = edev->xdev;
|
||||||
|
if (!xdev)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
slot = edev->hci.hci_address;
|
||||||
|
rc = pci_xhci_xfer_complete(xdev, xfer, slot, epid, &intr);
|
||||||
|
|
||||||
|
if (rc)
|
||||||
|
return -1;
|
||||||
|
else if (intr)
|
||||||
|
return 1;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
pci_xhci_usb_dev_intr_cb(void *hci_data, void *udev_data)
|
||||||
|
{
|
||||||
|
struct pci_xhci_dev_emu *edev;
|
||||||
|
|
||||||
|
edev = hci_data;
|
||||||
|
if (edev && edev->xdev)
|
||||||
|
pci_xhci_assert_interrupt(edev->xdev);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static struct pci_xhci_dev_emu*
|
static struct pci_xhci_dev_emu*
|
||||||
pci_xhci_dev_create(struct pci_xhci_vdev *xdev, void *dev_data)
|
pci_xhci_dev_create(struct pci_xhci_vdev *xdev, void *dev_data)
|
||||||
{
|
{
|
||||||
@ -432,15 +485,22 @@ pci_xhci_dev_create(struct pci_xhci_vdev *xdev, void *dev_data)
|
|||||||
if (!ue)
|
if (!ue)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
/* TODO: following function pointers will be populated in future */
|
/*
|
||||||
|
* TODO: at present, the following functions are
|
||||||
|
* enough. But for the purpose to be compatible with
|
||||||
|
* usb_mouse.c, the high level design including the
|
||||||
|
* function interface should be changed and refined
|
||||||
|
* in future.
|
||||||
|
*/
|
||||||
ue->ue_init = usb_dev_init;
|
ue->ue_init = usb_dev_init;
|
||||||
ue->ue_request = usb_dev_request;
|
ue->ue_request = usb_dev_request;
|
||||||
ue->ue_data = NULL;
|
ue->ue_data = usb_dev_data;
|
||||||
ue->ue_info = usb_dev_info;
|
ue->ue_info = usb_dev_info;
|
||||||
ue->ue_reset = usb_dev_reset;
|
ue->ue_reset = usb_dev_reset;
|
||||||
ue->ue_remove = NULL;
|
ue->ue_remove = NULL;
|
||||||
ue->ue_stop = NULL;
|
ue->ue_stop = NULL;
|
||||||
ue->ue_deinit = usb_dev_deinit;
|
ue->ue_deinit = usb_dev_deinit;
|
||||||
|
ue->ue_devtype = USB_DEV_PORT_MAPPER;
|
||||||
|
|
||||||
ud = ue->ue_init(dev_data, NULL);
|
ud = ue->ue_init(dev_data, NULL);
|
||||||
if (!ud)
|
if (!ud)
|
||||||
@ -489,10 +549,16 @@ pci_xhci_dev_destroy(struct pci_xhci_dev_emu *de)
|
|||||||
ue = de->dev_ue;
|
ue = de->dev_ue;
|
||||||
ud = de->dev_instance;
|
ud = de->dev_instance;
|
||||||
if (ue) {
|
if (ue) {
|
||||||
assert(ue->ue_deinit);
|
if (ue->ue_devtype == USB_DEV_PORT_MAPPER) {
|
||||||
ue->ue_deinit(ud);
|
assert(ue->ue_deinit);
|
||||||
|
if (ue->ue_deinit)
|
||||||
|
ue->ue_deinit(ud);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
free(ue);
|
|
||||||
|
if (ue->ue_devtype == USB_DEV_PORT_MAPPER)
|
||||||
|
free(ue);
|
||||||
|
|
||||||
free(de);
|
free(de);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -879,9 +945,11 @@ pci_xhci_init_ep(struct pci_xhci_dev_emu *dev, int epid)
|
|||||||
|
|
||||||
if (devep->ep_xfer == NULL) {
|
if (devep->ep_xfer == NULL) {
|
||||||
devep->ep_xfer = malloc(sizeof(struct usb_data_xfer));
|
devep->ep_xfer = malloc(sizeof(struct usb_data_xfer));
|
||||||
if (devep->ep_xfer)
|
if (devep->ep_xfer) {
|
||||||
USB_DATA_XFER_INIT(devep->ep_xfer);
|
USB_DATA_XFER_INIT(devep->ep_xfer);
|
||||||
else
|
devep->ep_xfer->dev = (void *)dev;
|
||||||
|
devep->ep_xfer->epid = epid;
|
||||||
|
} else
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@ -1821,6 +1889,7 @@ pci_xhci_xfer_complete(struct pci_xhci_vdev *xdev,
|
|||||||
}
|
}
|
||||||
|
|
||||||
xfer->ndata--;
|
xfer->ndata--;
|
||||||
|
xfer->head = (xfer->head + 1) % USB_MAX_XFER_BLOCKS;
|
||||||
edtla += xfer->data[i].bdone;
|
edtla += xfer->data[i].bdone;
|
||||||
|
|
||||||
trb->dwTrb3 = (trb->dwTrb3 & ~0x1) | (xfer->data[i].ccs);
|
trb->dwTrb3 = (trb->dwTrb3 & ~0x1) | (xfer->data[i].ccs);
|
||||||
@ -1929,7 +1998,12 @@ pci_xhci_try_usb_xfer(struct pci_xhci_vdev *xdev,
|
|||||||
if (USB_DATA_GET_ERRCODE(&xfer->data[xfer->head]) ==
|
if (USB_DATA_GET_ERRCODE(&xfer->data[xfer->head]) ==
|
||||||
USB_NAK)
|
USB_NAK)
|
||||||
err = XHCI_TRB_ERROR_SUCCESS;
|
err = XHCI_TRB_ERROR_SUCCESS;
|
||||||
} else {
|
}
|
||||||
|
/*
|
||||||
|
* Only for usb_mouse.c, emulation with port mapping will do it
|
||||||
|
* by the libusb callback function.
|
||||||
|
*/
|
||||||
|
else if (dev->dev_ue->ue_devtype == USB_DEV_STATIC) {
|
||||||
err = pci_xhci_xfer_complete(xdev, xfer, slot, epid,
|
err = pci_xhci_xfer_complete(xdev, xfer, slot, epid,
|
||||||
&do_intr);
|
&do_intr);
|
||||||
if (err == XHCI_TRB_ERROR_SUCCESS && do_intr)
|
if (err == XHCI_TRB_ERROR_SUCCESS && do_intr)
|
||||||
@ -2050,13 +2124,12 @@ retry:
|
|||||||
/* fall through */
|
/* fall through */
|
||||||
|
|
||||||
case XHCI_TRB_TYPE_DATA_STAGE:
|
case XHCI_TRB_TYPE_DATA_STAGE:
|
||||||
xfer_block =
|
xfer_block = usb_data_xfer_append(xfer,
|
||||||
usb_data_xfer_append(xfer, (void *)(trbflags &
|
(void *)(trbflags & XHCI_TRB_3_IDT_BIT ?
|
||||||
XHCI_TRB_3_IDT_BIT ?
|
&trb->qwTrb0 :
|
||||||
&trb->qwTrb0 :
|
XHCI_GADDR(xdev, trb->qwTrb0)),
|
||||||
XHCI_GADDR(xdev,
|
trb->dwTrb2 & 0x1FFFF, (void *)addr,
|
||||||
trb->qwTrb0)),
|
ccs);
|
||||||
trb->dwTrb2 & 0x1FFFF, (void *)addr, ccs);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case XHCI_TRB_TYPE_STATUS_STAGE:
|
case XHCI_TRB_TYPE_STATUS_STAGE:
|
||||||
@ -2200,8 +2273,14 @@ pci_xhci_device_doorbell(struct pci_xhci_vdev *xdev,
|
|||||||
if (ep_ctx->qwEpCtx2 == 0)
|
if (ep_ctx->qwEpCtx2 == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In USB emulation with port mapping, the following transfer should
|
||||||
|
* NOT be called, or else the interrupt transfer will result
|
||||||
|
* of invalid and infinite loop. It is used by usb_mouse.c only.
|
||||||
|
*/
|
||||||
/* handle pending transfers */
|
/* handle pending transfers */
|
||||||
if (devep->ep_xfer->ndata > 0) {
|
if (dev->dev_ue && dev->dev_ue->ue_devtype == USB_DEV_STATIC &&
|
||||||
|
devep->ep_xfer->ndata > 0) {
|
||||||
pci_xhci_try_usb_xfer(xdev, dev, devep, ep_ctx, slot, epid);
|
pci_xhci_try_usb_xfer(xdev, dev, devep, ep_ctx, slot, epid);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -3090,10 +3169,10 @@ pci_xhci_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts)
|
|||||||
if (xdev->ndevices == 0)
|
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,
|
||||||
NULL, NULL, xdev,
|
pci_xhci_usb_dev_notify_cb,
|
||||||
usb_get_log_level()) < 0) {
|
pci_xhci_usb_dev_intr_cb,
|
||||||
|
xdev, usb_get_log_level()) < 0)
|
||||||
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);
|
||||||
|
@ -809,6 +809,7 @@ struct usb_devemu ue_mouse = {
|
|||||||
.ue_emu = "tablet",
|
.ue_emu = "tablet",
|
||||||
.ue_usbver = 3,
|
.ue_usbver = 3,
|
||||||
.ue_usbspeed = USB_SPEED_HIGH,
|
.ue_usbspeed = USB_SPEED_HIGH,
|
||||||
|
.ue_devtype = USB_DEV_STATIC,
|
||||||
.ue_init = umouse_init,
|
.ue_init = umouse_init,
|
||||||
.ue_request = umouse_request,
|
.ue_request = umouse_request,
|
||||||
.ue_data = umouse_data_handler,
|
.ue_data = umouse_data_handler,
|
||||||
|
@ -44,6 +44,167 @@
|
|||||||
|
|
||||||
static struct usb_dev_sys_ctx_info g_ctx;
|
static struct usb_dev_sys_ctx_info g_ctx;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
assert(libusb_xfer);
|
||||||
|
assert(libusb_xfer->user_data);
|
||||||
|
|
||||||
|
req = libusb_xfer->user_data;
|
||||||
|
len = libusb_xfer->actual_length;
|
||||||
|
xfer = req->xfer;
|
||||||
|
|
||||||
|
assert(xfer);
|
||||||
|
assert(xfer->dev);
|
||||||
|
UPRINTF(LDBG, "xfer_comp: ep %d with %d bytes. status %d,%d\n",
|
||||||
|
req->xfer->epid, len, libusb_xfer->status,
|
||||||
|
xfer->ndata);
|
||||||
|
|
||||||
|
/* lock for protecting the transfer */
|
||||||
|
USB_DATA_XFER_LOCK(xfer);
|
||||||
|
xfer->status = USB_ERR_NORMAL_COMPLETION;
|
||||||
|
|
||||||
|
/* in case the xfer is reset by the USB_DATA_XFER_RESET */
|
||||||
|
if (xfer->reset == 1 ||
|
||||||
|
libusb_xfer->status != LIBUSB_TRANSFER_COMPLETED) {
|
||||||
|
UPRINTF(LDBG, "ep%d reset detected\r\n", xfer->epid);
|
||||||
|
xfer->reset = 0;
|
||||||
|
USB_DATA_XFER_UNLOCK(xfer);
|
||||||
|
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];
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
reset_out:
|
||||||
|
if (short_data)
|
||||||
|
xfer->status = USB_ERR_SHORT_XFER;
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
|
||||||
|
/* 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)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
for (i = 0; i < xfer->ndata; i++) {
|
||||||
|
block = &xfer->data[idx];
|
||||||
|
|
||||||
|
if (block->processed) {
|
||||||
|
idx = (idx + 1) % USB_MAX_XFER_BLOCKS;
|
||||||
|
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
|
static inline int
|
||||||
usb_dev_err_convert(int err)
|
usb_dev_err_convert(int err)
|
||||||
{
|
{
|
||||||
@ -346,6 +507,104 @@ usb_dev_reset(void *pdata)
|
|||||||
return 0;
|
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;
|
||||||
|
|
||||||
|
UPRINTF(LDBG, "%s: DIR=%s|EP=%x|*%s*, data %d %d-%d\n", __func__,
|
||||||
|
dir ? "IN" : "OUT", epid, type == USB_ENDPOINT_BULK ?
|
||||||
|
"BULK" : "INT", data_size,
|
||||||
|
blk_start, blk_start + blk_count - 1);
|
||||||
|
|
||||||
|
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
|
int
|
||||||
usb_dev_request(void *pdata, struct usb_data_xfer *xfer)
|
usb_dev_request(void *pdata, struct usb_data_xfer *xfer)
|
||||||
{
|
{
|
||||||
@ -648,6 +907,8 @@ usb_dev_sys_init(usb_dev_sys_cb conn_cb, usb_dev_sys_cb disconn_cb,
|
|||||||
g_ctx.hci_data = hci_data;
|
g_ctx.hci_data = hci_data;
|
||||||
g_ctx.conn_cb = conn_cb;
|
g_ctx.conn_cb = conn_cb;
|
||||||
g_ctx.disconn_cb = disconn_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_conn_evt = LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED;
|
||||||
native_disconn_evt = LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT;
|
native_disconn_evt = LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT;
|
||||||
native_pid = LIBUSB_HOTPLUG_MATCH_ANY;
|
native_pid = LIBUSB_HOTPLUG_MATCH_ANY;
|
||||||
|
@ -34,7 +34,8 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
#define USB_MAX_XFER_BLOCKS 8
|
#define USB_MAX_XFER_BLOCKS 256
|
||||||
|
|
||||||
#define USB_XFER_OUT 0
|
#define USB_XFER_OUT 0
|
||||||
#define USB_XFER_IN 1
|
#define USB_XFER_IN 1
|
||||||
|
|
||||||
@ -64,6 +65,11 @@ enum token_type {
|
|||||||
TOKEN_SETUP
|
TOKEN_SETUP
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum usb_dev_type {
|
||||||
|
USB_DEV_STATIC = 0,
|
||||||
|
USB_DEV_PORT_MAPPER
|
||||||
|
};
|
||||||
|
|
||||||
struct usb_hci;
|
struct usb_hci;
|
||||||
struct usb_device_request;
|
struct usb_device_request;
|
||||||
struct usb_data_xfer;
|
struct usb_data_xfer;
|
||||||
@ -73,6 +79,7 @@ struct usb_devemu {
|
|||||||
char *ue_emu; /* name of device emulation */
|
char *ue_emu; /* name of device emulation */
|
||||||
int ue_usbver; /* usb version: 2 or 3 */
|
int ue_usbver; /* usb version: 2 or 3 */
|
||||||
int ue_usbspeed; /* usb device speed */
|
int ue_usbspeed; /* usb device speed */
|
||||||
|
int ue_devtype;
|
||||||
|
|
||||||
/* instance creation */
|
/* instance creation */
|
||||||
void *(*ue_init)(void *pdata, char *opt);
|
void *(*ue_init)(void *pdata, char *opt);
|
||||||
@ -134,6 +141,10 @@ struct usb_data_xfer {
|
|||||||
int ndata; /* # of data items */
|
int ndata; /* # of data items */
|
||||||
int head;
|
int head;
|
||||||
int tail;
|
int tail;
|
||||||
|
void *dev; /* struct pci_xhci_dev_emu *dev */
|
||||||
|
int epid; /* related endpoint id */
|
||||||
|
int pid; /* token id */
|
||||||
|
int reset; /* detect ep reset */
|
||||||
int status;
|
int status;
|
||||||
pthread_mutex_t mtx;
|
pthread_mutex_t mtx;
|
||||||
};
|
};
|
||||||
@ -153,15 +164,21 @@ enum USB_ERRCODE {
|
|||||||
|
|
||||||
#define USB_DATA_OK(x, i) ((x)->data[(i)].buf != NULL)
|
#define USB_DATA_OK(x, i) ((x)->data[(i)].buf != NULL)
|
||||||
|
|
||||||
#define USB_DATA_XFER_INIT(x) do { \
|
#define USB_DATA_XFER_INIT(x) do { \
|
||||||
memset((x), 0, sizeof(*(x))); \
|
pthread_mutexattr_t attr; \
|
||||||
pthread_mutex_init(&((x)->mtx), NULL); \
|
pthread_mutexattr_init(&attr); \
|
||||||
} while (0)
|
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); \
|
||||||
|
memset((x), 0, sizeof(*(x))); \
|
||||||
|
pthread_mutex_init(&((x)->mtx), &attr); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
#define USB_DATA_XFER_RESET(x) do { \
|
#define USB_DATA_XFER_RESET(x) do { \
|
||||||
|
pthread_mutex_lock(&((x)->mtx)); \
|
||||||
memset((x)->data, 0, sizeof((x)->data)); \
|
memset((x)->data, 0, sizeof((x)->data)); \
|
||||||
(x)->ndata = 0; \
|
(x)->ndata = 0; \
|
||||||
(x)->head = (x)->tail = 0; \
|
(x)->head = (x)->tail = 0; \
|
||||||
|
(x)->reset = 1; \
|
||||||
|
pthread_mutex_unlock((&(x)->mtx)); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define USB_DATA_XFER_LOCK(x) \
|
#define USB_DATA_XFER_LOCK(x) \
|
||||||
|
@ -83,6 +83,30 @@ struct usb_dev {
|
|||||||
libusb_device_handle *handle;
|
libusb_device_handle *handle;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The purpose to implement struct usb_dev_req is to adapt
|
||||||
|
* struct usb_data_xfer to make a proper data format to talk
|
||||||
|
* with libusb.
|
||||||
|
*/
|
||||||
|
struct usb_dev_req {
|
||||||
|
struct usb_dev *udev;
|
||||||
|
int in;
|
||||||
|
int seq;
|
||||||
|
/*
|
||||||
|
* buffer could include data from multiple
|
||||||
|
* usb_data_xfer_block, so here need some
|
||||||
|
* data to record it.
|
||||||
|
*/
|
||||||
|
uint8_t *buffer;
|
||||||
|
int buf_length;
|
||||||
|
int blk_start;
|
||||||
|
int blk_count;
|
||||||
|
|
||||||
|
struct usb_data_xfer *xfer;
|
||||||
|
struct libusb_transfer *libusb_xfer;
|
||||||
|
struct usb_data_xfer_block *setup_blk;
|
||||||
|
};
|
||||||
|
|
||||||
/* callback type used by code from HCD layer */
|
/* callback type used by code from HCD layer */
|
||||||
typedef int (*usb_dev_sys_cb)(void *hci_data, void *dev_data);
|
typedef int (*usb_dev_sys_cb)(void *hci_data, void *dev_data);
|
||||||
|
|
||||||
@ -100,6 +124,8 @@ struct usb_dev_sys_ctx_info {
|
|||||||
*/
|
*/
|
||||||
usb_dev_sys_cb conn_cb;
|
usb_dev_sys_cb conn_cb;
|
||||||
usb_dev_sys_cb disconn_cb;
|
usb_dev_sys_cb disconn_cb;
|
||||||
|
usb_dev_sys_cb notify_cb;
|
||||||
|
usb_dev_sys_cb intr_cb;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* private data from HCD layer
|
* private data from HCD layer
|
||||||
@ -116,4 +142,5 @@ void usb_dev_deinit(void *pdata);
|
|||||||
int usb_dev_info(void *pdata, int type, void *value, int size);
|
int usb_dev_info(void *pdata, int type, void *value, int size);
|
||||||
int usb_dev_request(void *pdata, struct usb_data_xfer *xfer);
|
int usb_dev_request(void *pdata, struct usb_data_xfer *xfer);
|
||||||
int usb_dev_reset(void *pdata);
|
int usb_dev_reset(void *pdata);
|
||||||
|
int usb_dev_data(void *pdata, struct usb_data_xfer *xfer, int dir, int epctx);
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user