usb: dwc3: ep0: add handling for unaligned OUT transfers
authorFelipe Balbi <balbi@ti.com>
Sat, 27 Aug 2011 19:18:09 +0000 (22:18 +0300)
committerFelipe Balbi <balbi@ti.com>
Fri, 9 Sep 2011 10:02:05 +0000 (13:02 +0300)
In case we have transfers which aren't aligned
to wMaxPacketSize, we need to be careful with
how we start the transfer with the HW. OUT
transfers _must_ be aligned with wMaxPacketSize
and in order to guarantee that, we use a bounce
buffer.

Signed-off-by: Felipe Balbi <balbi@ti.com>
drivers/usb/dwc3/ep0.c

index a6fc5c3..f1e0a5e 100644 (file)
@@ -185,10 +185,29 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep,
        req->epnum              = dep->number;
 
        list_add_tail(&req->list, &dep->request_list);
-       dwc3_map_buffer_to_dma(req);
+       if (req->request.length == 0) {
+               ret = dwc3_ep0_start_trans(dwc, dep->number,
+                               dwc->ctrl_req_addr, 0);
+       } else if ((req->request.length % dep->endpoint.maxpacket)
+                       && (dep->number == 0)) {
+               dwc->ep0_bounced = true;
+
+               WARN_ON(req->request.length > dep->endpoint.maxpacket);
+
+               /*
+                * REVISIT in case request length is bigger than EP0
+                * wMaxPacketSize, we will need two chained TRBs to handle
+                * the transfer.
+                */
+               ret = dwc3_ep0_start_trans(dwc, dep->number,
+                               dwc->ep0_bounce_addr, dep->endpoint.maxpacket);
+       } else {
+               dwc3_map_buffer_to_dma(req);
+
+               ret = dwc3_ep0_start_trans(dwc, dep->number,
+                               req->request.dma, req->request.length);
+       }
 
-       ret = dwc3_ep0_start_trans(dwc, dep->number, req->request.dma,
-                       req->request.length);
        if (ret < 0) {
                list_del(&req->list);
                dwc3_unmap_buffer_from_dma(req);
@@ -655,8 +674,16 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
 
        dwc3_trb_to_nat(dwc->ep0_trb, &trb);
 
-       transferred = ur->length - trb.length;
-       ur->actual += transferred;
+       if (dwc->ep0_bounced) {
+               struct dwc3_ep  *ep0 = dwc->eps[0];
+
+               transferred = min(ur->length, dep->endpoint.maxpacket - trb.length);
+               memcpy(ur->buf, dwc->ep0_bounce, transferred);
+               dwc->ep0_bounced = false;
+       } else {
+               transferred = ur->length - trb.length;
+               ur->actual += transferred;
+       }
 
        if ((epnum & 1) && ur->actual < ur->length) {
                /* for some reason we did not get everything out */