Re: [PATCH 2/2] USB: gadget: update ci13xxx to work with g_ether

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 12/12/2010 11:42 PM, Pavan Kondeti wrote:
> On 12/11/2010 1:35 PM, Artem Leonenko wrote:
>>
>> There is one nasty scenario when CI13xxx driver fails:
>> 	a) two or more rx requests are queued (g_ether does that)
>> 	b) rx request completed, interrupt fires and ci13xxx dequeues rq
>> 	c) request complete() callback gets called and in turn it calls ep_queue()
>> 		c1) in ep_queue() request gets added to the TAIL of the rx queue list
>> 	d) ep gets primed with rq from (b)
>> 	e) interrupt fires
>> 	f) request gets popped from queue head for hw dequeue
>> 	G) requets from queue head wasn't enqueued
>> 		g1)  isr_tr_complete_low() doesn't
>> 		enqueue more requests and it doesn't prime EP,
>> 		rx traffic stalls
>>
>> Solution:
>> 	a) enque queued requests ASAP, i.e. before calling complete() callback.
>> 	b) don't HW enqueue and prime endpoint with recently added request and
>> 	use the oldest request in the queue.
>>
>> Fixed issues:
>> 	a) ep_queue() may return an error code despite request was successfully
>> 	added to the queue (if _hardware_enqueue() fails)
>> 	b) Added requests are always processed in LIFO order, even if they are
>> 	added in complete() callback
>> 	c) Finally more than two and more queued requests are processed
>> consistently,
>> 	even if they were added in complete() callback
>>
>> And as a part of the fix to make g_ether (and especially booting with
>> NFS root) I had
>> to increase NET_IP_ALIGN to mips32 natural value (4).
>>
>> The fix was successfully tested on MIPS based SoC with 4KEc core and
>> CI13612 core. Board successfully boots with NFS root using g_ether
>> on ci13xxx udc
>>
>> Signed-off-by: Artem Leonenko <tikkeri@xxxxxxxxx>
>> ---
>>  drivers/usb/gadget/ci13xxx_udc.c |   15 ++++++++-------
>>  drivers/usb/gadget/u_ether.h     |    5 +++++
>>  2 files changed, 13 insertions(+), 7 deletions(-)
>>
>> diff --git a/drivers/usb/gadget/ci13xxx_udc.c
>> b/drivers/usb/gadget/ci13xxx_udc.c
>> index d788c7d..2c06cad 100644
>> --- a/drivers/usb/gadget/ci13xxx_udc.c
>> +++ b/drivers/usb/gadget/ci13xxx_udc.c
>> @@ -1789,18 +1789,17 @@ __acquires(mEp->lock)
>>
>>  	dbg_done(_usb_addr(mEp), mReq->ptr->token, retval);
>>
>> -	if (!mReq->req.no_interrupt && mReq->req.complete != NULL) {
>> -		spin_unlock(mEp->lock);
>> -		mReq->req.complete(&mEp->ep, &mReq->req);
>> -		spin_lock(mEp->lock);
>> -	}
>> -
>>  	if (!list_empty(&mEp->qh[mEp->dir].queue)) {
>>  		mReq = list_entry(mEp->qh[mEp->dir].queue.next,
>>  				  struct ci13xxx_req, queue);
> 
> You should use another variable for storing the request pointer. Because
> mReq is used below again to call the request complete function.

My bad, I made a mistake during merging my changes from 2.6.29 to
timesys 2.6.32 and than to linux-usb. Already fixed that in 'v2' patches.

> This patch worked fine (ping works with g_ether) for me after making
> this change. I did not modify NET_IP_ALIGN.

I'm dropping that change then.

>>  		_hardware_enqueue(mEp, mReq);
>>  	}
>>
>> +	if (!mReq->req.no_interrupt && mReq->req.complete != NULL) {
>> +		spin_unlock(mEp->lock);
>> +		mReq->req.complete(&mEp->ep, &mReq->req);
>> +		spin_lock(mEp->lock);
>> +	}
>>   done:
>>  	return retval;
>>  }
>> @@ -2170,7 +2169,9 @@ static int ep_queue(struct usb_ep *ep, struct
>> usb_request *req,
>>  	mReq->req.actual = 0;
>>  	list_add_tail(&mReq->queue, &mEp->qh[mEp->dir].queue);
>>
>> -	retval = _hardware_enqueue(mEp, mReq);
>> +	if (list_is_singular(&mEp->qh[mEp->dir].queue))
>> +		retval = _hardware_enqueue(mEp, mReq);
>> +
>>  	if (retval == -EALREADY || retval == -EBUSY) {
>>  		dbg_event(_usb_addr(mEp), "QUEUE", retval);
>>  		retval = 0;
> 
> Given that _hardware_enqueue is called only when endpoint is in
> un-primed state, may be we should pass the value returned by
> _hardware_enqueue back to usb_ep_queue() caller.
> 

How can we do that if caller puts more than one request to the queue? If
there are already requests in the queue then ep_queue returns and
_hardware_enqueue will be called later in isr_tr_complete_low().


>> diff --git a/drivers/usb/gadget/u_ether.h b/drivers/usb/gadget/u_ether.h
>> index 3c8c0c9..c4bb92e 100644
>> --- a/drivers/usb/gadget/u_ether.h
>> +++ b/drivers/usb/gadget/u_ether.h
>> @@ -23,6 +23,11 @@
>>  #ifndef __U_ETHER_H
>>  #define __U_ETHER_H
>>
>> +#if defined(CONFIG_USB_GADGET_CI13XXX)
>> +#define NET_IP_ALIGN                    4
>> +#endif
>> +
>> +
>>  #include <linux/err.h>
>>  #include <linux/if_ether.h>
>>  #include <linux/usb/composite.h>
>> --
>> 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
> 
> 

--
Cheers,
Artem
--
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


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux