summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorVardan Mikayelyan <mvardan@synopsys.com>2016-02-17 00:01:53 +0100
committerFelipe Balbi <balbi@kernel.org>2016-02-17 09:32:01 +0100
commit3142a16b9816c24501103a92010a283eb6e2766c (patch)
tree3b30002e20da753a9a37af23a4d39cdaa3b9da37 /drivers
parentusb: dwc3: Fix assignment of EP transfer resources (diff)
downloadlinux-3142a16b9816c24501103a92010a283eb6e2766c.tar.xz
linux-3142a16b9816c24501103a92010a283eb6e2766c.zip
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 26a19ea699060fde ("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: 26a19ea699060fde ("usb: dwc2: host: fix use of qtd after free in desc dma mode") Cc: Gregory Herrero <gregory.herrero@intel.com> Signed-off-by: Vardan Mikayelyan <mvardan@synopsys.com> Signed-off-by: John Youn <johnyoun@synopsys.com> Signed-off-by: Felipe Balbi <balbi@kernel.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/usb/dwc2/hcd_ddma.c21
1 files changed, 10 insertions, 11 deletions
diff --git a/drivers/usb/dwc2/hcd_ddma.c b/drivers/usb/dwc2/hcd_ddma.c
index 36606fc33c0d..89db47a1ffed 100644
--- a/drivers/usb/dwc2/hcd_ddma.c
+++ b/drivers/usb/dwc2/hcd_ddma.c
@@ -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,
halt_status, n_bytes,
xfer_done);
- if (*xfer_done && urb->status != -EINPROGRESS)
- failed = 1;
-
- if (failed) {
+ if (failed || (*xfer_done && urb->status != -EINPROGRESS)) {
dwc2_host_complete(hsotg, qtd, urb->status);
dwc2_hcd_qtd_unlink_and_free(hsotg, qtd, qh);
- dev_vdbg(hsotg->dev, "failed=%1x xfer_done=%1x status=%08x\n",
- failed, *xfer_done, urb->status);
+ dev_vdbg(hsotg->dev, "failed=%1x xfer_done=%1x\n",
+ failed, *xfer_done);
return failed;
}
@@ -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) {
int i;
+ int qtd_desc_count;
qtd = list_entry(qtd_item, struct dwc2_qtd, qtd_list_entry);
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,
desc_num, halt_status,
- &xfer_done)) {
- qtd = NULL;
- break;
- }
+ &xfer_done))
+ goto stop_scan;
+
desc_num++;
}
}
+stop_scan:
if (qh->ep_type != USB_ENDPOINT_XFER_CONTROL) {
/*
* Resetting the data toggle for bulk and interrupt endpoints