diff options
author | Pratyush Anand <pratyush.anand@st.com> | 2012-05-25 09:24:56 -0400 |
---|---|---|
committer | Felipe Balbi <balbi@ti.com> | 2012-06-04 11:23:04 -0400 |
commit | d6d6ec7b8854ab62f7f3907656d5d29eb47532e7 (patch) | |
tree | b2efe8b1eb005a331747277053267c2714f94969 /drivers/usb/dwc3 | |
parent | d05b81824e672a48b3566634622849866de2b787 (diff) |
usb: dwc3: Fix missed isoc IN transaction
If an IN transfer is missed on isoc endpoint, then driver must insure
that next ep_queue is properly handled.
This patch fixes this issue by starting a new transfer for next queued
request.
Signed-off-by: Pratyush Anand <pratyush.anand@st.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
Diffstat (limited to 'drivers/usb/dwc3')
-rw-r--r-- | drivers/usb/dwc3/core.h | 3 | ||||
-rw-r--r-- | drivers/usb/dwc3/gadget.c | 73 |
2 files changed, 52 insertions, 24 deletions
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index e402022b9e15..fcb8be2ec1c9 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h | |||
@@ -405,6 +405,7 @@ struct dwc3_event_buffer { | |||
405 | * @number: endpoint number (1 - 15) | 405 | * @number: endpoint number (1 - 15) |
406 | * @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK | 406 | * @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK |
407 | * @res_trans_idx: Resource transfer index | 407 | * @res_trans_idx: Resource transfer index |
408 | * @current_uf: Current uf received through last event parameter | ||
408 | * @interval: the intervall on which the ISOC transfer is started | 409 | * @interval: the intervall on which the ISOC transfer is started |
409 | * @name: a human readable name e.g. ep1out-bulk | 410 | * @name: a human readable name e.g. ep1out-bulk |
410 | * @direction: true for TX, false for RX | 411 | * @direction: true for TX, false for RX |
@@ -428,6 +429,7 @@ struct dwc3_ep { | |||
428 | #define DWC3_EP_WEDGE (1 << 2) | 429 | #define DWC3_EP_WEDGE (1 << 2) |
429 | #define DWC3_EP_BUSY (1 << 4) | 430 | #define DWC3_EP_BUSY (1 << 4) |
430 | #define DWC3_EP_PENDING_REQUEST (1 << 5) | 431 | #define DWC3_EP_PENDING_REQUEST (1 << 5) |
432 | #define DWC3_EP_MISSED_ISOC (1 << 6) | ||
431 | 433 | ||
432 | /* This last one is specific to EP0 */ | 434 | /* This last one is specific to EP0 */ |
433 | #define DWC3_EP0_DIR_IN (1 << 31) | 435 | #define DWC3_EP0_DIR_IN (1 << 31) |
@@ -437,6 +439,7 @@ struct dwc3_ep { | |||
437 | u8 number; | 439 | u8 number; |
438 | u8 type; | 440 | u8 type; |
439 | u8 res_trans_idx; | 441 | u8 res_trans_idx; |
442 | u16 current_uf; | ||
440 | u32 interval; | 443 | u32 interval; |
441 | 444 | ||
442 | char name[20]; | 445 | char name[20]; |
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index be87e82b36c7..0cab69e51380 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c | |||
@@ -990,6 +990,34 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param, | |||
990 | return 0; | 990 | return 0; |
991 | } | 991 | } |
992 | 992 | ||
993 | static void __dwc3_gadget_start_isoc(struct dwc3 *dwc, | ||
994 | struct dwc3_ep *dep, u32 cur_uf) | ||
995 | { | ||
996 | u32 uf; | ||
997 | |||
998 | if (list_empty(&dep->request_list)) { | ||
999 | dev_vdbg(dwc->dev, "ISOC ep %s run out for requests.\n", | ||
1000 | dep->name); | ||
1001 | return; | ||
1002 | } | ||
1003 | |||
1004 | /* 4 micro frames in the future */ | ||
1005 | uf = cur_uf + dep->interval * 4; | ||
1006 | |||
1007 | __dwc3_gadget_kick_transfer(dep, uf, 1); | ||
1008 | } | ||
1009 | |||
1010 | static void dwc3_gadget_start_isoc(struct dwc3 *dwc, | ||
1011 | struct dwc3_ep *dep, const struct dwc3_event_depevt *event) | ||
1012 | { | ||
1013 | u32 cur_uf, mask; | ||
1014 | |||
1015 | mask = ~(dep->interval - 1); | ||
1016 | cur_uf = event->parameters & mask; | ||
1017 | |||
1018 | __dwc3_gadget_start_isoc(dwc, dep, cur_uf); | ||
1019 | } | ||
1020 | |||
993 | static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) | 1021 | static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) |
994 | { | 1022 | { |
995 | struct dwc3 *dwc = dep->dwc; | 1023 | struct dwc3 *dwc = dep->dwc; |
@@ -1019,8 +1047,14 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) | |||
1019 | 1047 | ||
1020 | list_add_tail(&req->list, &dep->request_list); | 1048 | list_add_tail(&req->list, &dep->request_list); |
1021 | 1049 | ||
1022 | if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && (dep->flags & DWC3_EP_BUSY)) | 1050 | if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) { |
1023 | dep->flags |= DWC3_EP_PENDING_REQUEST; | 1051 | if (dep->flags & DWC3_EP_BUSY) { |
1052 | dep->flags |= DWC3_EP_PENDING_REQUEST; | ||
1053 | } else if (dep->flags & DWC3_EP_MISSED_ISOC) { | ||
1054 | __dwc3_gadget_start_isoc(dwc, dep, dep->current_uf); | ||
1055 | dep->flags &= ~DWC3_EP_MISSED_ISOC; | ||
1056 | } | ||
1057 | } | ||
1024 | 1058 | ||
1025 | /* | 1059 | /* |
1026 | * There are two special cases: | 1060 | * There are two special cases: |
@@ -1591,6 +1625,7 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep, | |||
1591 | struct dwc3_trb *trb; | 1625 | struct dwc3_trb *trb; |
1592 | unsigned int count; | 1626 | unsigned int count; |
1593 | unsigned int s_pkt = 0; | 1627 | unsigned int s_pkt = 0; |
1628 | unsigned int trb_status; | ||
1594 | 1629 | ||
1595 | do { | 1630 | do { |
1596 | req = next_request(&dep->req_queued); | 1631 | req = next_request(&dep->req_queued); |
@@ -1616,9 +1651,18 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep, | |||
1616 | 1651 | ||
1617 | if (dep->direction) { | 1652 | if (dep->direction) { |
1618 | if (count) { | 1653 | if (count) { |
1619 | dev_err(dwc->dev, "incomplete IN transfer %s\n", | 1654 | trb_status = DWC3_TRB_SIZE_TRBSTS(trb->size); |
1620 | dep->name); | 1655 | if (trb_status == DWC3_TRBSTS_MISSED_ISOC) { |
1621 | status = -ECONNRESET; | 1656 | dev_dbg(dwc->dev, "incomplete IN transfer %s\n", |
1657 | dep->name); | ||
1658 | dep->current_uf = event->parameters & | ||
1659 | ~(dep->interval - 1); | ||
1660 | dep->flags |= DWC3_EP_MISSED_ISOC; | ||
1661 | } else { | ||
1662 | dev_err(dwc->dev, "incomplete IN transfer %s\n", | ||
1663 | dep->name); | ||
1664 | status = -ECONNRESET; | ||
1665 | } | ||
1622 | } | 1666 | } |
1623 | } else { | 1667 | } else { |
1624 | if (count && (event->status & DEPEVT_STATUS_SHORT)) | 1668 | if (count && (event->status & DEPEVT_STATUS_SHORT)) |
@@ -1690,25 +1734,6 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc, | |||
1690 | } | 1734 | } |
1691 | } | 1735 | } |
1692 | 1736 | ||
1693 | static void dwc3_gadget_start_isoc(struct dwc3 *dwc, | ||
1694 | struct dwc3_ep *dep, const struct dwc3_event_depevt *event) | ||
1695 | { | ||
1696 | u32 uf, mask; | ||
1697 | |||
1698 | if (list_empty(&dep->request_list)) { | ||
1699 | dev_vdbg(dwc->dev, "ISOC ep %s run out for requests.\n", | ||
1700 | dep->name); | ||
1701 | return; | ||
1702 | } | ||
1703 | |||
1704 | mask = ~(dep->interval - 1); | ||
1705 | uf = event->parameters & mask; | ||
1706 | /* 4 micro frames in the future */ | ||
1707 | uf += dep->interval * 4; | ||
1708 | |||
1709 | __dwc3_gadget_kick_transfer(dep, uf, 1); | ||
1710 | } | ||
1711 | |||
1712 | static void dwc3_process_ep_cmd_complete(struct dwc3_ep *dep, | 1737 | static void dwc3_process_ep_cmd_complete(struct dwc3_ep *dep, |
1713 | const struct dwc3_event_depevt *event) | 1738 | const struct dwc3_event_depevt *event) |
1714 | { | 1739 | { |