usb: chipidea: udc: improve error recovery for ISO transfer
authorXu Yang <xu.yang_2@nxp.com>
Thu, 26 Sep 2024 02:29:06 +0000 (10:29 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 4 Oct 2024 13:14:09 +0000 (15:14 +0200)
Impove device mode ISO transfer error tolerant by reprime the corresponding
endpoint.

The recovery steps when error occurs:
- Delete the error dTD from dQH and giveback request to user.
- Do reprime if dQH is not empty.
- Do prime when new dTD is queued if dQH is empty

Acked-by: Peter Chen <peter.chen@kernel.org>
Signed-off-by: Xu Yang <xu.yang_2@nxp.com>
Link: https://lore.kernel.org/r/20240926022906.473319-3-xu.yang_2@nxp.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/chipidea/udc.c

index 6752c86c6eb9c49299adc5e5421f2b5f8a13b306..8a9b31fd5c89d8ddf4bb1bbe184a9c094983d38c 100644 (file)
@@ -824,6 +824,7 @@ static int _hardware_dequeue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq)
        unsigned remaining_length;
        unsigned actual = hwreq->req.length;
        struct ci_hdrc *ci = hwep->ci;
+       bool is_isoc = hwep->type == USB_ENDPOINT_XFER_ISOC;
 
        if (hwreq->req.status != -EALREADY)
                return -EINVAL;
@@ -837,7 +838,7 @@ static int _hardware_dequeue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq)
                        int n = hw_ep_bit(hwep->num, hwep->dir);
 
                        if (ci->rev == CI_REVISION_24 ||
-                           ci->rev == CI_REVISION_22)
+                           ci->rev == CI_REVISION_22 || is_isoc)
                                if (!hw_read(ci, OP_ENDPTSTAT, BIT(n)))
                                        reprime_dtd(ci, hwep, node);
                        hwreq->req.status = -EALREADY;
@@ -856,11 +857,15 @@ static int _hardware_dequeue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq)
                        hwreq->req.status = -EPROTO;
                        break;
                } else if ((TD_STATUS_TR_ERR & hwreq->req.status)) {
-                       hwreq->req.status = -EILSEQ;
-                       break;
+                       if (is_isoc) {
+                               hwreq->req.status = 0;
+                       } else {
+                               hwreq->req.status = -EILSEQ;
+                               break;
+                       }
                }
 
-               if (remaining_length) {
+               if (remaining_length && !is_isoc) {
                        if (hwep->dir == TX) {
                                hwreq->req.status = -EPROTO;
                                break;