diff options
author | patacongo <patacongo@7fd9a85b-ad96-42d3-883c-3090e2eb8679> | 2012-08-07 20:18:56 +0000 |
---|---|---|
committer | patacongo <patacongo@7fd9a85b-ad96-42d3-883c-3090e2eb8679> | 2012-08-07 20:18:56 +0000 |
commit | ed303232fa84efe26f95a86e671995cfb712f321 (patch) | |
tree | 9bcbe9c4ed8c3d704c08ea4e346005ee47f6a69f /nuttx | |
parent | 756fc3f2db435855a76535c9863ad5d5d197b9dc (diff) | |
download | px4-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/ChangeLog | 7 | ||||
-rw-r--r-- | nuttx/Documentation/NuttShell.html | 901 | ||||
-rw-r--r-- | nuttx/Documentation/NuttX.html | 14 | ||||
-rw-r--r-- | nuttx/TODO | 25 | ||||
-rw-r--r-- | nuttx/arch/arm/src/lpc43xx/Make.defs | 7 | ||||
-rw-r--r-- | nuttx/arch/arm/src/lpc43xx/lpc43_usb0dev.c | 2663 |
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 "Built-In" 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> 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> 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, …); +</pre></ul> +<p> + So if you only wanted to output "Hello, World!" 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, "e;Hello, World!"e;); + 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 "<code>nsh> help</code>". +</p> +<p> + So, for you sample commnd, you would add the following the to the <code>g_cmdmap[]</code> table: +</p> +<ul><pre> +{ "mycmd", 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 "Built-In" 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 "Built-In" Applications for historic reasons. + That terminology is somewhat confusing because the actual NSH commands as described above are truly "built-into" 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 "nsh> help". + 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 "Named Applications". + 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 "<code>nsh> help</code>" 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 "built-in" 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("Hello, World!!\n"); + 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 "Register: $1" + @echo "{ \"$1\", $2, $3, $4 }," >> "$(APPDIR)/namedapp/namedapp_list.h" + @echo "EXTERN int $4(int argc, char *argv[]);" >> "$(APPDIR)/namedapp/namedapp_proto.h" +endef +</pre></ul> + <p> + When this macro runs, you will see the output in the build "<code>Register: hello</code>", 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=<application name> +</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><application name></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 (&) 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/<board>/<configuration>/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> 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 > 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/<board>/include</code>will be used. + NOTE when the OS is configured, <code>include/arch/board</code> will be linked to <code>configs/<board>/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; +} |