Manage ep0 state in software to add handling of status OUT stage.
Just toggling hsotg->setup in s3c_hsotg_handle_outdone leaves it in
wrong state in 2-stage control transfers.
Moreover, there is no need to handle SetupDone as requests can be
complete on XferCmpl of status stage.

Signed-off-by: Mian Yousaf Kaukab <yousaf.kau...@intel.com>
---
 drivers/usb/dwc2/core.h   |  13 +++-
 drivers/usb/dwc2/gadget.c | 151 ++++++++++++++++++++++------------------------
 2 files changed, 83 insertions(+), 81 deletions(-)

diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
index b5e50e7..1801785 100644
--- a/drivers/usb/dwc2/core.h
+++ b/drivers/usb/dwc2/core.h
@@ -196,6 +196,15 @@ enum dwc2_lx_state {
 #define DWC2_G_P_LEGACY_TX_FIFO_SIZE {256, 256, 256, 256, 768, 768, 768, \
                                           768, 0, 0, 0, 0, 0, 0, 0}
 
+/* Gadget ep0 states */
+enum dwc2_ep0_state {
+       DWC2_EP0_SETUP,
+       DWC2_EP0_DATA_IN,
+       DWC2_EP0_DATA_OUT,
+       DWC2_EP0_STATUS_IN,
+       DWC2_EP0_STATUS_OUT,
+};
+
 /**
  * struct dwc2_core_params - Parameters for configuring the core
  *
@@ -563,7 +572,7 @@ struct dwc2_hw_params {
  * @ep0_buff:           Buffer for EP0 reply data, if needed.
  * @ctrl_buff:          Buffer for EP0 control requests.
  * @ctrl_req:           Request for EP0 control packets.
- * @setup:              NAK management for EP0 SETUP
+ * @ep0_state:          EP0 control transfers state
  * @last_rst:           Time of last reset
  * @eps:                The endpoints being supplied to the gadget framework
  * @g_using_dma:          Indicate if dma usage is enabled
@@ -696,11 +705,11 @@ struct dwc2_hsotg {
        struct usb_request *ctrl_req;
        void *ep0_buff;
        void *ctrl_buff;
+       enum dwc2_ep0_state ep0_state;
 
        struct usb_gadget gadget;
        unsigned int enabled:1;
        unsigned int connected:1;
-       unsigned int setup:1;
        unsigned long last_rst;
        struct s3c_hsotg_ep *eps_in[MAX_EPS_CHANNELS];
        struct s3c_hsotg_ep *eps_out[MAX_EPS_CHANNELS];
diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c
index 4ccf59b..5f00a72 100644
--- a/drivers/usb/dwc2/gadget.c
+++ b/drivers/usb/dwc2/gadget.c
@@ -641,15 +641,12 @@ static void s3c_hsotg_start_req(struct dwc2_hsotg *hsotg,
        ctrl |= DXEPCTL_EPENA;  /* ensure ep enabled */
        ctrl |= DXEPCTL_USBACTEP;
 
-       dev_dbg(hsotg->dev, "setup req:%d\n", hsotg->setup);
+       dev_dbg(hsotg->dev, "ep0 state:%d\n", hsotg->ep0_state);
 
        /* For Setup request do not clear NAK */
-       if (hsotg->setup && index == 0)
-               hsotg->setup = 0;
-       else
+       if (!(index == 0 && hsotg->ep0_state == DWC2_EP0_SETUP))
                ctrl |= DXEPCTL_CNAK;   /* clear NAK set by core */
 
-
        dev_dbg(hsotg->dev, "%s: DxEPCTL=0x%08x\n", __func__, ctrl);
        writel(ctrl, hsotg->regs + epctrl_reg);
 
@@ -868,8 +865,6 @@ static int s3c_hsotg_send_reply(struct dwc2_hsotg *hsotg,
 
        if (length)
                memcpy(req->buf, buff, length);
-       else
-               ep->sent_zlp = 1;
 
        ret = s3c_hsotg_ep_queue(&ep->ep, req, GFP_ATOMIC);
        if (ret) {
@@ -1083,26 +1078,20 @@ static void s3c_hsotg_process_control(struct dwc2_hsotg 
*hsotg,
        int ret = 0;
        u32 dcfg;
 
-       ep0->sent_zlp = 0;
-
        dev_dbg(hsotg->dev, "ctrl Req=%02x, Type=%02x, V=%04x, L=%04x\n",
                 ctrl->bRequest, ctrl->bRequestType,
                 ctrl->wValue, ctrl->wLength);
 
-       /*
-        * record the direction of the request, for later use when enquing
-        * packets onto EP0.
-        */
-
-       ep0->dir_in = (ctrl->bRequestType & USB_DIR_IN) ? 1 : 0;
-       dev_dbg(hsotg->dev, "ctrl: dir_in=%d\n", ep0->dir_in);
-
-       /*
-        * if we've no data with this request, then the last part of the
-        * transaction is going to implicitly be IN.
-        */
-       if (ctrl->wLength == 0)
+       if (ctrl->wLength == 0) {
                ep0->dir_in = 1;
+               hsotg->ep0_state = DWC2_EP0_STATUS_IN;
+       } else if (ctrl->bRequestType & USB_DIR_IN) {
+               ep0->dir_in = 1;
+               hsotg->ep0_state = DWC2_EP0_DATA_IN;
+       } else {
+               ep0->dir_in = 0;
+               hsotg->ep0_state = DWC2_EP0_DATA_OUT;
+       }
 
        if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
                switch (ctrl->bRequest) {
@@ -1201,6 +1190,8 @@ static void s3c_hsotg_enqueue_setup(struct dwc2_hsotg 
*hsotg)
        }
 
        hsotg->eps_out[0]->dir_in = 0;
+       hsotg->eps_out[0]->sent_zlp = 0;
+       hsotg->ep0_state = DWC2_EP0_SETUP;
 
        ret = s3c_hsotg_ep_queue(&hsotg->eps_out[0]->ep, req, GFP_ATOMIC);
        if (ret < 0) {
@@ -1212,6 +1203,27 @@ static void s3c_hsotg_enqueue_setup(struct dwc2_hsotg 
*hsotg)
        }
 }
 
+static void s3c_hsotg_program_zlp(struct dwc2_hsotg *hsotg,
+                                       struct s3c_hsotg_ep *hs_ep)
+{
+       u32 ctrl;
+       u8 index = hs_ep->index;
+       u32 epctl_reg = hs_ep->dir_in ? DIEPCTL(index) : DOEPCTL(index);
+       u32 epsiz_reg = hs_ep->dir_in ? DIEPTSIZ(index) : DOEPTSIZ(index);
+
+       dev_dbg(hsotg->dev, "Sending zero-length packet on ep%d\n", index);
+
+       writel(DXEPTSIZ_MC(1) | DXEPTSIZ_PKTCNT(1) |
+                       DXEPTSIZ_XFERSIZE(0), hsotg->regs +
+                       epsiz_reg);
+
+       ctrl = readl(hsotg->regs + epctl_reg);
+       ctrl |= DXEPCTL_CNAK;  /* clear NAK set by core */
+       ctrl |= DXEPCTL_EPENA; /* ensure ep enabled */
+       ctrl |= DXEPCTL_USBACTEP;
+       writel(ctrl, hsotg->regs + epctl_reg);
+}
+
 /**
  * s3c_hsotg_complete_request - complete a request given to us
  * @hsotg: The device state.
@@ -1344,9 +1356,9 @@ static void s3c_hsotg_rx_data(struct dwc2_hsotg *hsotg, 
int ep_idx, int size)
 }
 
 /**
- * s3c_hsotg_send_zlp - send zero-length packet on control endpoint
+ * s3c_hsotg_ep0_zlp - send/receive zero-length packet on control endpoint
  * @hsotg: The device instance
- * @req: The request currently on this endpoint
+ * @dir_in: If IN zlp
  *
  * Generate a zero-length IN packet request for terminating a SETUP
  * transaction.
@@ -1355,51 +1367,25 @@ static void s3c_hsotg_rx_data(struct dwc2_hsotg *hsotg, 
int ep_idx, int size)
  * currently believed that we do not need to wait for any space in
  * the TxFIFO.
  */
-static void s3c_hsotg_send_zlp(struct dwc2_hsotg *hsotg,
-                              struct s3c_hsotg_req *req)
+static void s3c_hsotg_ep0_zlp(struct dwc2_hsotg *hsotg, bool dir_in)
 {
-       u32 ctrl;
-
-       if (!req) {
-               dev_warn(hsotg->dev, "%s: no request?\n", __func__);
-               return;
-       }
-
-       if (req->req.length == 0) {
-               hsotg->eps_out[0]->sent_zlp = 1;
-               s3c_hsotg_enqueue_setup(hsotg);
-               return;
-       }
-
        /* eps_out[0] is used in both directions */
-       hsotg->eps_out[0]->dir_in = 1;
-       hsotg->eps_out[0]->sent_zlp = 1;
-
-       dev_dbg(hsotg->dev, "sending zero-length packet\n");
+       hsotg->eps_out[0]->dir_in = dir_in;
+       hsotg->ep0_state = dir_in ? DWC2_EP0_STATUS_IN : DWC2_EP0_STATUS_OUT;
 
-       /* issue a zero-sized packet to terminate this */
-       writel(DXEPTSIZ_MC(1) | DXEPTSIZ_PKTCNT(1) |
-              DXEPTSIZ_XFERSIZE(0), hsotg->regs + DIEPTSIZ(0));
-
-       ctrl = readl(hsotg->regs + DIEPCTL0);
-       ctrl |= DXEPCTL_CNAK;  /* clear NAK set by core */
-       ctrl |= DXEPCTL_EPENA; /* ensure ep enabled */
-       ctrl |= DXEPCTL_USBACTEP;
-       writel(ctrl, hsotg->regs + DIEPCTL0);
+       s3c_hsotg_program_zlp(hsotg, hsotg->eps_out[0]);
 }
 
 /**
  * s3c_hsotg_handle_outdone - handle receiving OutDone/SetupDone from RXFIFO
  * @hsotg: The device instance
  * @epnum: The endpoint received from
- * @was_setup: Set if processing a SetupDone event.
  *
  * The RXFIFO has delivered an OutDone event, which means that the data
  * transfer for an OUT endpoint has been completed, either by a short
  * packet or by the finish of a transfer.
  */
-static void s3c_hsotg_handle_outdone(struct dwc2_hsotg *hsotg,
-                                    int epnum, bool was_setup)
+static void s3c_hsotg_handle_outdone(struct dwc2_hsotg *hsotg, int epnum)
 {
        u32 epsize = readl(hsotg->regs + DOEPTSIZ(epnum));
        struct s3c_hsotg_ep *hs_ep = hsotg->eps_out[epnum];
@@ -1413,6 +1399,13 @@ static void s3c_hsotg_handle_outdone(struct dwc2_hsotg 
*hsotg,
                return;
        }
 
+       if (epnum == 0 && hsotg->ep0_state == DWC2_EP0_STATUS_OUT) {
+               dev_dbg(hsotg->dev, "zlp packet received\n");
+               s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, 0);
+               s3c_hsotg_enqueue_setup(hsotg);
+               return;
+       }
+
        if (using_dma(hsotg)) {
                unsigned size_done;
 
@@ -1435,12 +1428,6 @@ static void s3c_hsotg_handle_outdone(struct dwc2_hsotg 
*hsotg,
        if (req->actual < req->length && size_left == 0) {
                s3c_hsotg_start_req(hsotg, hs_ep, hs_req, true);
                return;
-       } else if (epnum == 0) {
-               /*
-                * After was_setup = 1 =>
-                * set CNAK for non Setup requests
-                */
-               hsotg->setup = was_setup ? 0 : 1;
        }
 
        if (req->actual < req->length && req->short_not_ok) {
@@ -1453,13 +1440,10 @@ static void s3c_hsotg_handle_outdone(struct dwc2_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);
+       if (epnum == 0 && hsotg->ep0_state == DWC2_EP0_DATA_OUT) {
+               /* Move to STATUS IN */
+               s3c_hsotg_ep0_zlp(hsotg, true);
+               return;
        }
 
        s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, result);
@@ -1525,7 +1509,7 @@ static void s3c_hsotg_handle_rx(struct dwc2_hsotg *hsotg)
                        s3c_hsotg_read_frameno(hsotg));
 
                if (!using_dma(hsotg))
-                       s3c_hsotg_handle_outdone(hsotg, epnum, false);
+                       s3c_hsotg_handle_outdone(hsotg, epnum);
                break;
 
        case GRXSTS_PKTSTS_SETUPDONE:
@@ -1533,8 +1517,7 @@ static void s3c_hsotg_handle_rx(struct dwc2_hsotg *hsotg)
                        "SetupDone (Frame=0x%08x, DOPEPCTL=0x%08x)\n",
                        s3c_hsotg_read_frameno(hsotg),
                        readl(hsotg->regs + DOEPCTL(0)));
-
-               s3c_hsotg_handle_outdone(hsotg, epnum, true);
+               /* XferCmpl is used to complete the transfers */
                break;
 
        case GRXSTS_PKTSTS_OUTRX:
@@ -1547,6 +1530,8 @@ static void s3c_hsotg_handle_rx(struct dwc2_hsotg *hsotg)
                        s3c_hsotg_read_frameno(hsotg),
                        readl(hsotg->regs + DOEPCTL(0)));
 
+               WARN_ON(hsotg->ep0_state != DWC2_EP0_SETUP);
+
                s3c_hsotg_rx_data(hsotg, epnum, size);
                break;
 
@@ -1726,9 +1711,10 @@ static void s3c_hsotg_complete_in(struct dwc2_hsotg 
*hsotg,
        }
 
        /* Finish ZLP handling for IN EP0 transactions */
-       if (hsotg->eps_out[0]->sent_zlp) {
-               dev_dbg(hsotg->dev, "zlp packet received\n");
+       if (hs_ep->index == 0 && hsotg->ep0_state == DWC2_EP0_STATUS_IN) {
+               dev_dbg(hsotg->dev, "zlp packet sent\n");
                s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, 0);
+               s3c_hsotg_enqueue_setup(hsotg);
                return;
        }
 
@@ -1770,7 +1756,7 @@ static void s3c_hsotg_complete_in(struct dwc2_hsotg 
*hsotg,
            !(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);
+               s3c_hsotg_program_zlp(hsotg, hs_ep);
 
                return;
        }
@@ -1778,8 +1764,16 @@ static void s3c_hsotg_complete_in(struct dwc2_hsotg 
*hsotg,
        if (!size_left && hs_req->req.actual < hs_req->req.length) {
                dev_dbg(hsotg->dev, "%s trying more for req...\n", __func__);
                s3c_hsotg_start_req(hsotg, hs_ep, hs_req, true);
-       } else
-               s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, 0);
+               return;
+       }
+
+       if (hs_ep->index == 0 && hsotg->ep0_state == DWC2_EP0_DATA_IN) {
+               /* Move to STATUS OUT */
+               s3c_hsotg_ep0_zlp(hsotg, false);
+               return;
+       }
+
+       s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, 0);
 }
 
 /**
@@ -1830,8 +1824,7 @@ static void s3c_hsotg_epint(struct dwc2_hsotg *hsotg, 
unsigned int idx,
                                if (dir_in)
                                        WARN_ON_ONCE(1);
                                else
-                                       s3c_hsotg_handle_outdone(hsotg,
-                                                               0, true);
+                                       s3c_hsotg_handle_outdone(hsotg, 0);
                        }
                } else {
                        if (hs_ep->isochronous && hs_ep->interval == 1) {
@@ -1862,7 +1855,7 @@ static void s3c_hsotg_epint(struct dwc2_hsotg *hsotg, 
unsigned int idx,
                                 *  here as we ignore the RXFIFO.
                                 */
 
-                               s3c_hsotg_handle_outdone(hsotg, idx, false);
+                               s3c_hsotg_handle_outdone(hsotg, idx);
                        }
                }
        }
-- 
2.1.0

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to