mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-06-18 19:57:31 +00:00
DM USB: xHCI: Support control transfer for USB port mapper.
This patch implements the control transfer for port mapper. With this patch, USB2.0 device can be enumerated successfully in user OS. Change-Id: I567bd00ca310d68375acd94a5cc5bcd287665df1 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
b12527f35d
commit
caf4916e99
@ -427,10 +427,10 @@ pci_xhci_dev_create(struct pci_xhci_vdev *xdev, void *dev_data)
|
||||
|
||||
/* TODO: following function pointers will be populated in future */
|
||||
ue->ue_init = usb_dev_init;
|
||||
ue->ue_request = NULL;
|
||||
ue->ue_request = usb_dev_request;
|
||||
ue->ue_data = NULL;
|
||||
ue->ue_info = usb_dev_info;
|
||||
ue->ue_reset = NULL;
|
||||
ue->ue_reset = usb_dev_reset;
|
||||
ue->ue_remove = NULL;
|
||||
ue->ue_stop = NULL;
|
||||
ue->ue_deinit = usb_dev_deinit;
|
||||
|
@ -59,6 +59,129 @@ usb_dev_err_convert(int err)
|
||||
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)
|
||||
{
|
||||
@ -100,6 +223,220 @@ usb_dev_native_toggle_if_drivers(struct usb_dev *udev, int attach)
|
||||
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;
|
||||
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_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_READ_DEVICE):
|
||||
UPRINTF(LDBG, "UR_SET_ADDRESS\n");
|
||||
udev->addr = value;
|
||||
goto out;
|
||||
case UREQ(UR_SET_CONFIG, UT_READ_DEVICE):
|
||||
UPRINTF(LDBG, "UR_SET_CONFIG\n");
|
||||
usb_dev_set_config(udev, xfer, value & 0xff);
|
||||
goto out;
|
||||
case UREQ(UR_SET_INTERFACE, UT_READ_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 == 0) {
|
||||
UPRINTF(LDBG, "UR_CLEAR_HALT\n");
|
||||
libusb_clear_halt(udev->handle, index);
|
||||
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)
|
||||
{
|
||||
|
@ -31,13 +31,39 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
#include <stdbool.h>
|
||||
#include "types.h"
|
||||
|
||||
#define USB_MAX_XFER_BLOCKS 8
|
||||
|
||||
#define USB_XFER_OUT 0
|
||||
#define USB_XFER_IN 1
|
||||
|
||||
#define USB_DIR_OUT 0
|
||||
#define USB_DIR_IN 0x80
|
||||
|
||||
#define LIBUSB_TIMEOUT 10000
|
||||
|
||||
#define USB_CFG_ATT_ONE (1 << 7) /* should always be set */
|
||||
#define USB_CFG_ATT_SELFPOWER (1 << 6)
|
||||
#define USB_CFG_ATT_WAKEUP (1 << 5)
|
||||
#define USB_CFG_ATT_BATTERY (1 << 4)
|
||||
|
||||
enum endpoint_type {
|
||||
USB_ENDPOINT_CONTROL = 0,
|
||||
USB_ENDPOINT_ISOC,
|
||||
USB_ENDPOINT_BULK,
|
||||
USB_ENDPOINT_INT,
|
||||
USB_ENDPOINT_INVALID = 255
|
||||
};
|
||||
|
||||
#define USB_INTERFACE_INVALID 255
|
||||
|
||||
enum token_type {
|
||||
TOKEN_OUT = 0,
|
||||
TOKEN_IN,
|
||||
TOKEN_SETUP
|
||||
};
|
||||
|
||||
struct usb_hci;
|
||||
struct usb_device_request;
|
||||
struct usb_data_xfer;
|
||||
@ -108,6 +134,7 @@ struct usb_data_xfer {
|
||||
int ndata; /* # of data items */
|
||||
int head;
|
||||
int tail;
|
||||
int status;
|
||||
pthread_mutex_t mtx;
|
||||
};
|
||||
|
||||
|
@ -32,10 +32,18 @@
|
||||
#ifndef _USB_DEVICE_H
|
||||
#define _USB_DEVICE_H
|
||||
#include <libusb-1.0/libusb.h>
|
||||
#include "usb_core.h"
|
||||
|
||||
#define USB_NUM_INTERFACE 16
|
||||
#define USB_NUM_ENDPOINT 15
|
||||
|
||||
#define USB_EP_ADDR(d) ((d)->bEndpointAddress)
|
||||
#define USB_EP_ATTR(d) ((d)->bmAttributes)
|
||||
#define USB_EP_PID(d) (USB_EP_ADDR(d) & USB_DIR_IN ? TOKEN_IN : TOKEN_OUT)
|
||||
#define USB_EP_TYPE(d) (USB_EP_ATTR(d) & 0x3)
|
||||
#define USB_EP_NR(d) (USB_EP_ADDR(d) & 0xF)
|
||||
#define USB_EP_ERR_TYPE 0xFF
|
||||
|
||||
enum {
|
||||
USB_INFO_VERSION,
|
||||
USB_INFO_SPEED,
|
||||
@ -106,4 +114,6 @@ int usb_dev_sys_init(usb_dev_sys_cb conn_cb, usb_dev_sys_cb disconn_cb,
|
||||
void *usb_dev_init(void *pdata, char *opt);
|
||||
void usb_dev_deinit(void *pdata);
|
||||
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_reset(void *pdata);
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user