DM USB: xHCI: implement the emulation for overrun and underrun event

The overrun and underrun event are used in the timing control machanism,
this patch is used to implement this feature.

Tracked-On: #3628
Signed-off-by: Xiaoguang Wu <xiaoguang.wu@intel.com>
Acked-by: Yu Wang <yu1.wang@intel.com>
This commit is contained in:
Xiaoguang Wu 2019-08-16 20:23:51 +08:00 committed by wenlingz
parent 7cb45bc968
commit f1b142e6e0
1 changed files with 95 additions and 3 deletions

View File

@ -94,6 +94,7 @@
#include "usb_pmapper.h"
#include "vmmapi.h"
#include "dm_string.h"
#include "timer.h"
#undef LOG_TAG
#define LOG_TAG "xHCI: "
@ -183,6 +184,13 @@ struct pci_xhci_trb_ring {
uint32_t ccs; /* consumer cycle state */
};
struct xhci_ep_timer_data {
uint32_t slot;
uint8_t epnum;
uint8_t dir;
struct pci_xhci_dev_emu *dev;
};
/* device endpoint transfer/stream rings */
struct pci_xhci_dev_ep {
union {
@ -201,6 +209,8 @@ struct pci_xhci_dev_ep {
#define ep_sctx_trbs _ep_trb_rings._epu_sctx_trbs
struct usb_xfer *ep_xfer; /* transfer chain */
struct acrn_timer isoc_timer;
struct xhci_ep_timer_data timer_data;
pthread_mutex_t mtx;
};
@ -473,6 +483,7 @@ static int pci_xhci_parse_log_level(struct pci_xhci_vdev *xdev, char *opts);
static int pci_xhci_parse_extcap(struct pci_xhci_vdev *xdev, char *opts);
static int pci_xhci_convert_speed(int lspeed);
static void pci_xhci_free_usb_xfer(struct usb_xfer *xfer);
static void pci_xhci_isoc_handler(void *arg, uint64_t param);
#define XHCI_OPT_MAX_LEN 32
static struct pci_xhci_option_elem xhci_option_table[] = {
@ -1588,7 +1599,7 @@ pci_xhci_free_usb_xfer(struct usb_xfer *xfer)
}
static int
pci_xhci_init_ep(struct pci_xhci_dev_emu *dev, int epid)
pci_xhci_init_ep(struct pci_xhci_dev_emu *dev, int epid, uint32_t slot)
{
struct xhci_dev_ctx *dev_ctx;
struct pci_xhci_dev_ep *devep;
@ -1650,9 +1661,26 @@ pci_xhci_init_ep(struct pci_xhci_dev_emu *dev, int epid)
goto errout;
}
devep->timer_data.dev = dev;
devep->timer_data.slot = slot;
devep->timer_data.epnum = epid;
devep->timer_data.dir = (epid & 0x1) ? TOKEN_IN : TOKEN_OUT;
devep->isoc_timer.clockid = CLOCK_MONOTONIC;
rc = acrn_timer_init(&devep->isoc_timer, pci_xhci_isoc_handler,
&devep->timer_data);
if (rc < 0) {
UPRINTF(LFTL, "ep%d: failed to create isoc timer\r\n", epid);
goto errout;
}
return 0;
errout:
pci_xhci_free_usb_xfer(devep->ep_xfer);
devep->ep_xfer = NULL;
devep->timer_data.dev = NULL;
devep->timer_data.slot = 0;
devep->timer_data.epnum = 0;
pthread_mutex_destroy(&devep->mtx);
return -1;
}
@ -1681,6 +1709,11 @@ pci_xhci_disable_ep(struct pci_xhci_dev_emu *dev, int epid)
devep->ep_xfer = NULL;
}
acrn_timer_deinit(&devep->isoc_timer);
devep->timer_data.dev = NULL;
devep->timer_data.slot = 0;
devep->timer_data.epnum = 0;
pthread_mutex_unlock(&devep->mtx);
pthread_mutex_destroy(&devep->mtx);
}
@ -2055,7 +2088,7 @@ pci_xhci_cmd_address_device(struct pci_xhci_vdev *xdev,
ep0_ctx->dwEpCtx0 = (ep0_ctx->dwEpCtx0 & ~0x7) |
XHCI_EPCTX_0_EPSTATE_SET(XHCI_ST_EPCTX_RUNNING);
if (pci_xhci_init_ep(dev, 1)) {
if (pci_xhci_init_ep(dev, 1, slot)) {
cmderr = XHCI_TRB_ERROR_INCOMPAT_DEV;
goto done;
}
@ -2173,7 +2206,7 @@ pci_xhci_cmd_config_ep(struct pci_xhci_vdev *xdev,
memcpy(ep_ctx, iep_ctx, sizeof(struct xhci_endp_ctx));
if (pci_xhci_init_ep(dev, i)) {
if (pci_xhci_init_ep(dev, i, slot)) {
cmderr = XHCI_TRB_ERROR_RESOURCE;
goto error;
}
@ -2868,6 +2901,7 @@ pci_xhci_handle_transfer(struct pci_xhci_vdev *xdev,
struct xhci_block hcb;
struct usb_block *xfer_block;
struct usb_block *prev_block;
struct itimerspec delay;
uint64_t val;
uint32_t trbflags;
int do_intr, err;
@ -2956,6 +2990,22 @@ retry:
break;
case XHCI_TRB_TYPE_ISOCH:
/* According to xHCI spec 4.10.3.1 and 4.14.2.1, the
* condition for judging {under,over}run event is
* 'empty ring'. But it didn't define how long the
* 'empty' state takes to identify this scenario. As
* an experience value, 100 ms (100 ESIT) is used to
* decide whether the {under,over}run event should be
* reported to the Guest OS.
*/
delay.it_interval.tv_sec = 0;
delay.it_interval.tv_nsec = 0;
delay.it_value.tv_sec = 0;
delay.it_value.tv_nsec = 100000000;
if (acrn_timer_settime(&devep->isoc_timer, &delay)) {
UPRINTF(LFTL, "isoc timer set time failed\n");
goto errout;
}
/* fall through */
case XHCI_TRB_TYPE_NORMAL:
@ -4144,6 +4194,48 @@ errout:
return xdev->ndevices;
}
static void
pci_xhci_isoc_handler(void *arg, uint64_t param)
{
struct xhci_ep_timer_data *pdata;
struct xhci_trb trb;
struct xhci_trb trb_underrun = {
.qwTrb0 = 0,
.dwTrb2 = XHCI_TRB_2_ERROR_SET(XHCI_TRB_ERROR_RING_UNDERRUN),
.dwTrb3 = XHCI_TRB_3_TYPE_SET(XHCI_TRB_EVENT_TRANSFER)
};
struct xhci_trb trb_overrun = {
.qwTrb0 = 0,
.dwTrb2 = XHCI_TRB_2_ERROR_SET(XHCI_TRB_ERROR_RING_OVERRUN),
.dwTrb3 = XHCI_TRB_3_TYPE_SET(XHCI_TRB_EVENT_TRANSFER)
};
pdata = arg;
if (pdata == NULL) {
UPRINTF(LFTL, "%s NULL arg\r\n", __func__);
return;
}
if (pdata->dir == TOKEN_OUT) {
trb_underrun.dwTrb3 |= (XHCI_TRB_3_SLOT_SET(pdata->slot) |
XHCI_TRB_3_EP_SET(pdata->epnum));
trb = trb_underrun;
} else {
trb_overrun.dwTrb3 |= (XHCI_TRB_3_SLOT_SET(pdata->slot) |
XHCI_TRB_3_EP_SET(pdata->epnum));
trb = trb_overrun;
}
if (!pdata->dev || !pdata->dev->xdev) {
UPRINTF(LFTL, "%s: error, {x,}dev == NULL\r\n", __func__);
return;
}
pci_xhci_insert_event(pdata->dev->xdev, &trb, 1);
UPRINTF(LINF, "send %srun to slot %d ep %d\r\n", pdata->dir == TOKEN_IN
? "under" : "over", pdata->slot, pdata->epnum);
}
static int
pci_xhci_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts)
{