aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVardan Mikayelyan <mvardan@synopsys.com>2016-02-16 18:01:53 -0500
committerFelipe Balbi <balbi@kernel.org>2016-02-17 03:32:01 -0500
commit3142a16b9816c24501103a92010a283eb6e2766c (patch)
tree3b30002e20da753a9a37af23a4d39cdaa3b9da37
parentc450960187f45d4260db87c7dd4fc0bceb5565d8 (diff)
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>
-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,
1174 failed = dwc2_update_non_isoc_urb_state_ddma(hsotg, chan, qtd, dma_desc, 1174 failed = dwc2_update_non_isoc_urb_state_ddma(hsotg, chan, qtd, dma_desc,
1175 halt_status, n_bytes, 1175 halt_status, n_bytes,
1176 xfer_done); 1176 xfer_done);
1177 if (*xfer_done && urb->status != -EINPROGRESS) 1177 if (failed || (*xfer_done && urb->status != -EINPROGRESS)) {
1178 failed = 1;
1179
1180 if (failed) {
1181 dwc2_host_complete(hsotg, qtd, urb->status); 1178 dwc2_host_complete(hsotg, qtd, urb->status);
1182 dwc2_hcd_qtd_unlink_and_free(hsotg, qtd, qh); 1179 dwc2_hcd_qtd_unlink_and_free(hsotg, qtd, qh);
1183 dev_vdbg(hsotg->dev, "failed=%1x xfer_done=%1x status=%08x\n", 1180 dev_vdbg(hsotg->dev, "failed=%1x xfer_done=%1x\n",
1184 failed, *xfer_done, urb->status); 1181 failed, *xfer_done);
1185 return failed; 1182 return failed;
1186 } 1183 }
1187 1184
@@ -1236,21 +1233,23 @@ static void dwc2_complete_non_isoc_xfer_ddma(struct dwc2_hsotg *hsotg,
1236 1233
1237 list_for_each_safe(qtd_item, qtd_tmp, &qh->qtd_list) { 1234 list_for_each_safe(qtd_item, qtd_tmp, &qh->qtd_list) {
1238 int i; 1235 int i;
1236 int qtd_desc_count;
1239 1237
1240 qtd = list_entry(qtd_item, struct dwc2_qtd, qtd_list_entry); 1238 qtd = list_entry(qtd_item, struct dwc2_qtd, qtd_list_entry);
1241 xfer_done = 0; 1239 xfer_done = 0;
1240 qtd_desc_count = qtd->n_desc;
1242 1241
1243 for (i = 0; i < qtd->n_desc; i++) { 1242 for (i = 0; i < qtd_desc_count; i++) {
1244 if (dwc2_process_non_isoc_desc(hsotg, chan, chnum, qtd, 1243 if (dwc2_process_non_isoc_desc(hsotg, chan, chnum, qtd,
1245 desc_num, halt_status, 1244 desc_num, halt_status,
1246 &xfer_done)) { 1245 &xfer_done))
1247 qtd = NULL; 1246 goto stop_scan;
1248 break; 1247
1249 }
1250 desc_num++; 1248 desc_num++;
1251 } 1249 }
1252 } 1250 }
1253 1251
1252stop_scan:
1254 if (qh->ep_type != USB_ENDPOINT_XFER_CONTROL) { 1253 if (qh->ep_type != USB_ENDPOINT_XFER_CONTROL) {
1255 /* 1254 /*
1256 * Resetting the data toggle for bulk and interrupt endpoints 1255 * Resetting the data toggle for bulk and interrupt endpoints