incubator-nuttx/Documentation/applications/nsh/customizing.rst

214 lines
8.1 KiB
ReStructuredText

*************************
Customizing the NuttShell
*************************
**Overview.** 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 with Bash
shell programming. The paragraphs in this appendix will focus on
customizing NSH: Adding new commands, changing the initialization
sequence, etc.
The NSH Library and NSH Initialization
**************************************
**Overview.** NSH is implemented as a library that can be found at
``apps/nshlib``. 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 ``apps/examples/nsh/nsh_main.c``
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 ``main()`` function. That initialization performed
by that example is discussed in the following paragraphs.
NSH Initialization sequence
~~~~~~~~~~~~~~~~~~~~~~~~~~~
The NSH start-up sequence is very simple. As an example, the code at
``apps/system/nsh/nsh_main.c`` illustrates how to start NSH. It simple
does the following:
#. This function calls ``nsh_initialize()`` which initializes the NSH
library. ``nsh_initialize()`` is described in more detail below.
#. If the Telnetconsole is enabled, it calls ``nsh_telnetstart()`` which
resides in the NSH library. ``nsh_telnetstart()`` will start the
Telnet daemon that will listen for Telnet connections and start
remote NSH sessions.
#. If a local console is enabled (probably on a serial port), then
``nsh_consolemain()`` is called. ``nsh_consolemain()`` also resides
in the NSH library. ``nsh_consolemain()`` does not return so that
finished the entire NSH initialization sequence.
``nsh_initialize()``
~~~~~~~~~~~~~~~~~~~~
The NSH initialization function, ``nsh_initialize()``, be found in
``apps/nshlib/nsh_init.c``. It does only three things:
#. ``nsh_romfsetc()``: If so configured, it executes an NSH start-up
script that can be found at ``/etc/init.d/rcS`` in the target file
system. The ``nsh_romfsetc()`` function can be found in
``apps/nshlib/nsh_romfsetc.c``. This function will (1) register a
ROMFS file system, then (2) mount the ROMFS file system. ``/etc`` is
the default location where a read-only, ROMFS file system is mounted
by ``nsh_romfsetc()``.
The ROMFS image is, itself, just built into the firmware. By default,
this ``rcS`` start-up script contains the following logic::
# Create a RAMDISK and mount it at XXXRDMOUNTPOINTXXX
mkrd -m XXXMKRDMINORXXX -s XXMKRDSECTORSIZEXXX XXMKRDBLOCKSXXX
mkfatfs /dev/ramXXXMKRDMINORXXX
mount -t vfat /dev/ramXXXMKRDMINORXXX XXXRDMOUNTPOINTXXX
Where the ``XXXX*XXXX`` strings get replaced in the template when the
ROMFS image is created:
- ``XXXMKRDMINORXXX`` will become the RAM device minor number.
Default: 0
- ``XXMKRDSECTORSIZEXXX`` will become the RAM device sector size
- ``XXMKRDBLOCKSXXX`` will become the number of sectors in the
device.
- ``XXXRDMOUNTPOINTXXX`` will become the configured mount point.
Default: ``/etc``
By default, the substituted values would yield an ``rcS`` file like::
# Create a RAMDISK and mount it at /tmp
mkrd -m 1 -s 512 1024
mkfatfs /dev/ram1
mount -t vfat /dev/ram1 /tmp
This script will, then:
- Create a RAMDISK of size 512*1024 bytes at ``/dev/ram1``,
- Format a FAT file system on the RAM disk at ``/dev/ram1``, and
then
- Mount the FAT file system at a configured mountpoint, ``/tmp``.
This ``rcS`` template file can be found at
``apps/nshlib/rcS.template``. The resulting ROMFS file system can be
found in ``apps/nshlib/nsh_romfsimg.h``.
#. ``board_app_initialize()``: Next any architecture-specific NSH
initialization will be performed (if any). For the STM3240G-EVAL,
this architecture specific initialization can be found at
``boards/arm/stm32/stm3240g-eval/src/stm32_appinit.c``. This it does
things like: (1) Initialize SPI devices, (2) Initialize SDIO, and (3)
mount any SD cards that may be inserted.
#. ``nsh_netinit()``: The ``nsh_netinit()`` function can be found in
``apps/nshlib/nsh_netinit.c``.
NSH Commands
************
**Overview.** NSH supports a variety of commands as part of the NSH
program. All of the NSH commands are listed in the NSH documentation
`above <#cmdoverview>`__. 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:
For example, if network support is disabled, then all network-related
commands will be missing from the list of commands presented by
'``nsh> help``'. You can see the specific command dependencies in the
table `above <#cmddependencies>`__.
Adding New NSH Commands
~~~~~~~~~~~~~~~~~~~~~~~
New commands can be added to the NSH very easily. You simply need to add
two things:
#. The implementation of your command, and
#. A new entry in the NSH command table
**Implementation of Your Command.** For example, if you want to add a
new a new command called ``mycmd`` to NSH, you would first implement the
``mycmd`` code in a function with this prototype:
.. code-block:: c
int cmd_mycmd(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv);
The ``argc`` and ``argv`` are used to pass command line arguments to the
NSH command. Command line parameters are passed in a very standard way:
``argv[0]`` will be the name of the command, and ``argv[1]`` through
``argv[argc-1]`` are the additional arguments provided on the NSH
command line.
The first parameter, ``vtbl``, 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 ``vtbl`` argument
when you interact with the NSH logic. The only use you will need to make
of the ``vtbl`` argument will be for outputting data to the console. You
don't use ``printf()`` within NSH commands. Instead you would use:
.. code-block:: c
void nsh_output(FAR struct nsh_vtbl_s *vtbl, const char *fmt, ...);
So if you only wanted to output "Hello, World!" on the console, then
your whole command implementation might be:
.. code-block:: c
int cmd_mycmd(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
{
nsh_output(vtbl, "Hello, World!");
return 0;
}
The prototype for the new command should be placed in
``apps/examples/nshlib/nsh.h``.
**Adding You Command to the NSH Command Table**. All of the commands
support by NSH appear in a single table called:
.. code-block:: c
const struct cmdmap_s g_cmdmap[]
That table can be found in the file
``apps/examples/nshlib/nsh_parse.c``. The structure ``cmdmap_s`` is also
defined in ``apps/nshlib/nsh_parse.c``:
.. code-block:: c
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 */
};
This structure provides everything that you need to describe your
command: Its name (``cmd``), the function that handles the command
(``cmd_mycmd()``), 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 "``nsh> help``".
So, for you sample command, you would add the following the to the
``g_cmdmap[]`` table:
.. code-block:: c
{ "mycmd", cmd_mycmd, 1, 1, NULL },
This entry is particularly simply because ``mycmd`` is so simple. Look
at the other commands in ``g_cmdmap[]`` for more complex examples.