Skip to content

Commit 00fbfd6

Browse files
xiaoguangwulijinxia
authored andcommitted
DM USB: fix an USB endpoint reset flow issue
Original code will reset the whole USB device when xHCI Reset Endpoint command is received, this behavior is not right. This patch is used to fix it. And according to xhci spec 4.6.8, if the endpoint is not in the halted state, xHC should reject to execute this command and the Context State Error should be returned. This patch also add this logic. Change-Id: I55a5918148d82d103fb3eb27d582f9676f9f61d3 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>
1 parent cb93887 commit 00fbfd6

File tree

2 files changed

+81
-27
lines changed

2 files changed

+81
-27
lines changed

devicemodel/hw/pci/xhci.c

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1748,15 +1748,19 @@ pci_xhci_cmd_reset_ep(struct pci_xhci_vdev *xdev,
17481748
goto done;
17491749
}
17501750

1751-
devep = &dev->eps[epid];
1752-
if (devep->ep_xfer != NULL)
1753-
USB_DATA_XFER_RESET(devep->ep_xfer);
1754-
17551751
dev_ctx = dev->dev_ctx;
17561752
assert(dev_ctx != NULL);
1757-
17581753
ep_ctx = &dev_ctx->ctx_ep[epid];
17591754

1755+
if ((ep_ctx->dwEpCtx0 & 0x7) != XHCI_ST_EPCTX_HALTED) {
1756+
cmderr = XHCI_TRB_ERROR_CONTEXT_STATE;
1757+
goto done;
1758+
}
1759+
1760+
devep = &dev->eps[epid];
1761+
if (devep->ep_xfer != NULL)
1762+
USB_DATA_XFER_RESET(devep->ep_xfer);
1763+
17601764
ep_ctx->dwEpCtx0 = (ep_ctx->dwEpCtx0 & ~0x7) | XHCI_ST_EPCTX_STOPPED;
17611765

17621766
if (XHCI_EPCTX_0_MAXP_STREAMS_GET(ep_ctx->dwEpCtx0) == 0)
@@ -1766,13 +1770,6 @@ pci_xhci_cmd_reset_ep(struct pci_xhci_vdev *xdev,
17661770
epid, ep_ctx->dwEpCtx0, ep_ctx->dwEpCtx1, ep_ctx->qwEpCtx2,
17671771
ep_ctx->dwEpCtx4);
17681772

1769-
if (type == XHCI_TRB_TYPE_RESET_EP &&
1770-
(dev->dev_ue->ue_reset == NULL ||
1771-
dev->dev_ue->ue_reset(dev->dev_instance) < 0)) {
1772-
cmderr = XHCI_TRB_ERROR_ENDP_NOT_ON;
1773-
goto done;
1774-
}
1775-
17761773
done:
17771774
return cmderr;
17781775
}
@@ -2159,7 +2156,7 @@ pci_xhci_xfer_complete(struct pci_xhci_vdev *xdev,
21592156
uint32_t trbflags;
21602157
uint32_t edtla;
21612158
uint32_t i;
2162-
int err;
2159+
int err = XHCI_TRB_ERROR_SUCCESS;
21632160

21642161
dev = XHCI_SLOTDEV_PTR(xdev, slot);
21652162
devep = &dev->eps[epid];
@@ -2169,7 +2166,20 @@ pci_xhci_xfer_complete(struct pci_xhci_vdev *xdev,
21692166

21702167
ep_ctx = &dev_ctx->ctx_ep[epid];
21712168

2172-
err = XHCI_TRB_ERROR_SUCCESS;
2169+
/* err is used as completion code and sent to guest driver */
2170+
switch (xfer->status) {
2171+
case USB_ERR_STALLED:
2172+
ep_ctx->dwEpCtx0 = (ep_ctx->dwEpCtx0 & ~0x7) |
2173+
XHCI_ST_EPCTX_HALTED;
2174+
err = XHCI_TRB_ERROR_STALL;
2175+
break;
2176+
case USB_ERR_NORMAL_COMPLETION:
2177+
break;
2178+
default:
2179+
/* FIXME: should process other failures */
2180+
UPRINTF(LFTL, "unknown error %d\r\n", xfer->status);
2181+
}
2182+
21732183
*do_intr = 0;
21742184
edtla = 0;
21752185

@@ -2494,6 +2504,9 @@ pci_xhci_handle_transfer(struct pci_xhci_vdev *xdev,
24942504

24952505
UPRINTF(LDBG, "[%d]: xfer->ndata %u\r\n", __LINE__, xfer->ndata);
24962506

2507+
if (xfer->ndata <= 0)
2508+
goto errout;
2509+
24972510
if (epid == 1) {
24982511
err = USB_ERR_NOT_STARTED;
24992512
if (dev->dev_ue->ue_request != NULL)
@@ -2525,7 +2538,9 @@ pci_xhci_handle_transfer(struct pci_xhci_vdev *xdev,
25252538
pci_xhci_assert_interrupt(xdev);
25262539

25272540
if (do_retry) {
2528-
USB_DATA_XFER_RESET(xfer);
2541+
if (epid == 1)
2542+
USB_DATA_XFER_RESET(xfer);
2543+
25292544
UPRINTF(LDBG, "[%d]: retry:continuing with next TRBs\r\n",
25302545
__LINE__);
25312546
goto retry;

devicemodel/hw/platform/usb_pmapper.c

Lines changed: 51 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
#define LOG_TAG "USBPM: "
1919

2020
static struct usb_dev_sys_ctx_info g_ctx;
21+
static inline uint8_t usb_dev_get_ep_type(struct usb_dev *udev, int pid,
22+
int epnum);
2123

2224
static void
2325
usb_dev_comp_req(struct libusb_transfer *libusb_xfer)
@@ -30,12 +32,15 @@ usb_dev_comp_req(struct libusb_transfer *libusb_xfer)
3032
int bstart, bcount;
3133

3234
assert(libusb_xfer);
33-
assert(libusb_xfer->user_data);
3435

36+
/* async request */
3537
req = libusb_xfer->user_data;
3638
len = libusb_xfer->actual_length;
37-
xfer = req->xfer;
39+
assert(req);
40+
assert(req->udev);
3841

42+
/* async transfer */
43+
xfer = req->xfer;
3944
assert(xfer);
4045
assert(xfer->dev);
4146

@@ -52,13 +57,39 @@ usb_dev_comp_req(struct libusb_transfer *libusb_xfer)
5257
USB_DATA_XFER_LOCK(xfer);
5358
xfer->status = USB_ERR_NORMAL_COMPLETION;
5459

60+
switch (libusb_xfer->status) {
61+
case LIBUSB_TRANSFER_STALL:
62+
xfer->status = USB_ERR_STALLED;
63+
goto out;
64+
case LIBUSB_TRANSFER_NO_DEVICE:
65+
case LIBUSB_TRANSFER_ERROR:
66+
case LIBUSB_TRANSFER_TIMED_OUT:
67+
case LIBUSB_TRANSFER_CANCELLED:
68+
case LIBUSB_TRANSFER_OVERFLOW:
69+
/* FIXME: should treat every failure properly */
70+
UPRINTF(LWRN, "failure: %x\r\n", libusb_xfer->status);
71+
break;
72+
case LIBUSB_TRANSFER_COMPLETED:
73+
break;
74+
default:
75+
UPRINTF(LWRN, "unknown failure: %x\r\n", libusb_xfer->status);
76+
break;
77+
}
78+
5579
/* in case the xfer is reset by the USB_DATA_XFER_RESET */
56-
if (xfer->reset == 1 ||
57-
libusb_xfer->status != LIBUSB_TRANSFER_COMPLETED) {
80+
if (xfer->reset == 1) {
5881
UPRINTF(LDBG, "ep%d reset detected\r\n", xfer->epid);
5982
xfer->reset = 0;
60-
USB_DATA_XFER_UNLOCK(xfer);
61-
goto reset_out;
83+
/* ONLY interrupt transfer needs this.
84+
* The transfer here is an old one before endpoint reset, so it
85+
* should be discarded. But for bulk transfer, the transfer here
86+
* is a new one after reset, so it should be kept.
87+
*/
88+
if (usb_dev_get_ep_type(req->udev, xfer->pid & 1,
89+
xfer->epid / 2) == USB_ENDPOINT_INT) {
90+
UPRINTF(LDBG, "goto reset out\r\n");
91+
goto reset_out;
92+
}
6293
}
6394

6495
/* post process the usb transfer data */
@@ -84,10 +115,9 @@ usb_dev_comp_req(struct libusb_transfer *libusb_xfer)
84115
idx = (idx + 1) % USB_MAX_XFER_BLOCKS;
85116
}
86117

87-
reset_out:
88118
if (short_data)
89119
xfer->status = USB_ERR_SHORT_XFER;
90-
120+
out:
91121
/* notify the USB core this transfer is over */
92122
if (g_ctx.notify_cb)
93123
do_intr = g_ctx.notify_cb(xfer->dev, xfer);
@@ -96,6 +126,7 @@ usb_dev_comp_req(struct libusb_transfer *libusb_xfer)
96126
if (do_intr && g_ctx.intr_cb)
97127
g_ctx.intr_cb(xfer->dev, NULL);
98128

129+
reset_out:
99130
/* unlock and release memory */
100131
USB_DATA_XFER_UNLOCK(xfer);
101132
libusb_free_transfer(libusb_xfer);
@@ -649,11 +680,19 @@ usb_dev_request(void *pdata, struct usb_data_xfer *xfer)
649680
usb_dev_set_if(udev, index, value, xfer);
650681
goto out;
651682
case UREQ(UR_CLEAR_FEATURE, UT_WRITE_ENDPOINT):
652-
if (value == 0) {
653-
UPRINTF(LDBG, "UR_CLEAR_HALT\n");
654-
libusb_clear_halt(udev->handle, index);
655-
goto out;
683+
if (value) {
684+
/* according to usb spec (ch9), this is impossible */
685+
UPRINTF(LWRN, "Clear Feature request with non-zero "
686+
"value %d\r\n", value);
687+
break;
656688
}
689+
690+
UPRINTF(LDBG, "UR_CLEAR_HALT\n");
691+
rc = libusb_clear_halt(udev->handle, index);
692+
if (rc)
693+
UPRINTF(LWRN, "fail to clear halted ep, rc %d\r\n", rc);
694+
goto out;
695+
657696
}
658697

659698
/* send it to physical device */

0 commit comments

Comments
 (0)