All of lore.kernel.org
 help / color / mirror / Atom feed
* RE: Cannot stall an endpoint 0 control transfer from a data stage cal lback function
@ 2007-05-17 16:35 Geoffrey Tam
  2007-05-18 16:25 ` Tony Lindgren
  0 siblings, 1 reply; 9+ messages in thread
From: Geoffrey Tam @ 2007-05-17 16:35 UTC (permalink / raw)
  To: 'linux-omap-open-source@linux.omap.com'

For some reason, this did not make it into the mailing list archive, so I'm
sending again.

-----Original Message-----
From: Geoffrey Tam 
Sent: Monday, May 14, 2007 2:24 PM
To: 'tony@atomide.com'
Cc: 'linux-omap-open-source@linux.omap.com'
Subject: RE: Cannot stall an endpoint 0 control transfer from a data
stage cal lback function


I have tried using g_ep0.c from the linux-omap git tree
(git://source.mvista.com/git/linux-omap-2.6.git). This resulted in compiler
errors.

So, I tried copying all USB files:
- driver/usb/* using "cp -R drivers/usb/*"
- include/linux using "cp include/linux/usb*" and "cp include/linux/usb/*"
This resulted in compiler errors when building the modules using "make
ARCH=arm CROSS_COMPILE=arm_v5t_le- modules". Building the kernel worked
fine. I'm not sure if there is a problem with my config.

I took a look at the code in the git tree. It did not look like any of my
patch was implemented. Perhaps, I grabbed the wrong git tree?

My main problem is I need to stall endpoint 0 from the callback function
when it is called either in the setup or data stage. The code seems to set
the software state machine back to MGC_END0_STAGE_SETUP before the callback
is called. This prevents software from setting the stall condition using
musb_g_ep0_halt(). Also, the driver signals to the hardware before the
callback is called that the data stage has completed. This prevents the
hardware from setting the stall condition from the callback function.

I am not sure why the mass storage gadget fails to enumerate with my fixes.
Unfortunately, my Davinci eval board does not like Linux 2.6.20 - it hangs
shortly after it is loaded. So, I cannot look into this.

I will try again when I get the chance.

Geoffrey

-----Original Message-----
From: tony@atomide.com [mailto:tony@atomide.com]
Sent: Friday, May 11, 2007 2:11 PM
To: Geoffrey Tam
Cc: 'linux-omap-open-source@linux.omap.com'
Subject: Re: Cannot stall an endpoint 0 control transfer from a data
stage cal lback function


* Geoffrey Tam <geoffrey@evertz.com> [070509 11:12]:
> Also sent to 'linux-usb-devel@lists.sourceforge.net'.
> 
> patch was created as per Documentation/SubmittingPatches.

Thanks, I tried it out quickly and looks like the last part of your
patch is already in the linux-omap git tree.

After applying the other parts, the mass storage gadget did not
enumerate though... I'll look into it again when I have a chance.

Do you have any chance to  try the current linux-omap git
tree and see how it works for you, then see what happens with
your patch?

Regards,

Tony

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: Cannot stall an endpoint 0 control transfer from a data stage cal lback function
  2007-05-17 16:35 Cannot stall an endpoint 0 control transfer from a data stage cal lback function Geoffrey Tam
@ 2007-05-18 16:25 ` Tony Lindgren
  0 siblings, 0 replies; 9+ messages in thread
From: Tony Lindgren @ 2007-05-18 16:25 UTC (permalink / raw)
  To: Geoffrey Tam; +Cc: 'linux-omap-open-source@linux.omap.com'

* Geoffrey Tam <geoffrey@evertz.com> [070517 09:40]:
> For some reason, this did not make it into the mailing list archive, so I'm
> sending again.
> 
> -----Original Message-----
> From: Geoffrey Tam 
> Sent: Monday, May 14, 2007 2:24 PM
> To: 'tony@atomide.com'
> Cc: 'linux-omap-open-source@linux.omap.com'
> Subject: RE: Cannot stall an endpoint 0 control transfer from a data
> stage cal lback function
> 
> 
> I have tried using g_ep0.c from the linux-omap git tree
> (git://source.mvista.com/git/linux-omap-2.6.git). This resulted in compiler
> errors.
> 
> So, I tried copying all USB files:
> - driver/usb/* using "cp -R drivers/usb/*"
> - include/linux using "cp include/linux/usb*" and "cp include/linux/usb/*"
> This resulted in compiler errors when building the modules using "make
> ARCH=arm CROSS_COMPILE=arm_v5t_le- modules". Building the kernel worked
> fine. I'm not sure if there is a problem with my config.
> 
> I took a look at the code in the git tree. It did not look like any of my
> patch was implemented. Perhaps, I grabbed the wrong git tree?

That's the right tree. Well only one part of you patch seems to be
implemented there.

> My main problem is I need to stall endpoint 0 from the callback function
> when it is called either in the setup or data stage. The code seems to set
> the software state machine back to MGC_END0_STAGE_SETUP before the callback
> is called. This prevents software from setting the stall condition using
> musb_g_ep0_halt(). Also, the driver signals to the hardware before the
> callback is called that the data stage has completed. This prevents the
> hardware from setting the stall condition from the callback function.
> 
> I am not sure why the mass storage gadget fails to enumerate with my fixes.
> Unfortunately, my Davinci eval board does not like Linux 2.6.20 - it hangs
> shortly after it is loaded. So, I cannot look into this.

Yeah just tried it again, and all gadgets fail to enumerate after it..
Hopefully you'll get a chance to try it with the latest git tree at some
point :)

Tony

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: Cannot stall an endpoint 0 control transfer from a data stage cal lback function
  2007-08-21 23:58 ` David Brownell
@ 2007-08-24 12:09   ` Tony Lindgren
  0 siblings, 0 replies; 9+ messages in thread
From: Tony Lindgren @ 2007-08-24 12:09 UTC (permalink / raw)
  To: David Brownell; +Cc: Geoffrey Tam, linux-omap-open-source

* David Brownell <david-b@pacbell.net> [070821 16:58]:
> On Tuesday 21 August 2007, Geoffrey Tam wrote:
> > Sorry for the long delay.
> > 
> > I just tried out the latest code on the git tree, and it does not work for
> > me. It does not allow me to stall an endpoint.
> 
> I was wondering what happened to this problem report ... I cleaned
> up your old patch and fixed some things that were wrong, maybe this
> one will work better.
> 
> - Dave
> 
> 
> 
> ======	CUT HERE
> Gadget drivers are supposed to be able to cause EP0 protocol stalls by
> issuing an appropriate request from the callback issued when the DATA
> stage completes ... not only from the setup() callback or from some
> thread that decides how to handle the request.
> 
> This fix is based on a patch from Geoffrey Tam <geoffrey@evertz.com>,
> and addresses that by updating the endpoint state AFTER the callback
> is issued, providing the correct IRQ-acking CSR to the halt() so it
> can just mask in the SEND_STALL bit, and ensuring that only the CSR
> is still written only once even on this new code path.
> 
> Also includes a few small cleanups:  avoid "this" variable name, and
> pack device bitfields more efficiently (wasting less space).
> 
> Allegedly helps file_storage on Davinci.

I've pushed this as the driver seems to work after the patch :)

Tony

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: Cannot stall an endpoint 0 control transfer from a data stage cal lback function
  2007-08-21 20:10 Geoffrey Tam
@ 2007-08-21 23:58 ` David Brownell
  2007-08-24 12:09   ` Tony Lindgren
  0 siblings, 1 reply; 9+ messages in thread
From: David Brownell @ 2007-08-21 23:58 UTC (permalink / raw)
  To: linux-omap-open-source; +Cc: Geoffrey Tam

On Tuesday 21 August 2007, Geoffrey Tam wrote:
> Sorry for the long delay.
> 
> I just tried out the latest code on the git tree, and it does not work for
> me. It does not allow me to stall an endpoint.

I was wondering what happened to this problem report ... I cleaned
up your old patch and fixed some things that were wrong, maybe this
one will work better.

- Dave



======	CUT HERE
Gadget drivers are supposed to be able to cause EP0 protocol stalls by
issuing an appropriate request from the callback issued when the DATA
stage completes ... not only from the setup() callback or from some
thread that decides how to handle the request.

This fix is based on a patch from Geoffrey Tam <geoffrey@evertz.com>,
and addresses that by updating the endpoint state AFTER the callback
is issued, providing the correct IRQ-acking CSR to the halt() so it
can just mask in the SEND_STALL bit, and ensuring that only the CSR
is still written only once even on this new code path.

Also includes a few small cleanups:  avoid "this" variable name, and
pack device bitfields more efficiently (wasting less space).

Allegedly helps file_storage on Davinci.

---
 drivers/usb/musb/musb_core.h       |    8 ++--
 drivers/usb/musb/musb_gadget_ep0.c |   61 +++++++++++++++++++++++++------------
 2 files changed, 46 insertions(+), 23 deletions(-)

--- o26.orig/drivers/usb/musb/musb_gadget_ep0.c	2007-08-21 16:52:00.000000000 -0700
+++ o26/drivers/usb/musb/musb_gadget_ep0.c	2007-08-21 16:54:11.000000000 -0700
@@ -197,8 +197,8 @@ service_in_request(struct musb *musb,
  */
 static void musb_g_ep0_giveback(struct musb *musb, struct usb_request *req)
 {
-	musb->ep0_state = MUSB_EP0_STAGE_SETUP;
 	musb_g_giveback(&musb->endpoints[0].ep_in, req, 0);
+	musb->ep0_state = MUSB_EP0_STAGE_SETUP;
 }
 
 /*
@@ -434,13 +434,13 @@ stall:
 /* we have an ep0out data packet
  * Context:  caller holds controller lock
  */
-static void ep0_rxstate(struct musb *this)
+static void ep0_rxstate(struct musb *musb)
 {
-	void __iomem		*regs = this->control_ep->regs;
+	void __iomem		*regs = musb->control_ep->regs;
 	struct usb_request	*req;
 	u16			tmp;
 
-	req = next_ep0_request(this);
+	req = next_ep0_request(musb);
 
 	/* read packet and ack; or stall because of gadget driver bug:
 	 * should have provided the rx buffer before setup() returned.
@@ -455,25 +455,29 @@ static void ep0_rxstate(struct musb *thi
 			req->status = -EOVERFLOW;
 			tmp = len;
 		}
-		musb_read_fifo(&this->endpoints[0], tmp, buf);
+		musb_read_fifo(&musb->endpoints[0], tmp, buf);
 		req->actual += tmp;
 		tmp = MUSB_CSR0_P_SVDRXPKTRDY;
 		if (tmp < 64 || req->actual == req->length) {
-			this->ep0_state = MUSB_EP0_STAGE_STATUSIN;
+			musb->ep0_state = MUSB_EP0_STAGE_STATUSIN;
 			tmp |= MUSB_CSR0_P_DATAEND;
 		} else
 			req = NULL;
 	} else
 		tmp = MUSB_CSR0_P_SVDRXPKTRDY | MUSB_CSR0_P_SENDSTALL;
-	musb_writew(regs, MUSB_CSR0, tmp);
 
 
-	/* NOTE:  we "should" hold off reporting DATAEND and going to
-	 * STATUSIN until after the completion handler decides whether
-	 * to issue a stall instead, since this hardware can do that.
+	/* Completion handler may choose to stall, e.g. because the
+	 * message just received holds invalid data.
 	 */
-	if (req)
-		musb_g_ep0_giveback(this, req);
+	if (req) {
+		musb->ackpend = tmp;
+		musb_g_ep0_giveback(musb, req);
+		if (!musb->ackpend)
+			return;
+		musb->ackpend = 0;
+	}
+	musb_writew(regs, MUSB_CSR0, tmp);
 }
 
 /*
@@ -511,16 +515,21 @@ static void ep0_txstate(struct musb *mus
 	} else
 		request = NULL;
 
-	/* send it out, triggering a "txpktrdy cleared" irq */
-	musb_writew(regs, MUSB_CSR0, csr);
-
 	/* report completions as soon as the fifo's loaded; there's no
 	 * win in waiting till this last packet gets acked.  (other than
 	 * very precise fault reporting, needed by USB TMC; possible with
 	 * this hardware, but not usable from portable gadget drivers.)
 	 */
-	if (request)
+	if (request) {
+		musb->ackpend = csr;
 		musb_g_ep0_giveback(musb, request);
+		if (!musb->ackpend)
+			return;
+		musb->ackpend = 0;
+	}
+
+	/* send it out, triggering a "txpktrdy cleared" irq */
+	musb_writew(regs, MUSB_CSR0, csr);
 }
 
 /*
@@ -920,6 +929,7 @@ static int musb_g_ep0_halt(struct usb_ep
 	musb = ep->musb;
 	base = musb->mregs;
 	regs = musb->control_ep->regs;
+	status = 0;
 
 	spin_lock_irqsave(&musb->lock, flags);
 
@@ -928,17 +938,30 @@ static int musb_g_ep0_halt(struct usb_ep
 		goto cleanup;
 	}
 
+	musb_ep_select(base, 0);
+	csr = musb->ackpend;
+
 	switch (musb->ep0_state) {
+
+	/* Stalls are usually issued after parsing SETUP packet, either
+	 * directly in irq context from setup() or else later.
+	 */
 	case MUSB_EP0_STAGE_TX:		/* control-IN data */
 	case MUSB_EP0_STAGE_ACKWAIT:	/* STALL for zero-length data */
 	case MUSB_EP0_STAGE_RX:		/* control-OUT data */
-		status = 0;
-
-		musb_ep_select(base, 0);
 		csr = musb_readw(regs, MUSB_CSR0);
+		/* FALLTHROUGH */
+
+	/* It's also OK to issue stalls during callbacks when a non-empty
+	 * DATA stage buffer has been read (or even written).
+	 */
+	case MUSB_EP0_STAGE_STATUSIN:	/* control-OUT status */
+	case MUSB_EP0_STAGE_STATUSOUT:	/* control-IN status */
+
 		csr |= MUSB_CSR0_P_SENDSTALL;
 		musb_writew(regs, MUSB_CSR0, csr);
 		musb->ep0_state = MUSB_EP0_STAGE_SETUP;
+		musb->ackpend = 0;
 		break;
 	default:
 		DBG(1, "ep0 can't halt in state %d\n", musb->ep0_state);
--- o26.orig/drivers/usb/musb/musb_core.h	2007-08-21 16:52:00.000000000 -0700
+++ o26/drivers/usb/musb/musb_core.h	2007-08-21 16:54:11.000000000 -0700
@@ -393,6 +393,9 @@ struct musb {
 
 	u8			min_power;	/* vbus for periph, in mA/2 */
 
+	int			a_wait_bcon;	/* VBUS timeout in msecs */
+	unsigned long		idle_timeout;	/* Next timeout in jiffies */
+
 	/* active means connected and not suspended */
 	unsigned		is_active:1;
 
@@ -400,9 +403,6 @@ struct musb {
 	unsigned is_host:1;
 	unsigned ignore_disconnect:1;	/* during bus resets */
 
-	int			a_wait_bcon;	/* VBUS timeout in msecs */
-	unsigned long		idle_timeout;	/* Next timeout in jiffies */
-
 #ifdef C_MP_TX
 	unsigned bulk_split:1;
 #define	can_bulk_split(musb,type) \
@@ -442,10 +442,10 @@ struct musb {
 	unsigned test_mode:1;
 	unsigned softconnect:1;
 
-	enum musb_g_ep0_state	ep0_state;
 	u8			address;
 	u8			test_mode_nr;
 	u16			ackpend;		/* ep0 */
+	enum musb_g_ep0_state	ep0_state;
 	struct usb_gadget	g;			/* the gadget */
 	struct usb_gadget_driver *gadget_driver;	/* its driver */
 #endif

^ permalink raw reply	[flat|nested] 9+ messages in thread

* RE: Cannot stall an endpoint 0 control transfer from a data stage cal lback function
@ 2007-08-21 20:10 Geoffrey Tam
  2007-08-21 23:58 ` David Brownell
  0 siblings, 1 reply; 9+ messages in thread
From: Geoffrey Tam @ 2007-08-21 20:10 UTC (permalink / raw)
  To: 'linux-omap-open-source@linux.omap.com'

Sorry for the long delay.

I just tried out the latest code on the git tree, and it does not work for
me. It does not allow me to stall an endpoint.

-----Original Message-----
From: Kevin Hilman [mailto:khilman@mvista.com]
Sent: Friday, May 18, 2007 2:57 PM
To: Geoffrey Tam
Cc: Tony Lindgren
Subject: RE: Cannot stall an endpoint 0 control transfer from a data
stage cal lback function


On Thu, 2007-05-17 at 12:35 -0400, Geoffrey Tam wrote:

> I am not sure why the mass storage gadget fails to enumerate with my
fixes.
> Unfortunately, my Davinci eval board does not like Linux 2.6.20 - it hangs
> shortly after it is loaded. So, I cannot look into this.
> 
> I will try again when I get the chance.

Geoffrey,

Can you try again with the updated DaVinci git tree.  I just merged the
USB support from the OMAP tree into the DaVinci tree.  

What exactly does your EVM board do when booting.  Can you send me a
console log?

Kevin

^ permalink raw reply	[flat|nested] 9+ messages in thread

* RE: Cannot stall an endpoint 0 control transfer from a data stage cal lback function
@ 2007-05-14 18:24 Geoffrey Tam
  0 siblings, 0 replies; 9+ messages in thread
From: Geoffrey Tam @ 2007-05-14 18:24 UTC (permalink / raw)
  To: 'tony@atomide.com'
  Cc: 'linux-omap-open-source@linux.omap.com'

I have tried using g_ep0.c from the linux-omap git tree
(git://source.mvista.com/git/linux-omap-2.6.git). This resulted in compiler
errors.

So, I tried copying all USB files:
- driver/usb/* using "cp -R drivers/usb/*"
- include/linux using "cp include/linux/usb*" and "cp include/linux/usb/*"
This resulted in compiler errors when building the modules using "make
ARCH=arm CROSS_COMPILE=arm_v5t_le- modules". Building the kernel worked
fine. I'm not sure if there is a problem with my config.

I took a look at the code in the git tree. It did not look like any of my
patch was implemented. Perhaps, I grabbed the wrong git tree?

My main problem is I need to stall endpoint 0 from the callback function
when it is called either in the setup or data stage. The code seems to set
the software state machine back to MGC_END0_STAGE_SETUP before the callback
is called. This prevents software from setting the stall condition using
musb_g_ep0_halt(). Also, the driver signals to the hardware before the
callback is called that the data stage has completed. This prevents the
hardware from setting the stall condition from the callback function.

I am not sure why the mass storage gadget fails to enumerate with my fixes.
Unfortunately, my Davinci eval board does not like Linux 2.6.20 - it hangs
shortly after it is loaded. So, I cannot look into this.

I will try again when I get the chance.

Geoffrey

-----Original Message-----
From: tony@atomide.com [mailto:tony@atomide.com]
Sent: Friday, May 11, 2007 2:11 PM
To: Geoffrey Tam
Cc: 'linux-omap-open-source@linux.omap.com'
Subject: Re: Cannot stall an endpoint 0 control transfer from a data
stage cal lback function


* Geoffrey Tam <geoffrey@evertz.com> [070509 11:12]:
> Also sent to 'linux-usb-devel@lists.sourceforge.net'.
> 
> patch was created as per Documentation/SubmittingPatches.

Thanks, I tried it out quickly and looks like the last part of your
patch is already in the linux-omap git tree.

After applying the other parts, the mass storage gadget did not
enumerate though... I'll look into it again when I have a chance.

Do you have any chance to  try the current linux-omap git
tree and see how it works for you, then see what happens with
your patch?

Regards,

Tony

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: Cannot stall an endpoint 0 control transfer from a data stage cal lback function
  2007-05-09 18:07 Geoffrey Tam
@ 2007-05-11 18:10 ` tony
  0 siblings, 0 replies; 9+ messages in thread
From: tony @ 2007-05-11 18:10 UTC (permalink / raw)
  To: Geoffrey Tam; +Cc: 'linux-omap-open-source@linux.omap.com'

* Geoffrey Tam <geoffrey@evertz.com> [070509 11:12]:
> Also sent to 'linux-usb-devel@lists.sourceforge.net'.
> 
> patch was created as per Documentation/SubmittingPatches.

Thanks, I tried it out quickly and looks like the last part of your
patch is already in the linux-omap git tree.

After applying the other parts, the mass storage gadget did not
enumerate though... I'll look into it again when I have a chance.

Do you have any chance to  try the current linux-omap git
tree and see how it works for you, then see what happens with
your patch?

Regards,

Tony

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Cannot stall an endpoint 0 control transfer from a data stage cal lback function
@ 2007-05-09 21:56 Geoffrey Tam
  0 siblings, 0 replies; 9+ messages in thread
From: Geoffrey Tam @ 2007-05-09 21:56 UTC (permalink / raw)
  To: 'linux-omap-open-source@linux.omap.com'



> -----Original Message-----
> From: Geoffrey Tam 
> Sent: Wednesday, May 09, 2007 5:09 PM
> To: linux-usb-devel@lists.sourceforge.net
> Cc: 'david-b@pacbell.net'
> Subject: RE: [linux-usb-devel] Cannot stall an endpoint 0 control
> transfer from a data stage callback function
> 
> 
> I just took a look at the Linux-OMAP tree 
> (http://source.mvista.com/git/gitweb.cgi?p=linux-omap-2.6.git;
a=blob;h=aad53fdd2a3041967b2e249c77368c06a663a455>
;hb=bffa508892990cb399a10542b39d4e54387c5549;f=drivers/usb/mus
> b/g_ep0.c), and it doesn't look like my problem will be 
> solved by this code.
> 
> Did I look in the right spot?
> 
> > -----Original Message-----
> > From: David Brownell [mailto:david-b@pacbell.net]
> > Sent: Wednesday, May 09, 2007 4:38 AM
> > To: Geoffrey Tam
> > Cc: linux-usb-devel@lists.sourceforge.net; Greg KH
> > Subject: Re: [linux-usb-devel] Cannot stall an endpoint 0 control
> > transfer from a data stage cal lback function
> > 
> > 
> > On Wednesday 09 May 2007, Greg KH wrote:
> > 
> > > Can you take a look at the file, 
> Documentation/SubmittingPatches and
> > > resend this based on the directions written there for how 
> > to create a
> > > patch in a manner so that we can apply it?
> > > 
> > > Also, if you are having mvista specific issues, I suggest 
> contacting
> > > them, there is nothing we can do about their kernels, and 
> > we do not know
> > > what they add or remove from them, based on the 
> kernel.org releases.
> > 
> > More to the point, that GIT tree for DaVinci doesn't have the
> > latest code for that driver.  Grab it from the Linux-OMAP tree
> > instead ... you'll find various bugs have been fixed since
> > January (or whenever that tree was last updated).
> > 
> > - Dave
> > 
> > 
> 

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Cannot stall an endpoint 0 control transfer from a data stage cal lback function
@ 2007-05-09 18:07 Geoffrey Tam
  2007-05-11 18:10 ` tony
  0 siblings, 1 reply; 9+ messages in thread
From: Geoffrey Tam @ 2007-05-09 18:07 UTC (permalink / raw)
  To: 'linux-omap-open-source@linux.omap.com'

[-- Attachment #1: Type: text/plain, Size: 6023 bytes --]

Also sent to 'linux-usb-devel@lists.sourceforge.net'.

patch was created as per Documentation/SubmittingPatches.
     
> -----Original Message-----
> From: Kevin Hilman [mailto:khilman@mvista.com]
> Sent: Wednesday, May 09, 2007 12:35 PM
> To: Geoffrey Tam
> Cc: 'linux-usb-devel@lists.sourceforge.net'
> Subject: RE: [linux-usb-devel] Cannot stall an endpoint 0 control
> transfer from a data stage callback function
> 
> 
> On Wed, 2007-05-09 at 11:14 -0400, Geoffrey Tam wrote:
> > Here is the patch created as per instructions in
> > Documentation/SubmittingPatches.
> > 
> > Does anyone know if there is a MontaVista USB developer 
> mailing list?
> 
> The latest code for this MUSB driver is maintained as part of the OMAP
> git tree.  The major rework of the Mentor code was done primarily by
> David Brownell, and more recently with several patches from 
> the OMAP git
> developers.
> 
> I suggest you send your patch to the OMAP mailing list[1].  Once it is
> approved/merged into the OMAP tree, it will make it into the DaVinci
> tree.  Note however, that the DaVinci tree is not actively maintained.
> Right now we are focusing efforts on getting the DaVinci codebase into
> mainline instead of maintaining a separate tree.
> 
> Kevin
> 
> [1] http://linux.omap.com/mailman/listinfo/linux-omap-open-source
> 
> > > Hi,
> > > 
> > > On Wed, May 09, 2007 at 12:52:46AM -0700, ext Greg KH wrote:
> > > > On Thu, May 03, 2007 at 04:02:59PM -0400, Geoffrey Tam wrote:
> > > > > I am new to the Linux open source community. I am not 
> > > sure if I am posting
> > > > > this to the correct mailing list. Please tell me where 
> > > this should be posted
> > > > > if it is in the wrong spot. I have subscribed to this 
> > > mailing list.
> > > > > 
> > > > > I am using Linux 2.6.20. It was retrieved from the 
> > > MontaVista git tree
> > > > > (source.mvista.com/git/linux-davinci-2.6.gitCVS:) in 
> > > February 2007. I am
> > > > > using the TI Davinci processor.
> > > > > 
> > > > > My USB gadget supports 2 endpoint 0 control transfers: 
> > > "get variable" and
> > > > > "set variable".
> > > > > 
> > > > > The "get" function must stall endpoint 0 if the variable 
> > > id retrieved from
> > > > > the endpoint 0 setup stage packet is invalid. The current 
> > > implementation of
> > > > > the USB gadget supports this.
> > > > > 
> > > > > The "set" function receives the variable id in the 
> > > endpoint 0 setup stage
> > > > > packet. The value to set the variable to is received in 
> > > the data stage
> > > > > packet. If this value is outside of the variable's valid 
> > > range, it must
> > > > > stall endpoint 0. The current implementation of the USB 
> > > gadget does not
> > > > > support this.
> > > > > 
> > > > > This is a legacy protocol, so it cannot be changed as 
> > > this would break other
> > > > > applications that already use it.
> > > > > 
> > > > > Through my debugging, I believe that the souce of my 
> problems are:
> > > > > - The state machine is going to the setup stage 
> > > (MGC_END0_STAGE_SETUP) too
> > > > > early.
> > > > > - The USB peripheral hardware is being notified with "end 
> > > of data stage" too
> > > > > early (MGC_M_CSR0_P_DATAEND bit in MGC_O_HDRC_CSR0 being 
> > > written too early).
> > > > > 
> > > > > I noticed that on 22 Mar 2007, an update to the git tree 
> > > was made to correct
> > > > > problems on endpoint 0
> > > > > 
> > > (source.mvista.com/git/gitweb.cgi?p=linux-davinci-2.6.git;a=lo
> > > g). I scanned
> > > > > the changes that were made, and do not believe that my 
> > > changes are included.
> > > > > 
> > > > > I made changes to the code, and this has fixed the 
> > > problem for me. I would
> > > > > appreciate if someone could review them for correctness, 
> > > and if possible,
> > > > > add them to the git tree.
> > > > > 
> > > > > Only 1 file had to be modified: drivers/usb/musb/g_ep0.c
> > > > > 
> > > > > There are 4 files included in this e-mail:
> > > > > - g_ep0-2_6_20.c is the original Linux 2.6.20 g_ep0.c 
> > > from the git tree.
> > > > > - g_ep0.c is my modified file.
> > > > > - PATCH contains the differences between the 2 files.
> > > > > - README contains information on how PATCH was generated, 
> > > and why the
> > > > > changes were made.
> > > > 
> > > > Can you take a look at the file, 
> Documentation/SubmittingPatches and
> > > > resend this based on the directions written there for how 
> > > to create a
> > > > patch in a manner so that we can apply it?
> > > > 
> > > > Also, if you are having mvista specific issues, I 
> suggest contacting
> > > > them, there is nothing we can do about their kernels, and 
> > > we do not know
> > > > what they add or remove from them, based on the 
> kernel.org releases.
> > > 
> > > And also, musb driver is not yet pushed into 
> linux-2.6.git. Maybe you
> > > would like to sign to linux-omap mailing list. Here's the link:
> > > http://linux.omap.com/mailman/listinfo/linux-omap-open-source
> > > 
> > > Also, you would like to send this email to Kevin Hilman 
> > > <khilman@mvista.com>. He's the maintainer
> > > of the linux-davinci gittree.
> > > 
> > > 
> > > > 
> > > > thanks,
> > > > 
> > > > greg k-h
> > > > 
> > > > 
> > > --------------------------------------------------------------
> > > -----------
> > > > This SF.net email is sponsored by DB2 Express
> > > > Download DB2 Express C - the FREE version of DB2 
> express and take
> > > > control of your XML. No limits. Just data. Click to get it now.
> > > > http://sourceforge.net/powerbar/db2/
> > > > _______________________________________________
> > > > linux-usb-devel@lists.sourceforge.net
> > > > To unsubscribe, use the last form field at:
> > > > https://lists.sourceforge.net/lists/listinfo/linux-usb-devel
> > > 
> > > -- 
> > > Best Regards,
> > > 
> > > Felipe Balbi
> > > felipe.lima@indt.org.br
> > > +55 92 2126-1003
> > > 
> > > Kernel Developers Team
> > > 
> > 
> 


 <<patch>>  <<g_ep0-2_6_20.c>>  <<g_ep0.c>>  <<PATCH>>  <<README>> 

[-- Attachment #2: patch --]
[-- Type: application/octet-stream, Size: 1775 bytes --]

--- linux_mv_davinci/drivers/usb/musb/g_ep0.c.orig	2007-05-09 10:56:29.000000000 -0400
+++ linux_mv_davinci/drivers/usb/musb/g_ep0.c	2007-03-22 15:16:35.000000000 -0400
@@ -196,7 +196,6 @@ service_in_request(struct musb *pThis,
  */
 static void musb_g_ep0_giveback(struct musb *pThis, struct usb_request *req)
 {
-	pThis->ep0_state = MGC_END0_STAGE_SETUP;
 	musb_g_giveback(&pThis->aLocalEnd[0].ep_in, req, 0);
 }
 
@@ -456,7 +455,6 @@ static void ep0_rxstate(struct musb *thi
 			req = NULL;
 	} else
 		tmp = MGC_M_CSR0_P_SVDRXPKTRDY | MGC_M_CSR0_P_SENDSTALL;
-	musb_writew(regs, MGC_O_HDRC_CSR0, tmp);
 
 
 	/* NOTE:  we "should" hold off reporting DATAEND and going to
@@ -465,6 +463,7 @@ static void ep0_rxstate(struct musb *thi
 	 */
 	if (req)
 		musb_g_ep0_giveback(this, req);
+	musb_writew(regs, MGC_O_HDRC_CSR0, tmp);
 }
 
 /*
@@ -503,7 +502,6 @@ static void ep0_txstate(struct musb *pTh
 		pRequest = NULL;
 
 	/* send it out, triggering a "txpktrdy cleared" irq */
-	musb_writew(regs, MGC_O_HDRC_CSR0, wCsrVal);
 
 	/* report completions as soon as the fifo's loaded; there's no
 	 * win in waiting till this last packet gets acked.  (other than
@@ -512,6 +510,7 @@ static void ep0_txstate(struct musb *pTh
 	 */
 	if (pRequest)
 		musb_g_ep0_giveback(pThis, pRequest);
+	musb_writew(regs, MGC_O_HDRC_CSR0, wCsrVal);
 }
 
 /*
@@ -937,6 +936,8 @@ static int musb_g_ep0_halt(struct usb_ep
 	case MGC_END0_STAGE_TX:		/* control-IN data */
 	case MGC_END0_STAGE_ACKWAIT:	/* STALL for zero-length data */
 	case MGC_END0_STAGE_RX:		/* control-OUT data */
+	case MGC_END0_STAGE_STATUSIN:		/* control-OUT status */
+	case MGC_END0_STAGE_STATUSOUT:		/* control-IN status */
 		status = 0;
 
 		MGC_SelectEnd(base, 0);

[-- Attachment #3: g_ep0-2_6_20.c --]
[-- Type: application/octet-stream, Size: 25881 bytes --]

/******************************************************************
 * Copyright 2005 Mentor Graphics Corporation
 * Copyright (C) 2005-2006 by Texas Instruments
 *
 * This file is part of the Inventra Controller Driver for Linux.
 *
 * The Inventra Controller Driver for Linux is free software; you
 * can redistribute it and/or modify it under the terms of the GNU
 * General Public License version 2 as published by the Free Software
 * Foundation.
 *
 * The Inventra Controller Driver for Linux is distributed in
 * the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with The Inventra Controller Driver for Linux ; if not,
 * write to the Free Software Foundation, Inc., 59 Temple Place,
 * Suite 330, Boston, MA  02111-1307  USA
 *
 * ANY DOWNLOAD, USE, REPRODUCTION, MODIFICATION OR DISTRIBUTION
 * OF THIS DRIVER INDICATES YOUR COMPLETE AND UNCONDITIONAL ACCEPTANCE
 * OF THOSE TERMS.THIS DRIVER IS PROVIDED "AS IS" AND MENTOR GRAPHICS
 * MAKES NO WARRANTIES, EXPRESS OR IMPLIED, RELATED TO THIS DRIVER.
 * MENTOR GRAPHICS SPECIFICALLY DISCLAIMS ALL IMPLIED WARRANTIES
 * OF MERCHANTABILITY; FITNESS FOR A PARTICULAR PURPOSE AND
 * NON-INFRINGEMENT.  MENTOR GRAPHICS DOES NOT PROVIDE SUPPORT
 * SERVICES OR UPDATES FOR THIS DRIVER, EVEN IF YOU ARE A MENTOR
 * GRAPHICS SUPPORT CUSTOMER.
 ******************************************************************/

#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/timer.h>
#include <linux/spinlock.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/interrupt.h>

#include "musbdefs.h"

/* ep0 is always musb->aLocalEnd[0].ep_in */
#define	next_ep0_request(musb)	next_in_request(&(musb)->aLocalEnd[0])

/*
 * Locking note:  we use only the controller lock, for simpler correctness.
 * It's always held with IRQs blocked.
 *
 * It protects the ep0 request queue as well as ep0_state, not just the
 * controller and indexed registers.  And that lock stays held unless it
 * needs to be dropped to allow reentering this driver ... like upcalls to
 * the gadget driver, or adjusting endpoint halt status.
 */

static char *decode_ep0stage(u8 stage)
{
	switch(stage) {
	case MGC_END0_STAGE_SETUP:	return "idle";
	case MGC_END0_STAGE_TX:		return "in";
	case MGC_END0_STAGE_RX:		return "out";
	case MGC_END0_STAGE_ACKWAIT:	return "wait";
	case MGC_END0_STAGE_STATUSIN:	return "in/status";
	case MGC_END0_STAGE_STATUSOUT:	return "out/status";
	default:			return "?";
	}
}

/* handle a standard GET_STATUS request
 * Context:  caller holds controller lock
 */
static int service_tx_status_request(
	struct musb *pThis,
	const struct usb_ctrlrequest *pControlRequest)
{
	void __iomem	*pBase = pThis->pRegs;
	int handled = 1;
	u8 bResult[2], bEnd = 0;
	const u8 bRecip = pControlRequest->bRequestType & USB_RECIP_MASK;

	bResult[1] = 0;

	switch (bRecip) {
	case USB_RECIP_DEVICE:
		bResult[0] = pThis->bIsSelfPowered << USB_DEVICE_SELF_POWERED;
		bResult[0] |= pThis->bMayWakeup << USB_DEVICE_REMOTE_WAKEUP;
#ifdef CONFIG_USB_MUSB_OTG
		if (pThis->g.is_otg) {
			bResult[0] |= pThis->g.b_hnp_enable
				<< USB_DEVICE_B_HNP_ENABLE;
			bResult[0] |= pThis->g.a_alt_hnp_support
				<< USB_DEVICE_A_ALT_HNP_SUPPORT;
			bResult[0] |= pThis->g.a_hnp_support
				<< USB_DEVICE_A_HNP_SUPPORT;
		}
#endif
		break;

	case USB_RECIP_INTERFACE:
		bResult[0] = 0;
		break;

	case USB_RECIP_ENDPOINT: {
		int		is_in;
		struct musb_ep	*ep;
		u16		tmp;
		void __iomem	*regs;

		bEnd = (u8) pControlRequest->wIndex;
		if (!bEnd) {
			bResult[0] = 0;
			break;
		}

		is_in = bEnd & USB_DIR_IN;
		if (is_in) {
			bEnd &= 0x0f;
			ep = &pThis->aLocalEnd[bEnd].ep_in;
		} else {
			ep = &pThis->aLocalEnd[bEnd].ep_out;
		}
		regs = pThis->aLocalEnd[bEnd].regs;

		if (bEnd >= MUSB_C_NUM_EPS || !ep->desc) {
			handled = -EINVAL;
			break;
		}

		MGC_SelectEnd(pBase, bEnd);
		if (is_in)
			tmp = musb_readw(regs, MGC_O_HDRC_TXCSR)
						& MGC_M_TXCSR_P_SENDSTALL;
		else
			tmp = musb_readw(regs, MGC_O_HDRC_RXCSR)
						& MGC_M_RXCSR_P_SENDSTALL;
		MGC_SelectEnd(pBase, 0);

		bResult[0] = tmp ? 1 : 0;
		} break;

	default:
		/* class, vendor, etc ... delegate */
		handled = 0;
		break;
	}

	/* fill up the fifo; caller updates csr0 */
	if (handled > 0) {
		u16	len = le16_to_cpu(pControlRequest->wLength);

		if (len > 2)
			len = 2;
		musb_write_fifo(&pThis->aLocalEnd[0], len, bResult);
	}

	return handled;
}

/*
 * handle a control-IN request, the end0 buffer contains the current request
 * that is supposed to be a standard control request. Assumes the fifo to
 * be at least 2 bytes long.
 *
 * @return 0 if the request was NOT HANDLED,
 * < 0 when error
 * > 0 when the request is processed
 *
 * Context:  caller holds controller lock
 */
static int
service_in_request(struct musb *pThis,
		const struct usb_ctrlrequest *pControlRequest)
{
	int handled = 0;	/* not handled */

	if ((pControlRequest->bRequestType & USB_TYPE_MASK)
			== USB_TYPE_STANDARD) {
		switch (pControlRequest->bRequest) {
		case USB_REQ_GET_STATUS:
			handled = service_tx_status_request(pThis,
					pControlRequest);
			break;

		/* case USB_REQ_SYNC_FRAME: */

		default:
			break;
		}
	}
	return handled;
}

/*
 * Context:  caller holds controller lock
 */
static void musb_g_ep0_giveback(struct musb *pThis, struct usb_request *req)
{
	pThis->ep0_state = MGC_END0_STAGE_SETUP;
	musb_g_giveback(&pThis->aLocalEnd[0].ep_in, req, 0);
}

/*
 * Handle all control requests with no DATA stage, including standard
 * requests such as:
 * USB_REQ_SET_CONFIGURATION, USB_REQ_SET_INTERFACE, unrecognized
 *	always delegated to the gadget driver
 * USB_REQ_SET_ADDRESS, USB_REQ_CLEAR_FEATURE, USB_REQ_SET_FEATURE
 *	always handled here, except for class/vendor/... features
 *
 * Context:  caller holds controller lock
 */
static int
service_zero_data_request(struct musb *pThis,
		struct usb_ctrlrequest *pControlRequest)
__releases(pThis->Lock)
__acquires(pThis->Lock)
{
	int handled = -EINVAL;
	void __iomem *pBase = pThis->pRegs;
	const u8 bRecip = pControlRequest->bRequestType & USB_RECIP_MASK;

	/* the gadget driver handles everything except what we MUST handle */
	if ((pControlRequest->bRequestType & USB_TYPE_MASK)
			== USB_TYPE_STANDARD) {
		switch (pControlRequest->bRequest) {
		case USB_REQ_SET_ADDRESS:
			/* change it after the status stage */
			pThis->bSetAddress = TRUE;
			pThis->bAddress = (u8) (pControlRequest->wValue & 0x7f);
			handled = 1;
			break;

		case USB_REQ_CLEAR_FEATURE:
			switch (bRecip) {
			case USB_RECIP_DEVICE:
				if (pControlRequest->wValue
						!= USB_DEVICE_REMOTE_WAKEUP)
					break;
				pThis->bMayWakeup = 0;
				handled = 1;
				break;
			case USB_RECIP_INTERFACE:
				break;
			case USB_RECIP_ENDPOINT:{
				const u8 bEnd = pControlRequest->wIndex & 0x0f;
				struct musb_ep *pEnd;

				if (bEnd == 0
						|| bEnd >= MUSB_C_NUM_EPS
						|| pControlRequest->wValue
							!= USB_ENDPOINT_HALT)
					break;

				if (pControlRequest->wIndex & USB_DIR_IN)
					pEnd = &pThis->aLocalEnd[bEnd].ep_in;
				else
					pEnd = &pThis->aLocalEnd[bEnd].ep_out;
				if (!pEnd->desc)
					break;

				/* REVISIT do it directly, no locking games */
				spin_unlock(&pThis->Lock);
				musb_gadget_set_halt(&pEnd->end_point, 0);
				spin_lock(&pThis->Lock);

				/* select ep0 again */
				MGC_SelectEnd(pBase, 0);
				handled = 1;
				} break;
			default:
				/* class, vendor, etc ... delegate */
				handled = 0;
				break;
			}
			break;

		case USB_REQ_SET_FEATURE:
			switch (bRecip) {
			case USB_RECIP_DEVICE:
				handled = 1;
				switch (pControlRequest->wValue) {
				case USB_DEVICE_REMOTE_WAKEUP:
					pThis->bMayWakeup = 1;
					break;
				case USB_DEVICE_TEST_MODE:
					if (pThis->g.speed != USB_SPEED_HIGH)
						goto stall;
					if (pControlRequest->wIndex & 0xff)
						goto stall;

					switch (pControlRequest->wIndex >> 8) {
					case 1:
						pr_debug("TEST_J\n");
						/* TEST_J */
						pThis->bTestModeValue =
							MGC_M_TEST_J;
						break;
					case 2:
						/* TEST_K */
						pr_debug("TEST_K\n");
						pThis->bTestModeValue =
							MGC_M_TEST_K;
						break;
					case 3:
						/* TEST_SE0_NAK */
						pr_debug("TEST_SE0_NAK\n");
						pThis->bTestModeValue =
							MGC_M_TEST_SE0_NAK;
						break;
					case 4:
						/* TEST_PACKET */
						pr_debug("TEST_PACKET\n");
						pThis->bTestModeValue =
							MGC_M_TEST_PACKET;
						break;
					default:
						goto stall;
					}

					/* enter test mode after irq */
					if (handled > 0)
						pThis->bTestMode = TRUE;
					break;
#ifdef CONFIG_USB_MUSB_OTG
				case USB_DEVICE_B_HNP_ENABLE:
					if (!pThis->g.is_otg)
						goto stall;
					{ u8 devctl;
					pThis->g.b_hnp_enable = 1;
					devctl = musb_readb(pBase,
							MGC_O_HDRC_DEVCTL);
					musb_writeb(pBase, MGC_O_HDRC_DEVCTL,
						devctl | MGC_M_DEVCTL_HR);
					}
					break;
				case USB_DEVICE_A_HNP_SUPPORT:
					if (!pThis->g.is_otg)
						goto stall;
					pThis->g.a_hnp_support = 1;
					break;
				case USB_DEVICE_A_ALT_HNP_SUPPORT:
					if (!pThis->g.is_otg)
						goto stall;
					pThis->g.a_alt_hnp_support = 1;
					break;
#endif
stall:
				default:
					handled = -EINVAL;
					break;
				}
				break;

			case USB_RECIP_INTERFACE:
				break;

			case USB_RECIP_ENDPOINT:{
				const u8		bEnd =
					pControlRequest->wIndex & 0x0f;
				struct musb_ep		*pEnd;
				struct musb_hw_ep	*ep;
				void __iomem		*regs;
				int			is_in;
				u16			csr;

				if (bEnd == 0
						|| bEnd >= MUSB_C_NUM_EPS
						|| pControlRequest->wValue
							!= USB_ENDPOINT_HALT)
					break;

				ep = pThis->aLocalEnd + bEnd;
				regs = ep->regs;
				is_in = pControlRequest->wIndex & USB_DIR_IN;
				if (is_in)
					pEnd = &ep->ep_in;
				else
					pEnd = &ep->ep_out;
				if (!pEnd->desc)
					break;

				MGC_SelectEnd(pBase, bEnd);
				if (is_in) {
					csr = musb_readw(regs,
							MGC_O_HDRC_TXCSR);
					if (csr & MGC_M_TXCSR_FIFONOTEMPTY)
						csr |= MGC_M_TXCSR_FLUSHFIFO;
					csr |= MGC_M_TXCSR_P_SENDSTALL
						| MGC_M_TXCSR_CLRDATATOG
						| MGC_M_TXCSR_P_WZC_BITS;
					musb_writew(regs, MGC_O_HDRC_TXCSR,
							csr);
				} else {
					csr = musb_readw(regs,
							MGC_O_HDRC_RXCSR);
					csr |= MGC_M_RXCSR_P_SENDSTALL
						| MGC_M_RXCSR_FLUSHFIFO
						| MGC_M_RXCSR_CLRDATATOG
						| MGC_M_TXCSR_P_WZC_BITS;
					musb_writew(regs, MGC_O_HDRC_RXCSR,
							csr);
				}

				/* select ep0 again */
				MGC_SelectEnd(pBase, 0);
				handled = 1;
				} break;

			default:
				/* class, vendor, etc ... delegate */
				handled = 0;
				break;
			}
			break;
		default:
			/* delegate SET_CONFIGURATION, etc */
			handled = 0;
		}
	} else
		handled = 0;
	return handled;
}

/* we have an ep0out data packet
 * Context:  caller holds controller lock
 */
static void ep0_rxstate(struct musb *this)
{
	void __iomem		*regs = this->control_ep->regs;
	struct usb_request	*req;
	u16			tmp;

	req = next_ep0_request(this);

	/* read packet and ack; or stall because of gadget driver bug:
	 * should have provided the rx buffer before setup() returned.
	 */
	if (req) {
		void		*buf = req->buf + req->actual;
		unsigned	len = req->length - req->actual;

		/* read the buffer */
		tmp = musb_readb(regs, MGC_O_HDRC_COUNT0);
		if (tmp > len) {
			req->status = -EOVERFLOW;
			tmp = len;
		}
		musb_read_fifo(&this->aLocalEnd[0], tmp, buf);
		req->actual += tmp;
		tmp = MGC_M_CSR0_P_SVDRXPKTRDY;
		if (tmp < 64 || req->actual == req->length) {
			this->ep0_state = MGC_END0_STAGE_STATUSIN;
			tmp |= MGC_M_CSR0_P_DATAEND;
		} else
			req = NULL;
	} else
		tmp = MGC_M_CSR0_P_SVDRXPKTRDY | MGC_M_CSR0_P_SENDSTALL;
	musb_writew(regs, MGC_O_HDRC_CSR0, tmp);


	/* NOTE:  we "should" hold off reporting DATAEND and going to
	 * STATUSIN until after the completion handler decides whether
	 * to issue a stall instead, since this hardware can do that.
	 */
	if (req)
		musb_g_ep0_giveback(this, req);
}

/*
 * transmitting to the host (IN), this code might be called from IRQ
 * and from kernel thread.
 *
 * Context:  caller holds controller lock
 */
static void ep0_txstate(struct musb *pThis)
{
	void __iomem		*regs = pThis->control_ep->regs;
	struct usb_request	*pRequest = next_ep0_request(pThis);
	u16			wCsrVal = MGC_M_CSR0_TXPKTRDY;
	u8			*pFifoSource;
	u8			wFifoCount;

	if (!pRequest) {
		// WARN_ON(1);
		DBG(2, "odd; csr0 %04x\n", musb_readw(regs, MGC_O_HDRC_CSR0));
		return;
	}

	/* load the data */
	pFifoSource = (u8 *) pRequest->buf + pRequest->actual;
	wFifoCount = min((unsigned) MGC_END0_FIFOSIZE,
		pRequest->length - pRequest->actual);
	musb_write_fifo(&pThis->aLocalEnd[0], wFifoCount, pFifoSource);
	pRequest->actual += wFifoCount;

	/* update the flags */
	if (wFifoCount < MUSB_MAX_END0_PACKET
			|| pRequest->actual == pRequest->length) {
		pThis->ep0_state = MGC_END0_STAGE_STATUSOUT;
		wCsrVal |= MGC_M_CSR0_P_DATAEND;
	} else
		pRequest = NULL;

	/* send it out, triggering a "txpktrdy cleared" irq */
	musb_writew(regs, MGC_O_HDRC_CSR0, wCsrVal);

	/* report completions as soon as the fifo's loaded; there's no
	 * win in waiting till this last packet gets acked.  (other than
	 * very precise fault reporting, needed by USB TMC; possible with
	 * this hardware, but not usable from portable gadget drivers.)
	 */
	if (pRequest)
		musb_g_ep0_giveback(pThis, pRequest);
}

/*
 * Read a SETUP packet (struct usb_ctrlrequest) from the hardware.
 * Fields are left in USB byte-order.
 *
 * Context:  caller holds controller lock.
 */
static void
musb_read_setup(struct musb *pThis, struct usb_ctrlrequest *req)
{
	struct usb_request	*r;
	void __iomem		*regs = pThis->control_ep->regs;

	musb_read_fifo(&pThis->aLocalEnd[0], sizeof *req, (u8 *)req);

	/* NOTE:  earlier 2.6 versions changed setup packets to host
	 * order, but now USB packets always stay in USB byte order.
	 */
	DBG(3, "SETUP req%02x.%02x v%04x i%04x l%d\n",
		req->bRequestType,
		req->bRequest,
		le16_to_cpu(req->wValue),
		le16_to_cpu(req->wIndex),
		le16_to_cpu(req->wLength));

	/* clean up any leftover transfers */
	r = next_ep0_request(pThis);
	if (r)
		musb_g_ep0_giveback(pThis, r);

	/* For zero-data requests we want to delay the STATUS stage to
	 * avoid SETUPEND errors.  If we read data (OUT), delay accepting
	 * packets until there's a buffer to store them in.
	 *
	 * If we write data, the controller acts happier if we enable
	 * the TX FIFO right away, and give the controller a moment
	 * to switch modes...
	 */
	pThis->bSetAddress = FALSE;
	pThis->ackpend = MGC_M_CSR0_P_SVDRXPKTRDY;
	if (req->wLength == 0) {
		if (req->bRequestType & USB_DIR_IN)
			pThis->ackpend |= MGC_M_CSR0_TXPKTRDY;
		pThis->ep0_state = MGC_END0_STAGE_ACKWAIT;
	} else if (req->bRequestType & USB_DIR_IN) {
		pThis->ep0_state = MGC_END0_STAGE_TX;
		musb_writew(regs, MGC_O_HDRC_CSR0, MGC_M_CSR0_P_SVDRXPKTRDY);
		while ((musb_readw(regs, MGC_O_HDRC_CSR0)
				& MGC_M_CSR0_RXPKTRDY) != 0)
			cpu_relax();
		pThis->ackpend = 0;
	} else
		pThis->ep0_state = MGC_END0_STAGE_RX;
}

static int
forward_to_driver(struct musb *musb,
		const struct usb_ctrlrequest *pControlRequest)
__releases(musb->Lock)
__acquires(musb->Lock)
{
	int retval;
	if (!musb->pGadgetDriver)
		return -EOPNOTSUPP;
	spin_unlock(&musb->Lock);
	retval = musb->pGadgetDriver->setup(&musb->g, pControlRequest);
	spin_lock(&musb->Lock);
	return retval;
}

/*
 * Handle peripheral ep0 interrupt
 * @param pThis this
 *
 * Context: irq handler; we won't re-enter the driver that way.
 */
irqreturn_t musb_g_ep0_irq(struct musb *pThis)
{
	u16		wCsrVal;
	u16		wCount;
	void __iomem	*pBase = pThis->pRegs;
	void __iomem	*regs = pThis->aLocalEnd[0].regs;
	irqreturn_t	retval = IRQ_NONE;

	MGC_SelectEnd(pBase, 0);	/* select ep0 */
	wCsrVal = musb_readw(regs, MGC_O_HDRC_CSR0);
	wCount = musb_readb(regs, MGC_O_HDRC_COUNT0);

	DBG(4, "csr %04x, count %d, myaddr %d, ep0stage %s\n",
			wCsrVal, wCount,
			musb_readb(pBase, MGC_O_HDRC_FADDR),
			decode_ep0stage(pThis->ep0_state));

	/* I sent a stall.. need to acknowledge it now.. */
	if (wCsrVal & MGC_M_CSR0_P_SENTSTALL) {
		musb_writew(regs, MGC_O_HDRC_CSR0,
				wCsrVal & ~MGC_M_CSR0_P_SENTSTALL);
		retval = IRQ_HANDLED;
		pThis->ep0_state = MGC_END0_STAGE_SETUP;
		wCsrVal = musb_readw(regs, MGC_O_HDRC_CSR0);
	}

	/* request ended "early" */
	if (wCsrVal & MGC_M_CSR0_P_SETUPEND) {
		musb_writew(regs, MGC_O_HDRC_CSR0, MGC_M_CSR0_P_SVDSETUPEND);
		retval = IRQ_HANDLED;
		pThis->ep0_state = MGC_END0_STAGE_SETUP;
		wCsrVal = musb_readw(regs, MGC_O_HDRC_CSR0);
		/* NOTE:  request may need completion */
	}

	/* docs from Mentor only describe tx, rx, and idle/setup states.
	 * we need to handle nuances around status stages, and also the
	 * case where status and setup stages come back-to-back ...
	 */
	switch (pThis->ep0_state) {

	case MGC_END0_STAGE_TX:
		/* irq on clearing txpktrdy */
		if ((wCsrVal & MGC_M_CSR0_TXPKTRDY) == 0) {
			ep0_txstate(pThis);
			retval = IRQ_HANDLED;
		}
		break;

	case MGC_END0_STAGE_RX:
		/* irq on set rxpktrdy */
		if (wCsrVal & MGC_M_CSR0_RXPKTRDY) {
			ep0_rxstate(pThis);
			retval = IRQ_HANDLED;
		}
		break;

	case MGC_END0_STAGE_STATUSIN:
		/* end of sequence #2 (OUT/RX state) or #3 (no data) */

		/* update address (if needed) only @ the end of the
		 * status phase per usb spec, which also guarantees
		 * we get 10 msec to receive this irq... until this
		 * is done we won't see the next packet.
		 */
		if (pThis->bSetAddress) {
			pThis->bSetAddress = FALSE;
			musb_writeb(pBase, MGC_O_HDRC_FADDR, pThis->bAddress);
		}

		/* enter test mode if needed (exit by reset) */
		else if (pThis->bTestMode) {
			DBG(1, "entering TESTMODE\n");

			if (MGC_M_TEST_PACKET == pThis->bTestModeValue)
				musb_load_testpacket(pThis);

			musb_writeb(pBase, MGC_O_HDRC_TESTMODE,
					pThis->bTestModeValue);
		}
		/* FALLTHROUGH */

	case MGC_END0_STAGE_STATUSOUT:
		/* end of sequence #1: write to host (TX state) */
		{
			struct usb_request	*req;

			req = next_ep0_request(pThis);
			if (req)
				musb_g_ep0_giveback(pThis, req);
		}
		retval = IRQ_HANDLED;
		pThis->ep0_state = MGC_END0_STAGE_SETUP;
		/* FALLTHROUGH */

	case MGC_END0_STAGE_SETUP:
		if (wCsrVal & MGC_M_CSR0_RXPKTRDY) {
			struct usb_ctrlrequest	setup;
			int			handled = 0;

			if (wCount != 8) {
				ERR("SETUP packet len %d != 8 ?\n", wCount);
				break;
			}
			musb_read_setup(pThis, &setup);
			retval = IRQ_HANDLED;

			/* sometimes the RESET won't be reported */
			if (unlikely(pThis->g.speed == USB_SPEED_UNKNOWN)) {
				u8	power;

				printk(KERN_NOTICE "%s: peripheral reset "
						"irq lost!\n",
						musb_driver_name);
				power = musb_readb(pBase, MGC_O_HDRC_POWER);
				pThis->g.speed = (power & MGC_M_POWER_HSMODE)
					? USB_SPEED_HIGH : USB_SPEED_FULL;

			}

			switch (pThis->ep0_state) {

			/* sequence #3 (no data stage), includes requests
			 * we can't forward (notably SET_ADDRESS and the
			 * device/endpoint feature set/clear operations)
			 * plus SET_CONFIGURATION and others we must
			 */
			case MGC_END0_STAGE_ACKWAIT:
				handled = service_zero_data_request(
						pThis, &setup);

				/* status stage might be immediate */
				if (handled > 0) {
					pThis->ackpend |= MGC_M_CSR0_P_DATAEND;
					pThis->ep0_state =
						MGC_END0_STAGE_STATUSIN;
				}
				break;

			/* sequence #1 (IN to host), includes GET_STATUS
			 * requests that we can't forward, GET_DESCRIPTOR
			 * and others that we must
			 */
			case MGC_END0_STAGE_TX:
				handled = service_in_request(pThis, &setup);
				if (handled > 0) {
					pThis->ackpend = MGC_M_CSR0_TXPKTRDY
						| MGC_M_CSR0_P_DATAEND;
					pThis->ep0_state =
						MGC_END0_STAGE_STATUSOUT;
				}
				break;

			/* sequence #2 (OUT from host), always forward */
			default:		/* MGC_END0_STAGE_RX */
				break;
			}

			DBG(3, "handled %d, csr %04x, ep0stage %s\n",
				handled, wCsrVal,
				decode_ep0stage(pThis->ep0_state));

			/* unless we need to delegate this to the gadget
			 * driver, we know how to wrap this up:  csr0 has
			 * not yet been written.
			 */
			if (handled < 0)
				goto stall;
			else if (handled > 0)
				goto finish;

			handled = forward_to_driver(pThis, &setup);
			if (handled < 0) {
				MGC_SelectEnd(pBase, 0);
stall:
				DBG(3, "stall (%d)\n", handled);
				pThis->ackpend |= MGC_M_CSR0_P_SENDSTALL;
				pThis->ep0_state = MGC_END0_STAGE_SETUP;
finish:
				musb_writew(regs, MGC_O_HDRC_CSR0,
						pThis->ackpend);
				pThis->ackpend = 0;
			}
		}
		break;

	case MGC_END0_STAGE_ACKWAIT:
		/* This should not happen. But happens with tusb6010 with
		 * g_file_storage and high speed. Do nothing.
		 */
		retval = IRQ_HANDLED;
		break;

	default:
		/* "can't happen" */
		WARN_ON(1);
		musb_writew(regs, MGC_O_HDRC_CSR0, MGC_M_CSR0_P_SENDSTALL);
		pThis->ep0_state = MGC_END0_STAGE_SETUP;
		break;
	}

	return retval;
}


static int
musb_g_ep0_enable(struct usb_ep *ep, const struct usb_endpoint_descriptor *desc)
{
	/* always enabled */
	return -EINVAL;
}

static int musb_g_ep0_disable(struct usb_ep *e)
{
	/* always enabled */
	return -EINVAL;
}

static void *musb_g_ep0_alloc_buffer(struct usb_ep *ep, unsigned bytes,
			dma_addr_t * dma, gfp_t gfp_flags)
{
	*dma = DMA_ADDR_INVALID;
	return kmalloc(bytes, gfp_flags);
}

static void musb_g_ep0_free_buffer(struct usb_ep *ep, void *address,
			dma_addr_t dma, unsigned bytes)
{
	kfree(address);
}

static int
musb_g_ep0_queue(struct usb_ep *e, struct usb_request *r, gfp_t gfp_flags)
{
	struct musb_ep		*ep;
	struct musb_request	*req;
	struct musb		*musb;
	int			status;
	unsigned long		lockflags;
	void __iomem		*regs;

	if (!e || !r)
		return -EINVAL;

	ep = to_musb_ep(e);
	musb = ep->pThis;
	regs = musb->control_ep->regs;

	req = to_musb_request(r);
	req->musb = musb;
	req->request.actual = 0;
	req->request.status = -EINPROGRESS;
	req->bTx = ep->is_in;

	spin_lock_irqsave(&musb->Lock, lockflags);

	if (!list_empty(&ep->req_list)) {
		status = -EBUSY;
		goto cleanup;
	}

	switch (musb->ep0_state) {
	case MGC_END0_STAGE_RX:		/* control-OUT data */
	case MGC_END0_STAGE_TX:		/* control-IN data */
	case MGC_END0_STAGE_ACKWAIT:	/* zero-length data */
		status = 0;
		break;
	default:
		DBG(1, "ep0 request queued in state %d\n",
				musb->ep0_state);
		status = -EINVAL;
		goto cleanup;
	}

	/* add request to the list */
	list_add_tail(&(req->request.list), &(ep->req_list));

	DBG(3, "queue to %s (%s), length=%d\n",
			ep->name, ep->is_in ? "IN/TX" : "OUT/RX",
			req->request.length);

	MGC_SelectEnd(musb->pRegs, 0);

	/* sequence #1, IN ... start writing the data */
	if (musb->ep0_state == MGC_END0_STAGE_TX)
		ep0_txstate(musb);

	/* sequence #3, no-data ... issue IN status */
	else if (musb->ep0_state == MGC_END0_STAGE_ACKWAIT) {
		if (req->request.length)
			status = -EINVAL;
		else {
			musb->ep0_state = MGC_END0_STAGE_STATUSIN;
			musb_writew(regs, MGC_O_HDRC_CSR0,
					musb->ackpend | MGC_M_CSR0_P_DATAEND);
			musb->ackpend = 0;
			musb_g_ep0_giveback(ep->pThis, r);
		}

	/* else for sequence #2 (OUT), caller provides a buffer
	 * before the next packet arrives.  deferred responses
	 * (after SETUP is acked) are racey.
	 */
	} else if (musb->ackpend) {
		musb_writew(regs, MGC_O_HDRC_CSR0, musb->ackpend);
		musb->ackpend = 0;
	}

cleanup:
	spin_unlock_irqrestore(&musb->Lock, lockflags);
	return status;
}

static int
musb_g_ep0_dequeue(struct usb_ep *ep, struct usb_request *req)
{
	/* we just won't support this */
	return -EINVAL;
}

static int musb_g_ep0_halt(struct usb_ep *e, int value)
{
	struct musb_ep		*ep;
	struct musb		*musb;
	void __iomem		*base, *regs;
	unsigned long		flags;
	int			status;
	u16			csr;

	if (!e || !value)
		return -EINVAL;

	ep = to_musb_ep(e);
	musb = ep->pThis;
	base = musb->pRegs;
	regs = musb->control_ep->regs;

	spin_lock_irqsave(&musb->Lock, flags);

	if (!list_empty(&ep->req_list)) {
		status = -EBUSY;
		goto cleanup;
	}

	switch (musb->ep0_state) {
	case MGC_END0_STAGE_TX:		/* control-IN data */
	case MGC_END0_STAGE_ACKWAIT:	/* STALL for zero-length data */
	case MGC_END0_STAGE_RX:		/* control-OUT data */
		status = 0;

		MGC_SelectEnd(base, 0);
		csr = musb_readw(regs, MGC_O_HDRC_CSR0);
		csr |= MGC_M_CSR0_P_SENDSTALL;
		musb_writew(regs, MGC_O_HDRC_CSR0, csr);
		musb->ep0_state = MGC_END0_STAGE_SETUP;
		break;
	default:
		DBG(1, "ep0 can't halt in state %d\n", musb->ep0_state);
		status = -EINVAL;
	}

cleanup:
	spin_unlock_irqrestore(&musb->Lock, flags);
	return status;
}

const struct usb_ep_ops musb_g_ep0_ops = {
	.enable		= musb_g_ep0_enable,
	.disable	= musb_g_ep0_disable,
	.alloc_request	= musb_alloc_request,
	.free_request	= musb_free_request,
	.alloc_buffer	= musb_g_ep0_alloc_buffer,
	.free_buffer	= musb_g_ep0_free_buffer,
	.queue		= musb_g_ep0_queue,
	.dequeue	= musb_g_ep0_dequeue,
	.set_halt	= musb_g_ep0_halt,
	.fifo_status	= NULL,
	.fifo_flush	= NULL,
};

[-- Attachment #4: g_ep0.c --]
[-- Type: application/octet-stream, Size: 25954 bytes --]

/******************************************************************
 * Copyright 2005 Mentor Graphics Corporation
 * Copyright (C) 2005-2006 by Texas Instruments
 *
 * This file is part of the Inventra Controller Driver for Linux.
 *
 * The Inventra Controller Driver for Linux is free software; you
 * can redistribute it and/or modify it under the terms of the GNU
 * General Public License version 2 as published by the Free Software
 * Foundation.
 *
 * The Inventra Controller Driver for Linux is distributed in
 * the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
 * License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with The Inventra Controller Driver for Linux ; if not,
 * write to the Free Software Foundation, Inc., 59 Temple Place,
 * Suite 330, Boston, MA  02111-1307  USA
 *
 * ANY DOWNLOAD, USE, REPRODUCTION, MODIFICATION OR DISTRIBUTION
 * OF THIS DRIVER INDICATES YOUR COMPLETE AND UNCONDITIONAL ACCEPTANCE
 * OF THOSE TERMS.THIS DRIVER IS PROVIDED "AS IS" AND MENTOR GRAPHICS
 * MAKES NO WARRANTIES, EXPRESS OR IMPLIED, RELATED TO THIS DRIVER.
 * MENTOR GRAPHICS SPECIFICALLY DISCLAIMS ALL IMPLIED WARRANTIES
 * OF MERCHANTABILITY; FITNESS FOR A PARTICULAR PURPOSE AND
 * NON-INFRINGEMENT.  MENTOR GRAPHICS DOES NOT PROVIDE SUPPORT
 * SERVICES OR UPDATES FOR THIS DRIVER, EVEN IF YOU ARE A MENTOR
 * GRAPHICS SUPPORT CUSTOMER.
 ******************************************************************/

#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/timer.h>
#include <linux/spinlock.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/interrupt.h>

#include "musbdefs.h"

/* ep0 is always musb->aLocalEnd[0].ep_in */
#define	next_ep0_request(musb)	next_in_request(&(musb)->aLocalEnd[0])

/*
 * Locking note:  we use only the controller lock, for simpler correctness.
 * It's always held with IRQs blocked.
 *
 * It protects the ep0 request queue as well as ep0_state, not just the
 * controller and indexed registers.  And that lock stays held unless it
 * needs to be dropped to allow reentering this driver ... like upcalls to
 * the gadget driver, or adjusting endpoint halt status.
 */

static char *decode_ep0stage(u8 stage)
{
	switch(stage) {
	case MGC_END0_STAGE_SETUP:	return "idle";
	case MGC_END0_STAGE_TX:		return "in";
	case MGC_END0_STAGE_RX:		return "out";
	case MGC_END0_STAGE_ACKWAIT:	return "wait";
	case MGC_END0_STAGE_STATUSIN:	return "in/status";
	case MGC_END0_STAGE_STATUSOUT:	return "out/status";
	default:			return "?";
	}
}

/* handle a standard GET_STATUS request
 * Context:  caller holds controller lock
 */
static int service_tx_status_request(
	struct musb *pThis,
	const struct usb_ctrlrequest *pControlRequest)
{
	void __iomem	*pBase = pThis->pRegs;
	int handled = 1;
	u8 bResult[2], bEnd = 0;
	const u8 bRecip = pControlRequest->bRequestType & USB_RECIP_MASK;

	bResult[1] = 0;

	switch (bRecip) {
	case USB_RECIP_DEVICE:
		bResult[0] = pThis->bIsSelfPowered << USB_DEVICE_SELF_POWERED;
		bResult[0] |= pThis->bMayWakeup << USB_DEVICE_REMOTE_WAKEUP;
#ifdef CONFIG_USB_MUSB_OTG
		if (pThis->g.is_otg) {
			bResult[0] |= pThis->g.b_hnp_enable
				<< USB_DEVICE_B_HNP_ENABLE;
			bResult[0] |= pThis->g.a_alt_hnp_support
				<< USB_DEVICE_A_ALT_HNP_SUPPORT;
			bResult[0] |= pThis->g.a_hnp_support
				<< USB_DEVICE_A_HNP_SUPPORT;
		}
#endif
		break;

	case USB_RECIP_INTERFACE:
		bResult[0] = 0;
		break;

	case USB_RECIP_ENDPOINT: {
		int		is_in;
		struct musb_ep	*ep;
		u16		tmp;
		void __iomem	*regs;

		bEnd = (u8) pControlRequest->wIndex;
		if (!bEnd) {
			bResult[0] = 0;
			break;
		}

		is_in = bEnd & USB_DIR_IN;
		if (is_in) {
			bEnd &= 0x0f;
			ep = &pThis->aLocalEnd[bEnd].ep_in;
		} else {
			ep = &pThis->aLocalEnd[bEnd].ep_out;
		}
		regs = pThis->aLocalEnd[bEnd].regs;

		if (bEnd >= MUSB_C_NUM_EPS || !ep->desc) {
			handled = -EINVAL;
			break;
		}

		MGC_SelectEnd(pBase, bEnd);
		if (is_in)
			tmp = musb_readw(regs, MGC_O_HDRC_TXCSR)
						& MGC_M_TXCSR_P_SENDSTALL;
		else
			tmp = musb_readw(regs, MGC_O_HDRC_RXCSR)
						& MGC_M_RXCSR_P_SENDSTALL;
		MGC_SelectEnd(pBase, 0);

		bResult[0] = tmp ? 1 : 0;
		} break;

	default:
		/* class, vendor, etc ... delegate */
		handled = 0;
		break;
	}

	/* fill up the fifo; caller updates csr0 */
	if (handled > 0) {
		u16	len = le16_to_cpu(pControlRequest->wLength);

		if (len > 2)
			len = 2;
		musb_write_fifo(&pThis->aLocalEnd[0], len, bResult);
	}

	return handled;
}

/*
 * handle a control-IN request, the end0 buffer contains the current request
 * that is supposed to be a standard control request. Assumes the fifo to
 * be at least 2 bytes long.
 *
 * @return 0 if the request was NOT HANDLED,
 * < 0 when error
 * > 0 when the request is processed
 *
 * Context:  caller holds controller lock
 */
static int
service_in_request(struct musb *pThis,
		const struct usb_ctrlrequest *pControlRequest)
{
	int handled = 0;	/* not handled */

	if ((pControlRequest->bRequestType & USB_TYPE_MASK)
			== USB_TYPE_STANDARD) {
		switch (pControlRequest->bRequest) {
		case USB_REQ_GET_STATUS:
			handled = service_tx_status_request(pThis,
					pControlRequest);
			break;

		/* case USB_REQ_SYNC_FRAME: */

		default:
			break;
		}
	}
	return handled;
}

/*
 * Context:  caller holds controller lock
 */
static void musb_g_ep0_giveback(struct musb *pThis, struct usb_request *req)
{
	musb_g_giveback(&pThis->aLocalEnd[0].ep_in, req, 0);
}

/*
 * Handle all control requests with no DATA stage, including standard
 * requests such as:
 * USB_REQ_SET_CONFIGURATION, USB_REQ_SET_INTERFACE, unrecognized
 *	always delegated to the gadget driver
 * USB_REQ_SET_ADDRESS, USB_REQ_CLEAR_FEATURE, USB_REQ_SET_FEATURE
 *	always handled here, except for class/vendor/... features
 *
 * Context:  caller holds controller lock
 */
static int
service_zero_data_request(struct musb *pThis,
		struct usb_ctrlrequest *pControlRequest)
__releases(pThis->Lock)
__acquires(pThis->Lock)
{
	int handled = -EINVAL;
	void __iomem *pBase = pThis->pRegs;
	const u8 bRecip = pControlRequest->bRequestType & USB_RECIP_MASK;

	/* the gadget driver handles everything except what we MUST handle */
	if ((pControlRequest->bRequestType & USB_TYPE_MASK)
			== USB_TYPE_STANDARD) {
		switch (pControlRequest->bRequest) {
		case USB_REQ_SET_ADDRESS:
			/* change it after the status stage */
			pThis->bSetAddress = TRUE;
			pThis->bAddress = (u8) (pControlRequest->wValue & 0x7f);
			handled = 1;
			break;

		case USB_REQ_CLEAR_FEATURE:
			switch (bRecip) {
			case USB_RECIP_DEVICE:
				if (pControlRequest->wValue
						!= USB_DEVICE_REMOTE_WAKEUP)
					break;
				pThis->bMayWakeup = 0;
				handled = 1;
				break;
			case USB_RECIP_INTERFACE:
				break;
			case USB_RECIP_ENDPOINT:{
				const u8 bEnd = pControlRequest->wIndex & 0x0f;
				struct musb_ep *pEnd;

				if (bEnd == 0
						|| bEnd >= MUSB_C_NUM_EPS
						|| pControlRequest->wValue
							!= USB_ENDPOINT_HALT)
					break;

				if (pControlRequest->wIndex & USB_DIR_IN)
					pEnd = &pThis->aLocalEnd[bEnd].ep_in;
				else
					pEnd = &pThis->aLocalEnd[bEnd].ep_out;
				if (!pEnd->desc)
					break;

				/* REVISIT do it directly, no locking games */
				spin_unlock(&pThis->Lock);
				musb_gadget_set_halt(&pEnd->end_point, 0);
				spin_lock(&pThis->Lock);

				/* select ep0 again */
				MGC_SelectEnd(pBase, 0);
				handled = 1;
				} break;
			default:
				/* class, vendor, etc ... delegate */
				handled = 0;
				break;
			}
			break;

		case USB_REQ_SET_FEATURE:
			switch (bRecip) {
			case USB_RECIP_DEVICE:
				handled = 1;
				switch (pControlRequest->wValue) {
				case USB_DEVICE_REMOTE_WAKEUP:
					pThis->bMayWakeup = 1;
					break;
				case USB_DEVICE_TEST_MODE:
					if (pThis->g.speed != USB_SPEED_HIGH)
						goto stall;
					if (pControlRequest->wIndex & 0xff)
						goto stall;

					switch (pControlRequest->wIndex >> 8) {
					case 1:
						pr_debug("TEST_J\n");
						/* TEST_J */
						pThis->bTestModeValue =
							MGC_M_TEST_J;
						break;
					case 2:
						/* TEST_K */
						pr_debug("TEST_K\n");
						pThis->bTestModeValue =
							MGC_M_TEST_K;
						break;
					case 3:
						/* TEST_SE0_NAK */
						pr_debug("TEST_SE0_NAK\n");
						pThis->bTestModeValue =
							MGC_M_TEST_SE0_NAK;
						break;
					case 4:
						/* TEST_PACKET */
						pr_debug("TEST_PACKET\n");
						pThis->bTestModeValue =
							MGC_M_TEST_PACKET;
						break;
					default:
						goto stall;
					}

					/* enter test mode after irq */
					if (handled > 0)
						pThis->bTestMode = TRUE;
					break;
#ifdef CONFIG_USB_MUSB_OTG
				case USB_DEVICE_B_HNP_ENABLE:
					if (!pThis->g.is_otg)
						goto stall;
					{ u8 devctl;
					pThis->g.b_hnp_enable = 1;
					devctl = musb_readb(pBase,
							MGC_O_HDRC_DEVCTL);
					musb_writeb(pBase, MGC_O_HDRC_DEVCTL,
						devctl | MGC_M_DEVCTL_HR);
					}
					break;
				case USB_DEVICE_A_HNP_SUPPORT:
					if (!pThis->g.is_otg)
						goto stall;
					pThis->g.a_hnp_support = 1;
					break;
				case USB_DEVICE_A_ALT_HNP_SUPPORT:
					if (!pThis->g.is_otg)
						goto stall;
					pThis->g.a_alt_hnp_support = 1;
					break;
#endif
stall:
				default:
					handled = -EINVAL;
					break;
				}
				break;

			case USB_RECIP_INTERFACE:
				break;

			case USB_RECIP_ENDPOINT:{
				const u8		bEnd =
					pControlRequest->wIndex & 0x0f;
				struct musb_ep		*pEnd;
				struct musb_hw_ep	*ep;
				void __iomem		*regs;
				int			is_in;
				u16			csr;

				if (bEnd == 0
						|| bEnd >= MUSB_C_NUM_EPS
						|| pControlRequest->wValue
							!= USB_ENDPOINT_HALT)
					break;

				ep = pThis->aLocalEnd + bEnd;
				regs = ep->regs;
				is_in = pControlRequest->wIndex & USB_DIR_IN;
				if (is_in)
					pEnd = &ep->ep_in;
				else
					pEnd = &ep->ep_out;
				if (!pEnd->desc)
					break;

				MGC_SelectEnd(pBase, bEnd);
				if (is_in) {
					csr = musb_readw(regs,
							MGC_O_HDRC_TXCSR);
					if (csr & MGC_M_TXCSR_FIFONOTEMPTY)
						csr |= MGC_M_TXCSR_FLUSHFIFO;
					csr |= MGC_M_TXCSR_P_SENDSTALL
						| MGC_M_TXCSR_CLRDATATOG
						| MGC_M_TXCSR_P_WZC_BITS;
					musb_writew(regs, MGC_O_HDRC_TXCSR,
							csr);
				} else {
					csr = musb_readw(regs,
							MGC_O_HDRC_RXCSR);
					csr |= MGC_M_RXCSR_P_SENDSTALL
						| MGC_M_RXCSR_FLUSHFIFO
						| MGC_M_RXCSR_CLRDATATOG
						| MGC_M_TXCSR_P_WZC_BITS;
					musb_writew(regs, MGC_O_HDRC_RXCSR,
							csr);
				}

				/* select ep0 again */
				MGC_SelectEnd(pBase, 0);
				handled = 1;
				} break;

			default:
				/* class, vendor, etc ... delegate */
				handled = 0;
				break;
			}
			break;
		default:
			/* delegate SET_CONFIGURATION, etc */
			handled = 0;
		}
	} else
		handled = 0;
	return handled;
}

/* we have an ep0out data packet
 * Context:  caller holds controller lock
 */
static void ep0_rxstate(struct musb *this)
{
	void __iomem		*regs = this->control_ep->regs;
	struct usb_request	*req;
	u16			tmp;

	req = next_ep0_request(this);

	/* read packet and ack; or stall because of gadget driver bug:
	 * should have provided the rx buffer before setup() returned.
	 */
	if (req) {
		void		*buf = req->buf + req->actual;
		unsigned	len = req->length - req->actual;

		/* read the buffer */
		tmp = musb_readb(regs, MGC_O_HDRC_COUNT0);
		if (tmp > len) {
			req->status = -EOVERFLOW;
			tmp = len;
		}
		musb_read_fifo(&this->aLocalEnd[0], tmp, buf);
		req->actual += tmp;
		tmp = MGC_M_CSR0_P_SVDRXPKTRDY;
		if (tmp < 64 || req->actual == req->length) {
			this->ep0_state = MGC_END0_STAGE_STATUSIN;
			tmp |= MGC_M_CSR0_P_DATAEND;
		} else
			req = NULL;
	} else
		tmp = MGC_M_CSR0_P_SVDRXPKTRDY | MGC_M_CSR0_P_SENDSTALL;


	/* NOTE:  we "should" hold off reporting DATAEND and going to
	 * STATUSIN until after the completion handler decides whether
	 * to issue a stall instead, since this hardware can do that.
	 */
	if (req)
		musb_g_ep0_giveback(this, req);
	musb_writew(regs, MGC_O_HDRC_CSR0, tmp);
}

/*
 * transmitting to the host (IN), this code might be called from IRQ
 * and from kernel thread.
 *
 * Context:  caller holds controller lock
 */
static void ep0_txstate(struct musb *pThis)
{
	void __iomem		*regs = pThis->control_ep->regs;
	struct usb_request	*pRequest = next_ep0_request(pThis);
	u16			wCsrVal = MGC_M_CSR0_TXPKTRDY;
	u8			*pFifoSource;
	u8			wFifoCount;

	if (!pRequest) {
		// WARN_ON(1);
		DBG(2, "odd; csr0 %04x\n", musb_readw(regs, MGC_O_HDRC_CSR0));
		return;
	}

	/* load the data */
	pFifoSource = (u8 *) pRequest->buf + pRequest->actual;
	wFifoCount = min((unsigned) MGC_END0_FIFOSIZE,
		pRequest->length - pRequest->actual);
	musb_write_fifo(&pThis->aLocalEnd[0], wFifoCount, pFifoSource);
	pRequest->actual += wFifoCount;

	/* update the flags */
	if (wFifoCount < MUSB_MAX_END0_PACKET
			|| pRequest->actual == pRequest->length) {
		pThis->ep0_state = MGC_END0_STAGE_STATUSOUT;
		wCsrVal |= MGC_M_CSR0_P_DATAEND;
	} else
		pRequest = NULL;

	/* send it out, triggering a "txpktrdy cleared" irq */

	/* report completions as soon as the fifo's loaded; there's no
	 * win in waiting till this last packet gets acked.  (other than
	 * very precise fault reporting, needed by USB TMC; possible with
	 * this hardware, but not usable from portable gadget drivers.)
	 */
	if (pRequest)
		musb_g_ep0_giveback(pThis, pRequest);
	musb_writew(regs, MGC_O_HDRC_CSR0, wCsrVal);
}

/*
 * Read a SETUP packet (struct usb_ctrlrequest) from the hardware.
 * Fields are left in USB byte-order.
 *
 * Context:  caller holds controller lock.
 */
static void
musb_read_setup(struct musb *pThis, struct usb_ctrlrequest *req)
{
	struct usb_request	*r;
	void __iomem		*regs = pThis->control_ep->regs;

	musb_read_fifo(&pThis->aLocalEnd[0], sizeof *req, (u8 *)req);

	/* NOTE:  earlier 2.6 versions changed setup packets to host
	 * order, but now USB packets always stay in USB byte order.
	 */
	DBG(3, "SETUP req%02x.%02x v%04x i%04x l%d\n",
		req->bRequestType,
		req->bRequest,
		le16_to_cpu(req->wValue),
		le16_to_cpu(req->wIndex),
		le16_to_cpu(req->wLength));

	/* clean up any leftover transfers */
	r = next_ep0_request(pThis);
	if (r)
		musb_g_ep0_giveback(pThis, r);

	/* For zero-data requests we want to delay the STATUS stage to
	 * avoid SETUPEND errors.  If we read data (OUT), delay accepting
	 * packets until there's a buffer to store them in.
	 *
	 * If we write data, the controller acts happier if we enable
	 * the TX FIFO right away, and give the controller a moment
	 * to switch modes...
	 */
	pThis->bSetAddress = FALSE;
	pThis->ackpend = MGC_M_CSR0_P_SVDRXPKTRDY;
	if (req->wLength == 0) {
		if (req->bRequestType & USB_DIR_IN)
			pThis->ackpend |= MGC_M_CSR0_TXPKTRDY;
		pThis->ep0_state = MGC_END0_STAGE_ACKWAIT;
	} else if (req->bRequestType & USB_DIR_IN) {
		pThis->ep0_state = MGC_END0_STAGE_TX;
		musb_writew(regs, MGC_O_HDRC_CSR0, MGC_M_CSR0_P_SVDRXPKTRDY);
		while ((musb_readw(regs, MGC_O_HDRC_CSR0)
				& MGC_M_CSR0_RXPKTRDY) != 0)
			cpu_relax();
		pThis->ackpend = 0;
	} else
		pThis->ep0_state = MGC_END0_STAGE_RX;
}

static int
forward_to_driver(struct musb *musb,
		const struct usb_ctrlrequest *pControlRequest)
__releases(musb->Lock)
__acquires(musb->Lock)
{
	int retval;
	if (!musb->pGadgetDriver)
		return -EOPNOTSUPP;
	spin_unlock(&musb->Lock);
	retval = musb->pGadgetDriver->setup(&musb->g, pControlRequest);
	spin_lock(&musb->Lock);
	return retval;
}

/*
 * Handle peripheral ep0 interrupt
 * @param pThis this
 *
 * Context: irq handler; we won't re-enter the driver that way.
 */
irqreturn_t musb_g_ep0_irq(struct musb *pThis)
{
	u16		wCsrVal;
	u16		wCount;
	void __iomem	*pBase = pThis->pRegs;
	void __iomem	*regs = pThis->aLocalEnd[0].regs;
	irqreturn_t	retval = IRQ_NONE;

	MGC_SelectEnd(pBase, 0);	/* select ep0 */
	wCsrVal = musb_readw(regs, MGC_O_HDRC_CSR0);
	wCount = musb_readb(regs, MGC_O_HDRC_COUNT0);

	DBG(4, "csr %04x, count %d, myaddr %d, ep0stage %s\n",
			wCsrVal, wCount,
			musb_readb(pBase, MGC_O_HDRC_FADDR),
			decode_ep0stage(pThis->ep0_state));

	/* I sent a stall.. need to acknowledge it now.. */
	if (wCsrVal & MGC_M_CSR0_P_SENTSTALL) {
		musb_writew(regs, MGC_O_HDRC_CSR0,
				wCsrVal & ~MGC_M_CSR0_P_SENTSTALL);
		retval = IRQ_HANDLED;
		pThis->ep0_state = MGC_END0_STAGE_SETUP;
		wCsrVal = musb_readw(regs, MGC_O_HDRC_CSR0);
	}

	/* request ended "early" */
	if (wCsrVal & MGC_M_CSR0_P_SETUPEND) {
		musb_writew(regs, MGC_O_HDRC_CSR0, MGC_M_CSR0_P_SVDSETUPEND);
		retval = IRQ_HANDLED;
		pThis->ep0_state = MGC_END0_STAGE_SETUP;
		wCsrVal = musb_readw(regs, MGC_O_HDRC_CSR0);
		/* NOTE:  request may need completion */
	}

	/* docs from Mentor only describe tx, rx, and idle/setup states.
	 * we need to handle nuances around status stages, and also the
	 * case where status and setup stages come back-to-back ...
	 */
	switch (pThis->ep0_state) {

	case MGC_END0_STAGE_TX:
		/* irq on clearing txpktrdy */
		if ((wCsrVal & MGC_M_CSR0_TXPKTRDY) == 0) {
			ep0_txstate(pThis);
			retval = IRQ_HANDLED;
		}
		break;

	case MGC_END0_STAGE_RX:
		/* irq on set rxpktrdy */
		if (wCsrVal & MGC_M_CSR0_RXPKTRDY) {
			ep0_rxstate(pThis);
			retval = IRQ_HANDLED;
		}
		break;

	case MGC_END0_STAGE_STATUSIN:
		/* end of sequence #2 (OUT/RX state) or #3 (no data) */

		/* update address (if needed) only @ the end of the
		 * status phase per usb spec, which also guarantees
		 * we get 10 msec to receive this irq... until this
		 * is done we won't see the next packet.
		 */
		if (pThis->bSetAddress) {
			pThis->bSetAddress = FALSE;
			musb_writeb(pBase, MGC_O_HDRC_FADDR, pThis->bAddress);
		}

		/* enter test mode if needed (exit by reset) */
		else if (pThis->bTestMode) {
			DBG(1, "entering TESTMODE\n");

			if (MGC_M_TEST_PACKET == pThis->bTestModeValue)
				musb_load_testpacket(pThis);

			musb_writeb(pBase, MGC_O_HDRC_TESTMODE,
					pThis->bTestModeValue);
		}
		/* FALLTHROUGH */

	case MGC_END0_STAGE_STATUSOUT:
		/* end of sequence #1: write to host (TX state) */
		{
			struct usb_request	*req;

			req = next_ep0_request(pThis);
			if (req)
				musb_g_ep0_giveback(pThis, req);
		}
		retval = IRQ_HANDLED;
		pThis->ep0_state = MGC_END0_STAGE_SETUP;
		/* FALLTHROUGH */

	case MGC_END0_STAGE_SETUP:
		if (wCsrVal & MGC_M_CSR0_RXPKTRDY) {
			struct usb_ctrlrequest	setup;
			int			handled = 0;

			if (wCount != 8) {
				ERR("SETUP packet len %d != 8 ?\n", wCount);
				break;
			}
			musb_read_setup(pThis, &setup);
			retval = IRQ_HANDLED;

			/* sometimes the RESET won't be reported */
			if (unlikely(pThis->g.speed == USB_SPEED_UNKNOWN)) {
				u8	power;

				printk(KERN_NOTICE "%s: peripheral reset "
						"irq lost!\n",
						musb_driver_name);
				power = musb_readb(pBase, MGC_O_HDRC_POWER);
				pThis->g.speed = (power & MGC_M_POWER_HSMODE)
					? USB_SPEED_HIGH : USB_SPEED_FULL;

			}

			switch (pThis->ep0_state) {

			/* sequence #3 (no data stage), includes requests
			 * we can't forward (notably SET_ADDRESS and the
			 * device/endpoint feature set/clear operations)
			 * plus SET_CONFIGURATION and others we must
			 */
			case MGC_END0_STAGE_ACKWAIT:
				handled = service_zero_data_request(
						pThis, &setup);

				/* status stage might be immediate */
				if (handled > 0) {
					pThis->ackpend |= MGC_M_CSR0_P_DATAEND;
					pThis->ep0_state =
						MGC_END0_STAGE_STATUSIN;
				}
				break;

			/* sequence #1 (IN to host), includes GET_STATUS
			 * requests that we can't forward, GET_DESCRIPTOR
			 * and others that we must
			 */
			case MGC_END0_STAGE_TX:
				handled = service_in_request(pThis, &setup);
				if (handled > 0) {
					pThis->ackpend = MGC_M_CSR0_TXPKTRDY
						| MGC_M_CSR0_P_DATAEND;
					pThis->ep0_state =
						MGC_END0_STAGE_STATUSOUT;
				}
				break;

			/* sequence #2 (OUT from host), always forward */
			default:		/* MGC_END0_STAGE_RX */
				break;
			}

			DBG(3, "handled %d, csr %04x, ep0stage %s\n",
				handled, wCsrVal,
				decode_ep0stage(pThis->ep0_state));

			/* unless we need to delegate this to the gadget
			 * driver, we know how to wrap this up:  csr0 has
			 * not yet been written.
			 */
			if (handled < 0)
				goto stall;
			else if (handled > 0)
				goto finish;

			handled = forward_to_driver(pThis, &setup);
			if (handled < 0) {
				MGC_SelectEnd(pBase, 0);
stall:
				DBG(3, "stall (%d)\n", handled);
				pThis->ackpend |= MGC_M_CSR0_P_SENDSTALL;
				pThis->ep0_state = MGC_END0_STAGE_SETUP;
finish:
				musb_writew(regs, MGC_O_HDRC_CSR0,
						pThis->ackpend);
				pThis->ackpend = 0;
			}
		}
		break;

	case MGC_END0_STAGE_ACKWAIT:
		/* This should not happen. But happens with tusb6010 with
		 * g_file_storage and high speed. Do nothing.
		 */
		retval = IRQ_HANDLED;
		break;

	default:
		/* "can't happen" */
		WARN_ON(1);
		musb_writew(regs, MGC_O_HDRC_CSR0, MGC_M_CSR0_P_SENDSTALL);
		pThis->ep0_state = MGC_END0_STAGE_SETUP;
		break;
	}

	return retval;
}


static int
musb_g_ep0_enable(struct usb_ep *ep, const struct usb_endpoint_descriptor *desc)
{
	/* always enabled */
	return -EINVAL;
}

static int musb_g_ep0_disable(struct usb_ep *e)
{
	/* always enabled */
	return -EINVAL;
}

static void *musb_g_ep0_alloc_buffer(struct usb_ep *ep, unsigned bytes,
			dma_addr_t * dma, gfp_t gfp_flags)
{
	*dma = DMA_ADDR_INVALID;
	return kmalloc(bytes, gfp_flags);
}

static void musb_g_ep0_free_buffer(struct usb_ep *ep, void *address,
			dma_addr_t dma, unsigned bytes)
{
	kfree(address);
}

static int
musb_g_ep0_queue(struct usb_ep *e, struct usb_request *r, gfp_t gfp_flags)
{
	struct musb_ep		*ep;
	struct musb_request	*req;
	struct musb		*musb;
	int			status;
	unsigned long		lockflags;
	void __iomem		*regs;

	if (!e || !r)
		return -EINVAL;

	ep = to_musb_ep(e);
	musb = ep->pThis;
	regs = musb->control_ep->regs;

	req = to_musb_request(r);
	req->musb = musb;
	req->request.actual = 0;
	req->request.status = -EINPROGRESS;
	req->bTx = ep->is_in;

	spin_lock_irqsave(&musb->Lock, lockflags);

	if (!list_empty(&ep->req_list)) {
		status = -EBUSY;
		goto cleanup;
	}

	switch (musb->ep0_state) {
	case MGC_END0_STAGE_RX:		/* control-OUT data */
	case MGC_END0_STAGE_TX:		/* control-IN data */
	case MGC_END0_STAGE_ACKWAIT:	/* zero-length data */
		status = 0;
		break;
	default:
		DBG(1, "ep0 request queued in state %d\n",
				musb->ep0_state);
		status = -EINVAL;
		goto cleanup;
	}

	/* add request to the list */
	list_add_tail(&(req->request.list), &(ep->req_list));

	DBG(3, "queue to %s (%s), length=%d\n",
			ep->name, ep->is_in ? "IN/TX" : "OUT/RX",
			req->request.length);

	MGC_SelectEnd(musb->pRegs, 0);

	/* sequence #1, IN ... start writing the data */
	if (musb->ep0_state == MGC_END0_STAGE_TX)
		ep0_txstate(musb);

	/* sequence #3, no-data ... issue IN status */
	else if (musb->ep0_state == MGC_END0_STAGE_ACKWAIT) {
		if (req->request.length)
			status = -EINVAL;
		else {
			musb->ep0_state = MGC_END0_STAGE_STATUSIN;
			musb_writew(regs, MGC_O_HDRC_CSR0,
					musb->ackpend | MGC_M_CSR0_P_DATAEND);
			musb->ackpend = 0;
			musb_g_ep0_giveback(ep->pThis, r);
		}

	/* else for sequence #2 (OUT), caller provides a buffer
	 * before the next packet arrives.  deferred responses
	 * (after SETUP is acked) are racey.
	 */
	} else if (musb->ackpend) {
		musb_writew(regs, MGC_O_HDRC_CSR0, musb->ackpend);
		musb->ackpend = 0;
	}

cleanup:
	spin_unlock_irqrestore(&musb->Lock, lockflags);
	return status;
}

static int
musb_g_ep0_dequeue(struct usb_ep *ep, struct usb_request *req)
{
	/* we just won't support this */
	return -EINVAL;
}

static int musb_g_ep0_halt(struct usb_ep *e, int value)
{
	struct musb_ep		*ep;
	struct musb		*musb;
	void __iomem		*base, *regs;
	unsigned long		flags;
	int			status;
	u16			csr;

	if (!e || !value)
		return -EINVAL;

	ep = to_musb_ep(e);
	musb = ep->pThis;
	base = musb->pRegs;
	regs = musb->control_ep->regs;

	spin_lock_irqsave(&musb->Lock, flags);

	if (!list_empty(&ep->req_list)) {
		status = -EBUSY;
		goto cleanup;
	}

	switch (musb->ep0_state) {
	case MGC_END0_STAGE_TX:		/* control-IN data */
	case MGC_END0_STAGE_ACKWAIT:	/* STALL for zero-length data */
	case MGC_END0_STAGE_RX:		/* control-OUT data */
	case MGC_END0_STAGE_STATUSIN:		/* control-OUT status */
	case MGC_END0_STAGE_STATUSOUT:		/* control-IN status */
		status = 0;

		MGC_SelectEnd(base, 0);
		csr = musb_readw(regs, MGC_O_HDRC_CSR0);
		csr |= MGC_M_CSR0_P_SENDSTALL;
		musb_writew(regs, MGC_O_HDRC_CSR0, csr);
		musb->ep0_state = MGC_END0_STAGE_SETUP;
		break;
	default:
		DBG(1, "ep0 can't halt in state %d\n", musb->ep0_state);
		status = -EINVAL;
	}

cleanup:
	spin_unlock_irqrestore(&musb->Lock, flags);
	return status;
}

const struct usb_ep_ops musb_g_ep0_ops = {
	.enable		= musb_g_ep0_enable,
	.disable	= musb_g_ep0_disable,
	.alloc_request	= musb_alloc_request,
	.free_request	= musb_free_request,
	.alloc_buffer	= musb_g_ep0_alloc_buffer,
	.free_buffer	= musb_g_ep0_free_buffer,
	.queue		= musb_g_ep0_queue,
	.dequeue	= musb_g_ep0_dequeue,
	.set_halt	= musb_g_ep0_halt,
	.fifo_status	= NULL,
	.fifo_flush	= NULL,
};

[-- Attachment #5: PATCH --]
[-- Type: application/octet-stream, Size: 411 bytes --]

199d198
< 	pThis->ep0_state = MGC_END0_STAGE_SETUP;
459d457
< 	musb_writew(regs, MGC_O_HDRC_CSR0, tmp);
467a466
> 	musb_writew(regs, MGC_O_HDRC_CSR0, tmp);
506d504
< 	musb_writew(regs, MGC_O_HDRC_CSR0, wCsrVal);
514a513
> 	musb_writew(regs, MGC_O_HDRC_CSR0, wCsrVal);
939a939,940
> 	case MGC_END0_STAGE_STATUSIN:		/* control-OUT status */
> 	case MGC_END0_STAGE_STATUSOUT:		/* control-IN status */

[-- Attachment #6: README --]
[-- Type: application/octet-stream, Size: 1256 bytes --]

DIFF was obtained by running diff g_ep0-2_6_20.c g_ep0.c > PATCH

In a USB control transfer, there are 3 stages: the setup, data, and status
stages. The callback function may be called after the data stage of a USB
control transfer, so the state machine cannot go back to the "setup" stage
after the callback is executed.
199d198
< 	pThis->ep0_state = MGC_END0_STAGE_SETUP;

The code cannot signal the completion of the data stage to the USB peripheral
until after the callback function determines whether to stall the endpoint.
This is consistent with the comment in this area of code.
459d457
< 	musb_writew(regs, MGC_O_HDRC_CSR0, tmp);
467a466
> 	musb_writew(regs, MGC_O_HDRC_CSR0, tmp);

The code cannot signal the completion of the data stage to the USB peripheral
until after the callback function determines whether to stall the endpoint.
506d504
< 	musb_writew(regs, MGC_O_HDRC_CSR0, wCsrVal);
514a513
> 	musb_writew(regs, MGC_O_HDRC_CSR0, wCsrVal);

According to section 8.5.3 of version 2.0 of the USB spec, a stall may occur in
either the data or status stages of the control transfer.
939a939,940
> 	case MGC_END0_STAGE_STATUSIN:		/* control-OUT status */
> 	case MGC_END0_STAGE_STATUSOUT:		/* control-IN status */

[-- Attachment #7: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2007-08-24 12:09 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-05-17 16:35 Cannot stall an endpoint 0 control transfer from a data stage cal lback function Geoffrey Tam
2007-05-18 16:25 ` Tony Lindgren
  -- strict thread matches above, loose matches on Subject: below --
2007-08-21 20:10 Geoffrey Tam
2007-08-21 23:58 ` David Brownell
2007-08-24 12:09   ` Tony Lindgren
2007-05-14 18:24 Geoffrey Tam
2007-05-09 21:56 Geoffrey Tam
2007-05-09 18:07 Geoffrey Tam
2007-05-11 18:10 ` tony

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.