Removing Device Drivers with NSH

NuttX and Unix-like Operating Systems Compared

There are many things that are called device drivers. In this Wiki page, we are referring only to character device drivers. In NuttX, character devices drivers are represented by device driver nodes in the top-level pseudo filesystem.

Standard Unix-like operating systems also support device driver nodes and they appear superficially like NuttX device driver nodes: Both look like files and usually reside under the top-level /dev directory. Both work-alike: Both can be accessed with standard POSIX files system commands like open(), close(), read(), write(), etc. But the similarities end there.

The payload of the standard Unix-like operating system device driver node is a device major and minor number. The major and minor device numbers are used to look up the actual device driver interface using logic and datastructures within the OS. The NuttX device node, on the other hand, holds that device driver interface directly with no intervening lookup. This is a less flexible design, but more efficient and more conservative a the limited resources of an embedded system.

In standard Unix-like operating systems, the device node can simply be deleted using the rm shell command or unlink() program interface. The node is removed is removed and nothing particular happens to the device driver itself (other than it may not longer be accessible).

But if the device node were simply removed in NuttX, then the entire device interface would also be removed and the driver would be basically broken. Internally, NuttX supports an interfaces called unregister_driver() that can be called to remove a device driver. So removing the device driver node must behave as though that interface were called.

The unlink method

How does NuttX accomplish this? It accomplish this using a special device driver method called unlink().

NuttX device drivers are implemented using a vtable of function pointers. That vtable defines the interface between the pseudo-file system and a device driver. This vtable is the structure struct file_operations defined in the header file [nuttx]/include/nuttx/fs/fs.h. That interface defines several intefaces which, for the most part, closely match the standard POSIX interfaces – open(), close(), read(), write(), etc. – and also includes a method called unlink(). This unlink() method is called by the NuttX VFS when the user attempts to remove a device driver nodes.

NOTE: Removal of device driver nodes is only permitted if CONFIG_DISABLE_PSEUDOFS_OPERATIONS is not defined. All of operations on the pseudo-file system may be suppressed in order to reduce the FLASH footprint in very resource limited systems.

Removing a Device Node from NSH

Here, then, is a summary of what should happen when you delete a device node using the NSH rm command:

  • The user enters the rm command. The NSH parser recognizes the rm command and transfers control to the NSH function cmd_rm().
  • cmd_rm() will verify the command, then call the standard POSIX unlink() interface. The logic of the VFS unlink() function in [nuttx/fs/vfs/fs_unlink.c will then run.
  • The VFS's unlink() will detect that the target to be removed is an a device node in the top-level pseudo-file system. It will call the device driver's unlink() method and, in any event, it will remove the device node from the pseudo-filesystem. The underlying resources needed to support the device driver interface may, however, persist until the device driver decides to free those resources.
  • When the device driver's unlink() method is called, it will determine if it is possible to free the device resources now. If so, it will free the device driver resources. But if, for example, there are clients of the device driver that still have open references to the device driver, it may defer freeing those resources until the last client has closed the device driver and there are no open references. In that case, it may simply set a flag indicating that the device driver has been unlinked.
  • If freeing of device driver resources has been deferred, then that flag will be examined later. For example, when the last client of the device driver has closed its reference to the driver it will check if the unlink operation has been deferred and, in that case, it will then free all of the device driver resources at that time.

WARNING

Some character device driver instances do not support the unlink() method. If you see problems removing character drivers in the manner described in this Wiki page, the most likely cause would be this missing implementation.