usb:hsotg:samsung: Sending ZLP packet for IN EP0 transfers
authorLukasz Majewski <l.majewski@samsung.com>
Fri, 4 May 2012 12:17:04 +0000 (14:17 +0200)
committerFelipe Balbi <balbi@ti.com>
Fri, 4 May 2012 12:53:12 +0000 (15:53 +0300)
This commit targets following scenarios for IN requests:

1. HOST requests e.g. 256B (which is a multiple of MPS = 64B).
Then NO ZLP shall be sent, since host expects exact number of bytes.

2. HOST requested 4096B, but our data for sending is 256B. In this
situation ZLP shall be send to tell HOST that no more data is available
and it shall not wait for more data. This prevents HOST from hanging.

Tested with:
- DFU gadget (various size of the sent data - also packet = MPS)
- Ethernet gadget (CDC and RNDIS)
- Multi Function Gadget (g_multi)

HW:
- Samsung's C210 Universal rev.0
- Samsung's C110 GONI

Signed-off-by: Lukasz Majewski <l.majewski@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
drivers/usb/gadget/s3c-hsotg.c

index 7b46af3e014f2e4ea12ef8e5d798c4b806d0807e..0a74a067749ae221e8e2021d79c5c1c7996418ac 100644 (file)
@@ -1555,6 +1555,10 @@ static void s3c_hsotg_handle_outdone(struct s3c_hsotg *hsotg,
        }
 
        if (epnum == 0) {
+               /*
+                * Condition req->complete != s3c_hsotg_complete_setup says:
+                * send ZLP when we have an asynchronous request from gadget
+                */
                if (!was_setup && req->complete != s3c_hsotg_complete_setup)
                        s3c_hsotg_send_zlp(hsotg, hs_req);
        }
@@ -1809,6 +1813,13 @@ static void s3c_hsotg_complete_in(struct s3c_hsotg *hsotg,
                return;
        }
 
+       /* Finish ZLP handling for IN EP0 transactions */
+       if (hsotg->eps[0].sent_zlp) {
+               dev_dbg(hsotg->dev, "zlp packet received\n");
+               s3c_hsotg_complete_request_lock(hsotg, hs_ep, hs_req, 0);
+               return;
+       }
+
        /* Calculate the size of the transfer by checking how much is left
         * in the endpoint size register and then working it out from
         * the amount we loaded for the transfer.
@@ -1828,9 +1839,28 @@ static void s3c_hsotg_complete_in(struct s3c_hsotg *hsotg,
                        __func__, hs_req->req.actual, size_done);
 
        hs_req->req.actual = size_done;
+       dev_dbg(hsotg->dev, "req->length:%d req->actual:%d req->zero:%d\n",
+               hs_req->req.length, hs_req->req.actual, hs_req->req.zero);
+
+       /*
+        * Check if dealing with Maximum Packet Size(MPS) IN transfer at EP0
+        * When sent data is a multiple MPS size (e.g. 64B ,128B ,192B
+        * ,256B ... ), after last MPS sized packet send IN ZLP packet to
+        * inform the host that no more data is available.
+        * The state of req.zero member is checked to be sure that the value to
+        * send is smaller than wValue expected from host.
+        * Check req.length to NOT send another ZLP when the current one is
+        * under completion (the one for which this completion has been called).
+        */
+       if (hs_req->req.length && hs_ep->index == 0 && hs_req->req.zero &&
+           hs_req->req.length == hs_req->req.actual &&
+           !(hs_req->req.length % hs_ep->ep.maxpacket)) {
+
+               dev_dbg(hsotg->dev, "ep0 zlp IN packet sent\n");
+               s3c_hsotg_send_zlp(hsotg, hs_req);
 
-       /* if we did all of the transfer, and there is more data left
-        * around, then try restarting the rest of the request */
+               return;
+       }
 
        if (!size_left && hs_req->req.actual < hs_req->req.length) {
                dev_dbg(hsotg->dev, "%s trying more for req...\n", __func__);