Re: [PATCH] usb: cdnsp: fix lack of ZLP for ep0
From: Peter Chen
Date: Sun Nov 27 2022 - 01:44:36 EST
On Tue, Nov 22, 2022 at 4:52 PM Pawel Laszczak <pawell@xxxxxxxxxxx> wrote:
>
> Patch implements the handling of ZLP for control transfer.
> To send the ZLP driver must prepare the extra TRB in TD with
> length set to zero and TRB type to TRB_NORMAL.
> The first TRB must have set TRB_CHAIN flag, TD_SIZE = 1
> and TRB type to TRB_DATA.
>
> cc: <stable@xxxxxxxxxxxxxxx>
> Fixes: 3d82904559f4 ("usb: cdnsp: cdns3 Add main part of Cadence USBSSP DRD Driver")
> Signed-off-by: Pawel Laszczak <pawell@xxxxxxxxxxx>
Reviewed-by: Peter Chen <peter.chen@xxxxxxxxxx>
Peter
> ---
> drivers/usb/cdns3/cdnsp-ring.c | 42 ++++++++++++++++++++++++++--------
> 1 file changed, 32 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/usb/cdns3/cdnsp-ring.c b/drivers/usb/cdns3/cdnsp-ring.c
> index 86e1141e150f..fa1fa9b2ff38 100644
> --- a/drivers/usb/cdns3/cdnsp-ring.c
> +++ b/drivers/usb/cdns3/cdnsp-ring.c
> @@ -2006,10 +2006,11 @@ int cdnsp_queue_bulk_tx(struct cdnsp_device *pdev, struct cdnsp_request *preq)
>
> int cdnsp_queue_ctrl_tx(struct cdnsp_device *pdev, struct cdnsp_request *preq)
> {
> - u32 field, length_field, remainder;
> + u32 field, length_field, zlp = 0;
> struct cdnsp_ep *pep = preq->pep;
> struct cdnsp_ring *ep_ring;
> int num_trbs;
> + u32 maxp;
> int ret;
>
> ep_ring = cdnsp_request_to_transfer_ring(pdev, preq);
> @@ -2019,26 +2020,33 @@ int cdnsp_queue_ctrl_tx(struct cdnsp_device *pdev, struct cdnsp_request *preq)
> /* 1 TRB for data, 1 for status */
> num_trbs = (pdev->three_stage_setup) ? 2 : 1;
>
> + maxp = usb_endpoint_maxp(pep->endpoint.desc);
> +
> + if (preq->request.zero && preq->request.length &&
> + (preq->request.length % maxp == 0)) {
> + num_trbs++;
> + zlp = 1;
> + }
> +
> ret = cdnsp_prepare_transfer(pdev, preq, num_trbs);
> if (ret)
> return ret;
>
> /* If there's data, queue data TRBs */
> - if (pdev->ep0_expect_in)
> - field = TRB_TYPE(TRB_DATA) | TRB_IOC;
> - else
> - field = TRB_ISP | TRB_TYPE(TRB_DATA) | TRB_IOC;
> -
> if (preq->request.length > 0) {
> - remainder = cdnsp_td_remainder(pdev, 0, preq->request.length,
> - preq->request.length, preq, 1, 0);
> + field = TRB_TYPE(TRB_DATA);
>
> - length_field = TRB_LEN(preq->request.length) |
> - TRB_TD_SIZE(remainder) | TRB_INTR_TARGET(0);
> + if (zlp)
> + field |= TRB_CHAIN;
> + else
> + field |= TRB_IOC | (pdev->ep0_expect_in ? 0 : TRB_ISP);
>
> if (pdev->ep0_expect_in)
> field |= TRB_DIR_IN;
>
> + length_field = TRB_LEN(preq->request.length) |
> + TRB_TD_SIZE(zlp) | TRB_INTR_TARGET(0);
> +
> cdnsp_queue_trb(pdev, ep_ring, true,
> lower_32_bits(preq->request.dma),
> upper_32_bits(preq->request.dma), length_field,
> @@ -2046,6 +2054,20 @@ int cdnsp_queue_ctrl_tx(struct cdnsp_device *pdev, struct cdnsp_request *preq)
> TRB_SETUPID(pdev->setup_id) |
> pdev->setup_speed);
>
> + if (zlp) {
> + field = TRB_TYPE(TRB_NORMAL) | TRB_IOC;
> +
> + if (!pdev->ep0_expect_in)
> + field = TRB_ISP;
> +
> + cdnsp_queue_trb(pdev, ep_ring, true,
> + lower_32_bits(preq->request.dma),
> + upper_32_bits(preq->request.dma), 0,
> + field | ep_ring->cycle_state |
> + TRB_SETUPID(pdev->setup_id) |
> + pdev->setup_speed);
> + }
> +
> pdev->ep0_stage = CDNSP_DATA_STAGE;
> }
>
> --
> 2.25.1
>