========================* 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:

  1. This function calls nsh_initialize() which initializes the NSH library. nsh_initialize() is described in more detail below.

  2. 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.

  3. 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 four things:

  1. nsh_romfsetc(): If so configured, it executes NSH system init and start-up script that can be found at /etc/init.d/rc.sysinit and /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 rc.sysinit system init 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 rc.sysinit 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 rc.sysinit.template template file can be found at apps/nshlib/rc.sysinit.template. The resulting ROMFS file system can be found in apps/nshlib/nsh_romfsimg.h.

  2. 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.

  3. nsh_netinit(): The nsh_netinit() function can be found in apps/nshlib/nsh_netinit.c.

  4. The start-up script rcS is executed after the system-init script to startup some application and other system service.

    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.

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. 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.

Adding New NSH Commands

New commands can be added to the NSH very easily. You simply need to add two things:

  1. The implementation of your command, and

  2. 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:

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:

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:

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:

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:

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:

{ "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.