ACRN: xHCI: Fix Transfer TRBs error transfer handler

According to xHCI Spec 4.10.1 Transfer TRBS chapter, there have three
scenarios shall generate a Transfer Event TRB when transfer TRBS:
1. If upon transfer completion of a TRB the Interrupt On Completion(IOC)
flag is set, the xHC shall generate a Transfer Event TRB.
2. A Short Packet will trigger the generation of a Transfer Event TRB on
the Event Ring if the Interrupt-on-Short (ISP) or Interrupt On Completion
(IOC) flags are set in the TRB that the Short Packet was detected on.
3. The detection of an error during a transfer shall always generate a Transfer
Event, irrespective of whether the Interrupt-on-Short or Interrupt On Completion
(IOC) flags are set in the Transfer TRB.
When an error condition is encountered which requires an endpoint to halt; the xHC
shall stop on the TRB in error, the endpoint shall be halted, and
software shall use a Set TR Dequeue Pointer Command to advance
the Transfer Ring to the next TD.

Tracked-On: #5263
Signed-off-by: Long Liu <long.liu@intel.com>
Acked-by: Yu Wang yu1.wang@intel.com
This commit is contained in:
Liu Long 2020-08-31 20:12:50 +08:00 committed by wenlingz
parent 64efb36c0e
commit b66e8797fa
1 changed files with 12 additions and 7 deletions

View File

@ -2845,11 +2845,14 @@ pci_xhci_xfer_complete(struct pci_xhci_vdev *xdev, struct usb_xfer *xfer,
if (err == XHCI_TRB_ERROR_SUCCESS && rem_len > 0)
err = XHCI_TRB_ERROR_SHORT_PKT;
/* Only interrupt if IOC or short packet */
if (!(trb->dwTrb3 & XHCI_TRB_3_IOC_BIT) &&
!((err == XHCI_TRB_ERROR_SHORT_PKT) &&
(trb->dwTrb3 & XHCI_TRB_3_ISP_BIT))) {
/* When transfer success and IOC bit not set or
* transfer is short packet and ISP bit is not set.
* */
if (((err == XHCI_TRB_ERROR_SUCCESS) &&
!(trb->dwTrb3 & XHCI_TRB_3_IOC_BIT)) ||
((err == XHCI_TRB_ERROR_SHORT_PKT) &&
(!(trb->dwTrb3 & XHCI_TRB_3_ISP_BIT) &&
!(trb->dwTrb3 & XHCI_TRB_3_IOC_BIT)))) {
i = index_inc(i, xfer->max_blk_cnt);
continue;
}
@ -2871,8 +2874,10 @@ pci_xhci_xfer_complete(struct pci_xhci_vdev *xdev, struct usb_xfer *xfer,
*do_intr = 1;
err = pci_xhci_insert_event(xdev, &evtrb, 0);
if (err != XHCI_TRB_ERROR_SUCCESS)
pci_xhci_insert_event(xdev, &evtrb, *do_intr);
/* The xHC stop on the TRB in error.*/
if (err != XHCI_TRB_ERROR_SUCCESS &&
err != XHCI_TRB_ERROR_SHORT_PKT)
break;
i = index_inc(i, xfer->max_blk_cnt);