diff options
author | Felipe Balbi <balbi@ti.com> | 2012-05-04 06:03:54 -0400 |
---|---|---|
committer | Felipe Balbi <balbi@ti.com> | 2012-06-03 16:08:18 -0400 |
commit | a0807881af93646b5d94b5594119df609e756945 (patch) | |
tree | f8fb042eefbfafefbedbc97101ebbda4223c8439 /drivers/usb/dwc3 | |
parent | 4552a0ca61cb2133bc77dc432ea20538ea28638a (diff) |
usb: dwc3: handle pending unaligned Control OUT data phase correctly
When DWC3_EP_PENDING_REQUEST flag is set for a Control OUT Data
phase transfer, we would be missing the proper handling for
unaligned OUT requests, thus hanging a transfer.
Since proper handling is already done on dwc3_ep0_do_control_data(),
we simply re-factor that function so it can be re-used from
__dwc3_gadget_ep0_queue().
Reported-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/ep0.c | 72 |
1 files changed, 43 insertions, 29 deletions
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index e6ca218ef130..15ec36eb461b 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c | |||
@@ -55,6 +55,8 @@ | |||
55 | #include "io.h" | 55 | #include "io.h" |
56 | 56 | ||
57 | static void dwc3_ep0_do_control_status(struct dwc3 *dwc, u32 epnum); | 57 | static void dwc3_ep0_do_control_status(struct dwc3 *dwc, u32 epnum); |
58 | static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, | ||
59 | struct dwc3_ep *dep, struct dwc3_request *req); | ||
58 | 60 | ||
59 | static const char *dwc3_ep0_state_string(enum dwc3_ep0_state state) | 61 | static const char *dwc3_ep0_state_string(enum dwc3_ep0_state state) |
60 | { | 62 | { |
@@ -150,9 +152,8 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep, | |||
150 | return 0; | 152 | return 0; |
151 | } | 153 | } |
152 | 154 | ||
153 | ret = dwc3_ep0_start_trans(dwc, direction, | 155 | __dwc3_ep0_do_control_data(dwc, dwc->eps[direction], req); |
154 | req->request.dma, req->request.length, | 156 | |
155 | DWC3_TRBCTL_CONTROL_DATA); | ||
156 | dep->flags &= ~(DWC3_EP_PENDING_REQUEST | | 157 | dep->flags &= ~(DWC3_EP_PENDING_REQUEST | |
157 | DWC3_EP0_DIR_IN); | 158 | DWC3_EP0_DIR_IN); |
158 | } else if (dwc->delayed_status) { | 159 | } else if (dwc->delayed_status) { |
@@ -787,35 +788,23 @@ static void dwc3_ep0_do_control_setup(struct dwc3 *dwc, | |||
787 | dwc3_ep0_out_start(dwc); | 788 | dwc3_ep0_out_start(dwc); |
788 | } | 789 | } |
789 | 790 | ||
790 | static void dwc3_ep0_do_control_data(struct dwc3 *dwc, | 791 | static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, |
791 | const struct dwc3_event_depevt *event) | 792 | struct dwc3_ep *dep, struct dwc3_request *req) |
792 | { | 793 | { |
793 | struct dwc3_ep *dep; | ||
794 | struct dwc3_request *req; | ||
795 | int ret; | 794 | int ret; |
796 | 795 | ||
797 | dep = dwc->eps[0]; | 796 | req->direction = !!dep->number; |
798 | |||
799 | if (list_empty(&dep->request_list)) { | ||
800 | dev_vdbg(dwc->dev, "pending request for EP0 Data phase\n"); | ||
801 | dep->flags |= DWC3_EP_PENDING_REQUEST; | ||
802 | |||
803 | if (event->endpoint_number) | ||
804 | dep->flags |= DWC3_EP0_DIR_IN; | ||
805 | return; | ||
806 | } | ||
807 | |||
808 | req = next_request(&dep->request_list); | ||
809 | req->direction = !!event->endpoint_number; | ||
810 | 797 | ||
811 | if (req->request.length == 0) { | 798 | if (req->request.length == 0) { |
812 | ret = dwc3_ep0_start_trans(dwc, event->endpoint_number, | 799 | ret = dwc3_ep0_start_trans(dwc, dep->number, |
813 | dwc->ctrl_req_addr, 0, | 800 | dwc->ctrl_req_addr, 0, |
814 | DWC3_TRBCTL_CONTROL_DATA); | 801 | DWC3_TRBCTL_CONTROL_DATA); |
815 | } else if ((req->request.length % dep->endpoint.maxpacket) | 802 | } else if ((req->request.length % dep->endpoint.maxpacket) |
816 | && (event->endpoint_number == 0)) { | 803 | && (dep->number == 0)) { |
804 | u32 transfer_size; | ||
805 | |||
817 | ret = usb_gadget_map_request(&dwc->gadget, &req->request, | 806 | ret = usb_gadget_map_request(&dwc->gadget, &req->request, |
818 | event->endpoint_number); | 807 | dep->number); |
819 | if (ret) { | 808 | if (ret) { |
820 | dev_dbg(dwc->dev, "failed to map request\n"); | 809 | dev_dbg(dwc->dev, "failed to map request\n"); |
821 | return; | 810 | return; |
@@ -823,6 +812,9 @@ static void dwc3_ep0_do_control_data(struct dwc3 *dwc, | |||
823 | 812 | ||
824 | WARN_ON(req->request.length > DWC3_EP0_BOUNCE_SIZE); | 813 | WARN_ON(req->request.length > DWC3_EP0_BOUNCE_SIZE); |
825 | 814 | ||
815 | transfer_size = roundup(req->request.length, | ||
816 | (u32) dep->endpoint.maxpacket); | ||
817 | |||
826 | dwc->ep0_bounced = true; | 818 | dwc->ep0_bounced = true; |
827 | 819 | ||
828 | /* | 820 | /* |
@@ -830,25 +822,47 @@ static void dwc3_ep0_do_control_data(struct dwc3 *dwc, | |||
830 | * DWC3_EP0_BOUNCE_SIZE we will need two chained | 822 | * DWC3_EP0_BOUNCE_SIZE we will need two chained |
831 | * TRBs to handle the transfer. | 823 | * TRBs to handle the transfer. |
832 | */ | 824 | */ |
833 | ret = dwc3_ep0_start_trans(dwc, event->endpoint_number, | 825 | ret = dwc3_ep0_start_trans(dwc, dep->number, |
834 | dwc->ep0_bounce_addr, dep->endpoint.maxpacket, | 826 | dwc->ep0_bounce_addr, transfer_size, |
835 | DWC3_TRBCTL_CONTROL_DATA); | 827 | DWC3_TRBCTL_CONTROL_DATA); |
836 | } else { | 828 | } else { |
837 | ret = usb_gadget_map_request(&dwc->gadget, &req->request, | 829 | ret = usb_gadget_map_request(&dwc->gadget, &req->request, |
838 | event->endpoint_number); | 830 | dep->number); |
839 | if (ret) { | 831 | if (ret) { |
840 | dev_dbg(dwc->dev, "failed to map request\n"); | 832 | dev_dbg(dwc->dev, "failed to map request\n"); |
841 | return; | 833 | return; |
842 | } | 834 | } |
843 | 835 | ||
844 | ret = dwc3_ep0_start_trans(dwc, event->endpoint_number, | 836 | ret = dwc3_ep0_start_trans(dwc, dep->number, req->request.dma, |
845 | req->request.dma, req->request.length, | 837 | req->request.length, DWC3_TRBCTL_CONTROL_DATA); |
846 | DWC3_TRBCTL_CONTROL_DATA); | ||
847 | } | 838 | } |
848 | 839 | ||
849 | WARN_ON(ret < 0); | 840 | WARN_ON(ret < 0); |
850 | } | 841 | } |
851 | 842 | ||
843 | static void dwc3_ep0_do_control_data(struct dwc3 *dwc, | ||
844 | const struct dwc3_event_depevt *event) | ||
845 | { | ||
846 | struct dwc3_ep *dep; | ||
847 | struct dwc3_request *req; | ||
848 | |||
849 | dep = dwc->eps[0]; | ||
850 | |||
851 | if (list_empty(&dep->request_list)) { | ||
852 | dev_vdbg(dwc->dev, "pending request for EP0 Data phase\n"); | ||
853 | dep->flags |= DWC3_EP_PENDING_REQUEST; | ||
854 | |||
855 | if (event->endpoint_number) | ||
856 | dep->flags |= DWC3_EP0_DIR_IN; | ||
857 | return; | ||
858 | } | ||
859 | |||
860 | req = next_request(&dep->request_list); | ||
861 | dep = dwc->eps[event->endpoint_number]; | ||
862 | |||
863 | __dwc3_ep0_do_control_data(dwc, dep, req); | ||
864 | } | ||
865 | |||
852 | static int dwc3_ep0_start_control_status(struct dwc3_ep *dep) | 866 | static int dwc3_ep0_start_control_status(struct dwc3_ep *dep) |
853 | { | 867 | { |
854 | struct dwc3 *dwc = dep->dwc; | 868 | struct dwc3 *dwc = dep->dwc; |