OSDN Git Service

USB: dwc3: Fix issues in isochronous endpoint handling
authorVijayavardhan Vennapusa <vvreddy@codeaurora.org>
Wed, 30 Jan 2013 12:05:45 +0000 (17:35 +0530)
committerDavid Keitel <dkeitel@codeaurora.org>
Tue, 22 Mar 2016 18:06:38 +0000 (11:06 -0700)
If there are no requests queued by function driver when
XFRNRDY interrupt is recieved, pending flag will be set.
Then if function driver queues request, don't queue ENDTXFR
command and wait for XFRNRDY interrupt.

START TRANSFER command will fail if the interval for which
requests are being queued is already expired. Handle this
case by queueing ENDXFER command and waiting till recieves
XFRNRDY interrupt.

Change-Id: I9498be3967e0f80b05cf0830affe5463f07e2976
Signed-off-by: Vijayavardhan Vennapusa <vvreddy@codeaurora.org>
Signed-off-by: Jack Pham <jackp@codeaurora.org>
drivers/usb/dwc3/core.h
drivers/usb/dwc3/gadget.c
drivers/usb/dwc3/gadget.h

index c377250..23213ec 100644 (file)
@@ -510,6 +510,7 @@ struct dwc3_ep_events {
  * @number: endpoint number (1 - 15)
  * @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK
  * @resource_index: Resource transfer index
+ * @current_uf: Current uf received through last event parameter
  * @interval: the interval on which the ISOC transfer is started
  * @name: a human readable name e.g. ep1out-bulk
  * @direction: true for TX, false for RX
@@ -545,6 +546,7 @@ struct dwc3_ep {
        u8                      number;
        u8                      type;
        u8                      resource_index;
+       u16                     current_uf;
        u32                     interval;
 
        char                    name[20];
index 33c16b6..5d69618 100644 (file)
@@ -354,7 +354,16 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
                        dwc3_trace(trace_dwc3_gadget,
                                        "Command Complete --> %d",
                                        DWC3_DEPCMD_STATUS(reg));
-                       if (DWC3_DEPCMD_STATUS(reg))
+
+                       /* SW issues START TRANSFER command to isochronous ep
+                        * with future frame interval. If future interval time
+                        * has already passed when core recieves command, core
+                        * will respond with an error(bit13 in Command complete
+                        * event. Hence return error in this case.
+                        */
+                       if (reg & 0x2000)
+                               return -EAGAIN;
+                       else if (DWC3_DEPCMD_STATUS(reg))
                                return -EINVAL;
                        return 0;
                }
@@ -978,7 +987,7 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param,
                int start_new)
 {
        struct dwc3_gadget_ep_cmd_params params;
-       struct dwc3_request             *req;
+       struct dwc3_request             *req, *req1, *n;
        struct dwc3                     *dwc = dep->dwc;
        int                             ret;
        u32                             cmd;
@@ -1027,6 +1036,35 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param,
        if (ret < 0) {
                dev_dbg(dwc->dev, "failed to send STARTTRANSFER command\n");
 
+               if ((ret == -EAGAIN) && start_new &&
+                               usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
+                       /* If bit13 in Command complete event is set, software
+                        * must issue ENDTRANSFER command and wait for
+                        * Xfernotready event to queue the requests again.
+                        */
+                       if (!dep->resource_index) {
+                               dep->resource_index =
+                                        dwc3_gadget_ep_get_transfer_index(dwc,
+                                                               dep->number);
+                               WARN_ON_ONCE(!dep->resource_index);
+                       }
+                       dwc3_stop_active_transfer(dwc, dep->number, true);
+                       list_for_each_entry_safe_reverse(req1, n,
+                                                &dep->req_queued, list) {
+                               req1->trb = NULL;
+                               dwc3_gadget_move_request_list_front(req1);
+                               if (req->request.num_mapped_sgs)
+                                       dep->busy_slot +=
+                                                req->request.num_mapped_sgs;
+                               else
+                                       dep->busy_slot++;
+                               if ((dep->busy_slot & DWC3_TRB_MASK) ==
+                                                       DWC3_TRB_NUM - 1)
+                                       dep->busy_slot++;
+                       }
+                       return ret;
+               }
+
                /*
                 * FIXME we need to iterate over the list of requests
                 * here and stop, unmap, free and del each of the linked
@@ -1055,6 +1093,8 @@ static void __dwc3_gadget_start_isoc(struct dwc3 *dwc,
        u32 uf;
        int ret;
 
+       dep->current_uf = cur_uf;
+
        if (list_empty(&dep->request_list)) {
                dwc3_trace(trace_dwc3_gadget,
                                "ISOC ep %s run out for requests",
@@ -1153,10 +1193,17 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
                 * notion of current microframe.
                 */
                if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
-                       if (list_empty(&dep->req_queued)) {
+                       /* If xfernotready event is recieved before issuing
+                        * START TRANSFER command, don't issue END TRANSFER.
+                        * Rather start queueing the requests by issuing START
+                        * TRANSFER command.
+                        */
+                       if (list_empty(&dep->req_queued) && dep->resource_index)
                                dwc3_stop_active_transfer(dwc, dep->number, true);
-                               dep->flags = DWC3_EP_ENABLED;
-                       }
+                       else
+                               __dwc3_gadget_start_isoc(dwc, dep,
+                                                       dep->current_uf);
+                       dep->flags &= ~DWC3_EP_PENDING_REQUEST;
                        return 0;
                }
 
@@ -2127,18 +2174,17 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
 
        if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
                        list_empty(&dep->req_queued)) {
-               if (list_empty(&dep->request_list)) {
+               if (list_empty(&dep->request_list))
                        /*
                         * If there is no entry in request list then do
                         * not issue END TRANSFER now. Just set PENDING
                         * flag, so that END TRANSFER is issued when an
                         * entry is added into request list.
                         */
-                       dep->flags = DWC3_EP_PENDING_REQUEST;
-               } else {
+                       dep->flags |= DWC3_EP_PENDING_REQUEST;
+               else
                        dwc3_stop_active_transfer(dwc, dep->number, true);
-                       dep->flags = DWC3_EP_ENABLED;
-               }
+               dep->flags &= ~DWC3_EP_MISSED_ISOC;
                return 1;
        }
 
index bff248c..2938cd6 100644 (file)
@@ -68,6 +68,14 @@ static inline struct dwc3_request *next_request(struct list_head *list)
        return list_first_entry(list, struct dwc3_request, list);
 }
 
+static inline void dwc3_gadget_move_request_list_front(struct dwc3_request *req)
+{
+       struct dwc3_ep          *dep = req->dep;
+
+       req->queued = false;
+       list_move(&req->list, &dep->request_list);
+}
+
 static inline void dwc3_gadget_move_request_queued(struct dwc3_request *req)
 {
        struct dwc3_ep          *dep = req->dep;