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.kaukab@xxxxxxxxx> --- 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 b5faca8..523e107 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); } } } -- 1.9.1 ---------------------------------------------------------------------- Intel Sweden AB Registered Office: Knarrarnasgatan 15, 164 40 Kista, Stockholm, Sweden Registration Number: 556189-6027 This e-mail and any attachments may contain confidential material for the sole use of the intended recipient(s). Any review or distribution by others is strictly prohibited. If you are not the intended recipient, please contact the sender and delete all copies. -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html