Commit 3142a16b authored by Vardan Mikayelyan's avatar Vardan Mikayelyan Committed by Felipe Balbi

usb: dwc2: host: fix logical omissions in dwc2_process_non_isoc_desc

Fixes memory manipulation issues and makes Host DDMA bulk transfers
work.

dwc2_process_non_isoc_desc() must return non zero value ONLY when
failure happens in one of the queued descriptors. After receiving
non zero value the caller must stop processing of remaining
QTDs and their descriptors from chain.

Commit 26a19ea6 ("usb: dwc2: host: fix use of qtd after
free in desc dma mode") breaks non_isoc transaction completion logic
in Host DDMA mode. There were bugs before that, but after this patch
dwc2_process_non_isoc_desc() returns fail status even if descriptor
was processed normally. This causes break from loop which is processing
remaining descriptors assigned to QTD, which is not correct for QTDs
containing more than one descriptor.

Current dwc2 driver gathers queued BULK URBs until receiving URB
without URB_NO_INTERRUPT flag. Once getting it, SW creates
descriptor chain, stores it in qh structure and passes start
address to HW. Multiple URB data is contained in that chain.
Hence on getting error on descriptor after its processing by HW,
SW should go out of both loops(qh->qtd, qtd->descs) and report
the failure.

Fixes: 26a19ea6 ("usb: dwc2: host: fix use of qtd after free in desc dma mode")
Cc: Gregory Herrero <gregory.herrero@intel.com>
Signed-off-by: default avatarVardan Mikayelyan <mvardan@synopsys.com>
Signed-off-by: default avatarJohn Youn <johnyoun@synopsys.com>
Signed-off-by: default avatarFelipe Balbi <balbi@kernel.org>
parent c4509601
...@@ -1174,14 +1174,11 @@ static int dwc2_process_non_isoc_desc(struct dwc2_hsotg *hsotg, ...@@ -1174,14 +1174,11 @@ static int dwc2_process_non_isoc_desc(struct dwc2_hsotg *hsotg,
failed = dwc2_update_non_isoc_urb_state_ddma(hsotg, chan, qtd, dma_desc, failed = dwc2_update_non_isoc_urb_state_ddma(hsotg, chan, qtd, dma_desc,
halt_status, n_bytes, halt_status, n_bytes,
xfer_done); xfer_done);
if (*xfer_done && urb->status != -EINPROGRESS) if (failed || (*xfer_done && urb->status != -EINPROGRESS)) {
failed = 1;
if (failed) {
dwc2_host_complete(hsotg, qtd, urb->status); dwc2_host_complete(hsotg, qtd, urb->status);
dwc2_hcd_qtd_unlink_and_free(hsotg, qtd, qh); dwc2_hcd_qtd_unlink_and_free(hsotg, qtd, qh);
dev_vdbg(hsotg->dev, "failed=%1x xfer_done=%1x status=%08x\n", dev_vdbg(hsotg->dev, "failed=%1x xfer_done=%1x\n",
failed, *xfer_done, urb->status); failed, *xfer_done);
return failed; return failed;
} }
...@@ -1236,21 +1233,23 @@ static void dwc2_complete_non_isoc_xfer_ddma(struct dwc2_hsotg *hsotg, ...@@ -1236,21 +1233,23 @@ static void dwc2_complete_non_isoc_xfer_ddma(struct dwc2_hsotg *hsotg,
list_for_each_safe(qtd_item, qtd_tmp, &qh->qtd_list) { list_for_each_safe(qtd_item, qtd_tmp, &qh->qtd_list) {
int i; int i;
int qtd_desc_count;
qtd = list_entry(qtd_item, struct dwc2_qtd, qtd_list_entry); qtd = list_entry(qtd_item, struct dwc2_qtd, qtd_list_entry);
xfer_done = 0; xfer_done = 0;
qtd_desc_count = qtd->n_desc;
for (i = 0; i < qtd->n_desc; i++) { for (i = 0; i < qtd_desc_count; i++) {
if (dwc2_process_non_isoc_desc(hsotg, chan, chnum, qtd, if (dwc2_process_non_isoc_desc(hsotg, chan, chnum, qtd,
desc_num, halt_status, desc_num, halt_status,
&xfer_done)) { &xfer_done))
qtd = NULL; goto stop_scan;
break;
}
desc_num++; desc_num++;
} }
} }
stop_scan:
if (qh->ep_type != USB_ENDPOINT_XFER_CONTROL) { if (qh->ep_type != USB_ENDPOINT_XFER_CONTROL) {
/* /*
* Resetting the data toggle for bulk and interrupt endpoints * Resetting the data toggle for bulk and interrupt endpoints
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment