aboutsummaryrefslogtreecommitdiff
path: root/nuttx
diff options
context:
space:
mode:
authorpatacongo <patacongo@7fd9a85b-ad96-42d3-883c-3090e2eb8679>2012-08-07 20:18:56 +0000
committerpatacongo <patacongo@7fd9a85b-ad96-42d3-883c-3090e2eb8679>2012-08-07 20:18:56 +0000
commited303232fa84efe26f95a86e671995cfb712f321 (patch)
tree9bcbe9c4ed8c3d704c08ea4e346005ee47f6a69f /nuttx
parent756fc3f2db435855a76535c9863ad5d5d197b9dc (diff)
downloadpx4-firmware-ed303232fa84efe26f95a86e671995cfb712f321.tar.gz
px4-firmware-ed303232fa84efe26f95a86e671995cfb712f321.tar.bz2
px4-firmware-ed303232fa84efe26f95a86e671995cfb712f321.zip
Update documentation to describe customization of NSH; Add the framework for a LPC43xx USB0 driver (not functional)
git-svn-id: https://nuttx.svn.sourceforge.net/svnroot/nuttx/trunk@5015 7fd9a85b-ad96-42d3-883c-3090e2eb8679
Diffstat (limited to 'nuttx')
-rw-r--r--nuttx/ChangeLog7
-rw-r--r--nuttx/Documentation/NuttShell.html901
-rw-r--r--nuttx/Documentation/NuttX.html14
-rw-r--r--nuttx/TODO25
-rw-r--r--nuttx/arch/arm/src/lpc43xx/Make.defs7
-rw-r--r--nuttx/arch/arm/src/lpc43xx/lpc43_usb0dev.c2663
6 files changed, 3599 insertions, 18 deletions
diff --git a/nuttx/ChangeLog b/nuttx/ChangeLog
index 603d2db97..614381c90 100644
--- a/nuttx/ChangeLog
+++ b/nuttx/ChangeLog
@@ -3131,4 +3131,11 @@
point numbers.
* lib/stdio/lib_libdtoa.c and lib_libvsprintf.c: Correct some floating
point options.
+ * arch/arm/lpc43xx/lpc32_usb0dev.c: Add framework for development of
+ an USB0, device-side driver for the LPC43XX. The initial check-in,
+ however, is simply for the LPC31xx driver with name changes. The
+ LPC31xx has the same USB IP, but will require some additional initialization
+ (and lots of testing) before it can be used with the LPC43xx.
+ * nuttx/Documentation/NuttShell.html: Added a section covering ways to
+ customize the behavior or NSH.
diff --git a/nuttx/Documentation/NuttShell.html b/nuttx/Documentation/NuttShell.html
index 0413a4695..a92cbadd8 100644
--- a/nuttx/Documentation/NuttShell.html
+++ b/nuttx/Documentation/NuttShell.html
@@ -335,6 +335,36 @@
<tr>
<td valign="top" width="22"><img height="20" width="20" src="favicon.ico"></td>
<td>
+ <a href="#customizingnsh">4.0 Customimizing the NuttShell</a>
+ </td>
+</tr>
+<tr>
+ <td><br></td>
+ <td>
+ <a href="#custonshlib">4.1 The NSH Library and NSH Initialization</a>
+ </td>
+</tr>
+<tr>
+ <td><br></td>
+ <td>
+ <a href="#custoncmds">4.2 NSH Commands</a>
+ </td>
+</tr>
+<tr>
+ <td><br></td>
+ <td>
+ <a href="#custapps">4.3 NSH &quot;Built-In&quot; Applications</a>
+ </td>
+</tr>
+<tr>
+ <td><br></td>
+ <td>
+ <a href="#custinit">4.4 Customizing NSH Initialization</a>
+ </td>
+</tr>
+<tr>
+ <td valign="top" width="22"><img height="20" width="20" src="favicon.ico"></td>
+ <td>
<a href="#index">Index</a>
</td>
</table>
@@ -655,6 +685,11 @@ mount -t vfat /dev/ram1 /tmp
file system image.
</p>
+<p>
+ <b>Further Information</b>.
+ See the section on <a href="#customizingnsh">Customimizing the NuttShell</a> for additional, more detailed information about the NSH start-up script and how to modify it.
+</p>
+
<table width ="100%">
<tr bgcolor="#e4e4e4">
<td>
@@ -2647,6 +2682,816 @@ nsh>
<table width ="100%">
<tr bgcolor="#e4e4e4">
<td>
+ <a name="customizingnsh"><h1>4.0 Customimizing the NuttShell</h1></a>
+ </td>
+ </tr>
+</table>
+
+<p>
+ <b>Overview.</b>
+ The NuttShell (NSH) is a simple shell application that may be used with NuttX.
+ It supports a variety of commands and is (very) loosely based on the bash shell and the common utilities used in Unix shell programming.
+ The paragraphs in this appendix will focus on customizing NSH: Adding new commands, changing the initialization sequence, etc.
+</p>
+
+<table width ="100%">
+ <tr bgcolor="#e4e4e4">
+ <td>
+ <a name="custonshlib"><h2>4.1 The NSH Library and NSH Initialization</h2></a>
+ </td>
+ </tr>
+</table>
+
+<p>
+ <b>Overview.</b>
+ NSH is implemented as a library that can be found at <code>apps/nshlib</code>.
+ As a library, it can be custom built into any application that follows the NSH initialization sequence described below.
+ As an example, the code at <code>apps/examples/nsh/nsh_main.c</code> illustrates how to start NSH and the logic there was intended to be incorporated into your own custom code.
+ Although code was generated simply as an example, in the end most people just use this example code as their application <code>main()</code> function.
+ That initialization performed by that example is discussed in the following paragraphs.
+</p>
+
+<h3>4.1.1 NSH Initialization sequence</h3>
+
+<p>
+ The NSH start-up sequence is very simple.
+ As an example, the code at <code>apps/examples/nsh/nsh_main.c</code> illustrates how to start NSH.
+ It simple does the following:
+</p>
+
+<ol>
+ <li>
+ <p>
+ If you have C++ static initializers, it will call your implementation of <code>up_cxxinitialize()</code> which will, in turn, call those static initializers.
+ For the case of the STM3240G-EVAL board, the implementation of <code>up_cxxinitialize()</code> can be found at <code>nuttx/configs/stm3240g-eval/src/up_cxxinitialize.c</code>.
+ </p>
+ <li>
+ <p>
+ This function then calls <code>nsh_initialize()</code> which initializes the NSH library.
+ <code>nsh_initialize()</code> is described in more detail below.
+ </p>
+ <li>
+ <p>
+ If the Telnetconsole is enabled, it calls <code>nsh_telnetstart()</code> which resides in the NSH library.
+ <code>nsh_telnetstart()</code> will start the Telnet daemon that will listen for Telnet connections and start remote NSH sessions.
+ </p>
+ <li>
+ <p>
+ If a local console is enabled (probably on a serial port), then <code>nsh_consolemain()</code> is called.
+ <code>nsh_consolemain()</code> also resides in the NSH library.
+ <code>nsh_consolemain()</code> does not return so that finished the entire NSH initialization sequence.
+ </p>
+</ol>
+
+<h3>4.1.2 <code>nsh_initialize()</code></h3>
+
+<p>
+ The NSH initialization function, <code>nsh_initialize()</code>, be found in <code>apps/nshlib/nsh_init.c</code>.
+ It does only three things:
+</p>
+
+<ol>
+ <li>
+ <p>
+ <code>nsh_romfsetc()</code>:
+ If so configured, it executes an NSH start-up script that can be found at <code>/etc/init.d/rcS</code> in the target file system.
+ The <code>nsh_romfsetc()</code> function can be found in <code>apps/nshlib/nsh_romfsetc.c</code>.
+ This function will (1) register a ROMFS file system, then (2) mount the ROMFS file system.
+ <code>/etc</code> is the default location where a read-only, ROMFS file system is mounted by <code>nsh_romfsetc()</code>.
+ </p>
+ <p>
+ The ROMFS image is, itself, just built into the firmware.
+ By default, this <code>rcS</code> start-up script contains the following logic:
+ </p>
+<ul><pre>
+# Create a RAMDISK and mount it at XXXRDMOUNTPOUNTXXX
+
+mkrd -m XXXMKRDMINORXXX -s XXMKRDSECTORSIZEXXX XXMKRDBLOCKSXXX
+mkfatfs /dev/ramXXXMKRDMINORXXX
+mount -t vfat /dev/ramXXXMKRDMINORXXX XXXRDMOUNTPOUNTXXX
+</pre></ul>
+
+ <p>
+ Where the <code>XXXX*XXXX</code> strings get replaced in the template when the ROMFS image is created:
+ </p>
+ <ul>
+ <li>
+ <p>
+ <code>XXXMKRDMINORXXX</code> will become the RAM device minor number.
+ Default: 0
+ </p>
+ <li>
+ <p>
+ <code>XXMKRDSECTORSIZEXXX</code> will become the RAM device sector size
+ </p>
+ <li>
+ <p>
+ <code>XXMKRDBLOCKSXXX</code> will become the number of sectors in the device.
+ </p>
+ <li>
+ <p>
+ <code>XXXRDMOUNTPOUNTXXX</code> will become the configured mount point.
+ Default: <code>/etc</code>
+ </p>
+ </ul>
+ <p>
+ By default, the substituted values would yield an <code>rcS</code> file like:
+ </p>
+<ul><pre>
+# Create a RAMDISK and mount it at /tmp
+
+mkrd -m 1 -s 512 1024
+mkfatfs /dev/ram1
+mount -t vfat /dev/ram1 /tmp
+</pre></ul>
+ <p>
+ This script will, then:
+ </p>
+ <ul>
+ <li>
+ <p>
+ Create a RAMDISK of size 512*1024 bytes at <code>/dev/ram1</code>,
+ </p>
+ <li>
+ <p>
+ Format a FAT file system on the RAM disk at <code>/dev/ram1</code>, and then
+ </p>
+ <li>
+ <p>
+ Mount the FAT filesystem at a configured mountpoint, <code>/tmp</code>.
+ </p>
+ </ul>
+ <p>
+ This <code>rcS</code> template file can be found at <code>apps/nshlib/rcS.template</code>.
+ The resulting ROMFS file system can be found in <code>apps/nshlib/nsh_romfsimg.h</code>.
+ </p>
+ <li>
+ <p>
+ <code>nsh_archinitialize()</code>:
+ Next any architecture-specific NSH initialization will be performed (if any).
+ For the STM3240G-EVAL, this architecture specific initialization can be found at <code>configs/stm3240g-eval/src/up_nsh.c</code>.
+ This it does things like: (1) Initialize SPI devices, (2) Initialize SDIO, and (3) mount any SD cards that may be inserted.
+ </p>
+ <li>
+ <p>
+ <code>nsh_netinit()</code>:
+ The <code>nsh_netinit()</code> function can be found in <code>apps/nshlib/nsh_netinit.c</code>.
+ </p>
+</ol>
+
+<table width ="100%">
+ <tr bgcolor="#e4e4e4">
+ <td>
+ <a name="custoncmds"><h2>4.2 NSH Commands</h2></a>
+ </td>
+ </tr>
+</table>
+
+<p>
+ <b>Overview.</b>
+ NSH supports a variety of commands as part of the NSH program.
+ All of the NSH commands are listed in the NSH documentation <a href="#cmdoverview">above</a>.
+ Not all of these commands may be available at any time, however.
+ Many commands depend upon certain NuttX configuration options.
+ You can enter the help command at the NSH prompt to see the commands actual available:
+</p>
+<ul><pre>
+nsh&gt; help
+</pre></ul>
+<p>
+ For example, if network support is disabled, then all network-related commands will be missing from the list of commands presented by '<code>nsh&gt; help</code>'.
+ You can see the specific command dependencies in the table <a href="#cmddependencies">above</a>.
+</p>
+
+<h3>4.2.1 Adding New NSH Commands</h3>
+
+<p>
+ New commands can be added to the NSH very easily.
+ You simply need to add two things:
+</p>
+<ol>
+ <li>
+ <p>
+ The implementation of your command, and
+ </p>
+ <li>
+ <p>
+ A new entry in the NSH command table
+ </p>
+</ol>
+
+<p>
+ <b>Implementation of Your Command.</b>
+ For example, if you want to add a new a new command called <code>mycmd</code> to NSH, you would first implement the <code>mycmd</code> code in a function with this prototype:
+</p>
+
+<ul></pre>
+int cmd_mycmd(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv);
+</pre></ul>
+
+<p>
+ The <code>argc</code> and <code>argv</code> are used to pass command line arguments to the NSH command.
+ Command line parameters are passed in a very standard way: <code>argv[0]</code> will be the name of the command, and <code>argv[1]</code> through <code>argv[argc-1]</code> are the additional arguments provided on the NSH command line.
+</p>
+<p>
+ The first parameter, <code>vtbl</code>, is special.
+ This is a pointer to session-specific state information.
+ You don't need to know the contents of the state information, but you do need to pass this <code>vtbl</code> argument when you interact with the NSH logic.
+ The only use you will need to make of the <code>vtbl</code> argument will be for outputting data to the console.
+ You don't use <code>printf()</code> within NSH commands.
+ Instead you would use:
+</p>
+<ul><pre>
+void nsh_output(FAR struct nsh_vtbl_s *vtbl, const char *fmt, &hellip;);
+</pre></ul>
+<p>
+ So if you only wanted to output &quot;Hello, World!&quot; on the console, then your whole command implementation might be:
+</p>
+<ul><pre>
+int cmd_mycmd(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
+{
+ nsh_output(vtbl, &quote;Hello, World!&quote;);
+ return 0;
+}
+</pre></ul>
+<p>
+ The prototype for the new command should be placed in <code>apps/examples/nshlib/nsh.h></code>.
+</p>
+
+<p>
+ <b>Adding You Command to the NSH Command Table</b>.
+ All of the commands support by NSH appear in a single table called:
+</p>
+<ul><pre>
+const struct cmdmap_s g_cmdmap[]
+</pre></ul>
+<p>
+ That table can be found in the file <code>apps/examples/nshlib/nsh_parse.c</code>.
+ The structure <code>cmdmap_s</code> is also defined in <code>apps/nshlib/nsh_parse.c</code>:
+</p>
+<ul><pre>
+struct cmdmap_s
+{
+ const char *cmd; /* Name of the command */
+ cmd_t handler; /* Function that handles the command */
+ uint8_t minargs; /* Minimum number of arguments (including command) */
+ uint8_t maxargs; /* Maximum number of arguments (including command) */
+ const char *usage; /* Usage instructions for 'help' command */
+};
+</pre></ul>
+<p>
+ This structure provides everything that you need to describe your command:
+ Its name (<code>cmd</code>), the function that handles the command (<code>cmd_mycmd()</code>), the minimum and maximum number of arguments needed by the command,
+ and a string describing the command line arguments.
+ That last string is what is printed when enter &quot;<code>nsh&gt help</code>&quot;.
+</p>
+<p>
+ So, for you sample commnd, you would add the following the to the <code>g_cmdmap[]</code> table:
+</p>
+<ul><pre>
+{ &quot;mycmd&quot;, cmd_mycmd, 1, 1, NULL },
+</pre></ul>
+
+<p>
+ This entry is particularly simply because <code>mycmd</code> is so simple.
+ Look at the other commands in <code>g_cmdmap[]</code> for more complex examples.
+</p>
+
+<table width ="100%">
+ <tr bgcolor="#e4e4e4">
+ <td>
+ <a name="custapps"><h2>4.3 NSH &quot;Built-In&quot; Applications</h2></a>
+ </td>
+ </tr>
+</table>
+
+<p>
+ <b>Overview.</b>
+ In addition to these commands that are a part of NSH, external programs can also be executed as NSH commands.
+ These external programs are called &quot;Built-In&quot; Applications for historic reasons.
+ That terminology is somewhat confusing because the actual NSH commands as described above are truly &quot;built-into&quot; NSH whereas these applications are really external to NuttX.
+</p>
+<p>
+ These applications are built-into NSH in the sense that they can be executed by simply typing the name of the application at the NSH prompt.
+ Built-in application support is enabled with the configuration option <code>CONFIG_NSH_BUILTIN_APPS</code>.
+ When this configuration option is set, you will also be able to see the built-in applications if you enter &quot;nsh&gt; help&quot;.
+ They will appear at the bottom of the list of NSH commands under:
+</p>
+
+<ul><pre>
+Builtin Apps:
+</pre></ul>
+<p>
+ Note that no detailed help information beyond the name of the built-in application is provided.
+</p>
+
+<h3>4.3.1 Named Applications</h3>
+
+<p>
+ <b>Overview.</b>
+ The underlying logic that supports the NSH built-in applications is called &quot;Named Applications&quot;.
+ The named application logic can be found at <code>apps/namedapp</code>.
+ This logic simply does the following:
+</p>
+
+<ol>
+ <li>
+ <p>
+ It supports registration mechanism so that named applications can dynamically register themselves at build time, and
+ </p>
+ <li>
+ <p>
+ Utility functions to look up, list, and execute the named applications.
+ </p>
+</ol>
+
+<p>
+ <b>Named Application Utility Functions</b>.
+ The utility functions exported by the named application logic are prototyped in <code>apps/include/apps.h</code>.
+ These utility functions include:
+</p>
+
+<ul>
+ <li>
+ <p>
+ <code>int namedapp_isavail(FAR const char *appname);</code>
+ Checks for availability of application registered as <code>appname</code> during build time.
+ </p>
+ <li>
+ <p>
+ <code>const char *namedapp_getname(int index);</code>
+ Returns a pointer to a name of built-in application pointed by the <code>index</code>.
+ This is the utility function that is used by NSH in order to list the available built-in applications when &quot;<code>nsh&gt; help</code>&quot; is entered.
+ </p>
+ <li>
+ <p>
+ <code>int exec_namedapp(FAR const char *appname, FAR const char **argv);</code>
+ Executes built-in named application registered during compile time.
+ This is the utility function used by NSH to execute the built-in application.
+ </p>
+</ul>
+
+<p>
+ <b>Autogenerated Header Files</b>.
+ Application entry points with their requirements are gathered together in two files when NuttX is first built:
+</p>
+<ol>
+ <li>
+ <p>
+ <code>apps/namedapp/namedapp_proto.h</code>:
+ Prototypes of application task entry points.
+ </p>
+ <li>
+ <p>
+ <code>apps/namedapp/namedapp_list.h</code>:
+ Application specific information and start-up requirements
+ </p>
+</ol>
+
+<p>
+ <b>Registration of Named Applications</b>.
+ The NuttX build occurs in several phases as different build targets are executed:
+ (1) <i>context</i> when the configuration is established,
+ (2) <i>depend </i>when target dependencies are generated, and
+ (3) <i>default</i> (<code>all</code>) when the normal compilation and link operations are performed.
+ Named application information is collected during the make <i>context</i> build phase.
+</p>
+
+<p>
+ An example application that can be &quot;built-in&quot; is be found in the <code>apps/examples/hello directory</code>.
+ Let's walk through this specific cause to illustrate the general way that built-in applications are created and how they register themselves so that they can be used from NSH.
+</p>
+
+<p>
+ <code><b>apps/examples/hello</code></b>.
+ The main routine for apps/examples/hello can be found in <code>apps/examples/hello/main.c</code>.
+ When <code>CONFIG_EXAMPLES_HELLO_BUILTIN</code> is defined, this main routine simplifies to:
+</p>
+<ul><pre>
+int hello_main(int argc, char *argv[])
+{
+ printf(&quot;Hello, World!!\n&quot;);
+ return 0;
+}
+</pre></ul>
+
+<p>
+ This is the built in function that will be registered during the <i>context</i> build phase of the NuttX build.
+ That registration is performed by logic in <code>apps/examples/hello/Makefile</code>.
+ But the build system gets to that logic through a rather tortuous path:
+</p>
+
+<ol>
+ <li>
+ <p>
+ The top-level context make target is in <code>nuttx/Makefile</code>.
+ All build targets depend upon the <i>context</i> build target.
+ For the <code>apps/</code> directory, this build target will execute the <i>context</i> target in the <code>apps/Makefile</code>.
+ </p>
+ <li>
+ <p>
+ The <code>apps/Makefile</code> will, in turn, execute the <i>context</i> targets in all of the configured sub-directories.
+ In our case will include the <code>Makefile</code> in <code>apps/examples</code>.
+ </p>
+ <li>
+ <p>
+ And finally, the <code>apps/examples/Makefile</code>will execute the <i>context</i> target in all configured <code>example</code>sub-directores, getting us finally to <code>apps/examples/Makefile</code> (which is covered below).</p>
+ <li>
+ <p>
+ At the conclusion of the <i>context</i> phase, the <code> apps/Makefile</code> will touch a file called <code>.context</code> in the <code>apps/</code> directory, preventing any further configurations during any subsequent <i>context</i> phase build attempts.
+ </p>
+</ol>
+
+<p>
+ <b>NOTE</b>:
+ Since this context build phase can only be executed one time, any subsequent configuration changes that you make will, then, not be reflected in the build sequence.
+ That is a common area of confusion.
+ Before you can instantiate the new configuration, you have to first get rid of the old configuration.
+ The most drastic way to this is:
+</p>
+<ul><pre>
+make distclean
+</pre></ul>
+<p>
+ But then you will have to re-configuration NuttX from scratch.
+ But if you only want to re-build the configuration in the <code>apps/</code> sub-directory, then there is a less labor-intensive way to do that.
+ The following NuttX make command will remove the configuration only from the <code>apps/</code> directory and will let you continue without re-configuring everything:
+</p>
+<ul><pre>
+make apps_distclean
+</pre></ul>
+
+<p>
+ Logic for the <code>context</code> target in <code>apps/examples/hello/Makefile</code> registers the <code>hello_main()</code> application in the <code>namedapp</code>'s <code>namedapp_proto.h</code>and <code>namedapp_list.h</code> files.
+ That logic that does that in <code>apps/examples/hello/Makefile</code> is abstracted below:
+</p>
+<ol>
+ <li>
+ <p>
+ First, the <code>Makefile</code> includes <code>apps/Make.defs</code>:
+ </p>
+<ul><pre>
+include $(APPDIR)/Make.defs
+</pre></ul>
+ <p>
+ This defines a macro called <code>REGISTER</code> that adds data to the <i>namedapp</i> header files:
+ </p>
+<ul><pre>
+define REGISTER
+ @echo &quot;Register: $1&quot;
+ @echo &quot;{ \&quot;$1\&quot;, $2, $3, $4 },&quot; &gt;&gt; &quot;$(APPDIR)/namedapp/namedapp_list.h&quot;
+ @echo &quot;EXTERN int $4(int argc, char *argv[]);&quot; &gt;&gt; &quot;$(APPDIR)/namedapp/namedapp_proto.h&quot;
+endef
+</pre></ul>
+ <p>
+ When this macro runs, you will see the output in the build &quot;<code>Register: hello</code>&quot;, that is a sure sign that the registration was successful.
+ </p>
+ <li>
+ <p>
+ The make file then defines the application name (<code>hello</code>), the task priority (default), and the stack size that will be allocated in the task runs (2Kb).
+ </p>
+<ul><pre>
+APPNAME = hello
+PRIORITY = SCHED_PRIORITY_DEFAULT
+STACKSIZE = 2048
+</pre></ul>
+
+ <li>
+ <p>
+ And finally, the <code>Makefile</code> invokes the <code>REGISTER</code> macro to added the <code>hello_main()</code> named application.
+ Then, when the system build completes, the <code>hello</code> command can be executed from the NSH command line.
+ When the <code>hello</code> command is executed, it will start the task with entry point <code>hello_main()</code> with the default priority and with a stack size of 2Kb.
+ </p>
+<ul><pre>
+.context:
+ifeq ($(CONFIG_EXAMPLES_HELLO_BUILTIN),y)
+ $(call REGISTER,$(APPNAME),$(PRIORITY),$(STACKSIZE),$(APPNAME)_main)
+ @touch $@
+endif
+</pre></ul>
+</ol>
+
+<p>
+ <b>Other Uses of Named Application.</b>
+ The primary purpose of named applications is to support command line execution of applications from NSH.
+ However, there are two other uses of named applications that should be mentioned.
+</p>
+
+<ol>
+ <li>
+ <p>
+ <b>Named Application Start-Up <code>main()</code> function</b>.
+ A named application can even be used as the main, start-up entry point into your embedded software.
+ When the user defines this option in the NuttX configuration file:
+ </p>
+<ul><pre>
+CONFIG_BUILTIN_APP_START=&lt;application name&gt;
+</pre></ul>
+ <p>
+ that application will be invoked immediately after system starts instead of the normal, default <code>user_start()</code> entry point.
+ Note that <code>&lt;application name&gt;</code> must be provided just as it would have been on the NSH command line.
+ For example, <code>hello</code> would result in <code>hello_main()</code> being started at power-up.
+ </p>
+ <p>
+ This option might be useful in some develop environments where you use NSH only during the debug phase, but want to eliminate NSH in the final product.
+ Setting <code>CONFIG_BUILTIN_APP_START</code> in this way will bypass NSH and execute your application just as if it were entered from the NSH command line.
+ </p>
+
+ <li>
+ <p><b><i>binfs</i></b>.
+ <i>binfs</i> is a tiny file system located at <code>apps/namedapp/binfs.c</code>.
+ This provides an alternative what of visualizing installed named applications.
+ Without <i>binfs</i>, you can see the installed named applications using the NSH help command.
+ <i>binfs</i> will create a tiny pseudo-file system mounted at <code>/bin</code>.
+ Using <i>binfs</i>, you can see the available named applications by listing the contents of <code>/bin</code> directory.
+ This gives some superficial Unix compatibility, but does not really add any new functionality.
+ </p>
+</ol>
+
+<h3>4.3.2 Synchronous Built-In Applications</h3>
+
+<p>
+ By default, built-in commands started from the NSH command line will run asynchronously with NSH.
+ If you want to force NSH to execute commands then wait for the command to execute, you can enable that feature by adding the following to the NuttX configuration file:
+</p>
+<ul><pre>
+CONFIG_SCHED_WAITPID=y
+</pre></ul>
+<p>
+ This configuration option enables support for the standard <code>waitpid()</code> RTOS interface.
+ When that interface is enabled, NSH will use it to wait, sleeping until the built-in application executes to completion.
+</p>
+<p>
+ Of course, even with <code>CONFIG_SCHED_WAITPID=y</code> defined, specific applications can still be forced to run asynchronously by adding the ampersand (&amp;) after the NSH command.
+</p>
+
+<h3>4.3.3 Application Configuration File</h3>
+
+<p>
+ <b>The appconfig File</b>.
+ A special configuration file is used to configure which applications are to be included in the build.
+ The source for this file is saved at <code>configs/&lt;board&gt;/&lt;configuration&gt;/appconfig</code>.
+ The existence of the <code>appconfig</code> file in the board configuration directory is sufficient to enable building of applications.
+</p>
+
+<p>
+ The <code>appconfig</code> file is copied into the <code>apps/</code> directory as <code>.config</code> when NuttX is configured.
+ <code>.config</code> is included by the top-level <code>apps/Makefile</code>.
+ As a minimum, this configuration file must define files to add to the <code>CONFIGURED_APPS</code> list like:
+</p>
+<ul><pre>
+CONFIGURED_APPS += examples/hello
+</pre></ul>
+
+<p>
+ <b>Changes in the Works</b>.
+ There are changes in the works that will obsolete the <code>appconfig</code> file.
+ These changes will implement an automated configuration system for NuttX.
+ One consequence of this new configuration system is that the <code>appconfig</code> file will become obsolete and will be replaced by a new mechanism for selecting applications.
+ This new mechanism is not yet available, but is dicussed here: <a href="http://tech.groups.yahoo.com/group/nuttx/message/1604">http://tech.groups.yahoo.com/group/nuttx/message/1604</a>.
+</p>
+
+<table width ="100%">
+ <tr bgcolor="#e4e4e4">
+ <td>
+ <a name="custinit"><h2>4.4 Customizing NSH Initialization</h2></a>
+ </td>
+ </tr>
+</table>
+
+<p>
+ <b>Ways to Customize NSH Initialization</b>.
+ There are three ways to customize the NSH start-up behavior.
+ Here they are presented in order of increasing difficulty:
+</p>
+
+<ol>
+ <li>
+ <p>
+ You can extend the initialization logic in <code>configs/stm3240g-eval/src/up_nsh.c</code>.
+ The logic there is called each time that NSH is started and is good place in particular for any device-related initialization.
+ </p>
+ <li>
+ <p>
+ You replace the sample code at <code>apps/examples/nsh/nsh_main.c</code> with whatever start-up logic that you want.
+ NSH is a library at <code>apps/nshlib</code>.
+ <code>apps.examplex/nsh</code> is just a tiny, example start-up function (<code>user_start()</code>) that that runs immediately and illustrates how to start NSH
+ If you want something else to run immediately then you can write your write your own custom <code>user_start()</code> function and then start other tasks from your custom <code>user_start()</code>.
+ </p>
+ <li>
+ <p>
+ NSH also supports a start-up script that executed when NSH first runs.
+ This mechanism has the advantage that the start-up script can contain any NSH commands and so can do a lot of work with very little coding.
+ The disadvantage is that is is considerably more complex to create the start-up script.
+ It is sufficiently complex that is deserves its own paragraph
+ </p>
+</ol>
+
+<h3>4.4.1 NuttShell Start up Scripts</h3>
+
+<p>
+ First of all you should look at <a href="#startupscript">NSH Start-Up Script</a> paragraph.
+ Most everything you need to know can be found there.
+ That information will be repeated and extended here for completeness.
+</p>
+
+<p>
+ <b>NSH Start-Up Script</b>.
+ NSH supports options to provide a start up script for NSH.
+ The start-up script contains any command support by NSH (i.e., that you see when you enter 'nsh&gt; help').
+ In general this capability is enabled with <code>CONFIG_NSH_ROMFSETC=y</code>, but has several other related configuration options as described with the <a href="#nshconfiguration">NSH-specific configuration settings</a> paragraph.
+ This capability also depends on:
+</p>
+
+<ul>
+ <li>
+ <p>
+ <code>CONFIG_DISABLE_MOUNTPOINT=n</code>.
+ If mount point support is disabled, then you cannot mount <i>any</i> file systems.
+ </p>
+ <li>
+ <p>
+ <code>CONFIG_NFILE_DESCRIPTORS &gt; 4</code>.
+ Of course you have to have file descriptions to use any thing in the file system.
+ </p>
+ <li>
+ <p>
+ <code>CONFIG_FS_ROMFS</code> enabled.
+ This option enables ROMFS file system support.
+ </p>
+</ul>
+
+<p>
+ <b>Default Start-Up Behavior</b>.
+ The implementation that is provided is intended to provide great flexibility for the use of Start-Up files.
+ This paragraph will discuss the general behavior when all of the configuration options are set to the default values.
+</p>
+<p>
+ In this default case, enabling <code>CONFIG_NSH_ROMFSETC</code> will cause NSH to behave as follows at NSH start-up time:
+</p>
+<ul>
+ <li>
+ <p>
+ NSH will create a read-only RAM disk (a ROM disk), containing a tiny ROMFS filesystem containing the following:
+ </p>
+<ul><pre>
+`--init.d/
+ `-- rcS
+</pre></ul>
+ <p>
+ Where <code>rcS</code> is the NSH start-up script.
+ </p>
+ <li>
+ <p>
+ NSH will then mount the ROMFS filesystem at <code>/etc</code>, resulting in:
+ </p>
+<ul><pre>
+|--dev/
+| `-- ram0
+`--etc/
+ `--init.d/
+ `-- rcS</PRE>
+</pre></ul>
+ <li>
+ <p>
+ By default, the contents of <code>rcS</code> script are:
+ </p>
+<ul><pre>
+# Create a RAMDISK and mount it at /tmp
+
+mkrd -m 1 -s 512 1024
+mkfatfs /dev/ram1
+mount -t vfat /dev/ram1 /tmp
+</pre></ul>
+ <li>
+ <p>
+ NSH will execute the script at <code>/etc/init.d/rcS</code> at start-up (before the first NSH prompt).
+ After execution of the script, the root FS will look like:
+ </p>
+<ul><pre>
+|--dev/
+| |-- ram0
+| `-- ram1
+|--etc/
+| `--init.d/
+| `-- rcS
+`--tmp/
+</pre></ul>
+</ul>
+
+<p>
+ <b>Example Configurations</b>.
+ Here are some configurations that have <code>CONFIG_NSH_ROMFSETC=y</code> in the NuttX configuration file.
+ They might provide useful examples:
+</p>
+<ul>
+ <code>configs/hymini-stm32v/nsh2<br>
+ configs/ntosd-dm320/nsh<br>
+ configs/sim/nsh<br>
+ configs/sim/nsh2<br>
+ configs/sim/nx<br>
+ configs/sim/nx11<br>
+ configs/sim/touchscreen<br>
+ configs/vsn/nsh</code>
+</ul>
+<p>
+ In most of these cases, the configuration sets up the <i>default</i> <code>/etc/init.d/rcS</code> script.
+ The default script is here: <code>apps/nshlib/rcS.template</code>.
+ (The funny values in the template like <code>XXXMKRDMINORXXX</code> get replaced via <code>sed</code> at build time).
+ This default configuration creates a ramdisk and mounts it at <code>/tmp</code> as discussed above.
+</p>
+<p>
+ If that default behavior is not what you want, then you can provide your own custom <code>rcS</code> script by defining <code>CONFIG_NSH_ARCHROMFS=y</code> in the configuration file.
+ The only example that uses a custom /<code>etc/init.d/rcS</code> file in the NuttX source tree is this one: <code>configs/vsn/nsh</code>.
+ The <code>configs/vsn/nsh/defconfig</code> file also has this definition:
+</p>
+<ul><code>CONFIG_NSH_ARCHROMFS=y</code> -- Support an architecture specific ROMFS file.</ul>
+
+<p>
+ <b>Modifying the ROMFS Image</b>.
+ The contents of the <code>/etc</code> directory are retained in the file <code>apps/nshlib/nsh_romfsimg.h</code> OR, if <code>CONFIG_NSH_ARCHROMFS</code> is defined, <code>include/arch/board/rcs.template</code>.
+ In order to modify the start-up behavior, there are three things to study:
+</p>
+
+<ol>
+ <li>
+ <p>
+ <b>Configuration Options.</b>
+ The additional <code>CONFIG_NSH_ROMFSETC</code> configuration options discussed with the other <a href="#nshconfiguration">NSH-specific configuration settings</a>.
+ </p>
+ <li>
+ <p>
+ <b><code>tools/mkromfsimg.sh</code> Script</b>.
+ The script <code>tools/mkromfsimg.sh</code> creates <code>nsh_romfsimg.h</code>.
+ It is not automatically executed.
+ If you want to change the configuration settings associated with creating and mounting the <code>/tmp</code> directory, then it will be necessary to re-generate this header file using the <code>tools/mkromfsimg.sh</code> script.
+ </p>
+ <p>
+ The behavior of this script depends upon several things:
+ </p>
+ <ol>
+ <li>
+ <p>
+ The configuration settings then installed configuration.
+ </p>
+ <li>
+ <p>
+ The <code>genromfs</code> tool(available from <a href="http://romfs.sourceforge.net/">http://romfs.sourceforge.net</a>) or included within the NuttX buildroot toolchain.
+ There is a snapshot here: <code>misc/tools/genromfs-0.5.2.tar.gz</code>.
+ </p>
+ <li>
+ <p>
+ The <code>xxd</code> tool that is used to generate the C header files (xxd is a normal part of a complete Linux or Cygwin installation, usually as part of the <code>vi</code> package).
+ </p>
+ <li>
+ <p>
+ The file <code>apps/nshlib/rcS.template</code> (OR, if <code>CONFIG_NSH_ARCHROMFS</code> is defined <code>include/arch/board/rcs.template</code>.
+ </p>
+ </ol>
+ <li>
+ <p>
+ <code><b>rcS.template</b></code>.
+ The file <code>apps/nshlib/rcS.template</code> contains the general form of the <code>rcS</code> file; configured values are plugged into this template file to produce the final <code>rcS</code> file.
+ </p>
+</ol>
+
+<p>
+ <b><code>rcS.template</code></b>.
+ The default <code>rcS.template</code>, </code><code>apps/nshlib/rcS.template</code>, generates the standard, default <code>apps/nshlib/nsh_romfsimg.h</code> file.
+</p>
+
+<p>
+ If <code>CONFIG_NSH_ARCHROMFS</code> is defined in the NuttX configuration file, then a custom, board-specific <code>nsh_romfsimg.h</code> file residing in <code>configs/&lt;board&gt;/include</code>will be used.
+ NOTE when the OS is configured, <code>include/arch/board</code> will be linked to <code>configs/&lt;board&gt;/include</code>.
+</p>
+
+<p>
+ As mention above, the only example that uses a custom <code>/etc/init.d/rcS</code> file in the NuttX source tree is this one: <code>configs/vsn/nsh</code>.
+ The custom script for the <code>configs/vsn</code> case is located at <code>configs/vsn/include/rcS.template</code>.
+</p>
+
+<p>
+ All of the startup-behavior is contained in <code>rcS.template</code>.
+ The role of <code>mkromfsimg.sh</code> script is to (1) apply the specific configuration settings to <code>rcS.template</code> to create the final <code>rcS</code>, and (2) to generate the header file <code>nsh_romfsimg.h</code> containg the ROMFS file system image.
+ To do this, <code>mkromfsimg.sh</code> uses two tools that must be installed in your system:
+</p>
+<ol>
+ <li>
+ <p>
+ The <code>genromfs</code> tool that is used to generate the ROMFS file system image.</code>
+ </p>
+ <li>
+ <p>
+ The <code>xxd</code> tool that is used to create the C header file.
+ </p>
+</ol>
+<p>
+ You can find the generated ROMFS file system for the <code>configs/vsn</code> case here: <code>configs/vsn/include/rcS.template</code>
+</p>
+
+<table width ="100%">
+ <tr bgcolor="#e4e4e4">
+ <td>
<a name="index"><h1>Index</h1></a>
</td>
</tr>
@@ -2657,15 +3502,28 @@ nsh>
<ul>
<li><a href="#builtinvars"><code>$?</code></a></li>
<li><a href="#cmdtest"><code>[</code></a></li>
+ <li><a href="#custoncmds">Adding NSH commands<</a></li>
+ <li><a href="#custapps"><code>appconfig</code></a></li>
+ <li><a href="#custapps">Application configuration file (<code>appconfig</code>)</a></li>
+ <li><a href="#custapps">Autogenerated header files</a></li>
<li><a href="#cmdoverview">Background commands</a></li>
<li><a href="#cmdoverview">Background command priority</a></li>
+ <li><a href="#custapps"><code>binfs</code></a></li>
+ <li><a href="#custapps">Built-In applications</a></li>
<li><a href="#builtinvars">Built-in variables</a></li>
<li><a href="#cmdcat"><code>cat</code></a></li>
<li><a href="#cmdcd"><code>cd</code></a></li>
<li><a href="#commands">Command summaries</a></li>
+ <li><a href="#custoncmds">Command table</a></li>
<li><a href="#conditional">Conditional command execution</a></li>
+ <li><a href="#custapps"><code>CONFIG_BUILTIN_APP_START</code></a></li>
+ <li><a href="#custinit"><code>CONFIG_DISABLE_MOUNTPOINT</code></a></li>
+ <li><a href="#custinit"><code>CONFIG_FS_ROMFS</code></a></li>
+ <li><a href="#custinit"><code>CONFIG_NFILE_DESCRIPTORS</code></a></li>
<li><a href="#nshconfiguration"><code>CONFIG_NSH_ARCHINIT</code></a></li>
+ <li><a href="#custinit"><code>CONFIG_NSH_ARCHROMFS</code></a></li>
<li><a href="#nshconfiguration"><code>CONFIG_NSH_BUILTIN_APPS</code></a></li>
+ <li><a href="#custapps"><code>CONFIG_NSH_BUILTIN_APPS</code></a></li>
<li><a href="#nshconfiguration"><code>CONFIG_NSH_CONSOLE</code></a></li>
<li><a href="#nshconfiguration"><code>CONFIG_NSH_DHCPC</code></a></li>
<li><a href="#nshconfiguration"><code>CONFIG_NSH_DISABLEBG</code></a></li>
@@ -2685,6 +3543,7 @@ nsh>
<li><a href="#nshconfiguration"><code>CONFIG_NSH_NOMAC</code></a></li>
<li><a href="#nshconfiguration"><code>CONFIG_NSH_ROMFSDEVNO</code></a></li>
<li><a href="#nshconfiguration"><code>CONFIG_NSH_ROMFSETC</code></a></li>
+ <li><a href="#custinit"><code>CONFIG_NSH_ROMFSETC</code></a></li>
<li><a href="#nshconfiguration"><code>CONFIG_NSH_ARCHROMFS</code></a></li>
<li><a href="#nshconfiguration"><code>CONFIG_NSH_ROMFSMOUNTPT</code></a></li>
<li><a href="#nshconfiguration"><code>CONFIG_NSH_ROMFSSECTSIZE</code></a></li>
@@ -2698,27 +3557,35 @@ nsh>
<li><a href="#nshconfiguration"><code>CONFIG_NSH_USBDEV_TRACEINIT</code></a></li>
<li><a href="#nshconfiguration"><code>CONFIG_NSH_USBDEV_TRACEINTERRUPTS</code></a></li>
<li><a href="#nshconfiguration"><code>CONFIG_NSH_USBDEV_TRACETRANSFERS</code></a></li>
+ <li><a href="#custapps"><code>CONFIG_SCHED_WAITPID</code></a></li>
+ <li><a href="#custapps"><code>CONFIGURED_APPS</code></a></li>
<li><a href="#configuration">Configuration settings</a></li>
<li><a href="#cmddependencies">Configuration settings, command dependencies</a></li>
<li><a href="#nshconfiguration">Configuration settings, NSH-specific</a></li>
<li><a href="#cmdcp"><code>cp</code></a></li>
<li><a href="#currentwd">Current working directory</a></li>
+ <li><a href="#customizingnsh">Customizing NSH</a></li>
+ <li><a href="#custinit">Customizing NSH initialization</a></li>
<li><a href="#cmddate"><code>date</code></a></li>
<li><a href="#cmddd"><code>dd</code></a></li>
<li><a href="#cmddf"><code>df</code></a></li>
-</ul></td>
-<td></ul>
<li><a href="#cmdecho"><code>echo</code></a></li>
<li><a href="#environvars">Environment Variables</a></li>
<li><a href="#startupscript"><code>/etc/init.d/rcS</code></a>
<li><a href="#cmdexec"><code>exec</code></a></li>
+ <li><a href="#custapps"><code>exec_namedapp()</code></a></li>
+</ul></td>
+<td></ul>
<li><a href="#cmdexit"><code>exit</code></a></li>
<li><a href="#cmdfree"><code>free</code></a></li>
+ <li><a href="#custoncmds"><code>g_cmdmap</code></a></li>
+ <li><a href="#custinit"><code>genromfs</code></a></li>
<li><a href="#cmdget"><code>get</code></a></li>
<li><a href="#frontend">Greeting</a></li>
<li><a href="#cmdhelp"><code>help</code></a></li>
<li><a href="#conditional"><code>if-then[-else]-fi</code></a></li>
<li><a href="#cmdifconfig"><code>ifconfig</code></a></li>
+ <li><a href="#custonshlib">Initialization sequence</a></li>
<li><a href="#cmdkill"><code>kill</code></a></li>
<li><a href="#cmdlosetup"><code>losetup</code></a></li>
<li><a href="#cmdls">ls</code></a></li>
@@ -2729,10 +3596,28 @@ nsh>
<li><a href="#cmdmkfatfs"><code>mkfatfs</code></a></li>
<li><a href="#cmdmkfifo"><code>mkfifo</code></a></li>
<li><a href="#cmdmkrd"><code>mkrd</code></a></li>
+ <li><a href="#custinit"><code>mkromfsimg.sh</code></a></li>
<li><a href="#cmdmount"><code>mount</code></a></li>
<li><a href="#cmdmv"><code>mv</code></a></li>
+ <li><a href="#custapps">Named application start-up <code>main()</code></a></li>
+ <li><a href="#custapps">Named applications</a></li>
+ <li><a href="#custapps"><code>namedapp_getname()</code></a></li>
+ <li><a href="#custapps"><code>namedapp_isavail()</code></a></li>
+ <li><a href="#custapps"><code>namedapp_list.h</code></a></li>
+ <li><a href="#custapps"><code>namedapp_proto.h</code></a></li>
<li><a href="#cmdnfsmount"><code>nfsmount</code></a></li>
<li><a href="#cmdoverview"><code>nice</code></a></li>
+ <li><a href="#custonshlib">NSH library (<code>nshlib</code>)</a></li>
+ <li><a href="#custonshlib"><code>nsh_archinitialize()</code></a></li>
+ <li><a href="#custonshlib"><code>nsh_consolemain()</code></a></li>
+ <li><a href="#custonshlib"><code>nsh_initialize()</code></a></li>
+ <li><a href="#custonshlib"><code>nsh_main()</code></a></li>
+ <li><a href="#custinit"><code>nsh_main.c</code></a></li>
+ <li><a href="#custonshlib"><code>nsh_netinit()</code></a></li>
+ <li><a href="#custoncmds"><code>nsh_output()</code></a></li>
+ <li><a href="#custonshlib"><code>nsh_romfsetc()</code></a></li>
+ <li><a href="#custonshlib"><code>nsh_telnetstart()</code></a></li>
+ <li><a href="#custonshlib"><code>nshlib</code></a></li>
<li><a href="#environvars"><code>OLDPWD</code></a></li>
<li><a href="#overview">Overview</a></li>
<li><a href="#cmdping"><code>ping</code></a></li>
@@ -2741,20 +3626,30 @@ nsh>
<li><a href="#cmdput"><code>put</code></a></li>
<li><a href="#cmdpwd"><code>pwd</code></a></li>
<li><a href="#environvars"><code>PWD</code></a></li>
+ <li><a href="#custinit"><code>rcS.template</code></a></li>
<li><a href="#cmdoverview">Re-directed commands</a></li>
+ <li><a href="#custapps">Registration of named applications</a></li>
<li><a href="#cmdrm"><code>rm</code></a></li>
<li><a href="#cmdrmdir"><code>rmdir</code></a></li>
+ <li><a href="#custinit">ROMFS, Modifying the ROMFS image</a></li>
<li><a href="#cmdset"><code>set</code></a></li>
<li><a href="#cmdsh"><code>sh</code></a></li>
<li><a href="#cmdoverview">Simple commands</a></li>
<li><a href="#cmdsleep"><code>sleep</code></a></li>
- <li><a href="#startupscript">start-up script</a>
+ <li><a href="#custinit">Start-up, Default behavior</a></li>
+ <li><a href="#startupscript">Start-up script</a>
+ <li><a href="#custinit">Start-up script</a></li>
+ <li><a href="#custapps">Synchronous built-in applications</a></li>
<li><a href="#cmdtest"><code>test</code></a></li>
<li><a href="#cmdunmount"><code>umount</code></a></li>
<li><a href="#cmdunset"><code>unset</code></a></li>
+ <li><a href="#custonshlib"><code>up_cxxinitialize()</code></a></li>
+ <li><a href="#custinit"><code>up_nsh.c</code></a></li>
<li><a href="#cmdusleep"><code>usleep</code></a></li>
+ <li><a href="#custapps"><code>waitpid()</code></a></li>
<li><a href="#cmdwget"><code>wget</code></a></li>
<li><a href="#cmdxd"><code>xd</code></a></li>
+ <li><a href="#custinit"><code>xxd</code></a></li>
</ul></td>
</tr></table>
diff --git a/nuttx/Documentation/NuttX.html b/nuttx/Documentation/NuttX.html
index 64f02f081..ed994d69b 100644
--- a/nuttx/Documentation/NuttX.html
+++ b/nuttx/Documentation/NuttX.html
@@ -260,14 +260,6 @@
<td><br></td>
<td>
<p>
- <li>Modular, micro-kernel</li>
- </p>
-</tr>
-
-<tr>
- <td><br></td>
- <td>
- <p>
<li>Fully pre-emptible.</li>
</p>
</tr>
@@ -365,7 +357,7 @@
<tr>
<td><br></td>
<td>
- <p>System logging.</li>
+ <li>System logging.</li>
</p>
</tr>
@@ -677,7 +669,7 @@
<td><br></td>
<td>
<p>
- <li>USB device controller drivers available for the NXP LPC17xx, LPC214x, LPC313x, STMicro STM32 and TI DM320.</li>
+ <li>USB device controller drivers available for the PIC32, NXP LPC17xx, LPC214x, LPC313x, LPC43xx, STMicro STM32 and TI DM320.</li>
</p>
</tr>
<tr>
@@ -776,7 +768,7 @@
<td><br></td>
<td>
<p>
- <li>Support for Analog-to-Digital conversion (ADC) and Digital-to-Analog conversion (DAC).</li>
+ <li>Support for Analog-to-Digital conversion (ADC), Digital-to-Analog conversion (DAC), multiplexers, and amplifiers.</li>
</p>
</tr>
diff --git a/nuttx/TODO b/nuttx/TODO
index 274545239..7ffcb9ca8 100644
--- a/nuttx/TODO
+++ b/nuttx/TODO
@@ -1,4 +1,4 @@
-NuttX TODO List (Last updated August 3, 2012)
+NuttX TODO List (Last updated August 7, 2012)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This file summarizes known NuttX bugs, limitations, inconsistencies with
@@ -22,7 +22,7 @@ nuttx/
(1) Documentation (Documentation/)
(6) Build system / Toolchains
(5) Linux/Cywgin simulation (arch/sim)
- (5) ARM (arch/arm/)
+ (6) ARM (arch/arm/)
(1) ARM/C5471 (arch/arm/src/c5471/)
(3) ARM/DM320 (arch/arm/src/dm320/)
(2) ARM/i.MX (arch/arm/src/imx/)
@@ -919,7 +919,7 @@ o Build system
Status: Open
Priority: Low.
- Title: KERNEL BUILD MODE ISSUES
+ Title: KERNEL BUILD MODE ISSUES - GRAPHICS/NSH PARTITIONING.
Description: In the kernel build mode (where NuttX is built as a monlithic
kernel and user code must trap into the protected kernel via
syscalls), the single user mode cannot be supported. In this
@@ -928,6 +928,9 @@ o Build system
this case, most of the user end functions in graphics/nxmu
must be moved to lib/nx and those functions must be built into
libuser.a to be linked with the user-space code.
+ A similar issue exists in NSH that uses some internal OS
+ interfaces that would not be available in a kernel build
+ (such as foreach_task, foreach_mountpoint, etc.).
Status: Open
Priority: Low -- the kernel build configuration is not fully fielded
yet.
@@ -1077,7 +1080,21 @@ o ARM (arch/arm/)
Priority: Low. The conditions of continous interrupts is really the problem.
If your design needs continous interrupts like this, please try
the above change and, please, submit a patch with the working fix.
-
+
+ Title: KERNEL MODE ISSUES - HANDLERS
+ Description: The is a design flaw in the ARM/Cortex trap handlers. Currently,
+ they try to process the SYSCALL within the trap handler. That
+ cannot work. There are two possibilities to fix this.
+ 1) Just enable interrupts in the trap handler and make sure that
+ that sufficient protection is in place to handler the nested
+ interrupts, or
+ 3) Return from the exception via a trampoline (such as is
+ currently done for signal handlers). In the trampoline,
+ the trap would processed in supervisor mode with interrupts
+ enabled.
+ Status: Open
+ Priority: medium-high.
+
o ARM/C5471 (arch/arm/src/c5471/)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/nuttx/arch/arm/src/lpc43xx/Make.defs b/nuttx/arch/arm/src/lpc43xx/Make.defs
index 4b75afffc..cc8de3b32 100644
--- a/nuttx/arch/arm/src/lpc43xx/Make.defs
+++ b/nuttx/arch/arm/src/lpc43xx/Make.defs
@@ -117,3 +117,10 @@ ifeq ($(CONFIG_LPC43_DAC),y)
CHIP_CSRCS += lpc43_adc.c
endif
endif
+
+ifeq ($(CONFIG_LPC43_USB0),y)
+ifeq ($(CONFIG_USBDEV),y)
+CHIP_CSRCS += lpc31_usb0dev.c
+endif
+endif
+
diff --git a/nuttx/arch/arm/src/lpc43xx/lpc43_usb0dev.c b/nuttx/arch/arm/src/lpc43xx/lpc43_usb0dev.c
new file mode 100644
index 000000000..40233ec0f
--- /dev/null
+++ b/nuttx/arch/arm/src/lpc43xx/lpc43_usb0dev.c
@@ -0,0 +1,2663 @@
+/*******************************************************************************
+ * arch/arm/src/lpc43xx/lpc43_usbdev.c
+ *
+ * Copyright (C) 2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * Part of the NuttX OS and based, in part, on the LPC31xx USB driver:
+ *
+ * Authors: David Hewson
+ * Gregory Nutt <spudmonkey@racsa.co.cr>
+ *
+ * Which, in turn, was based on the LPC2148 USB driver:
+ *
+ * Copyright (C) 2010-2012 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *******************************************************************************/
+
+/*******************************************************************************
+ * Included Files
+ *******************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/usb/usb.h>
+#include <nuttx/usb/usbdev.h>
+#include <nuttx/usb/usbdev_trace.h>
+
+#include <arch/irq.h>
+#include <arch/board/board.h>
+
+#include "chip.h"
+#include "up_arch.h"
+#include "up_internal.h"
+
+#include "lpc43_usbotg.h"
+#include "lpc43_evntrtr.h"
+#include "lpc43_syscreg.h"
+
+/*******************************************************************************
+ * Definitions
+ *******************************************************************************/
+
+/* Configuration ***************************************************************/
+
+#ifndef CONFIG_USBDEV_EP0_MAXSIZE
+# define CONFIG_USBDEV_EP0_MAXSIZE 64
+#endif
+
+#ifndef CONFIG_USBDEV_MAXPOWER
+# define CONFIG_USBDEV_MAXPOWER 100 /* mA */
+#endif
+
+/* Extremely detailed register debug that you would normally never want
+ * enabled.
+ */
+
+#undef CONFIG_LPC43_USBDEV_REGDEBUG
+
+/* Enable reading SOF from interrupt handler vs. simply reading on demand. Probably
+ * a bad idea... Unless there is some issue with sampling the SOF from hardware
+ * asynchronously.
+ */
+
+#ifdef CONFIG_LPC43_USBDEV_FRAME_INTERRUPT
+# define USB_FRAME_INT USBDEV_USBINTR_SRE
+#else
+# define USB_FRAME_INT 0
+#endif
+
+#ifdef CONFIG_DEBUG
+# define USB_ERROR_INT USBDEV_USBINTR_UEE
+#else
+# define USB_ERROR_INT 0
+#endif
+
+/* Debug ***********************************************************************/
+
+/* Trace error codes */
+
+#define LPC43_TRACEERR_ALLOCFAIL 0x0001
+#define LPC43_TRACEERR_BADCLEARFEATURE 0x0002
+#define LPC43_TRACEERR_BADDEVGETSTATUS 0x0003
+#define LPC43_TRACEERR_BADEPNO 0x0004
+#define LPC43_TRACEERR_BADEPGETSTATUS 0x0005
+#define LPC43_TRACEERR_BADEPTYPE 0x0006
+#define LPC43_TRACEERR_BADGETCONFIG 0x0007
+#define LPC43_TRACEERR_BADGETSETDESC 0x0008
+#define LPC43_TRACEERR_BADGETSTATUS 0x0009
+#define LPC43_TRACEERR_BADSETADDRESS 0x000a
+#define LPC43_TRACEERR_BADSETCONFIG 0x000b
+#define LPC43_TRACEERR_BADSETFEATURE 0x000c
+#define LPC43_TRACEERR_BINDFAILED 0x000d
+#define LPC43_TRACEERR_DISPATCHSTALL 0x000e
+#define LPC43_TRACEERR_DRIVER 0x000f
+#define LPC43_TRACEERR_DRIVERREGISTERED 0x0010
+#define LPC43_TRACEERR_EP0SETUPSTALLED 0x0011
+#define LPC43_TRACEERR_EPINNULLPACKET 0x0012
+#define LPC43_TRACEERR_EPOUTNULLPACKET 0x0013
+#define LPC43_TRACEERR_INVALIDCTRLREQ 0x0014
+#define LPC43_TRACEERR_INVALIDPARMS 0x0015
+#define LPC43_TRACEERR_IRQREGISTRATION 0x0016
+#define LPC43_TRACEERR_NOEP 0x0017
+#define LPC43_TRACEERR_NOTCONFIGURED 0x0018
+#define LPC43_TRACEERR_REQABORTED 0x0019
+
+/* Trace interrupt codes */
+
+#define LPC43_TRACEINTID_USB 0x0001
+#define LPC43_TRACEINTID_CLEARFEATURE 0x0002
+#define LPC43_TRACEINTID_DEVGETSTATUS 0x0003
+#define LPC43_TRACEINTID_DEVRESET 0x0004
+#define LPC43_TRACEINTID_DISPATCH 0x0005
+#define LPC43_TRACEINTID_EP0COMPLETE 0x0006
+#define LPC43_TRACEINTID_EP0NAK 0x0007
+#define LPC43_TRACEINTID_EP0SETUP 0x0008
+#define LPC43_TRACEINTID_EPGETSTATUS 0x0009
+#define LPC43_TRACEINTID_EPIN 0x000a
+#define LPC43_TRACEINTID_EPINQEMPTY 0x000b
+#define LPC43_TRACEINTID_EP0INSETADDRESS 0x000c
+#define LPC43_TRACEINTID_EPOUT 0x000d
+#define LPC43_TRACEINTID_EPOUTQEMPTY 0x000e
+#define LPC43_TRACEINTID_EP0SETUPSETADDRESS 0x000f
+#define LPC43_TRACEINTID_FRAME 0x0010
+#define LPC43_TRACEINTID_GETCONFIG 0x0011
+#define LPC43_TRACEINTID_GETSETDESC 0x0012
+#define LPC43_TRACEINTID_GETSETIF 0x0013
+#define LPC43_TRACEINTID_GETSTATUS 0x0014
+#define LPC43_TRACEINTID_IFGETSTATUS 0x0015
+#define LPC43_TRACEINTID_SETCONFIG 0x0016
+#define LPC43_TRACEINTID_SETFEATURE 0x0017
+#define LPC43_TRACEINTID_SUSPENDCHG 0x0018
+#define LPC43_TRACEINTID_SYNCHFRAME 0x0019
+
+/* Hardware interface **********************************************************/
+
+/* This represents a Endpoint Transfer Descriptor - note these must be 32 byte aligned */
+struct lpc43_dtd_s
+{
+ volatile uint32_t nextdesc; /* Address of the next DMA descripto in RAM */
+ volatile uint32_t config; /* Misc. bit encoded configuration information */
+ uint32_t buffer0; /* Buffer start address */
+ uint32_t buffer1; /* Buffer start address */
+ uint32_t buffer2; /* Buffer start address */
+ uint32_t buffer3; /* Buffer start address */
+ uint32_t buffer4; /* Buffer start address */
+ uint32_t xfer_len; /* Software only - transfer len that was queued */
+};
+
+/* DTD nextdesc field*/
+#define DTD_NEXTDESC_INVALID (1 << 0) /* Bit 0 : Next Descriptor Invalid */
+
+/* DTD config field */
+#define DTD_CONFIG_LENGTH(n) ((n) << 16) /* Bits 16-31 : Total bytes to transfer */
+#define DTD_CONFIG_IOC (1 << 15) /* Bit 15 : Interrupt on Completion */
+#define DTD_CONFIG_MULT_VARIABLE (0 << 10) /* Bits 10-11 : Number of packets executed per transacation descriptor (override) */
+#define DTD_CONFIG_MULT_NUM(n) ((n) << 10)
+#define DTD_CONFIG_ACTIVE (1 << 7) /* Bit 7 : Status Active */
+#define DTD_CONFIG_HALTED (1 << 6) /* Bit 6 : Status Halted */
+#define DTD_CONFIG_BUFFER_ERROR (1 << 5) /* Bit 6 : Status Buffer Error */
+#define DTD_CONFIG_TRANSACTION_ERROR (1 << 3) /* Bit 3 : Status Transaction Error */
+
+/* This represents a queue head - not these must be aligned to a 2048 byte boundary */
+struct lpc43_dqh_s
+{
+ uint32_t capability; /* Endpoint capability */
+ uint32_t currdesc; /* Current dTD pointer */
+ struct lpc43_dtd_s overlay; /* DTD overlay */
+ volatile uint32_t setup[2]; /* Set-up buffer */
+ uint32_t gap[4]; /* align to 64 bytes */
+};
+
+/* DQH capability field */
+#define DQH_CAPABILITY_MULT_VARIABLE (0 << 30) /* Bits 30-31 : Number of packets executed per transaction descriptor */
+#define DQH_CAPABILITY_MULT_NUM(n) ((n) << 30)
+#define DQH_CAPABILITY_ZLT (1 << 29) /* Bit 29 : Zero Length Termination Select */
+#define DQH_CAPABILITY_MAX_PACKET(n) ((n) << 16) /* Bits 16-29 : Maximum packet size of associated endpoint (<1024) */
+#define DQH_CAPABILITY_IOS (1 << 15) /* Bit 15 : Interrupt on Setup */
+
+/* Endpoints ******************************************************************/
+
+/* Number of endpoints */
+#define LPC43_NLOGENDPOINTS (4) /* ep0-3 */
+#define LPC43_NPHYSENDPOINTS (8) /* x2 for IN and OUT */
+
+/* Odd physical endpoint numbers are IN; even are OUT */
+#define LPC43_EPPHYIN(epphy) (((epphy)&1)!=0)
+#define LPC43_EPPHYOUT(epphy) (((epphy)&1)==0)
+
+#define LPC43_EPPHYIN2LOG(epphy) (((uint8_t)(epphy)>>1)|USB_DIR_IN)
+#define LPC43_EPPHYOUT2LOG(epphy) (((uint8_t)(epphy)>>1)|USB_DIR_OUT)
+
+/* Endpoint 0 is special... */
+#define LPC43_EP0_OUT (0)
+#define LPC43_EP0_IN (1)
+
+/* Each endpoint has somewhat different characteristics */
+#define LPC43_EPALLSET (0xff) /* All endpoints */
+#define LPC43_EPOUTSET (0x55) /* Even phy endpoint numbers are OUT EPs */
+#define LPC43_EPINSET (0xaa) /* Odd endpoint numbers are IN EPs */
+#define LPC43_EPCTRLSET (0x03) /* EP0 IN/OUT are control endpoints */
+#define LPC43_EPINTRSET (0xa8) /* Interrupt endpoints */
+#define LPC43_EPBULKSET (0xfc) /* Bulk endpoints */
+#define LPC43_EPISOCSET (0xfc) /* Isochronous endpoints */
+
+/* Maximum packet sizes for endpoints */
+#define LPC43_EP0MAXPACKET (64) /* EP0 max packet size (1-64) */
+#define LPC43_BULKMAXPACKET (512) /* Bulk endpoint max packet (8/16/32/64/512) */
+#define LPC43_INTRMAXPACKET (1024) /* Interrupt endpoint max packet (1 to 1024) */
+#define LPC43_ISOCMAXPACKET (512) /* Acutally 1..1023 */
+
+/* The address of the endpoint control register */
+#define LPC43_USBDEV_ENDPTCTRL(epphy) (LPC43_USBDEV_ENDPTCTRL0 + ((epphy)>>1)*4)
+
+/* Endpoint bit position in SETUPSTAT, PRIME, FLUSH, STAT, COMPLETE registers */
+#define LPC43_ENDPTSHIFT(epphy) (LPC43_EPPHYIN(epphy) ? (16 + ((epphy) >> 1)) : ((epphy) >> 1))
+#define LPC43_ENDPTMASK(epphy) (1 << LPC43_ENDPTSHIFT(epphy))
+#define LPC43_ENDPTMASK_ALL 0x000f000f
+
+/* Request queue operations ****************************************************/
+
+#define lpc43_rqempty(ep) ((ep)->head == NULL)
+#define lpc43_rqpeek(ep) ((ep)->head)
+
+/*******************************************************************************
+ * Private Types
+ *******************************************************************************/
+
+/* A container for a request so that the request may be retained in a list */
+
+struct lpc43_req_s
+{
+ struct usbdev_req_s req; /* Standard USB request */
+ struct lpc43_req_s *flink; /* Supports a singly linked list */
+};
+
+/* This is the internal representation of an endpoint */
+
+struct lpc43_ep_s
+{
+ /* Common endpoint fields. This must be the first thing defined in the
+ * structure so that it is possible to simply cast from struct usbdev_ep_s
+ * to struct lpc43_ep_s.
+ */
+
+ struct usbdev_ep_s ep; /* Standard endpoint structure */
+
+ /* LPC43XX-specific fields */
+
+ struct lpc43_usbdev_s *dev; /* Reference to private driver data */
+ struct lpc43_req_s *head; /* Request list for this endpoint */
+ struct lpc43_req_s *tail;
+ uint8_t epphy; /* Physical EP address */
+ uint8_t stalled:1; /* 1: Endpoint is stalled */
+};
+
+/* This structure retains the state of the USB device controller */
+
+struct lpc43_usbdev_s
+{
+ /* Common device fields. This must be the first thing defined in the
+ * structure so that it is possible to simply cast from struct usbdev_s
+ * to struct lpc43_usbdev_s.
+ */
+
+ struct usbdev_s usbdev;
+
+ /* The bound device class driver */
+
+ struct usbdevclass_driver_s *driver;
+
+ /* LPC43XX-specific fields */
+
+ uint8_t ep0state; /* State of certain EP0 operations */
+ uint8_t ep0buf[64]; /* buffer for EP0 short transfers */
+ uint8_t paddr; /* Address assigned by SETADDRESS */
+ uint8_t stalled:1; /* 1: Protocol stalled */
+ uint8_t selfpowered:1; /* 1: Device is self powered */
+ uint8_t paddrset:1; /* 1: Peripheral addr has been set */
+ uint8_t attached:1; /* 1: Host attached */
+ uint32_t softprio; /* Bitset of high priority interrupts */
+ uint32_t epavail; /* Bitset of available endpoints */
+#ifdef CONFIG_LPC43_USBDEV_FRAME_INTERRUPT
+ uint32_t sof; /* Last start-of-frame */
+#endif
+
+ /* The endpoint list */
+ struct lpc43_ep_s eplist[LPC43_NPHYSENDPOINTS];
+};
+
+#define EP0STATE_IDLE 0 /* Idle State, leave on receiving a setup packet or epsubmit */
+#define EP0STATE_SETUP_OUT 1 /* Setup Packet received - SET/CLEAR */
+#define EP0STATE_SETUP_IN 2 /* Setup Packet received - GET */
+#define EP0STATE_SHORTWRITE 3 /* Short write without a usb_request */
+#define EP0STATE_WAIT_NAK_OUT 4 /* Waiting for Host to illicit status phase (GET) */
+#define EP0STATE_WAIT_NAK_IN 5 /* Waiting for Host to illicit status phase (SET/CLEAR) */
+#define EP0STATE_WAIT_STATUS_OUT 6 /* Wait for status phase to complete */
+#define EP0STATE_WAIT_STATUS_IN 7 /* Wait for status phase to complete */
+#define EP0STATE_DATA_IN 8
+#define EP0STATE_DATA_OUT 9
+
+/*******************************************************************************
+ * Private Function Prototypes
+ *******************************************************************************/
+
+/* Register operations ********************************************************/
+
+#if defined(CONFIG_LPC43_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG)
+static uint32_t lpc43_getreg(uint32_t addr);
+static void lpc43_putreg(uint32_t val, uint32_t addr);
+#else
+# define lpc43_getreg(addr) getreg32(addr)
+# define lpc43_putreg(val,addr) putreg32(val,addr)
+#endif
+
+static inline void lpc43_clrbits(uint32_t mask, uint32_t addr);
+static inline void lpc43_setbits(uint32_t mask, uint32_t addr);
+static inline void lpc43_chgbits(uint32_t mask, uint32_t val, uint32_t addr);
+
+/* Request queue operations ****************************************************/
+
+static FAR struct lpc43_req_s *lpc43_rqdequeue(FAR struct lpc43_ep_s *privep);
+static bool lpc43_rqenqueue(FAR struct lpc43_ep_s *privep,
+ FAR struct lpc43_req_s *req);
+
+/* Low level data transfers and request operations *****************************/
+
+static inline void lpc43_writedtd(struct lpc43_dtd_s *dtd, const uint8_t *data,
+ uint32_t nbytes);
+static inline void lpc43_queuedtd(uint8_t epphy, struct lpc43_dtd_s *dtd);
+static inline void lpc43_ep0xfer(uint8_t epphy, uint8_t *data, uint32_t nbytes);
+static void lpc43_readsetup(uint8_t epphy, struct usb_ctrlreq_s *ctrl);
+
+static inline void lpc43_set_address(struct lpc43_usbdev_s *priv, uint16_t address);
+
+static void lpc43_flushep(struct lpc43_ep_s *privep);
+
+static int lpc43_progressep(struct lpc43_ep_s *privep);
+static inline void lpc43_abortrequest(struct lpc43_ep_s *privep,
+ struct lpc43_req_s *privreq, int16_t result);
+static void lpc43_reqcomplete(struct lpc43_ep_s *privep,
+ struct lpc43_req_s *privreq, int16_t result);
+
+static void lpc43_cancelrequests(struct lpc43_ep_s *privep, int16_t status);
+
+/* Interrupt handling **********************************************************/
+static struct lpc43_ep_s *lpc43_epfindbyaddr(struct lpc43_usbdev_s *priv,
+ uint16_t eplog);
+static void lpc43_dispatchrequest(struct lpc43_usbdev_s *priv,
+ const struct usb_ctrlreq_s *ctrl);
+static void lpc43_ep0configure(struct lpc43_usbdev_s *priv);
+static void lpc43_usbreset(struct lpc43_usbdev_s *priv);
+
+static inline void lpc43_ep0state(struct lpc43_usbdev_s *priv, uint16_t state);
+static void lpc43_ep0setup(struct lpc43_usbdev_s *priv);
+
+static void lpc43_ep0complete(struct lpc43_usbdev_s *priv, uint8_t epphy);
+static void lpc43_ep0nak(struct lpc43_usbdev_s *priv, uint8_t epphy);
+static bool lpc43_epcomplete(struct lpc43_usbdev_s *priv, uint8_t epphy);
+
+static int lpc43_usbinterrupt(int irq, FAR void *context);
+
+/* Endpoint operations *********************************************************/
+
+/* USB device controller operations ********************************************/
+
+static int lpc43_epconfigure(FAR struct usbdev_ep_s *ep,
+ const struct usb_epdesc_s *desc, bool last);
+static int lpc43_epdisable(FAR struct usbdev_ep_s *ep);
+static FAR struct usbdev_req_s *lpc43_epallocreq(FAR struct usbdev_ep_s *ep);
+static void lpc43_epfreereq(FAR struct usbdev_ep_s *ep,
+ FAR struct usbdev_req_s *);
+#ifdef CONFIG_ARCH_USBDEV_DMA
+static void *lpc43_epallocbuffer(FAR struct usbdev_ep_s *ep, unsigned bytes);
+static void lpc43_epfreebuffer(FAR struct usbdev_ep_s *ep, FAR void *buf);
+#endif
+static int lpc43_epsubmit(FAR struct usbdev_ep_s *ep,
+ struct usbdev_req_s *req);
+static int lpc43_epcancel(FAR struct usbdev_ep_s *ep,
+ struct usbdev_req_s *req);
+static int lpc43_epstall(FAR struct usbdev_ep_s *ep, bool resume);
+
+static FAR struct usbdev_ep_s *lpc43_allocep(FAR struct usbdev_s *dev,
+ uint8_t epno, bool in, uint8_t eptype);
+static void lpc43_freeep(FAR struct usbdev_s *dev, FAR struct usbdev_ep_s *ep);
+static int lpc43_getframe(struct usbdev_s *dev);
+static int lpc43_wakeup(struct usbdev_s *dev);
+static int lpc43_selfpowered(struct usbdev_s *dev, bool selfpowered);
+static int lpc43_pullup(struct usbdev_s *dev, bool enable);
+
+/*******************************************************************************
+ * Private Data
+ *******************************************************************************/
+
+/* Since there is only a single USB interface, all status information can be
+ * be simply retained in a single global instance.
+ */
+
+static struct lpc43_usbdev_s g_usbdev;
+
+static struct lpc43_dqh_s __attribute__((aligned(2048))) g_qh[LPC43_NPHYSENDPOINTS];
+static struct lpc43_dtd_s __attribute__((aligned(32))) g_td[LPC43_NPHYSENDPOINTS];
+
+static const struct usbdev_epops_s g_epops =
+{
+ .configure = lpc43_epconfigure,
+ .disable = lpc43_epdisable,
+ .allocreq = lpc43_epallocreq,
+ .freereq = lpc43_epfreereq,
+#ifdef CONFIG_ARCH_USBDEV_DMA
+ .allocbuffer = lpc43_epallocbuffer,
+ .freebuffer = lpc43_epfreebuffer,
+#endif
+ .submit = lpc43_epsubmit,
+ .cancel = lpc43_epcancel,
+ .stall = lpc43_epstall,
+};
+
+static const struct usbdev_ops_s g_devops =
+{
+ .allocep = lpc43_allocep,
+ .freeep = lpc43_freeep,
+ .getframe = lpc43_getframe,
+ .wakeup = lpc43_wakeup,
+ .selfpowered = lpc43_selfpowered,
+ .pullup = lpc43_pullup,
+};
+
+/*******************************************************************************
+ * Public Data
+ *******************************************************************************/
+
+/*******************************************************************************
+ * Private Functions
+ *******************************************************************************/
+
+/*******************************************************************************
+ * Name: lpc43_getreg
+ *
+ * Description:
+ * Get the contents of an LPC433x register
+ *
+ *******************************************************************************/
+
+#if defined(CONFIG_LPC43_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG)
+static uint32_t lpc43_getreg(uint32_t addr)
+{
+ static uint32_t prevaddr = 0;
+ static uint32_t preval = 0;
+ static uint32_t count = 0;
+
+ /* Read the value from the register */
+
+ uint32_t val = getreg32(addr);
+
+ /* Is this the same value that we read from the same registe last time? Are
+ * we polling the register? If so, suppress some of the output.
+ */
+
+ if (addr == prevaddr && val == preval)
+ {
+ if (count == 0xffffffff || ++count > 3)
+ {
+ if (count == 4)
+ {
+ lldbg("...\n");
+ }
+ return val;
+ }
+ }
+
+ /* No this is a new address or value */
+
+ else
+ {
+ /* Did we print "..." for the previous value? */
+
+ if (count > 3)
+ {
+ /* Yes.. then show how many times the value repeated */
+
+ lldbg("[repeats %d more times]\n", count-3);
+ }
+
+ /* Save the new address, value, and count */
+
+ prevaddr = addr;
+ preval = val;
+ count = 1;
+ }
+
+ /* Show the register value read */
+
+ lldbg("%08x->%08x\n", addr, val);
+ return val;
+}
+#endif
+
+/*******************************************************************************
+ * Name: lpc43_putreg
+ *
+ * Description:
+ * Set the contents of an LPC433x register to a value
+ *
+ *******************************************************************************/
+
+#if defined(CONFIG_LPC43_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG)
+static void lpc43_putreg(uint32_t val, uint32_t addr)
+{
+ /* Show the register value being written */
+
+ lldbg("%08x<-%08x\n", addr, val);
+
+ /* Write the value */
+
+ putreg32(val, addr);
+}
+#endif
+
+/*******************************************************************************
+ * Name: lpc43_clrbits
+ *
+ * Description:
+ * Clear bits in a register
+ *
+ *******************************************************************************/
+
+static inline void lpc43_clrbits(uint32_t mask, uint32_t addr)
+{
+ uint32_t reg = lpc43_getreg(addr);
+ reg &= ~mask;
+ lpc43_putreg(reg, addr);
+}
+
+/*******************************************************************************
+ * Name: lpc43_setbits
+ *
+ * Description:
+ * Set bits in a register
+ *
+ *******************************************************************************/
+
+static inline void lpc43_setbits(uint32_t mask, uint32_t addr)
+{
+ uint32_t reg = lpc43_getreg(addr);
+ reg |= mask;
+ lpc43_putreg(reg, addr);
+}
+
+/*******************************************************************************
+ * Name: lpc43_chgbits
+ *
+ * Description:
+ * Change bits in a register
+ *
+ *******************************************************************************/
+
+static inline void lpc43_chgbits(uint32_t mask, uint32_t val, uint32_t addr)
+{
+ uint32_t reg = lpc43_getreg(addr);
+ reg &= ~mask;
+ reg |= val;
+ lpc43_putreg(reg, addr);
+}
+
+/*******************************************************************************
+ * Name: lpc43_rqdequeue
+ *
+ * Description:
+ * Remove a request from an endpoint request queue
+ *
+ *******************************************************************************/
+
+static FAR struct lpc43_req_s *lpc43_rqdequeue(FAR struct lpc43_ep_s *privep)
+{
+ FAR struct lpc43_req_s *ret = privep->head;
+
+ if (ret)
+ {
+ privep->head = ret->flink;
+ if (!privep->head)
+ {
+ privep->tail = NULL;
+ }
+
+ ret->flink = NULL;
+ }
+
+ return ret;
+}
+
+/*******************************************************************************
+ * Name: lpc43_rqenqueue
+ *
+ * Description:
+ * Add a request from an endpoint request queue
+ *
+ *******************************************************************************/
+
+static bool lpc43_rqenqueue(FAR struct lpc43_ep_s *privep,
+ FAR struct lpc43_req_s *req)
+{
+ bool is_empty = !privep->head;
+
+ req->flink = NULL;
+ if (is_empty)
+ {
+ privep->head = req;
+ privep->tail = req;
+ }
+ else
+ {
+ privep->tail->flink = req;
+ privep->tail = req;
+ }
+ return is_empty;
+}
+
+/*******************************************************************************
+ * Name: lpc43_writedtd
+ *
+ * Description:
+ * Initialise a DTD to transfer the data
+ *
+ *******************************************************************************/
+
+static inline void lpc43_writedtd(struct lpc43_dtd_s *dtd, const uint8_t *data, uint32_t nbytes)
+{
+ dtd->nextdesc = DTD_NEXTDESC_INVALID;
+ dtd->config = DTD_CONFIG_LENGTH(nbytes) | DTD_CONFIG_IOC | DTD_CONFIG_ACTIVE;
+ dtd->buffer0 = ((uint32_t) data);
+ dtd->buffer1 = (((uint32_t) data) + 0x1000) & 0xfffff000;
+ dtd->buffer2 = (((uint32_t) data) + 0x2000) & 0xfffff000;
+ dtd->buffer3 = (((uint32_t) data) + 0x3000) & 0xfffff000;
+ dtd->buffer4 = (((uint32_t) data) + 0x4000) & 0xfffff000;
+ dtd->xfer_len = nbytes;
+}
+
+/*******************************************************************************
+ * Name: lpc43_queuedtd
+ *
+ * Description:
+ * Add the DTD to the device list
+ *
+ *******************************************************************************/
+
+static void lpc43_queuedtd(uint8_t epphy, struct lpc43_dtd_s *dtd)
+{
+ /* Queue the DTD onto the Endpoint */
+ /* NOTE - this only works when no DTD is currently queued */
+
+ g_qh[epphy].overlay.nextdesc = (uint32_t) dtd;
+ g_qh[epphy].overlay.config &= ~(DTD_CONFIG_ACTIVE | DTD_CONFIG_HALTED);
+
+ uint32_t bit = LPC43_ENDPTMASK(epphy);
+
+ lpc43_setbits (bit, LPC43_USBDEV_ENDPTPRIME);
+
+ while (lpc43_getreg (LPC43_USBDEV_ENDPTPRIME) & bit)
+ ;
+}
+
+/*******************************************************************************
+ * Name: lpc43_ep0xfer
+ *
+ * Description:
+ * Schedule a short transfer for Endpoint 0 (IN or OUT)
+ *
+ *******************************************************************************/
+
+static inline void lpc43_ep0xfer(uint8_t epphy, uint8_t *buf, uint32_t nbytes)
+{
+ struct lpc43_dtd_s *dtd = &g_td[epphy];
+
+ lpc43_writedtd(dtd, buf, nbytes);
+
+ lpc43_queuedtd(epphy, dtd);
+}
+
+/*******************************************************************************
+ * Name: lpc43_readsetup
+ *
+ * Description:
+ * Read a Setup packet from the DTD.
+ *
+ *******************************************************************************/
+static void lpc43_readsetup(uint8_t epphy, struct usb_ctrlreq_s *ctrl)
+{
+ struct lpc43_dqh_s *dqh = &g_qh[epphy];
+ int i;
+
+ do {
+ /* Set the trip wire */
+ lpc43_setbits(USBDEV_USBCMD_SUTW, LPC43_USBDEV_USBCMD);
+
+ /* copy the request... */
+ for (i = 0; i < 8; i++)
+ ((uint8_t *) ctrl)[i] = ((uint8_t *) dqh->setup)[i];
+
+ } while (!(lpc43_getreg(LPC43_USBDEV_USBCMD) & USBDEV_USBCMD_SUTW));
+
+ /* Clear the trip wire */
+ lpc43_clrbits(USBDEV_USBCMD_SUTW, LPC43_USBDEV_USBCMD);
+
+ /* Clear the Setup Interrupt */
+ lpc43_putreg (LPC43_ENDPTMASK(LPC43_EP0_OUT), LPC43_USBDEV_ENDPTSETUPSTAT);
+}
+
+/*******************************************************************************
+ * Name: lpc43_set_address
+ *
+ * Description:
+ * Set the devices USB address
+ *
+ *******************************************************************************/
+
+static inline void lpc43_set_address(struct lpc43_usbdev_s *priv, uint16_t address)
+{
+ priv->paddr = address;
+ priv->paddrset = address != 0;
+
+ lpc43_chgbits(USBDEV_DEVICEADDR_MASK, priv->paddr << USBDEV_DEVICEADDR_SHIFT,
+ LPC43_USBDEV_DEVICEADDR);
+}
+
+/*******************************************************************************
+ * Name: lpc43_flushep
+ *
+ * Description:
+ * Flush any primed descriptors from this ep
+ *
+ *******************************************************************************/
+
+static void lpc43_flushep(struct lpc43_ep_s *privep)
+{
+ uint32_t mask = LPC43_ENDPTMASK(privep->epphy);
+ do
+ {
+ lpc43_putreg (mask, LPC43_USBDEV_ENDPTFLUSH);
+ while ((lpc43_getreg(LPC43_USBDEV_ENDPTFLUSH) & mask) != 0)
+ ;
+ }
+ while ((lpc43_getreg(LPC43_USBDEV_ENDPTSTATUS) & mask) != 0);
+}
+
+
+/*******************************************************************************
+ * Name: lpc43_progressep
+ *
+ * Description:
+ * Progress the Endpoint by priming the first request into the queue head
+ *
+ *******************************************************************************/
+
+static int lpc43_progressep(struct lpc43_ep_s *privep)
+{
+ struct lpc43_dtd_s *dtd = &g_td[privep->epphy];
+ struct lpc43_req_s *privreq;
+
+ /* Check the request from the head of the endpoint request queue */
+
+ privreq = lpc43_rqpeek(privep);
+ if (!privreq)
+ {
+ usbtrace(TRACE_INTDECODE(LPC43_TRACEINTID_EPINQEMPTY), 0);
+ return OK;
+ }
+
+ /* Ignore any attempt to send a zero length packet */
+
+ if (privreq->req.len == 0)
+ {
+ /* If the class driver is responding to a setup packet, then wait for the
+ * host to illicit thr response */
+
+ if (privep->epphy == LPC43_EP0_IN && privep->dev->ep0state == EP0STATE_SETUP_OUT)
+ lpc43_ep0state (privep->dev, EP0STATE_WAIT_NAK_IN);
+ else
+ {
+ if (LPC43_EPPHYIN(privep->epphy))
+ usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_EPINNULLPACKET), 0);
+ else
+ usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_EPOUTNULLPACKET), 0);
+ }
+
+ lpc43_reqcomplete(privep, lpc43_rqdequeue(privep), OK);
+ return OK;
+ }
+
+ if (privep->epphy == LPC43_EP0_IN)
+ lpc43_ep0state (privep->dev, EP0STATE_DATA_IN);
+ else if (privep->epphy == LPC43_EP0_OUT)
+ lpc43_ep0state (privep->dev, EP0STATE_DATA_OUT);
+
+ int bytesleft = privreq->req.len - privreq->req.xfrd;
+
+ if (LPC43_EPPHYIN(privep->epphy))
+ usbtrace(TRACE_WRITE(privep->epphy), privreq->req.xfrd);
+ else
+ usbtrace(TRACE_READ(privep->epphy), privreq->req.xfrd);
+
+ /* Initialise the DTD to transfer the next chunk */
+
+ lpc43_writedtd (dtd, privreq->req.buf + privreq->req.xfrd, bytesleft);
+
+ /* then queue onto the DQH */
+ lpc43_queuedtd(privep->epphy, dtd);
+
+ return OK;
+}
+
+/*******************************************************************************
+ * Name: lpc43_abortrequest
+ *
+ * Description:
+ * Discard a request
+ *
+ *******************************************************************************/
+
+static inline void lpc43_abortrequest(struct lpc43_ep_s *privep,
+ struct lpc43_req_s *privreq,
+ int16_t result)
+{
+ usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_REQABORTED), (uint16_t)privep->epphy);
+
+ /* Save the result in the request structure */
+
+ privreq->req.result = result;
+
+ /* Callback to the request completion handler */
+
+ privreq->req.callback(&privep->ep, &privreq->req);
+}
+
+/*******************************************************************************
+ * Name: lpc43_reqcomplete
+ *
+ * Description:
+ * Handle termination of the request at the head of the endpoint request queue.
+ *
+ *******************************************************************************/
+
+static void lpc43_reqcomplete(struct lpc43_ep_s *privep,
+ struct lpc43_req_s *privreq, int16_t result)
+{
+ /* If endpoint 0, temporarily reflect the state of protocol stalled
+ * in the callback.
+ */
+
+ bool stalled = privep->stalled;
+ if (privep->epphy == LPC43_EP0_IN)
+ privep->stalled = privep->dev->stalled;
+
+ /* Save the result in the request structure */
+
+ privreq->req.result = result;
+
+ /* Callback to the request completion handler */
+
+ privreq->req.callback(&privep->ep, &privreq->req);
+
+ /* Restore the stalled indication */
+
+ privep->stalled = stalled;
+}
+
+/*******************************************************************************
+ * Name: lpc43_cancelrequests
+ *
+ * Description:
+ * Cancel all pending requests for an endpoint
+ *
+ *******************************************************************************/
+
+static void lpc43_cancelrequests(struct lpc43_ep_s *privep, int16_t status)
+{
+ if (!lpc43_rqempty(privep))
+ lpc43_flushep(privep);
+
+ while (!lpc43_rqempty(privep))
+ {
+ // FIXME: the entry at the head should be sync'd with the DTD
+ // FIXME: only report the error status if the transfer hasn't completed
+ usbtrace(TRACE_COMPLETE(privep->epphy),
+ (lpc43_rqpeek(privep))->req.xfrd);
+ lpc43_reqcomplete(privep, lpc43_rqdequeue(privep), status);
+ }
+}
+
+/*******************************************************************************
+ * Name: lpc43_epfindbyaddr
+ *
+ * Description:
+ * Find the physical endpoint structure corresponding to a logic endpoint
+ * address
+ *
+ *******************************************************************************/
+
+static struct lpc43_ep_s *lpc43_epfindbyaddr(struct lpc43_usbdev_s *priv,
+ uint16_t eplog)
+{
+ struct lpc43_ep_s *privep;
+ int i;
+
+ /* Endpoint zero is a special case */
+
+ if (USB_EPNO(eplog) == 0)
+ {
+ return &priv->eplist[0];
+ }
+
+ /* Handle the remaining */
+
+ for (i = 1; i < LPC43_NPHYSENDPOINTS; i++)
+ {
+ privep = &priv->eplist[i];
+
+ /* Same logical endpoint number? (includes direction bit) */
+
+ if (eplog == privep->ep.eplog)
+ {
+ /* Return endpoint found */
+
+ return privep;
+ }
+ }
+
+ /* Return endpoint not found */
+
+ return NULL;
+}
+
+/*******************************************************************************
+ * Name: lpc43_dispatchrequest
+ *
+ * Description:
+ * Provide unhandled setup actions to the class driver. This is logically part
+ * of the USB interrupt handler.
+ *
+ *******************************************************************************/
+
+static void lpc43_dispatchrequest(struct lpc43_usbdev_s *priv,
+ const struct usb_ctrlreq_s *ctrl)
+{
+ int ret = -EIO;
+
+ usbtrace(TRACE_INTDECODE(LPC43_TRACEINTID_DISPATCH), 0);
+ if (priv->driver)
+ {
+ /* Forward to the control request to the class driver implementation */
+
+ ret = CLASS_SETUP(priv->driver, &priv->usbdev, ctrl, NULL, 0);
+ }
+
+ if (ret < 0)
+ {
+ /* Stall on failure */
+
+ usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_DISPATCHSTALL), 0);
+ priv->stalled = true;
+ }
+}
+
+/*******************************************************************************
+ * Name: lpc43_ep0configure
+ *
+ * Description:
+ * Reset Usb engine
+ *
+ *******************************************************************************/
+
+static void lpc43_ep0configure(struct lpc43_usbdev_s *priv)
+{
+ /* Enable ep0 IN and ep0 OUT */
+ g_qh[LPC43_EP0_OUT].capability = (DQH_CAPABILITY_MAX_PACKET(CONFIG_USBDEV_EP0_MAXSIZE) |
+ DQH_CAPABILITY_IOS |
+ DQH_CAPABILITY_ZLT);
+
+ g_qh[LPC43_EP0_IN ].capability = (DQH_CAPABILITY_MAX_PACKET(CONFIG_USBDEV_EP0_MAXSIZE) |
+ DQH_CAPABILITY_IOS |
+ DQH_CAPABILITY_ZLT);
+
+ g_qh[LPC43_EP0_OUT].currdesc = DTD_NEXTDESC_INVALID;
+ g_qh[LPC43_EP0_IN ].currdesc = DTD_NEXTDESC_INVALID;
+
+ /* Enable EP0 */
+ lpc43_setbits (USBDEV_ENDPTCTRL0_RXE | USBDEV_ENDPTCTRL0_TXE, LPC43_USBDEV_ENDPTCTRL0);
+}
+
+/*******************************************************************************
+ * Name: lpc43_usbreset
+ *
+ * Description:
+ * Reset Usb engine
+ *
+ *******************************************************************************/
+
+static void lpc43_usbreset(struct lpc43_usbdev_s *priv)
+{
+ int epphy;
+
+ /* Disable all endpoints */
+
+ lpc43_clrbits (USBDEV_ENDPTCTRL_RXE | USBDEV_ENDPTCTRL_TXE, LPC43_USBDEV_ENDPTCTRL0);
+ lpc43_clrbits (USBDEV_ENDPTCTRL_RXE | USBDEV_ENDPTCTRL_TXE, LPC43_USBDEV_ENDPTCTRL1);
+ lpc43_clrbits (USBDEV_ENDPTCTRL_RXE | USBDEV_ENDPTCTRL_TXE, LPC43_USBDEV_ENDPTCTRL2);
+ lpc43_clrbits (USBDEV_ENDPTCTRL_RXE | USBDEV_ENDPTCTRL_TXE, LPC43_USBDEV_ENDPTCTRL3);
+
+ /* Clear all pending interrupts */
+
+ lpc43_putreg (lpc43_getreg(LPC43_USBDEV_ENDPTNAK), LPC43_USBDEV_ENDPTNAK);
+ lpc43_putreg (lpc43_getreg(LPC43_USBDEV_ENDPTSETUPSTAT), LPC43_USBDEV_ENDPTSETUPSTAT);
+ lpc43_putreg (lpc43_getreg(LPC43_USBDEV_ENDPTCOMPLETE), LPC43_USBDEV_ENDPTCOMPLETE);
+
+ /* Wait for all prime operations to have completed and then flush all DTDs */
+ while (lpc43_getreg (LPC43_USBDEV_ENDPTPRIME) != 0)
+ ;
+ lpc43_putreg (LPC43_ENDPTMASK_ALL, LPC43_USBDEV_ENDPTFLUSH);
+ while (lpc43_getreg (LPC43_USBDEV_ENDPTFLUSH))
+ ;
+
+ /* Reset endpoints */
+ for (epphy = 0; epphy < LPC43_NPHYSENDPOINTS; epphy++)
+ {
+ struct lpc43_ep_s *privep = &priv->eplist[epphy];
+
+ lpc43_cancelrequests (privep, -ESHUTDOWN);
+
+ /* Reset endpoint status */
+ privep->stalled = false;
+ }
+
+ /* Tell the class driver that we are disconnected. The class
+ * driver should then accept any new configurations. */
+
+ if (priv->driver)
+ CLASS_DISCONNECT(priv->driver, &priv->usbdev);
+
+ /* Set the interrupt Threshold control interval to 0 */
+ lpc43_chgbits(USBDEV_USBCMD_ITC_MASK, USBDEV_USBCMD_ITCIMME, LPC43_USBDEV_USBCMD);
+
+ /* Zero out the Endpoint queue heads */
+ memset ((void *) g_qh, 0, sizeof (g_qh));
+ memset ((void *) g_td, 0, sizeof (g_td));
+
+ /* Set USB address to 0 */
+ lpc43_set_address (priv, 0);
+
+ /* Initialise the Enpoint List Address */
+ lpc43_putreg ((uint32_t)g_qh, LPC43_USBDEV_ENDPOINTLIST);
+
+ /* EndPoint 0 initialization */
+ lpc43_ep0configure(priv);
+
+ /* Enable Device interrupts */
+ lpc43_putreg(USB_FRAME_INT | USB_ERROR_INT |
+ USBDEV_USBINTR_NAKE | USBDEV_USBINTR_SLE | USBDEV_USBINTR_URE | USBDEV_USBINTR_PCE | USBDEV_USBINTR_UE,
+ LPC43_USBDEV_USBINTR);
+}
+
+/*******************************************************************************
+ * Name: lpc43_setstate
+ *
+ * Description:
+ * Sets the EP0 state and manages the NAK interrupts
+ *
+ *******************************************************************************/
+
+static inline void lpc43_ep0state(struct lpc43_usbdev_s *priv, uint16_t state)
+{
+ priv->ep0state = state;
+
+ switch (state)
+ {
+ case EP0STATE_WAIT_NAK_IN:
+ lpc43_putreg (LPC43_ENDPTMASK(LPC43_EP0_IN), LPC43_USBDEV_ENDPTNAKEN);
+ break;
+ case EP0STATE_WAIT_NAK_OUT:
+ lpc43_putreg (LPC43_ENDPTMASK(LPC43_EP0_OUT), LPC43_USBDEV_ENDPTNAKEN);
+ break;
+ default:
+ lpc43_putreg(0, LPC43_USBDEV_ENDPTNAKEN);
+ break;
+ }
+}
+
+/*******************************************************************************
+ * Name: lpc43_ep0setup
+ *
+ * Description:
+ * USB Ctrl EP Setup Event. This is logically part of the USB interrupt
+ * handler. This event occurs when a setup packet is receive on EP0 OUT.
+ *
+ *******************************************************************************/
+
+static inline void lpc43_ep0setup(struct lpc43_usbdev_s *priv)
+{
+ struct lpc43_ep_s *privep;
+ struct usb_ctrlreq_s ctrl;
+ uint16_t value;
+ uint16_t index;
+ uint16_t len;
+
+ /* Terminate any pending requests - since all DTDs will have been retired
+ * because of the setup packet */
+
+ lpc43_cancelrequests(&priv->eplist[LPC43_EP0_OUT], -EPROTO);
+ lpc43_cancelrequests(&priv->eplist[LPC43_EP0_IN], -EPROTO);
+
+ /* Assume NOT stalled */
+
+ priv->eplist[LPC43_EP0_OUT].stalled = false;
+ priv->eplist[LPC43_EP0_IN].stalled = false;
+ priv->stalled = false;
+
+ /* Read EP0 setup data */
+ lpc43_readsetup(LPC43_EP0_OUT, &ctrl);
+
+ /* Starting a control request - update state */
+ lpc43_ep0state (priv, (ctrl.type & USB_REQ_DIR_IN) ? EP0STATE_SETUP_IN : EP0STATE_SETUP_OUT);
+
+ /* And extract the little-endian 16-bit values to host order */
+ value = GETUINT16(ctrl.value);
+ index = GETUINT16(ctrl.index);
+ len = GETUINT16(ctrl.len);
+
+ ullvdbg("type=%02x req=%02x value=%04x index=%04x len=%04x\n",
+ ctrl.type, ctrl.req, value, index, len);
+
+ /* Dispatch any non-standard requests */
+ if ((ctrl.type & USB_REQ_TYPE_MASK) != USB_REQ_TYPE_STANDARD)
+ lpc43_dispatchrequest(priv, &ctrl);
+ else
+ {
+ /* Handle standard request. Pick off the things of interest to the USB
+ * device controller driver; pass what is left to the class driver */
+ switch (ctrl.req)
+ {
+ case USB_REQ_GETSTATUS:
+ {
+ /* type: device-to-host; recipient = device, interface, endpoint
+ * value: 0
+ * index: zero interface endpoint
+ * len: 2; data = status
+ */
+
+ usbtrace(TRACE_INTDECODE(LPC43_TRACEINTID_GETSTATUS), 0);
+ if (!priv->paddrset || len != 2 ||
+ (ctrl.type & USB_REQ_DIR_IN) == 0 || value != 0)
+ {
+ priv->stalled = true;
+ }
+ else
+ {
+ switch (ctrl.type & USB_REQ_RECIPIENT_MASK)
+ {
+ case USB_REQ_RECIPIENT_ENDPOINT:
+ {
+ usbtrace(TRACE_INTDECODE(LPC43_TRACEINTID_EPGETSTATUS), 0);
+ privep = lpc43_epfindbyaddr(priv, index);
+ if (!privep)
+ {
+ usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_BADEPGETSTATUS), 0);
+ priv->stalled = true;
+ }
+ else
+ {
+ if (privep->stalled)
+ priv->ep0buf[0] = 1; /* Stalled */
+ else
+ priv->ep0buf[0] = 0; /* Not stalled */
+ priv->ep0buf[1] = 0;
+
+ lpc43_ep0xfer (LPC43_EP0_IN, priv->ep0buf, 2);
+ lpc43_ep0state (priv, EP0STATE_SHORTWRITE);
+ }
+ }
+ break;
+
+ case USB_REQ_RECIPIENT_DEVICE:
+ {
+ if (index == 0)
+ {
+ usbtrace(TRACE_INTDECODE(LPC43_TRACEINTID_DEVGETSTATUS), 0);
+
+ /* Features: Remote Wakeup=YES; selfpowered=? */
+
+ priv->ep0buf[0] = (priv->selfpowered << USB_FEATURE_SELFPOWERED) |
+ (1 << USB_FEATURE_REMOTEWAKEUP);
+ priv->ep0buf[1] = 0;
+
+ lpc43_ep0xfer(LPC43_EP0_IN, priv->ep0buf, 2);
+ lpc43_ep0state (priv, EP0STATE_SHORTWRITE);
+ }
+ else
+ {
+ usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_BADDEVGETSTATUS), 0);
+ priv->stalled = true;
+ }
+ }
+ break;
+
+ case USB_REQ_RECIPIENT_INTERFACE:
+ {
+ usbtrace(TRACE_INTDECODE(LPC43_TRACEINTID_IFGETSTATUS), 0);
+ priv->ep0buf[0] = 0;
+ priv->ep0buf[1] = 0;
+
+ lpc43_ep0xfer(LPC43_EP0_IN, priv->ep0buf, 2);
+ lpc43_ep0state (priv, EP0STATE_SHORTWRITE);
+ }
+ break;
+
+ default:
+ {
+ usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_BADGETSTATUS), 0);
+ priv->stalled = true;
+ }
+ break;
+ }
+ }
+ }
+ break;
+
+ case USB_REQ_CLEARFEATURE:
+ {
+ /* type: host-to-device; recipient = device, interface or endpoint
+ * value: feature selector
+ * index: zero interface endpoint;
+ * len: zero, data = none
+ */
+
+ usbtrace(TRACE_INTDECODE(LPC43_TRACEINTID_CLEARFEATURE), 0);
+ if ((ctrl.type & USB_REQ_RECIPIENT_MASK) != USB_REQ_RECIPIENT_ENDPOINT)
+ {
+ lpc43_dispatchrequest(priv, &ctrl);
+ }
+ else if (priv->paddrset != 0 && value == USB_FEATURE_ENDPOINTHALT && len == 0 &&
+ (privep = lpc43_epfindbyaddr(priv, index)) != NULL)
+ {
+ lpc43_epstall(&privep->ep, true);
+ lpc43_ep0state (priv, EP0STATE_WAIT_NAK_IN);
+ }
+ else
+ {
+ usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_BADCLEARFEATURE), 0);
+ priv->stalled = true;
+ }
+ }
+ break;
+
+ case USB_REQ_SETFEATURE:
+ {
+ /* type: host-to-device; recipient = device, interface, endpoint
+ * value: feature selector
+ * index: zero interface endpoint;
+ * len: 0; data = none
+ */
+
+ usbtrace(TRACE_INTDECODE(LPC43_TRACEINTID_SETFEATURE), 0);
+ if (((ctrl.type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE) &&
+ value == USB_FEATURE_TESTMODE)
+ {
+ ullvdbg("test mode: %d\n", index);
+ }
+ else if ((ctrl.type & USB_REQ_RECIPIENT_MASK) != USB_REQ_RECIPIENT_ENDPOINT)
+ {
+ lpc43_dispatchrequest(priv, &ctrl);
+ }
+ else if (priv->paddrset != 0 && value == USB_FEATURE_ENDPOINTHALT && len == 0 &&
+ (privep = lpc43_epfindbyaddr(priv, index)) != NULL)
+ {
+ lpc43_epstall(&privep->ep, false);
+ lpc43_ep0state (priv, EP0STATE_WAIT_NAK_IN);
+ }
+ else
+ {
+ usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_BADSETFEATURE), 0);
+ priv->stalled = true;
+ }
+ }
+ break;
+
+ case USB_REQ_SETADDRESS:
+ {
+ /* type: host-to-device; recipient = device
+ * value: device address
+ * index: 0
+ * len: 0; data = none
+ */
+ usbtrace(TRACE_INTDECODE(LPC43_TRACEINTID_EP0SETUPSETADDRESS), value);
+ if ((ctrl.type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE &&
+ index == 0 && len == 0 && value < 128)
+ {
+ /* Save the address. We cannot actually change to the next address until
+ * the completion of the status phase. */
+
+ priv->paddr = ctrl.value[0];
+ priv->paddrset = false;
+ lpc43_ep0state (priv, EP0STATE_WAIT_NAK_IN);
+ }
+ else
+ {
+ usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_BADSETADDRESS), 0);
+ priv->stalled = true;
+ }
+ }
+ break;
+
+ case USB_REQ_GETDESCRIPTOR:
+ /* type: device-to-host; recipient = device
+ * value: descriptor type and index
+ * index: 0 or language ID;
+ * len: descriptor len; data = descriptor
+ */
+ case USB_REQ_SETDESCRIPTOR:
+ /* type: host-to-device; recipient = device
+ * value: descriptor type and index
+ * index: 0 or language ID;
+ * len: descriptor len; data = descriptor
+ */
+ {
+ usbtrace(TRACE_INTDECODE(LPC43_TRACEINTID_GETSETDESC), 0);
+ if ((ctrl.type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE)
+ {
+ lpc43_dispatchrequest(priv, &ctrl);
+ }
+ else
+ {
+ usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_BADGETSETDESC), 0);
+ priv->stalled = true;
+ }
+ }
+ break;
+
+ case USB_REQ_GETCONFIGURATION:
+ /* type: device-to-host; recipient = device
+ * value: 0;
+ * index: 0;
+ * len: 1; data = configuration value
+ */
+ {
+ usbtrace(TRACE_INTDECODE(LPC43_TRACEINTID_GETCONFIG), 0);
+ if (priv->paddrset && (ctrl.type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE &&
+ value == 0 && index == 0 && len == 1)
+ {
+ lpc43_dispatchrequest(priv, &ctrl);
+ }
+ else
+ {
+ usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_BADGETCONFIG), 0);
+ priv->stalled = true;
+ }
+ }
+ break;
+
+ case USB_REQ_SETCONFIGURATION:
+ /* type: host-to-device; recipient = device
+ * value: configuration value
+ * index: 0;
+ * len: 0; data = none
+ */
+ {
+ usbtrace(TRACE_INTDECODE(LPC43_TRACEINTID_SETCONFIG), 0);
+ if ((ctrl.type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE &&
+ index == 0 && len == 0)
+ {
+ lpc43_dispatchrequest(priv, &ctrl);
+ }
+ else
+ {
+ usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_BADSETCONFIG), 0);
+ priv->stalled = true;
+ }
+ }
+ break;
+
+ case USB_REQ_GETINTERFACE:
+ /* type: device-to-host; recipient = interface
+ * value: 0
+ * index: interface;
+ * len: 1; data = alt interface
+ */
+ case USB_REQ_SETINTERFACE:
+ /* type: host-to-device; recipient = interface
+ * value: alternate setting
+ * index: interface;
+ * len: 0; data = none
+ */
+ {
+ usbtrace(TRACE_INTDECODE(LPC43_TRACEINTID_GETSETIF), 0);
+ lpc43_dispatchrequest(priv, &ctrl);
+ }
+ break;
+
+ case USB_REQ_SYNCHFRAME:
+ /* type: device-to-host; recipient = endpoint
+ * value: 0
+ * index: endpoint;
+ * len: 2; data = frame number
+ */
+ {
+ usbtrace(TRACE_INTDECODE(LPC43_TRACEINTID_SYNCHFRAME), 0);
+ }
+ break;
+
+ default:
+ {
+ usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_INVALIDCTRLREQ), 0);
+ priv->stalled = true;
+ }
+ break;
+ }
+ }
+
+ if (priv->stalled)
+ {
+ usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_EP0SETUPSTALLED), priv->ep0state);
+ lpc43_epstall(&priv->eplist[LPC43_EP0_IN].ep, false);
+ lpc43_epstall(&priv->eplist[LPC43_EP0_OUT].ep, false);
+ }
+}
+
+/*******************************************************************************
+ * Name: lpc43_ep0complete
+ *
+ * Description:
+ * Transfer complete handler for Endpoint 0
+ *
+ *******************************************************************************/
+
+static void lpc43_ep0complete(struct lpc43_usbdev_s *priv, uint8_t epphy)
+{
+ struct lpc43_ep_s *privep = &priv->eplist[epphy];
+
+ usbtrace(TRACE_INTDECODE(LPC43_TRACEINTID_EP0COMPLETE), (uint16_t)priv->ep0state);
+
+ switch (priv->ep0state)
+ {
+ case EP0STATE_DATA_IN:
+ if (lpc43_rqempty(privep))
+ return;
+
+ if (lpc43_epcomplete (priv, epphy))
+ lpc43_ep0state (priv, EP0STATE_WAIT_NAK_OUT);
+ break;
+
+ case EP0STATE_DATA_OUT:
+ if (lpc43_rqempty(privep))
+ return;
+
+ if (lpc43_epcomplete (priv, epphy))
+ lpc43_ep0state (priv, EP0STATE_WAIT_NAK_IN);
+ break;
+
+ case EP0STATE_SHORTWRITE:
+ lpc43_ep0state (priv, EP0STATE_WAIT_NAK_OUT);
+ break;
+
+ case EP0STATE_WAIT_STATUS_IN:
+ lpc43_ep0state (priv, EP0STATE_IDLE);
+
+ /* If we've received a SETADDRESS packet, then we set the address
+ * now that the status phase has completed */
+ if (! priv->paddrset && priv->paddr != 0)
+ {
+ usbtrace(TRACE_INTDECODE(LPC43_TRACEINTID_EP0INSETADDRESS), (uint16_t)priv->paddr);
+ lpc43_set_address (priv, priv->paddr);
+ }
+ break;
+
+ case EP0STATE_WAIT_STATUS_OUT:
+ lpc43_ep0state (priv, EP0STATE_IDLE);
+ break;
+
+ default:
+#ifdef CONFIG_DEBUG
+ DEBUGASSERT(priv->ep0state != EP0STATE_DATA_IN &&
+ priv->ep0state != EP0STATE_DATA_OUT &&
+ priv->ep0state != EP0STATE_SHORTWRITE &&
+ priv->ep0state != EP0STATE_WAIT_STATUS_IN &&
+ priv->ep0state != EP0STATE_WAIT_STATUS_OUT);
+#endif
+ priv->stalled = true;
+ break;
+ }
+
+ if (priv->stalled)
+ {
+ usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_EP0SETUPSTALLED), priv->ep0state);
+ lpc43_epstall(&priv->eplist[LPC43_EP0_IN].ep, false);
+ lpc43_epstall(&priv->eplist[LPC43_EP0_OUT].ep, false);
+ }
+}
+
+/*******************************************************************************
+ * Name: lpc43_ep0nak
+ *
+ * Description:
+ * Handle a NAK interrupt on EP0
+ *
+ *******************************************************************************/
+
+static void lpc43_ep0nak(struct lpc43_usbdev_s *priv, uint8_t epphy)
+{
+ usbtrace(TRACE_INTDECODE(LPC43_TRACEINTID_EP0NAK), (uint16_t)priv->ep0state);
+
+ switch (priv->ep0state)
+ {
+ case EP0STATE_WAIT_NAK_IN:
+ lpc43_ep0xfer (LPC43_EP0_IN, NULL, 0);
+ lpc43_ep0state (priv, EP0STATE_WAIT_STATUS_IN);
+ break;
+ case EP0STATE_WAIT_NAK_OUT:
+ lpc43_ep0xfer (LPC43_EP0_OUT, NULL, 0);
+ lpc43_ep0state (priv, EP0STATE_WAIT_STATUS_OUT);
+ break;
+ default:
+#ifdef CONFIG_DEBUG
+ DEBUGASSERT(priv->ep0state != EP0STATE_WAIT_NAK_IN &&
+ priv->ep0state != EP0STATE_WAIT_NAK_OUT);
+#endif
+ priv->stalled = true;
+ break;
+ }
+
+ if (priv->stalled)
+ {
+ usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_EP0SETUPSTALLED), priv->ep0state);
+ lpc43_epstall(&priv->eplist[LPC43_EP0_IN].ep, false);
+ lpc43_epstall(&priv->eplist[LPC43_EP0_OUT].ep, false);
+ }
+}
+
+/*******************************************************************************
+ * Name: lpc43_epcomplete
+ *
+ * Description:
+ * Transfer complete handler for Endpoints other than 0
+ * returns whether the request at the head has completed
+ *
+ *******************************************************************************/
+
+bool lpc43_epcomplete(struct lpc43_usbdev_s *priv, uint8_t epphy)
+{
+ struct lpc43_ep_s *privep = &priv->eplist[epphy];
+ struct lpc43_req_s *privreq = privep->head;
+ struct lpc43_dtd_s *dtd = &g_td[epphy];
+
+ if (privreq == NULL) /* This shouldn't really happen */
+ {
+ if (LPC43_EPPHYOUT(privep->epphy))
+ usbtrace(TRACE_INTDECODE(LPC43_TRACEINTID_EPINQEMPTY), 0);
+ else
+ usbtrace(TRACE_INTDECODE(LPC43_TRACEINTID_EPOUTQEMPTY), 0);
+ return true;
+ }
+
+ int xfrd = dtd->xfer_len - (dtd->config >> 16);
+
+ privreq->req.xfrd += xfrd;
+
+ bool complete = true;
+ if (LPC43_EPPHYOUT(privep->epphy))
+ {
+ /* read(OUT) completes when request filled, or a short transfer is received */
+
+ usbtrace(TRACE_INTDECODE(LPC43_TRACEINTID_EPIN), complete);
+ }
+ else
+ {
+ /* write(IN) completes when request finished, unless we need to terminate with a ZLP */
+
+ bool need_zlp = (xfrd == privep->ep.maxpacket) && ((privreq->req.flags & USBDEV_REQFLAGS_NULLPKT) != 0);
+
+ complete = (privreq->req.xfrd >= privreq->req.len && !need_zlp);
+
+ usbtrace(TRACE_INTDECODE(LPC43_TRACEINTID_EPOUT), complete);
+ }
+
+ /* If the transfer is complete, then dequeue and progress any further queued requests */
+
+ if (complete)
+ {
+ privreq = lpc43_rqdequeue (privep);
+ }
+
+ if (!lpc43_rqempty(privep))
+ {
+ lpc43_progressep(privep);
+ }
+
+ /* Now it's safe to call the completion callback as it may well submit a new request */
+
+ if (complete)
+ {
+ usbtrace(TRACE_COMPLETE(privep->epphy), privreq->req.xfrd);
+ lpc43_reqcomplete(privep, privreq, OK);
+ }
+
+ return complete;
+}
+
+
+/*******************************************************************************
+ * Name: lpc43_usbinterrupt
+ *
+ * Description:
+ * USB interrupt handler
+ *
+ *******************************************************************************/
+
+static int lpc43_usbinterrupt(int irq, FAR void *context)
+{
+ struct lpc43_usbdev_s *priv = &g_usbdev;
+ uint32_t disr, portsc1, n;
+
+ usbtrace(TRACE_INTENTRY(LPC43_TRACEINTID_USB), 0);
+
+ /* Read the interrupts and then clear them */
+ disr = lpc43_getreg(LPC43_USBDEV_USBSTS);
+ lpc43_putreg(disr, LPC43_USBDEV_USBSTS);
+
+ if (disr & USBDEV_USBSTS_URI)
+ {
+ usbtrace(TRACE_INTDECODE(LPC43_TRACEINTID_DEVRESET),0);
+
+ lpc43_usbreset(priv);
+
+ usbtrace(TRACE_INTEXIT(LPC43_TRACEINTID_USB), 0);
+ return OK;
+ }
+
+ if (disr & USBDEV_USBSTS_SLI)
+ {
+ // FIXME: what do we need to do here...
+ usbtrace(TRACE_INTDECODE(LPC43_TRACEINTID_SUSPENDCHG),0);
+ }
+
+ if (disr & USBDEV_USBSTS_PCI)
+ {
+ portsc1 = lpc43_getreg(LPC43_USBDEV_PORTSC1);
+
+ if (portsc1 & USBDEV_PRTSC1_HSP)
+ priv->usbdev.speed = USB_SPEED_HIGH;
+ else
+ priv->usbdev.speed = USB_SPEED_FULL;
+
+ if (portsc1 & USBDEV_PRTSC1_FPR)
+ {
+ /* FIXME: this occurs because of a J-to-K transition detected
+ * while the port is in SUSPEND state - presumambly this
+ * is where the host is resuming the device?
+ *
+ * - but do we need to "ack" the interrupt
+ */
+ }
+ }
+
+#ifdef CONFIG_LPC43_USBDEV_FRAME_INTERRUPT
+ if (disr & USBDEV_USBSTT_SRI)
+ {
+ usbtrace(TRACE_INTDECODE(LPC43_TRACEINTID_FRAME), 0);
+
+ priv->sof = (int)lpc43_getreg(LPC43_USBDEV_FRINDEX_OFFSET);
+ }
+#endif
+
+ if (disr & USBDEV_USBSTS_UEI)
+ {
+ /* FIXME: these occur when a transfer results in an error condition
+ * it is set alongside USBINT if the DTD also had its IOC
+ * bit set. */
+ }
+
+ if (disr & USBDEV_USBSTS_UI)
+ {
+ /* Handle completion interrupts */
+ uint32_t mask = lpc43_getreg (LPC43_USBDEV_ENDPTCOMPLETE);
+
+ if (mask)
+ {
+ /* Clear any NAK interrupt and completion interrupts */
+ lpc43_putreg (mask, LPC43_USBDEV_ENDPTNAK);
+ lpc43_putreg (mask, LPC43_USBDEV_ENDPTCOMPLETE);
+
+ if (mask & LPC43_ENDPTMASK(0))
+ lpc43_ep0complete(priv, 0);
+ if (mask & LPC43_ENDPTMASK(1))
+ lpc43_ep0complete(priv, 1);
+
+ for (n = 1; n < LPC43_NLOGENDPOINTS; n++)
+ {
+ if (mask & LPC43_ENDPTMASK((n<<1)))
+ lpc43_epcomplete (priv, (n<<1));
+ if (mask & LPC43_ENDPTMASK((n<<1)+1))
+ lpc43_epcomplete(priv, (n<<1)+1);
+ }
+ }
+
+ /* Handle setup interrupts */
+ uint32_t setupstat = lpc43_getreg(LPC43_USBDEV_ENDPTSETUPSTAT);
+ if (setupstat)
+ {
+ /* Clear the endpoint complete CTRL OUT and IN when a Setup is received */
+ lpc43_putreg(LPC43_ENDPTMASK(LPC43_EP0_IN) | LPC43_ENDPTMASK(LPC43_EP0_OUT),
+ LPC43_USBDEV_ENDPTCOMPLETE);
+
+ if (setupstat & LPC43_ENDPTMASK(LPC43_EP0_OUT))
+ {
+ usbtrace(TRACE_INTDECODE(LPC43_TRACEINTID_EP0SETUP), setupstat);
+ lpc43_ep0setup(priv);
+ }
+ }
+ }
+
+ if (disr & USBDEV_USBSTS_NAKI)
+ {
+ uint32_t pending = lpc43_getreg(LPC43_USBDEV_ENDPTNAK) & lpc43_getreg(LPC43_USBDEV_ENDPTNAKEN);
+ if (pending)
+ {
+ /* We shouldn't see NAK interrupts except on Endpoint 0 */
+ if (pending & LPC43_ENDPTMASK(0))
+ lpc43_ep0nak(priv, 0);
+ if (pending & LPC43_ENDPTMASK(1))
+ lpc43_ep0nak(priv, 1);
+ }
+
+ /* Clear the interrupts */
+ lpc43_putreg(pending, LPC43_USBDEV_ENDPTNAK);
+ }
+
+ usbtrace(TRACE_INTEXIT(LPC43_TRACEINTID_USB), 0);
+ return OK;
+}
+
+/*******************************************************************************
+ * Endpoint operations
+ *******************************************************************************/
+
+/*******************************************************************************
+ * Name: lpc43_epconfigure
+ *
+ * Description:
+ * Configure endpoint, making it usable
+ *
+ * Input Parameters:
+ * ep - the struct usbdev_ep_s instance obtained from allocep()
+ * desc - A struct usb_epdesc_s instance describing the endpoint
+ * last - true if this this last endpoint to be configured. Some hardware
+ * needs to take special action when all of the endpoints have been
+ * configured.
+ *
+ *******************************************************************************/
+
+static int lpc43_epconfigure(FAR struct usbdev_ep_s *ep,
+ FAR const struct usb_epdesc_s *desc,
+ bool last)
+{
+ FAR struct lpc43_ep_s *privep = (FAR struct lpc43_ep_s *)ep;
+
+ usbtrace(TRACE_EPCONFIGURE, privep->epphy);
+ DEBUGASSERT(desc->addr == ep->eplog);
+
+ /* Initialise EP capabilities */
+
+ uint16_t maxsize = GETUINT16(desc->mxpacketsize);
+ if ((desc->attr & USB_EP_ATTR_XFERTYPE_MASK) == USB_EP_ATTR_XFER_ISOC)
+ {
+ g_qh[privep->epphy].capability = (DQH_CAPABILITY_MAX_PACKET(maxsize) |
+ DQH_CAPABILITY_IOS |
+ DQH_CAPABILITY_ZLT);
+ }
+ else
+ {
+ g_qh[privep->epphy].capability = (DQH_CAPABILITY_MAX_PACKET(maxsize) |
+ DQH_CAPABILITY_ZLT);
+ }
+
+ /* Setup Endpoint Control Register */
+
+ if (LPC43_EPPHYIN(privep->epphy))
+ {
+ /* Reset the data toggles */
+ uint32_t cfg = USBDEV_ENDPTCTRL_TXR;
+
+ /* Set the endpoint type */
+ switch (desc->attr & USB_EP_ATTR_XFERTYPE_MASK)
+ {
+ case USB_EP_ATTR_XFER_CONTROL: cfg |= USBDEV_ENDPTCTRL_TXT_CTRL; break;
+ case USB_EP_ATTR_XFER_ISOC: cfg |= USBDEV_ENDPTCTRL_TXT_ISOC; break;
+ case USB_EP_ATTR_XFER_BULK: cfg |= USBDEV_ENDPTCTRL_TXT_BULK; break;
+ case USB_EP_ATTR_XFER_INT: cfg |= USBDEV_ENDPTCTRL_TXT_INTR; break;
+ }
+ lpc43_chgbits (0xFFFF0000, cfg, LPC43_USBDEV_ENDPTCTRL(privep->epphy));
+ }
+ else
+ {
+ /* Reset the data toggles */
+ uint32_t cfg = USBDEV_ENDPTCTRL_RXR;
+
+ /* Set the endpoint type */
+ switch (desc->attr & USB_EP_ATTR_XFERTYPE_MASK)
+ {
+ case USB_EP_ATTR_XFER_CONTROL: cfg |= USBDEV_ENDPTCTRL_RXT_CTRL; break;
+ case USB_EP_ATTR_XFER_ISOC: cfg |= USBDEV_ENDPTCTRL_RXT_ISOC; break;
+ case USB_EP_ATTR_XFER_BULK: cfg |= USBDEV_ENDPTCTRL_RXT_BULK; break;
+ }
+ lpc43_chgbits (0x0000FFFF, cfg, LPC43_USBDEV_ENDPTCTRL(privep->epphy));
+ }
+
+ /* Reset endpoint status */
+ privep->stalled = false;
+
+ /* Enable the endpoint */
+ if (LPC43_EPPHYIN(privep->epphy))
+ lpc43_setbits (USBDEV_ENDPTCTRL_TXE, LPC43_USBDEV_ENDPTCTRL(privep->epphy));
+ else
+ lpc43_setbits (USBDEV_ENDPTCTRL_RXE, LPC43_USBDEV_ENDPTCTRL(privep->epphy));
+
+ return OK;
+}
+
+/*******************************************************************************
+ * Name: lpc43_epdisable
+ *
+ * Description:
+ * The endpoint will no longer be used
+ *
+ *******************************************************************************/
+
+static int lpc43_epdisable(FAR struct usbdev_ep_s *ep)
+{
+ FAR struct lpc43_ep_s *privep = (FAR struct lpc43_ep_s *)ep;
+ irqstate_t flags;
+
+#ifdef CONFIG_DEBUG
+ if (!ep)
+ {
+ usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_INVALIDPARMS), 0);
+ return -EINVAL;
+ }
+#endif
+ usbtrace(TRACE_EPDISABLE, privep->epphy);
+
+ flags = irqsave();
+
+ /* Disable Endpoint */
+ if (LPC43_EPPHYIN(privep->epphy))
+ lpc43_clrbits (USBDEV_ENDPTCTRL_TXE, LPC43_USBDEV_ENDPTCTRL(privep->epphy));
+ else
+ lpc43_clrbits (USBDEV_ENDPTCTRL_RXE, LPC43_USBDEV_ENDPTCTRL(privep->epphy));
+
+ privep->stalled = true;
+
+ /* Cancel any ongoing activity */
+ lpc43_cancelrequests(privep, -ESHUTDOWN);
+
+ irqrestore(flags);
+ return OK;
+}
+
+/*******************************************************************************
+ * Name: lpc43_epallocreq
+ *
+ * Description:
+ * Allocate an I/O request
+ *
+ *******************************************************************************/
+
+static FAR struct usbdev_req_s *lpc43_epallocreq(FAR struct usbdev_ep_s *ep)
+{
+ FAR struct lpc43_req_s *privreq;
+
+#ifdef CONFIG_DEBUG
+ if (!ep)
+ {
+ usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_INVALIDPARMS), 0);
+ return NULL;
+ }
+#endif
+ usbtrace(TRACE_EPALLOCREQ, ((FAR struct lpc43_ep_s *)ep)->epphy);
+
+ privreq = (FAR struct lpc43_req_s *)malloc(sizeof(struct lpc43_req_s));
+ if (!privreq)
+ {
+ usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_ALLOCFAIL), 0);
+ return NULL;
+ }
+
+ memset(privreq, 0, sizeof(struct lpc43_req_s));
+ return &privreq->req;
+}
+
+/*******************************************************************************
+ * Name: lpc43_epfreereq
+ *
+ * Description:
+ * Free an I/O request
+ *
+ *******************************************************************************/
+
+static void lpc43_epfreereq(FAR struct usbdev_ep_s *ep, FAR struct usbdev_req_s *req)
+{
+ FAR struct lpc43_req_s *privreq = (FAR struct lpc43_req_s *)req;
+
+#ifdef CONFIG_DEBUG
+ if (!ep || !req)
+ {
+ usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_INVALIDPARMS), 0);
+ return;
+ }
+#endif
+
+ usbtrace(TRACE_EPFREEREQ, ((FAR struct lpc43_ep_s *)ep)->epphy);
+ free(privreq);
+}
+
+/*******************************************************************************
+ * Name: lpc43_epallocbuffer
+ *
+ * Description:
+ * Allocate an I/O buffer
+ *
+ *******************************************************************************/
+
+#ifdef CONFIG_ARCH_USBDEV_DMA
+static void *lpc43_epallocbuffer(FAR struct usbdev_ep_s *ep, unsigned bytes)
+{
+ usbtrace(TRACE_EPALLOCBUFFER, privep->epphy);
+ return malloc(bytes)
+}
+#endif
+
+/*******************************************************************************
+ * Name: lpc43_epfreebuffer
+ *
+ * Description:
+ * Free an I/O buffer
+ *
+ *******************************************************************************/
+
+#ifdef CONFIG_LPC433x_USBDEV_DMA
+static void lpc43_epfreebuffer(FAR struct usbdev_ep_s *ep, FAR void *buf)
+{
+ usbtrace(TRACE_EPFREEBUFFER, privep->epphy);
+ free(buf);
+}
+#endif
+
+/*******************************************************************************
+ * Name: lpc43_epsubmit
+ *
+ * Description:
+ * Submit an I/O request to the endpoint
+ *
+ *******************************************************************************/
+
+static int lpc43_epsubmit(FAR struct usbdev_ep_s *ep, FAR struct usbdev_req_s *req)
+{
+ FAR struct lpc43_req_s *privreq = (FAR struct lpc43_req_s *)req;
+ FAR struct lpc43_ep_s *privep = (FAR struct lpc43_ep_s *)ep;
+ FAR struct lpc43_usbdev_s *priv;
+ irqstate_t flags;
+ int ret = OK;
+
+#ifdef CONFIG_DEBUG
+ if (!req || !req->callback || !req->buf || !ep)
+ {
+ usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_INVALIDPARMS), 0);
+ ullvdbg("req=%p callback=%p buf=%p ep=%p\n", req, req->callback, req->buf, ep);
+ return -EINVAL;
+ }
+#endif
+
+ usbtrace(TRACE_EPSUBMIT, privep->epphy);
+ priv = privep->dev;
+
+ if (!priv->driver || priv->usbdev.speed == USB_SPEED_UNKNOWN)
+ {
+ usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_NOTCONFIGURED), priv->usbdev.speed);
+ return -ESHUTDOWN;
+ }
+
+ /* Handle the request from the class driver */
+
+ req->result = -EINPROGRESS;
+ req->xfrd = 0;
+
+ /* Disable Interrupts */
+
+ flags = irqsave();
+
+ /* If we are stalled, then drop all requests on the floor */
+
+ if (privep->stalled)
+ {
+ ret = -EBUSY;
+ }
+ else
+ {
+ /* Add the new request to the request queue for the endpoint */
+
+ if (LPC43_EPPHYIN(privep->epphy))
+ usbtrace(TRACE_INREQQUEUED(privep->epphy), privreq->req.len);
+ else
+ usbtrace(TRACE_OUTREQQUEUED(privep->epphy), privreq->req.len);
+
+ if (lpc43_rqenqueue(privep, privreq))
+ {
+ lpc43_progressep(privep);
+ }
+ }
+
+ irqrestore(flags);
+ return ret;
+}
+
+/*******************************************************************************
+ * Name: lpc43_epcancel
+ *
+ * Description:
+ * Cancel an I/O request previously sent to an endpoint
+ *
+ *******************************************************************************/
+
+static int lpc43_epcancel(FAR struct usbdev_ep_s *ep, FAR struct usbdev_req_s *req)
+{
+ FAR struct lpc43_ep_s *privep = (FAR struct lpc43_ep_s *)ep;
+ FAR struct lpc43_usbdev_s *priv;
+ irqstate_t flags;
+
+#ifdef CONFIG_DEBUG
+ if (!ep || !req)
+ {
+ usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_INVALIDPARMS), 0);
+ return -EINVAL;
+ }
+#endif
+
+ usbtrace(TRACE_EPCANCEL, privep->epphy);
+ priv = privep->dev;
+
+ flags = irqsave();
+
+ /* FIXME: if the request is the first, then we need to flush the EP
+ * otherwise just remove it from the list
+ *
+ * but ... all other implementations cancel all requests ...
+ */
+
+ lpc43_cancelrequests(privep, -ESHUTDOWN);
+ irqrestore(flags);
+ return OK;
+}
+
+/*******************************************************************************
+ * Name: lpc43_epstall
+ *
+ * Description:
+ * Stall or resume and endpoint
+ *
+ *******************************************************************************/
+
+static int lpc43_epstall(FAR struct usbdev_ep_s *ep, bool resume)
+{
+ FAR struct lpc43_ep_s *privep = (FAR struct lpc43_ep_s *)ep;
+ irqstate_t flags;
+
+ /* STALL or RESUME the endpoint */
+
+ flags = irqsave();
+ usbtrace(resume ? TRACE_EPRESUME : TRACE_EPSTALL, privep->epphy);
+
+ uint32_t addr = LPC43_USBDEV_ENDPTCTRL(privep->epphy);
+ uint32_t ctrl_xs = LPC43_EPPHYIN(privep->epphy) ? USBDEV_ENDPTCTRL_TXS : USBDEV_ENDPTCTRL_RXS;
+ uint32_t ctrl_xr = LPC43_EPPHYIN(privep->epphy) ? USBDEV_ENDPTCTRL_TXR : USBDEV_ENDPTCTRL_RXR;
+
+ if (resume)
+ {
+ privep->stalled = false;
+
+ /* Clear stall and reset the data toggle */
+
+ lpc43_chgbits (ctrl_xs | ctrl_xr, ctrl_xr, addr);
+ }
+ else
+ {
+ privep->stalled = true;
+
+ lpc43_setbits (ctrl_xs, addr);
+ }
+
+ irqrestore(flags);
+ return OK;
+}
+
+/*******************************************************************************
+ * Device operations
+ *******************************************************************************/
+
+/*******************************************************************************
+ * Name: lpc43_allocep
+ *
+ * Description:
+ * Allocate an endpoint matching the parameters.
+ *
+ * Input Parameters:
+ * eplog - 7-bit logical endpoint number (direction bit ignored). Zero means
+ * that any endpoint matching the other requirements will suffice. The
+ * assigned endpoint can be found in the eplog field.
+ * in - true: IN (device-to-host) endpoint requested
+ * eptype - Endpoint type. One of {USB_EP_ATTR_XFER_ISOC, USB_EP_ATTR_XFER_BULK,
+ * USB_EP_ATTR_XFER_INT}
+ *
+ *******************************************************************************/
+
+static FAR struct usbdev_ep_s *lpc43_allocep(FAR struct usbdev_s *dev, uint8_t eplog,
+ bool in, uint8_t eptype)
+{
+ FAR struct lpc43_usbdev_s *priv = (FAR struct lpc43_usbdev_s *)dev;
+ uint32_t epset = LPC43_EPALLSET & ~LPC43_EPCTRLSET;
+ irqstate_t flags;
+ int epndx = 0;
+
+ usbtrace(TRACE_DEVALLOCEP, (uint16_t)eplog);
+
+ /* Ignore any direction bits in the logical address */
+
+ eplog = USB_EPNO(eplog);
+
+ /* A logical address of 0 means that any endpoint will do */
+
+ if (eplog > 0)
+ {
+ /* Otherwise, we will return the endpoint structure only for the requested
+ * 'logical' endpoint. All of the other checks will still be performed.
+ *
+ * First, verify that the logical endpoint is in the range supported by
+ * by the hardware.
+ */
+
+ if (eplog >= LPC43_NLOGENDPOINTS)
+ {
+ usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_BADEPNO), (uint16_t)eplog);
+ return NULL;
+ }
+
+ /* Convert the logical address to a physical OUT endpoint address and
+ * remove all of the candidate endpoints from the bitset except for the
+ * the IN/OUT pair for this logical address.
+ */
+
+ epset &= 3 << (eplog << 1);
+ }
+
+ /* Get the subset matching the requested direction */
+
+ if (in)
+ {
+ epset &= LPC43_EPINSET;
+ }
+ else
+ {
+ epset &= LPC43_EPOUTSET;
+ }
+
+ /* Get the subset matching the requested type */
+
+ switch (eptype)
+ {
+ case USB_EP_ATTR_XFER_INT: /* Interrupt endpoint */
+ epset &= LPC43_EPINTRSET;
+ break;
+
+ case USB_EP_ATTR_XFER_BULK: /* Bulk endpoint */
+ epset &= LPC43_EPBULKSET;
+ break;
+
+ case USB_EP_ATTR_XFER_ISOC: /* Isochronous endpoint */
+ epset &= LPC43_EPISOCSET;
+ break;
+
+ case USB_EP_ATTR_XFER_CONTROL: /* Control endpoint -- not a valid choice */
+ default:
+ usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_BADEPTYPE), (uint16_t)eptype);
+ return NULL;
+ }
+
+ /* Is the resulting endpoint supported by the LPC433x? */
+
+ if (epset)
+ {
+ /* Yes.. now see if any of the request endpoints are available */
+
+ flags = irqsave();
+ epset &= priv->epavail;
+ if (epset)
+ {
+ /* Select the lowest bit in the set of matching, available endpoints */
+
+ for (epndx = 2; epndx < LPC43_NPHYSENDPOINTS; epndx++)
+ {
+ uint32_t bit = 1 << epndx;
+ if ((epset & bit) != 0)
+ {
+ /* Mark the IN/OUT endpoint no longer available */
+
+ priv->epavail &= ~(3 << (bit & ~1));
+ irqrestore(flags);
+
+ /* And return the pointer to the standard endpoint structure */
+
+ return &priv->eplist[epndx].ep;
+ }
+ }
+ /* Shouldn't get here */
+ }
+ irqrestore(flags);
+ }
+
+ usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_NOEP), (uint16_t)eplog);
+ return NULL;
+}
+
+/*******************************************************************************
+ * Name: lpc43_freeep
+ *
+ * Description:
+ * Free the previously allocated endpoint
+ *
+ *******************************************************************************/
+
+static void lpc43_freeep(FAR struct usbdev_s *dev, FAR struct usbdev_ep_s *ep)
+{
+ FAR struct lpc43_usbdev_s *priv = (FAR struct lpc43_usbdev_s *)dev;
+ FAR struct lpc43_ep_s *privep = (FAR struct lpc43_ep_s *)ep;
+ irqstate_t flags;
+
+ usbtrace(TRACE_DEVFREEEP, (uint16_t)privep->epphy);
+
+ if (priv && privep)
+ {
+ /* Mark the endpoint as available */
+
+ flags = irqsave();
+ priv->epavail |= (1 << privep->epphy);
+ irqrestore(flags);
+ }
+}
+
+/*******************************************************************************
+ * Name: lpc43_getframe
+ *
+ * Description:
+ * Returns the current frame number
+ *
+ *******************************************************************************/
+
+static int lpc43_getframe(struct usbdev_s *dev)
+{
+#ifdef CONFIG_LPC43_USBDEV_FRAME_INTERRUPT
+ FAR struct lpc43_usbdev_s *priv = (FAR struct lpc43_usbdev_s *)dev;
+
+ /* Return last valid value of SOF read by the interrupt handler */
+
+ usbtrace(TRACE_DEVGETFRAME, (uint16_t)priv->sof);
+ return priv->sof;
+#else
+ /* Return the last frame number detected by the hardware */
+
+ usbtrace(TRACE_DEVGETFRAME, 0);
+
+ /* FIXME: this actually returns the micro frame number! */
+ return (int)lpc43_getreg(LPC43_USBDEV_FRINDEX_OFFSET);
+#endif
+}
+
+/*******************************************************************************
+ * Name: lpc43_wakeup
+ *
+ * Description:
+ * Tries to wake up the host connected to this device
+ *
+ *******************************************************************************/
+
+static int lpc43_wakeup(struct usbdev_s *dev)
+{
+ irqstate_t flags;
+
+ usbtrace(TRACE_DEVWAKEUP, 0);
+
+ flags = irqsave();
+ lpc43_setbits(USBDEV_PRTSC1_FPR, LPC43_USBDEV_PORTSC1);
+ irqrestore(flags);
+ return OK;
+}
+
+/*******************************************************************************
+ * Name: lpc43_selfpowered
+ *
+ * Description:
+ * Sets/clears the device selfpowered feature
+ *
+ *******************************************************************************/
+
+static int lpc43_selfpowered(struct usbdev_s *dev, bool selfpowered)
+{
+ FAR struct lpc43_usbdev_s *priv = (FAR struct lpc43_usbdev_s *)dev;
+
+ usbtrace(TRACE_DEVSELFPOWERED, (uint16_t)selfpowered);
+
+#ifdef CONFIG_DEBUG
+ if (!dev)
+ {
+ usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_INVALIDPARMS), 0);
+ return -ENODEV;
+ }
+#endif
+
+ priv->selfpowered = selfpowered;
+ return OK;
+}
+
+/*******************************************************************************
+ * Name: lpc43_pullup
+ *
+ * Description:
+ * Software-controlled connect to/disconnect from USB host
+ *
+ *******************************************************************************/
+
+static int lpc43_pullup(struct usbdev_s *dev, bool enable)
+{
+ usbtrace(TRACE_DEVPULLUP, (uint16_t)enable);
+
+ irqstate_t flags = irqsave();
+ if (enable)
+ lpc43_setbits (USBDEV_USBCMD_RS, LPC43_USBDEV_USBCMD);
+ else
+ lpc43_clrbits (USBDEV_USBCMD_RS, LPC43_USBDEV_USBCMD);
+ irqrestore(flags);
+ return OK;
+}
+
+/*******************************************************************************
+ * Public Functions
+ *******************************************************************************/
+
+/*******************************************************************************
+ * Name: up_usbinitialize
+ *
+ * Description:
+ * Initialize USB hardware.
+ *
+ * Assumptions:
+ * - This function is called very early in the initialization sequence
+ * - PLL and GIO pin initialization is not performed here but should been in
+ * the low-level boot logic: PLL1 must be configured for operation at 48MHz
+ * and P0.23 and PO.31 in PINSEL1 must be configured for Vbus and USB connect
+ * LED.
+ *
+ *******************************************************************************/
+
+void up_usbinitialize(void)
+{
+ struct lpc43_usbdev_s *priv = &g_usbdev;
+ int i;
+
+ usbtrace(TRACE_DEVINIT, 0);
+
+ /* Disable USB interrupts */
+
+ lpc43_putreg(0, LPC43_USBDEV_USBINTR);
+
+ /* Initialize the device state structure */
+
+ memset(priv, 0, sizeof(struct lpc43_usbdev_s));
+ priv->usbdev.ops = &g_devops;
+ priv->usbdev.ep0 = &priv->eplist[LPC43_EP0_IN].ep;
+ priv->epavail = LPC43_EPALLSET;
+
+ /* Initialize the endpoint list */
+
+ for (i = 0; i < LPC43_NPHYSENDPOINTS; i++)
+ {
+ uint32_t bit = 1 << i;
+
+ /* Set endpoint operations, reference to driver structure (not
+ * really necessary because there is only one controller), and
+ * the physical endpoint number (which is just the index to the
+ * endpoint).
+ */
+ priv->eplist[i].ep.ops = &g_epops;
+ priv->eplist[i].dev = priv;
+
+ /* The index, i, is the physical endpoint address; Map this
+ * to a logical endpoint address usable by the class driver.
+ */
+
+ priv->eplist[i].epphy = i;
+ if (LPC43_EPPHYIN(i))
+ {
+ priv->eplist[i].ep.eplog = LPC43_EPPHYIN2LOG(i);
+ }
+ else
+ {
+ priv->eplist[i].ep.eplog = LPC43_EPPHYOUT2LOG(i);
+ }
+
+ /* The maximum packet size may depend on the type of endpoint */
+
+ if ((LPC43_EPCTRLSET & bit) != 0)
+ {
+ priv->eplist[i].ep.maxpacket = LPC43_EP0MAXPACKET;
+ }
+ else if ((LPC43_EPINTRSET & bit) != 0)
+ {
+ priv->eplist[i].ep.maxpacket = LPC43_INTRMAXPACKET;
+ }
+ else if ((LPC43_EPBULKSET & bit) != 0)
+ {
+ priv->eplist[i].ep.maxpacket = LPC43_BULKMAXPACKET;
+ }
+ else /* if ((LPC43_EPISOCSET & bit) != 0) */
+ {
+ priv->eplist[i].ep.maxpacket = LPC43_ISOCMAXPACKET;
+ }
+ }
+
+ /* Enable USB to AHB clock and to Event router*/
+
+ lpc43_enableclock (CLKID_USBOTGAHBCLK);
+ lpc43_enableclock (CLKID_EVENTROUTERPCLK);
+
+ /* Reset USB block */
+
+ lpc43_softreset (RESETID_USBOTGAHBRST);
+
+ /* Enable USB OTG PLL and wait for lock */
+
+ lpc43_putreg (0, LPC43_SYSCREG_USB_ATXPLLPDREG);
+
+ uint32_t bank = EVNTRTR_BANK(EVENTRTR_USBATXPLLLOCK);
+ uint32_t bit = EVNTRTR_BIT(EVENTRTR_USBATXPLLLOCK);
+
+ while (! (lpc43_getreg(LPC43_EVNTRTR_RSR(bank)) & (1 << bit)))
+ ;
+
+ /* Enable USB AHB clock */
+
+ lpc43_enableclock (CLKID_USBOTGAHBCLK);
+
+ /* Reset the controller */
+
+ lpc43_putreg (USBDEV_USBCMD_RST, LPC43_USBDEV_USBCMD);
+ while (lpc43_getreg (LPC43_USBDEV_USBCMD) & USBDEV_USBCMD_RST)
+ ;
+
+ /* Attach USB controller interrupt handler */
+
+ if (irq_attach(LPC43_IRQ_USBOTG, lpc43_usbinterrupt) != 0)
+ {
+ usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_IRQREGISTRATION),
+ (uint16_t)LPC43_IRQ_USBOTG);
+ goto errout;
+ }
+
+
+ /* Program the controller to be the USB device controller */
+
+ lpc43_putreg (USBDEV_USBMODE_SDIS | USBDEV_USBMODE_SLOM | USBDEV_USBMODE_CMDEVICE,
+ LPC43_USBDEV_USBMODE);
+
+ /* Disconnect device */
+
+ lpc43_pullup(&priv->usbdev, false);
+
+ /* Reset/Re-initialize the USB hardware */
+
+ lpc43_usbreset(priv);
+
+ return;
+
+errout:
+ up_usbuninitialize();
+}
+
+/*******************************************************************************
+ * Name: up_usbuninitialize
+ *******************************************************************************/
+
+void up_usbuninitialize(void)
+{
+ struct lpc43_usbdev_s *priv = &g_usbdev;
+ irqstate_t flags;
+
+ usbtrace(TRACE_DEVUNINIT, 0);
+
+ if (priv->driver)
+ {
+ usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_DRIVERREGISTERED), 0);
+ usbdev_unregister(priv->driver);
+ }
+
+ /* Disconnect device */
+
+ flags = irqsave();
+ lpc43_pullup(&priv->usbdev, false);
+ priv->usbdev.speed = USB_SPEED_UNKNOWN;
+
+ /* Disable and detach IRQs */
+
+ up_disable_irq(LPC43_IRQ_USBOTG);
+ irq_detach(LPC43_IRQ_USBOTG);
+
+ /* Reset the controller */
+
+ lpc43_putreg (USBDEV_USBCMD_RST, LPC43_USBDEV_USBCMD);
+ while (lpc43_getreg (LPC43_USBDEV_USBCMD) & USBDEV_USBCMD_RST)
+ ;
+
+ /* Turn off USB power and clocking */
+
+ lpc43_disableclock (CLKID_USBOTGAHBCLK);
+ lpc43_disableclock (CLKID_EVENTROUTERPCLK);
+
+
+ irqrestore(flags);
+}
+
+/*******************************************************************************
+ * Name: usbdev_register
+ *
+ * Description:
+ * Register a USB device class driver. The class driver's bind() method will be
+ * called to bind it to a USB device driver.
+ *
+ *******************************************************************************/
+
+int usbdev_register(struct usbdevclass_driver_s *driver)
+{
+ int ret;
+
+ usbtrace(TRACE_DEVREGISTER, 0);
+
+#ifdef CONFIG_DEBUG
+ if (!driver || !driver->ops->bind || !driver->ops->unbind ||
+ !driver->ops->disconnect || !driver->ops->setup)
+ {
+ usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_INVALIDPARMS), 0);
+ return -EINVAL;
+ }
+
+ if (g_usbdev.driver)
+ {
+ usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_DRIVER), 0);
+ return -EBUSY;
+ }
+#endif
+
+ /* First hook up the driver */
+
+ g_usbdev.driver = driver;
+
+ /* Then bind the class driver */
+
+ ret = CLASS_BIND(driver, &g_usbdev.usbdev);
+ if (ret)
+ {
+ usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_BINDFAILED), (uint16_t)-ret);
+ g_usbdev.driver = NULL;
+ }
+ else
+ {
+ /* Enable USB controller interrupts */
+
+ up_enable_irq(LPC43_IRQ_USBOTG);
+
+ /* FIXME: nothing seems to call DEV_CONNECT(), but we need to set
+ * the RS bit to enable the controller. It kind of makes sense
+ * to do this after the class has bound to us...
+ * GEN: This bug is really in the class driver. It should make the
+ * soft connect when it is ready to be enumerated. I have added
+ * that logic to the class drivers but left this logic here.
+ */
+
+ lpc43_pullup(&g_usbdev.usbdev, true);
+ }
+ return ret;
+}
+
+/*******************************************************************************
+ * Name: usbdev_unregister
+ *
+ * Description:
+ * Un-register usbdev class driver.If the USB device is connected to a USB host,
+ * it will first disconnect(). The driver is also requested to unbind() and clean
+ * up any device state, before this procedure finally returns.
+ *
+ *******************************************************************************/
+
+int usbdev_unregister(struct usbdevclass_driver_s *driver)
+{
+ usbtrace(TRACE_DEVUNREGISTER, 0);
+
+#ifdef CONFIG_DEBUG
+ if (driver != g_usbdev.driver)
+ {
+ usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_INVALIDPARMS), 0);
+ return -EINVAL;
+ }
+#endif
+
+ /* Unbind the class driver */
+
+ CLASS_UNBIND(driver, &g_usbdev.usbdev);
+
+ /* Disable USB controller interrupts */
+
+ up_disable_irq(LPC43_IRQ_USBOTG);
+
+ /* Unhook the driver */
+
+ g_usbdev.driver = NULL;
+ return OK;
+}