Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f2e81fb2 authored by Vijayavardhan Vennapusa's avatar Vijayavardhan Vennapusa Committed by Stephen Boyd
Browse files

USB: dwc3: Fix issues in isochronous endpoint handling



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: default avatarVijayavardhan Vennapusa <vvreddy@codeaurora.org>
parent 13a5275a
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -420,6 +420,7 @@ struct dwc3_event_buffer {
 * @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 intervall on which the ISOC transfer is started
 * @name: a human readable name e.g. ep1out-bulk
 * @direction: true for TX, false for RX
@@ -453,6 +454,7 @@ struct dwc3_ep {
	u8			number;
	u8			type;
	u8			resource_index;
	u16			current_uf;
	u32			interval;

	char			name[20];
+64 −18
Original line number Diff line number Diff line
@@ -355,6 +355,15 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
		if (!(reg & DWC3_DEPCMD_CMDACT)) {
			dev_vdbg(dwc->dev, "Command Complete --> %d\n",
					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
				return 0;
		}

@@ -948,7 +957,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;
@@ -997,6 +1006,34 @@ 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 ENDTRANDFER 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);
			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;
		} else {
			/*
			 * FIXME we need to iterate over the list of requests
			 * here and stop, unmap, free and del each of the linked
@@ -1007,6 +1044,7 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param,
			list_del(&req->list);
			return ret;
		}
	}

	dep->flags |= DWC3_EP_BUSY;

@@ -1024,6 +1062,8 @@ static void __dwc3_gadget_start_isoc(struct dwc3 *dwc,
{
	u32 uf;

	dep->current_uf = cur_uf;

	if (list_empty(&dep->request_list)) {
		dev_vdbg(dwc->dev, "ISOC ep %s run out for requests.\n",
			dep->name);
@@ -1096,10 +1136,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);
				dep->flags = DWC3_EP_ENABLED;
			}
			else
				__dwc3_gadget_start_isoc(dwc, dep,
							dep->current_uf);
			dep->flags &= ~DWC3_EP_PENDING_REQUEST;
			return 0;
		}

@@ -1840,18 +1887,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);
			dep->flags = DWC3_EP_ENABLED;
		}
		dep->flags &= ~DWC3_EP_MISSED_ISOC;
		return 1;
	}

+8 −0
Original line number Diff line number Diff line
@@ -94,6 +94,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;