Add partial TxFIFO logic to STM32 OTG FS device driver

git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@4570 42af7a65-404d-4744-a932-0658087f49c3
This commit is contained in:
patacongo 2012-04-07 19:38:13 +00:00
parent 1ad8ade695
commit f9544339a9
2 changed files with 273 additions and 86 deletions

View File

@ -8,6 +8,8 @@ README
- Installation Directories with Spaces in the Path
- Notes about Header Files
o Configuring NuttX
- Instantiating "Canned" Configurations
- NuttX Configuration Tool
o Toolchains
- Cross-Development Toolchains
- NuttX Buildroot Toolchain
@ -25,6 +27,7 @@ INSTALLATION
^^^^^^^^^^^^
Installing Cygwin
-----------------
NuttX may be installed and built on a Linux system or on a Windows
system if Cygwin is installed. Installing Cygwin on your Windows PC
@ -56,7 +59,8 @@ Installing Cygwin
fast nor reliable). The rest of these instructions assume that you
are at a bash command line prompt in either Linux or in Cygwin shell.
Download and Unpack:
Download and Unpack
-------------------
Download and unpack the NuttX tarball. If you are reading this, then
you have probably already done that. After unpacking, you will end
@ -65,7 +69,8 @@ Download and Unpack:
match the various instructions in the documentation and some scripts
in the source tree.
Semi-Optional apps/ Package:
Semi-Optional apps/ Package
---------------------------
All NuttX libraries and example code used to be in included within
the NuttX source tree. As of NuttX-6.0, this application code was
@ -98,7 +103,8 @@ Semi-Optional apps/ Package:
can be changed by editing your NuttX configuration file, but that
is another story).
Installation Directories with Spaces in the Path:
Installation Directories with Spaces in the Path
------------------------------------------------
The nuttx build directory should reside in a path that contains no
spaces in any higher level directory name. For example, under
@ -114,7 +120,8 @@ Installation Directories with Spaces in the Path:
Then I install NuttX in /home/nuttx and always build from
/home/nuttx/nuttx.
Notes about Header Files:
Notes about Header Files
------------------------
Other C-Library Header Files.
@ -167,6 +174,9 @@ Notes about Header Files:
CONFIGURING NUTTX
^^^^^^^^^^^^^^^^^
Instantiating "Canned" Configurations
-------------------------------------
"Canned" NuttX configuration files are retained in:
configs/<board-name>/<config-dir>
@ -206,10 +216,54 @@ easier. It is used as follows:
cd ${TOPDIR}/tools
./configure.sh <board-name>/<config-dir>
NuttX Configuration Tool
------------------------
An automated tool is under development to support re-configuration
of NuttX. This tool, however, is not yet quite ready for general
usage.
This automated tool is based on the kconfig-frontends application
available at http://ymorin.is-a-geek.org/projects/kconfig-frontends
(A snapshot of this tool is also available at ../misc/tools). This
application provides a tool called 'mconf' that is used by the NuttX
top-level Makefile. The following make target is provided:
make menuconfig
This make target will bring up NuttX configuration menus. The
'menuconfig' target depends on two things:
1. The Kconfig configuration data files that appear in almost all
NuttX directories. These data files are the part that is still
under development (patches are welcome!). The Kconfig files
contain configuration information for the configuration settings
relevant to the directory in which the Kconfig file resides.
NOTE: For a description of the syntax of this configuration file,
see ../misc/tools/kconfig-language.txt.
2. The 'mconf' tool. 'mconf' is part of the kconfig-frontends
package. You can download that package from the website
http://ymorin.is-a-geek.org/projects/kconfig-frontends or you
can use the snapshot in ../misc/tools.
Building may be as simple as 'configure; make; make install'
but there may be some build complexities, especially if you
are building under Cygwin. See the more detailed build
instructions at ../misc/tools/README.txt
The 'make install' step will, by default, install the 'mconf'
tool at /usr/local/bin/mconf. Where ever you choose to
install 'mconf', make certain that your PATH variable includes
a path to that installation directory.
TOOLCHAINS
^^^^^^^^^^
Cross-Development Toolchains
----------------------------
In order to build NuttX for your board, you will have to obtain a cross-
compiler to generate code for your target CPU. For each board,
@ -223,6 +277,7 @@ Cross-Development Toolchains
is optional but can save a lot of confusion in the future.
NuttX Buildroot Toolchain
-------------------------
For many configurations, a DIY set of tools is available for NuttX. These
tools can be downloaded from the NuttX SourceForge file repository. After
@ -236,6 +291,15 @@ NuttX Buildroot Toolchain
This toolchain is available for both the Linux and Cygwin development
environments.
Advantages: (1) NuttX header files are built into the tool chain,
and (2) related support tools like NXFLAT tools and the ROMFS
genromfs tools can be built into your toolchain.
Disadvantages: This tool chain is not was well supported as some other
toolchains. GNU tools are not my priority and so the buildroot tools
often get behind. For example, the is still no EABI support in the
NuttX buildroot toolchain for ARM.
SHELLS
^^^^^^
@ -270,6 +334,7 @@ BUILDING NUTTX
^^^^^^^^^^^^^^
Building
--------
NuttX builds in-place in the source tree. You do not need to create
any special build directories. Assuming that your Make.defs is setup
@ -286,6 +351,7 @@ Building
to see if that applies to your target.
Re-building
-----------
Re-building is normally simple -- just type make again.
@ -309,6 +375,7 @@ Re-building
then make NuttX.
Build Targets
-------------
Below is a summary of the build targets available in the top-level
NuttX Makefile:
@ -386,6 +453,7 @@ CYGWIN BUILD PROBLEMS
^^^^^^^^^^^^^^^^^^^^^
Strange Path Problems
---------------------
If you see strange behavior when building under Cygwin then you may have
a problem with your PATH variable. For example, if you see failures to
@ -410,6 +478,7 @@ The solution is either:
$ export PATH=/usr/local/bin:/usr/bin:/bin:$PATH
Window Native Toolchain Issues
------------------------------
There are many popular Windows native toolchains that may be used with NuttX.
Examples include CodeSourcery (for Windows), devkitARM, and several vendor-

View File

@ -375,16 +375,19 @@ static bool stm32_addlast(FAR struct stm32_ep_s *privep,
/* Special endpoint 0 data transfer logic */
static inline void stm32_ep0xfer(uint8_t epphy, FAR uint8_t *data, uint32_t nbytes);
static void stm32_ep0read(FAR uint8_t *dest, uint16_t len);
static void stm32_ep0configsetup(FAR struct stm32_usbdev_s *priv)
static void stm32_ep0configsetup(FAR struct stm32_usbdev_s *priv);
/* IN request handling */
/* IN request and TxFIFO handling */
static void stm32_epwritefifo(FAR struct stm32_ep_s *privep,
FAR uint8_t *buf, int nbytes);
static int stm32_wrrequest(FAR struct stm32_usbdev_s *priv,
FAR struct stm32_ep_s *privep);
/* OUT request handling */
/* OUT request and RxFIFO handling */
static void stm32_epreadfifo(FAR struct stm32_ep_s *privep,
FAR uint8_t *dest, uint16_t len);
static void stm32_epoutcomplete(FAR struct stm32_usbdev_s *priv,
FAR struct stm32_ep_s *privep);
static inline void stm32_epoutreceive(FAR struct stm32_ep_s *privep, int bcnt);
@ -426,6 +429,7 @@ static inline void stm32_epoutinterrupt(FAR struct stm32_usbdev_s *priv);
static inline void stm32_runtestmode(FAR struct stm32_usbdev_s *priv);
static inline void stm32_epin(FAR struct stm32_usbdev_s *priv, uint8_t epno);
static inline void stm32_txfifoempty(FAR struct stm32_usbdev_s *priv, int epno);
static inline void stm32_epininterrupt(FAR struct stm32_usbdev_s *priv);
/* Other second level interrupt processing */
@ -691,46 +695,6 @@ static inline void stm32_ep0xfer(uint8_t epphy, uint8_t *buf, uint32_t nbytes)
#warning "Missing Logic"
}
/*******************************************************************************
* Name: stm32_ep0read
*
* Description:
* Read a Setup packet from the DTD.
*
*******************************************************************************/
static void stm32_ep0read(FAR uint8_t *dest, uint16_t len)
{
uint32_t regaddr;
int i;
/* Get the address of the EP0 FIFO */
regaddr = STM32_OTGFS_DFIFO_DEP(0);
/* Read 32-bits and write 4 x 8-bits at time (to avoid unaligned accesses) */
for (i = 0; i < len; i += 4)
{
union
{
uint32_t w;
uint8_t b[4];
} data;
/* Read 1 x 32-bits of EP0 packet data */
data.w = stm32_getreg(regaddr);
/* Write 4 x 8-bits of EP0 packet data */
*dest++ = data.b[0];
*dest++ = data.b[1];
*dest++ = data.b[2];
*dest++ = data.b[3];
}
}
/*******************************************************************************
* Name: stm32_ep0configsetup
*
@ -749,6 +713,49 @@ static void stm32_ep0configsetup(FAR struct stm32_usbdev_s *priv)
stm32_putreg(regval, STM32_OTGFS_DOEPTSIZ0);
}
/****************************************************************************
* Name: stm32_epwritefifo
*
* Description:
* Send data to the endpoint's TxFIFO.
*
****************************************************************************/
static void stm32_epwritefifo(FAR struct stm32_ep_s *privep,
FAR uint8_t *buf, int nbytes)
{
uint32_t regaddr;
uint32_t regval;
int nwords;
int i;
/* Convert the number of bytes to words */
nwords = (nbytes + 3) >> 2;
/* Get the TxFIFO for this endpoint (same as the endpoint number) */
regaddr = STM32_OTGFS_DFIFO_DEP(privep->epphy);
/* Then transfer each word to the TxFIFO */
for (i = 0; i < nwords; i++)
{
/* Read four bytes from the source buffer (to avoid unaligned accesses)
* and pack these into one 32-bit word (little endian).
*/
regval = (uint32_t)*buf++;
regval |= ((uint32_t)*buf++) << 8;
regval |= ((uint32_t)*buf++) << 16;
regval |= ((uint32_t)*buf++) << 24;
/* Then write the packed data to the TxFIFO */
stm32_putreg(regval, regaddr);
}
}
/****************************************************************************
* Name: stm32_wrrequest
*
@ -757,16 +764,20 @@ static void stm32_ep0configsetup(FAR struct stm32_usbdev_s *priv)
*
****************************************************************************/
static int stm32_wrrequest(struct stm32_usbdev_s *priv, struct stm32_ep_s *privep)
static int stm32_wrrequest(FAR struct stm32_usbdev_s *priv,
FAR struct stm32_ep_s *privep)
{
struct stm32_req_s *privreq;
uint32_t regaddr;
uint32_t regval;
uint8_t *buf;
uint8_t epno;
int nbytes;
int nwords;
int bytesleft;
/* We get here when an IN endpoint interrupt occurs. So now we know that
* there is no TX transfer in progress.
/* We get here when an IN endpoint or Tx FIFO empty interrupt occurs. So
* now we know that there is no TX transfer in progress.
*/
privep->active = false;
@ -789,63 +800,149 @@ static int stm32_wrrequest(struct stm32_usbdev_s *priv, struct stm32_ep_s *prive
ullvdbg("epno=%d req=%p: len=%d xfrd=%d nullpkt=%d\n",
epno, privreq, privreq->req.len, privreq->req.xfrd, privep->zlp);
/* Get the number of bytes left to be sent in the packet */
/* Loop while there are still bytes to be transferred (or a zero-length-
* packet, ZLP, to be sent). The loop will also be terminated if there
* is insufficient space remaining in the TxFIFO to send a complete
* packet.
*/
bytesleft = privreq->req.len - privreq->req.xfrd;
nbytes = bytesleft;
/* Send the next packet */
if (nbytes > 0)
while (privreq->req.xfrd < privreq->req.len || privep->zlp)
{
/* Either send the maxpacketsize or all of the remaining data in
* the request.
/* Get the number of bytes left to be sent in the request */
bytesleft = privreq->req.len - privreq->req.xfrd;
nbytes = bytesleft;
/* Limit the size of the transfer to one full packet and handle
* zero-length packets (ZLPs).
*/
privep->zlp = 0;
if (nbytes >= privep->ep.maxpacket)
if (nbytes > 0)
{
nbytes = privep->ep.maxpacket;
/* Handle the case where this packet is exactly the
* maxpacketsize. Do we need to send a zero-length packet
* in this case?
/* Either send the maxpacketsize or all of the remaining data in
* the request.
*/
if (bytesleft == privep->ep.maxpacket &&
(privreq->req.flags & USBDEV_REQFLAGS_NULLPKT) != 0)
privep->zlp = 0;
if (nbytes >= privep->ep.maxpacket)
{
privep->zlp = 1;
nbytes = privep->ep.maxpacket;
/* Handle the case where this packet is exactly the
* maxpacketsize. Do we need to send a zero-length packet
* in this case?
*/
if (bytesleft == privep->ep.maxpacket &&
(privreq->req.flags & USBDEV_REQFLAGS_NULLPKT) != 0)
{
#warning "How, exactly, do I need to handle zero-length packets?"
privep->zlp = 1;
}
}
}
/* Get the transfer size in 32-bit words */
nwords = (nbytes + 3) >> 2;
/* Get the number of 32-bit words available in the TxFIFO. The
* DXTFSTS indicates the amount of free space available in the
* endpoint TxFIFO. Values are in terms of 32-bit words:
*
* 0: Endpoint TxFIFO is full
* 1: 1 word available
* 2: 2 words available
* n: n words available
*/
regaddr = STM32_OTGFS_DTXFSTS(privep->epphy);
regval = stm32_getreg(regaddr);
/* And terminate the loop if there is insufficient space in the TxFIFO
* hold the entire packet.
*/
if ((regval & OTGFS_DTXFSTS_MASK) < nwords)
{
/* The TxFIFO is full */
break;
}
/* Transfer data to the TxFIFO */
buf = privreq->req.buf + privreq->req.xfrd;
stm32_epwritefifo(privep, buf, nbytes);
/* If it was not before, the OUT endpoint is now actively transferring
* data.
*/
privep->active = true;
/* Update for the next time through the loop */
privreq->req.xfrd += nbytes;
}
/* Send the packet (might be a null packet nbytes == 0) */
buf = privreq->req.buf + privreq->req.xfrd;
stm32_epwrite(priv, privep, buf, nbytes);
privep->active = true;
/* Update for the next data IN interrupt */
privreq->req.xfrd += nbytes;
bytesleft = privreq->req.len - privreq->req.xfrd;
/* If all of the bytes were sent (including any final null packet)
* then we are finished with the transfer
*/
if (bytesleft == 0 && !privep->zlp)
if (privreq->req.xfrd >= privreq->req.len && !privep->zlp)
{
usbtrace(TRACE_COMPLETE(USB_EPNO(privep->ep.eplog)), privreq->req.xfrd);
privep->zlp = 0;
stm32_reqcomplete(privep, OK);
privep->zlp = 0;
privep->active = false;
}
return OK;
}
/*******************************************************************************
* Name: stm32_epreadfifo
*
* Description:
* Read packet from the EP0 RxFIFO.
*
*******************************************************************************/
static void stm32_epreadfifo(FAR struct stm32_ep_s *privep,
FAR uint8_t *dest, uint16_t len)
{
uint32_t regaddr;
int i;
/* Get the address of the endpoint FIFO */
regaddr = STM32_OTGFS_DFIFO_DEP(privep->epphy);
/* Read 32-bits and write 4 x 8-bits at time (to avoid unaligned accesses) */
for (i = 0; i < len; i += 4)
{
union
{
uint32_t w;
uint8_t b[4];
} data;
/* Read 1 x 32-bits of EP0 packet data */
data.w = stm32_getreg(regaddr);
/* Write 4 x 8-bits of EP0 packet data */
*dest++ = data.b[0];
*dest++ = data.b[1];
*dest++ = data.b[2];
*dest++ = data.b[3];
}
}
/*******************************************************************************
* Name: stm32_epoutcomplete
*
@ -943,7 +1040,7 @@ static inline void stm32_epoutreceive(FAR struct stm32_ep_s *privep, int bcnt)
/* Transfer the data from the RxFIFO to the request's data buffer */
stm32_ep0read(dest, readlen);
stm32_epreadfifo(privep, dest, readlen);
/* Update the number of bytes transferred */
@ -973,7 +1070,7 @@ static void stm32_epoutsetup(FAR struct stm32_usbdev_s *priv,
* just return, leaving the newly received request in the request queue.
*/
if (!priv->active)
if (!privep->active)
{
/* Loop until a valid request is found (or the request queue is empty).
* The loop is only need to look at the request queue again is an invalid
@ -2173,6 +2270,26 @@ static inline void stm32_epin(FAR struct stm32_usbdev_s *priv, uint8_t epno)
}
}
/****************************************************************************
* Name: stm32_txfifoempty
*
* Description:
* TxFIFO empty interrupt handling
*
****************************************************************************/
static inline void stm32_txfifoempty(FAR struct stm32_usbdev_s *priv, int epno)
{
FAR struct stm32_ep_s *privep = &priv->epin[epno];
/* Continue processing the write request queue. This may mean sending
* more dat from the exisiting request or terminating the current requests
* and (perhaps) starting the IN transfer from the next write request.
*/
stm32_wrrequest(priv, privep);
}
/*******************************************************************************
* Name: stm32_epininterrupt
*
@ -2482,7 +2599,8 @@ static inline void stm32_rxinterrupt(FAR struct stm32_usbdev_s *priv)
* last SETUP packet will be processed.
*/
stm32_ep0read((FAR uint8_t*)&priv->ctrlreq, USB_SIZEOF_CTRLREQ);
stm32_epreadfifo(&priv->epout[EP0], (FAR uint8_t*)&priv->ctrlreq,
USB_SIZEOF_CTRLREQ);
/* The SETUP data has been processed */