Skip to content

Commit

Permalink
usb: dwc3: Fix missed isoc IN transaction
Browse files Browse the repository at this point in the history
If an IN transfer is missed on isoc endpoint, then driver must insure
that next ep_queue is properly handled.
This patch fixes this issue by starting a new transfer for next queued
request.

Signed-off-by: Pratyush Anand <pratyush.anand@st.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
  • Loading branch information
Pratyush Anand authored and Felipe Balbi committed Jun 4, 2012
1 parent d05b818 commit d6d6ec7
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 24 deletions.
3 changes: 3 additions & 0 deletions drivers/usb/dwc3/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,7 @@ struct dwc3_event_buffer {
* @number: endpoint number (1 - 15)
* @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK
* @res_trans_idx: Resource transfer index
* @current_uf: Current uf received through last event parameter
* @interval: the intervall on which the ISOC transfer is started
* @name: a human readable name e.g. ep1out-bulk
* @direction: true for TX, false for RX
Expand All @@ -428,6 +429,7 @@ struct dwc3_ep {
#define DWC3_EP_WEDGE (1 << 2)
#define DWC3_EP_BUSY (1 << 4)
#define DWC3_EP_PENDING_REQUEST (1 << 5)
#define DWC3_EP_MISSED_ISOC (1 << 6)

/* This last one is specific to EP0 */
#define DWC3_EP0_DIR_IN (1 << 31)
Expand All @@ -437,6 +439,7 @@ struct dwc3_ep {
u8 number;
u8 type;
u8 res_trans_idx;
u16 current_uf;
u32 interval;

char name[20];
Expand Down
73 changes: 49 additions & 24 deletions drivers/usb/dwc3/gadget.c
Original file line number Diff line number Diff line change
Expand Up @@ -990,6 +990,34 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param,
return 0;
}

static void __dwc3_gadget_start_isoc(struct dwc3 *dwc,
struct dwc3_ep *dep, u32 cur_uf)
{
u32 uf;

if (list_empty(&dep->request_list)) {
dev_vdbg(dwc->dev, "ISOC ep %s run out for requests.\n",
dep->name);
return;
}

/* 4 micro frames in the future */
uf = cur_uf + dep->interval * 4;

__dwc3_gadget_kick_transfer(dep, uf, 1);
}

static void dwc3_gadget_start_isoc(struct dwc3 *dwc,
struct dwc3_ep *dep, const struct dwc3_event_depevt *event)
{
u32 cur_uf, mask;

mask = ~(dep->interval - 1);
cur_uf = event->parameters & mask;

__dwc3_gadget_start_isoc(dwc, dep, cur_uf);
}

static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
{
struct dwc3 *dwc = dep->dwc;
Expand Down Expand Up @@ -1019,8 +1047,14 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)

list_add_tail(&req->list, &dep->request_list);

if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && (dep->flags & DWC3_EP_BUSY))
dep->flags |= DWC3_EP_PENDING_REQUEST;
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
if (dep->flags & DWC3_EP_BUSY) {
dep->flags |= DWC3_EP_PENDING_REQUEST;
} else if (dep->flags & DWC3_EP_MISSED_ISOC) {
__dwc3_gadget_start_isoc(dwc, dep, dep->current_uf);
dep->flags &= ~DWC3_EP_MISSED_ISOC;
}
}

/*
* There are two special cases:
Expand Down Expand Up @@ -1591,6 +1625,7 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
struct dwc3_trb *trb;
unsigned int count;
unsigned int s_pkt = 0;
unsigned int trb_status;

do {
req = next_request(&dep->req_queued);
Expand All @@ -1616,9 +1651,18 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,

if (dep->direction) {
if (count) {
dev_err(dwc->dev, "incomplete IN transfer %s\n",
dep->name);
status = -ECONNRESET;
trb_status = DWC3_TRB_SIZE_TRBSTS(trb->size);
if (trb_status == DWC3_TRBSTS_MISSED_ISOC) {
dev_dbg(dwc->dev, "incomplete IN transfer %s\n",
dep->name);
dep->current_uf = event->parameters &
~(dep->interval - 1);
dep->flags |= DWC3_EP_MISSED_ISOC;
} else {
dev_err(dwc->dev, "incomplete IN transfer %s\n",
dep->name);
status = -ECONNRESET;
}
}
} else {
if (count && (event->status & DEPEVT_STATUS_SHORT))
Expand Down Expand Up @@ -1690,25 +1734,6 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc,
}
}

static void dwc3_gadget_start_isoc(struct dwc3 *dwc,
struct dwc3_ep *dep, const struct dwc3_event_depevt *event)
{
u32 uf, mask;

if (list_empty(&dep->request_list)) {
dev_vdbg(dwc->dev, "ISOC ep %s run out for requests.\n",
dep->name);
return;
}

mask = ~(dep->interval - 1);
uf = event->parameters & mask;
/* 4 micro frames in the future */
uf += dep->interval * 4;

__dwc3_gadget_kick_transfer(dep, uf, 1);
}

static void dwc3_process_ep_cmd_complete(struct dwc3_ep *dep,
const struct dwc3_event_depevt *event)
{
Expand Down

0 comments on commit d6d6ec7

Please sign in to comment.