Device Drivers

NuttX supports a variety of device drivers including:

  • Character Device Drivers,

  • Block Device Drivers, and

  • Other Specialized Drivers.

These different device driver types are discussed in the following paragraphs. Note: device driver support depends on the in-memory, pseudo file system that is enabled by default.

Character Device Drivers

Character device drivers have these properties:

  • include/nuttx/fs/fs.h. All structures and APIs needed to work with character drivers are provided in this header file.

  • struct file_operations. Each character device driver must implement an instance of struct file_operations. That structure defines a call table with the following methods:

  • int register_driver(const char *path, const struct file_operations *fops, mode_t mode, void *priv);. Each character driver registers itself by calling register_driver(), passing it the path where it will appear in the pseudo-file-system and it’s initialized instance of struct file_operations.

  • User Access. After it has been registered, the character driver can be accessed by user code using the standard driver operations including open(), close(), read(), write(), etc.

  • Specialized Character Drivers. Within the common character driver framework, there are different specific varieties of specialized character drivers. The unique requirements of the underlying device hardware often mandates some customization of the character driver. These customizations tend to take the form of:

    • Device-specific ioctl() commands used to performed specialized operations on the device. These ioctl() will be documented in header files under include/nuttx that detail the specific device interface.

    • Specialized I/O formats. Some devices will require that read() and/or write() operations use data conforming to a specific format, rather than a plain stream of bytes. These specialized I/O formats will be documented in header files under include/nuttx that detail the specific device interface. The typical representation of the I/O format will be a C structure definition.

    The specialized character drivers support by NuttX are documented in the following paragraphs.

  • Examples: drivers/dev_null.c, drivers/fifo.c, drivers/serial.c, etc.

Serial Device Drivers

  • include/nuttx/serial/serial.h. All structures and APIs needed to work with serial drivers are provided in this header file.

  • struct uart_ops_s. Each serial device driver must implement an instance of struct uart_ops_s. That structure defines a call table with the following methods:

  • int uart_register(FAR const char *path, FAR uart_dev_t *dev);. A serial driver may register itself by calling uart_register(), passing it the path where it will appear in the pseudo-file-system and it’s initialized instance of struct uart_ops_s. By convention, serial device drivers are registered at paths like /dev/ttyS0, /dev/ttyS1, etc. See the uart_register() implementation in drivers/serial.c.

  • User Access. Serial drivers are, ultimately, normal character drivers and are accessed as other character drivers.

  • Examples: arch/arm/src/stm32/stm32_serial.c, arch/arm/src/lpc214x/lpc214x_serial.c, arch/z16/src/z16f/z16f_serial.c, etc.

Touchscreen Device Drivers

NuttX supports a two-part touchscreen driver architecture.

  1. An “upper half”, generic driver that provides the common touchscreen interface to application level code, and

  2. A “lower half”, platform-specific driver that implements the low-level touchscreen controls to implement the touchscreen functionality.

Files supporting the touchscreen controller (TSC) driver can be found in the following locations:

  • Interface Definition. The header files for NuttX touchscreen drivers reside in the include/nuttx/include/input directory. The interface between the touchscreen controller “upper half” and “lower half” drivers are not common, but vary from controller-to-controller. Because of this, each touchscreen driver has its own unique header file that describes the “upper half”/”lower half” interface in that directory. The application level interface to each touchscreen driver, on the other hand, is the same for each touchscreen driver and is described include/nuttx/include/input/touchscreen.h. The touchscreen driver uses a standard character driver framework but read operations return specially formatted data.

  • “Upper Half” Driver. The controller-specific, “upper half” touchscreen drivers reside in the directory drivers/input.

  • “Lower Half” Drivers. Platform-specific touchscreen drivers reside in either: (1) The arch/<architecture>/src/<hardware> directory for the processor architectures that have build in touchscreen controllers or (2) the boards/<arch>/<chip>/<board>/src/ directory for boards that use an external touchscreen controller chip.

Analog (ADC/DAC) Drivers

The NuttX analog drivers are split into two parts:

  1. An “upper half”, generic driver that provides the common analog interface to application level code, and

  2. A “lower half”, platform-specific driver that implements the low-level controls to implement the analog functionality.

  • General header files for the NuttX analog drivers reside in include/nuttx/analog/. These header files includes both the application level interface to the analog driver as well as the interface between the “upper half” and “lower half” drivers.

  • Common analog logic and share-able analog drivers reside in the drivers/analog/.

  • Platform-specific drivers reside in arch/<architecture>/src/<hardware> directory for the specific processor <architecture> and for the specific <chip> analog peripheral devices.

ADC Drivers

  • include/nuttx/analog/adc.h. All structures and APIs needed to work with ADC drivers are provided in this header file. This header file includes:

    1. Structures and interface descriptions needed to develop a low-level, architecture-specific, ADC driver.

    2. To register the ADC driver with a common ADC character driver.

    3. Interfaces needed for interfacing user programs with the common ADC character driver.

  • drivers/analog/adc.c. The implementation of the common ADC character driver.

DAC Drivers

  • include/nuttx/analog/dac.h. All structures and APIs needed to work with DAC drivers are provided in this header file. This header file includes:

    1. Structures and interface descriptions needed to develop a low-level, architecture-specific, DAC driver.

    2. To register the DAC driver with a common DAC character driver.

    3. Interfaces needed for interfacing user programs with the common DAC character driver.

  • drivers/analog/dac.c. The implementation of the common DAC character driver.

PWM Drivers

For the purposes of this driver, a PWM device is any device that generates periodic output pulses of controlled frequency and pulse width. Such a device might be used, for example, to perform pulse-width modulated output or frequency/pulse-count modulated output (such as might be needed to control a stepper motor).

The NuttX PWM driver is split into two parts:

  1. An “upper half”, generic driver that provides the common PWM interface to application level code, and

  2. A “lower half”, platform-specific driver that implements the low-level timer controls to implement the PWM functionality.

Files supporting PWM can be found in the following locations:

  • Interface Definition. The header file for the NuttX PWM driver reside at include/nuttx/timers/pwm.h. This header file includes both the application level interface to the PWM driver as well as the interface between the “upper half” and “lower half” drivers. The PWM module uses a standard character driver framework. However, since the PWM driver is a devices control interface and not a data transfer interface, the majority of the functionality available to the application is implemented in driver ioctl calls.

  • “Upper Half” Driver. The generic, “upper half” PWM driver resides at drivers/pwm.c.

  • “Lower Half” Drivers. Platform-specific PWM drivers reside in arch/<architecture>/src/<hardware> directory for the specific processor <architecture> and for the specific <chip> PWM peripheral devices.

CAN Drivers

NuttX supports only a very low-level CAN driver. This driver supports only the data exchange and does not include any high-level CAN protocol. The NuttX CAN driver is split into two parts:

  1. An “upper half”, generic driver that provides the common CAN interface to application level code, and

  2. A “lower half”, platform-specific driver that implements the low-level timer controls to implement the CAN functionality.

Files supporting CAN can be found in the following locations:

  • Interface Definition. The header file for the NuttX CAN driver resides at include/nuttx/can/can.h. This header file includes both the application level interface to the CAN driver as well as the interface between the “upper half” and “lower half” drivers. The CAN module uses a standard character driver framework.

  • “Upper Half” Driver. The generic, “upper half” CAN driver resides at drivers/can.c.

  • “Lower Half” Drivers. Platform-specific CAN drivers reside in arch/<architecture>/src/<hardware> directory for the specific processor <architecture> and for the specific <chip> CAN peripheral devices.

Usage Note: When reading from the CAN driver multiple messages may be returned, depending on (1) the size the returned can messages, and (2) the size of the buffer provided to receive CAN messages. Never assume that a single message will be returned… if you do this, you will lose CAN data under conditions where your read buffer can hold more than one small message. Below is an example about how you should think of the CAN read operation:

Quadrature Encoder Drivers

NuttX supports a low-level, two-part Quadrature Encoder driver.

  1. An “upper half”, generic driver that provides the common Quadrature Encoder interface to application level code, and

  2. A “lower half”, platform-specific driver that implements the low-level timer controls to implement the Quadrature Encoder functionality.

Files supporting the Quadrature Encoder can be found in the following locations:

  • Interface Definition. The header file for the NuttX Quadrature Encoder driver reside at include/nuttx/sensors/qencoder.h. This header file includes both the application level interface to the Quadrature Encoder driver as well as the interface between the “upper half” and “lower half” drivers. The Quadrature Encoder module uses a standard character driver framework.

  • “Upper Half” Driver. The generic, “upper half” Quadrature Encoder driver resides at drivers/sensors/qencoder.c.

  • “Lower Half” Drivers. Platform-specific Quadrature Encoder drivers reside in arch/<architecture>/src/<hardware> directory for the specific processor <architecture> and for the specific <chip> Quadrature Encoder peripheral devices.

Timer Drivers

NuttX supports a low-level, two-part timer driver.

  1. An “upper half”, generic driver that provides the common timer interface to application level code, and

  2. A “lower half”, platform-specific driver that implements the low-level timer controls to implement the timer functionality.

Files supporting the timer driver can be found in the following locations:

  • Interface Definition. The header file for the NuttX timer driver reside at include/nuttx/timers/timer.h. This header file includes both the application level interface to the timer driver as well as the interface between the “upper half” and “lower half” drivers. The timer driver uses a standard character driver framework.

  • “Upper Half” Driver. The generic, “upper half” timer driver resides at drivers/timers/timer.c.

  • “Lower Half” Drivers. Platform-specific timer drivers reside in arch/<architecture>/src/<hardware> directory for the specific processor <architecture> and for the specific <chip> timer peripheral devices.

RTC Drivers

NuttX supports a low-level, two-part RealTime Clock (RTC) driver.

  1. An “upper half”, generic driver that provides the common RTC interface to application level code, and

  2. A “lower half”, platform-specific driver that implements the low-level timer controls to implement the RTC functionality.

Files supporting the RTC driver can be found in the following locations:

  • Interface Definition. The header file for the NuttX RTC driver reside at include/nuttx/timers/rtc.h. This header file includes both the application level interface to the RTC driver as well as the interface between the “upper half” and “lower half” drivers. The RTC driver uses a standard character driver framework.

  • “Upper Half” Driver. The generic, “upper half” RTC driver resides at drivers/timers/rtc.c.

  • “Lower Half” Drivers. Platform-specific RTC drivers reside in arch/<architecture>/src/<hardware> directory for the specific processor <architecture> and for the specific <chip> RTC peripheral devices.

Watchdog Timer Drivers

NuttX supports a low-level, two-part watchdog timer driver.

  1. An “upper half”, generic driver that provides the common watchdog timer interface to application level code, and

  2. A “lower half”, platform-specific driver that implements the low-level timer controls to implement the watchdog timer functionality.

Files supporting the watchdog timer driver can be found in the following locations:

  • Interface Definition. The header file for the NuttX watchdog timer driver reside at include/nuttx/timers/watchdog.h. This header file includes both the application level interface to the watchdog timer driver as well as the interface between the “upper half” and “lower half” drivers. The watchdog timer driver uses a standard character driver framework.

  • “Upper Half” Driver. The generic, “upper half” watchdog timer driver resides at drivers/timers/watchdog.c.

  • “Lower Half” Drivers. Platform-specific watchdog timer drivers reside in arch/<architecture>/src/<hardware> directory for the specific processor <architecture> and for the specific <chip> watchdog timer peripheral devices.

Keyboard/Keypad Drivers

Keypads vs. Keyboards Keyboards and keypads are really the same devices for NuttX. A keypad is thought of as simply a keyboard with fewer keys.

Special Commands. In NuttX, a keyboard/keypad driver is simply a character driver that may have an (optional) encoding/decoding layer on the data returned by the character driver. A keyboard may return simple text data (alphabetic, numeric, and punctuation) or control characters (enter, control-C, etc.) when a key is pressed. We can think about this the “normal” keyboard data stream. However, in addition, most keyboards support actions that cannot be represented as text or control data. Such actions include things like cursor controls (home, up arrow, page down, etc.), editing functions (insert, delete, etc.), volume controls, (mute, volume up, etc.) and other special functions. In this case, some special encoding may be required to multiplex the normal text data and special command key press data streams.

Key Press and Release Events Sometimes the time that a key is released is needed by applications as well. Thus, in addition to normal and special key press events, it may also be necessary to encode normal and special key release events.

Encoding/Decoding Layer. An optional encoding/decoding layer can be used with the basic character driver to encode the keyboard events into the text data stream. The function interfaces that comprise that encoding/decoding layer are defined in the header file include/nuttx/input/kbd_code.h. These functions provide an matched set of (a) driver encoding interfaces, and (b) application decoding interfaces.

  1. Driver Encoding Interfaces. These are interfaces used by the keyboard/keypad driver to encode keyboard events and data.

    • kbd_press()

      Function Prototype:

      Description:

      Input Parameters:

      • ch: The character to be added to the output stream.

      • stream: An instance of lib_outstream_s to perform the actual low-level put operation.

      Returned Value:

    • kbd_release()

      Function Prototype:

      Description:

      Input Parameters:

      • ch: The character associated with the key that was released.

      • stream: An instance of lib_outstream_s to perform the actual low-level put operation.

      Returned Value:

    • kbd_specpress()

      Function Prototype:

      Description:

      Input Parameters:

      • keycode: The command to be added to the output stream. The enumeration enum kbd_keycode_e keycode identifies all commands known to the system.

      • stream: An instance of lib_outstream_s to perform the actual low-level put operation.

      Returned Value:

    • kbd_specrel()

      Function Prototype:

      Description:

      Input Parameters:

      • keycode: The command to be added to the output stream. The enumeration enum kbd_keycode_e keycode identifies all commands known to the system.

      • stream: An instance of lib_outstream_s to perform the actual low-level put operation.

      Returned Value:

  2. Application Decoding Interfaces. These are user interfaces to decode the values returned by the keyboard/keypad driver.

    • kbd_decode()

      Function Prototype:

      Description:

      Input Parameters:

      • stream: An instance of lib_instream_s to perform the actual low-level get operation.

      • pch: The location to save the returned value. This may be either a normal, character code or a special command (i.e., a value from enum kbd_getstate_s.

      • state: A user provided buffer to support parsing. This structure should be cleared the first time that kbd_decode() is called.

      Returned Value:

      • KBD_PRESS (0)**: Indicates the successful receipt of normal, keyboard data. This corresponds to a keypress event. The returned value in pch is a simple byte of text or control data.

      • KBD_RELEASE (1)**: Indicates a key release event. The returned value in pch is the byte of text or control data corresponding to the released key.

      • KBD_SPECPRESS (2)**: Indicates the successful receipt of a special keyboard command. The returned value in pch is a value from enum kbd_getstate_s.

      • KBD_SPECREL (3)**: Indicates a special command key release event. The returned value in pch is a value from enum kbd_getstate_s.

      • KBD_ERROR (EOF)**: An error has getting the next character (reported by the stream). Normally indicates the end of file.

I/O Streams. Notice the use of the abstract I/O streams in these interfaces. These stream interfaces are defined in include/nuttx/streams.h.

Block Device Drivers

Block device drivers have these properties:

  • include/nuttx/fs/fs.h. All structures and APIs needed to work with block drivers are provided in this header file.

  • struct block_operations. Each block device driver must implement an instance of struct block_operations. That structure defines a call table with the following methods:

  • int register_blockdriver(const char *path, const struct block_operations *bops, mode_t mode, void *priv);. Each block driver registers itself by calling register_blockdriver(), passing it the path where it will appear in the pseudo-file-system and it’s initialized instance of struct block_operations.

  • User Access. Users do not normally access block drivers directly, rather, they access block drivers indirectly through the mount() API. The mount() API binds a block driver instance with a file system and with a mountpoint. Then the user may use the block driver to access the file system on the underlying media. Example: See the cmd_mount() implementation in apps/nshlib/nsh_fscmds.c.

  • Accessing a Character Driver as a Block Device. See the loop device at drivers/loop.c. Example: See the cmd_losetup() implementation in apps/nshlib/nsh_fscmds.c.

  • Accessing a Block Driver as Character Device. See the Block-to-Character (BCH) conversion logic in drivers/bch/. Example: See the cmd_dd() implementation in apps/nshlib/nsh_ddcmd.c.

  • Examples. drivers/loop.c, drivers/mmcsd/mmcsd_spi.c, drivers/ramdisk.c, etc.

Specialized Device Drivers

All device drivers that are accessible to application logic are either: (1) Character device drivers that can be accessed via the standard driver operations (open(), close(), read(), write(), etc.), or (2) block drivers that can be accessing only as part of mounting a file system or other special use cases as described in the preceding paragraph.

In addition to this, there are also specialized “drivers” that can be used only within the OS logic itself and are not accessible to application logic. These specialized drivers are discussed in the following paragraphs.

Ethernet Device Drivers

  • include/nuttx/net/netdev.h. All structures and APIs needed to work with Ethernet drivers are provided in this header file. The structure struct net_driver_s defines the interface and is passed to the network via netdev_register().

  • int netdev_register(FAR struct net_driver_s *dev, enum net_lltype_e lltype);. Each Ethernet driver registers itself by calling netdev_register().

  • Examples: drivers/net/dm90x0.c, arch/drivers/arm/src/c5471/c5471_ethernet.c, arch/z80/src/ez80/ez80_emac.c, etc.

SPI Device Drivers

  • include/nuttx/spi/spi.h. All structures and APIs needed to work with SPI drivers are provided in this header file.

  • struct spi_ops_s. Each SPI device driver must implement an instance of struct spi_ops_s. That structure defines a call table with the following methods:

  • Binding SPI Drivers. SPI drivers are not normally directly accessed by user code, but are usually bound to another, higher level device driver. See for example, int mmcsd_spislotinitialize(int minor, int slotno, FAR struct spi_dev_s *spi) in drivers/mmcsd/mmcsd_spi.c. In general, the binding sequence is:

    1. Get an instance of struct spi_dev_s from the hardware-specific SPI device driver, and

    2. Provide that instance to the initialization method of the higher level device driver.

  • Examples: drivers/loop.c, drivers/mmcsd/mmcsd_spi.c, drivers/ramdisk.c, etc.

I2C Device Drivers

  • include/nuttx/i2c/i2c.h. All structures and APIs needed to work with I2C drivers are provided in this header file.

  • struct i2c_ops_s. Each I2C device driver must implement an instance of struct i2c_ops_s. That structure defines a call table with the following methods:

  • Binding I2C Drivers. I2C drivers are not normally directly accessed by user code, but are usually bound to another, higher level device driver. In general, the binding sequence is:

    1. Get an instance of struct i2c_master_s from the hardware-specific I2C device driver, and

    2. Provide that instance to the initialization method of the higher level device driver.

  • Examples: arch/z80/src/ez80/ez80_i2c.c, arch/z80/src/z8/z8_i2c.c, etc.

Frame Buffer Drivers

  • include/nuttx/video/fb.h. All structures and APIs needed to work with frame buffer drivers are provided in this header file.

  • struct fb_vtable_s. Each frame buffer device driver must implement an instance of struct fb_vtable_s. That structure defines a call table with the following methods:

    Get information about the video controller configuration and the configuration of each color plane.

    The following are provided only if the video hardware supports RGB color mapping:

    The following are provided only if the video hardware supports a hardware cursor:

  • Binding Frame Buffer Drivers. Frame buffer drivers are not normally directly accessed by user code, but are usually bound to another, higher level device driver. In general, the binding sequence is:

    1. Get an instance of struct fb_vtable_s from the hardware-specific frame buffer device driver, and

    2. Provide that instance to the initialization method of the higher level device driver.

  • Examples: arch/sim/src/up_framebuffer.c. See also the usage of the frame buffer driver in the graphics/ directory.

LCD Drivers

  • include/nuttx/lcd/lcd.h. Structures and APIs needed to work with LCD drivers are provided in this header file. This header file also depends on some of the same definitions used for the frame buffer driver as provided in include/nuttx/video/fb.h.

  • struct lcd_dev_s. Each LCD device driver must implement an instance of struct lcd_dev_s. That structure defines a call table with the following methods:

    Get information about the LCD video controller configuration and the configuration of each LCD color plane.

    The following are provided only if the video hardware supports RGB color mapping:

    The following are provided only if the video hardware supports a hardware cursor:

    Get the LCD panel power status (0: full off - CONFIG_LCD_MAXPOWER: full on). On backlit LCDs, this setting may correspond to the backlight setting.

    Enable/disable LCD panel power (0: full off - CONFIG_LCD_MAXPOWER: full on). On backlit LCDs, this setting may correspond to the backlight setting.

    Get the current contrast setting (0-CONFIG_LCD_MAXCONTRAST) */

    Set LCD panel contrast (0-CONFIG_LCD_MAXCONTRAST)

  • Binding LCD Drivers. LCD drivers are not normally directly accessed by user code, but are usually bound to another, higher level device driver. In general, the binding sequence is:

    1. Get an instance of struct lcd_dev_s from the hardware-specific LCD device driver, and

    2. Provide that instance to the initialization method of the higher level device driver.

  • Examples: drivers/lcd/p14201.c, boards/arm/sam34/sam3u-ek/src/up_lcd.c. See also the usage of the LCD driver in the graphics/ directory.

Memory Technology Device Drivers

  • include/nuttx/mtd/mtd.h. All structures and APIs needed to work with MTD drivers are provided in this header file.

  • struct mtd_dev_s. Each MTD device driver must implement an instance of struct mtd_dev_s. That structure defines a call table with the following methods:

    Erase the specified erase blocks (units are erase blocks):

    Read/write from the specified read/write blocks:

    Some devices may support byte oriented reads (optional). Most MTD devices are inherently block oriented so byte-oriented accesses are not supported. It is recommended that low-level drivers not support read() if it requires buffering.

    Some devices may also support byte oriented writes (optional). Most MTD devices are inherently block oriented so byte-oriented accesses are not supported. It is recommended that low-level drivers not support read() if it requires buffering. This interface is only available if CONFIG_MTD_BYTE_WRITE is defined.

    Support other, less frequently used commands:

    • MTDIOC_GEOMETRY: Get MTD geometry

    • MTDIOC_XIPBASE:: Convert block to physical address for eXecute-In-Place

    • MTDIOC_BULKERASE: Erase the entire device

    is provided via a single ioctl method (see include/nuttx/fs/ioctl.h):

  • Binding MTD Drivers. MTD drivers are not normally directly accessed by user code, but are usually bound to another, higher level device driver. In general, the binding sequence is:

    1. Get an instance of struct mtd_dev_s from the hardware-specific MTD device driver, and

    2. Provide that instance to the initialization method of the higher level device driver.

  • Examples: drivers/mtd/m25px.c and drivers/mtd/ftl.c

SDIO Device Drivers

  • include/nuttx/sdio.h. All structures and APIs needed to work with SDIO drivers are provided in this header file.

  • struct sdio_dev_s. Each SDIO device driver must implement an instance of struct sdio_dev_s. That structure defines a call table with the following methods:

    Mutual exclusion:

    Initialization/setup:

    Command/Status/Data Transfer:

    Event/Callback support:

    DMA support:

  • Binding SDIO Drivers. SDIO drivers are not normally directly accessed by user code, but are usually bound to another, higher level device driver. In general, the binding sequence is:

    1. Get an instance of struct sdio_dev_s from the hardware-specific SDIO device driver, and

    2. Provide that instance to the initialization method of the higher level device driver.

  • Examples: arch/arm/src/stm32/stm32_sdio.c and drivers/mmcsd/mmcsd_sdio.c

USB Host-Side Drivers

  • include/nuttx/usb/usbhost.h. All structures and APIs needed to work with USB host-side drivers are provided in this header file.

  • struct usbhost_driver_s and struct usbhost_connection_s. Each USB host controller driver must implement an instance of struct usbhost_driver_s and struct usbhost_connection_s: struct usbhost_driver_s provides the interface between the USB host driver and the USB class driver; struct usbhost_connection_s provides the interface between the USB host driver and platform-specific connection management and device enumeration logic. These structures are defined in include/nuttx/usb/usbhost.h.

    Examples: arch/arm/src/lpc17xx_40xx/lpc17_40_usbhost.c, arch/arm/src/stm32/stm32_otgfshost.c, arch/arm/src/sama5/sam_ohci.c, and arch/arm/src/sama5/sam_ehci.c.

  • struct usbhost_class_s. Each USB host class driver must implement an instance of struct usbhost_class_s. This structure is also defined in include/nuttx/usb/usbhost.h.

    Examples: drivers/usbhost/usbhost_storage.c

  • USB Host Class Driver Registry. The NuttX USB host infrastructure includes a registry. During its initialization, each USB host class driver must call the interface, usbhost_registerclass() in order add its interface to the registry. Later, when a USB device is connected, the USB host controller will look up the USB host class driver that is needed to support the connected device in this registry.

    Examples: drivers/usbhost/usbhost_registry.c, drivers/usbhost/usbhost_registerclass.c, and drivers/usbhost/usbhost_findclass.c,

  • Detection and Enumeration of Connected Devices. Each USB host device controller supports two methods that are used to detect and enumeration newly connected devices (and also detect disconnected devices):

    • int (*wait)(FAR struct usbhost_connection_s *drvr, FAR const bool *connected);

      Wait for a device to be connected or disconnected.

    • int (*enumerate)(FAR struct usbhost_connection_s *drvr, int rhpndx);

      Enumerate the device connected to a root hub port. As part of this enumeration process, the driver will (1) get the device’s configuration descriptor, (2) extract the class ID info from the configuration descriptor, (3) call usbhost_findclass() to find the class that supports this device, (4) call the create() method on the struct usbhost_registry_s interface to get a class instance, and finally (5) call the connect() method of the struct usbhost_class_s interface. After that, the class is in charge of the sequence of operations.

  • Binding USB Host-Side Drivers. USB host-side controller drivers are not normally directly accessed by user code, but are usually bound to another, higher level USB host class driver. The class driver exports the standard NuttX device interface so that the connected USB device can be accessed just as with other, similar, on-board devices. For example, the USB host mass storage class driver (drivers/usbhost/usbhost_storage.c) will register a standard, NuttX block driver interface (like /dev/sda) that can be used to mount a file system just as with any other other block driver instance. In general, the binding sequence is:

    1. Each USB host class driver includes an initialization entry point that is called from the application at initialization time. This driver calls usbhost_registerclass() during this initialization in order to makes itself available in the event the device that it supports is connected.

      Examples: The function usbhost_msc_initialize() in the file drivers/usbhost/usbhost_storage.c

    2. Each application must include a waiter thread thread that (1) calls the USB host controller driver’s wait() to detect the connection of a device, and then (2) call the USB host controller driver’s enumerate method to bind the registered USB host class driver to the USB host controller driver.

      Examples: The function nsh_waiter() in the file boards/arm/lpc17xx_40xx/olimex-lpc1766stk/src/lpc17_40_appinit.c.

    3. As part of its operation during the binding operation, the USB host class driver will register an instances of a standard NuttX driver under the /dev directory. To repeat the above example, the USB host mass storage class driver (drivers/usbhost/usbhost_storage.c) will register a standard, NuttX block driver interface (like /dev/sda) that can be used to mount a file system just as with any other other block driver instance.

      Examples: See the call to register_blockdriver() in the function usbhost_initvolume() in the file drivers/usbhost/usbhost_storage.c.

USB Device-Side Drivers

  • include/nuttx/usb/usbdev.h. All structures and APIs needed to work with USB device-side drivers are provided in this header file.

  • include/nuttx/usb/usbdev_trace.h. Declarations needed to work with the NuttX USB device driver trace capability. That USB trace capability is detailed in separate document.

  • struct usbdev_s. Each USB device controller driver must implement an instance of struct usbdev_s. This structure is defined in include/nuttx/usb/usbdev.h.

    Examples: arch/arm/src/dm320/dm320_usbdev.c, arch/arm/src/lpc17xx_40xx/lpc17_40_usbdev.c, arch/arm/src/lpc214x/lpc214x_usbdev.c, arch/arm/src/lpc313x/lpc313x_usbdev.c, and arch/arm/src/stm32/stm32_usbdev.c.

  • struct usbdevclass_driver_s. Each USB device class driver must implement an instance of struct usbdevclass_driver_s. This structure is also defined in include/nuttx/usb/usbdev.h.

    Examples: drivers/usbdev/pl2303.c and drivers/usbdev/usbmsc.c

  • Binding USB Device-Side Drivers. USB device-side controller drivers are not normally directly accessed by user code, but are usually bound to another, higher level USB device class driver. The class driver is then configured to export the USB device functionality. In general, the binding sequence is:

    1. Each USB device class driver includes an initialization entry point that is called from the application at initialization time.

      Examples: The function usbdev_serialinitialize() in the file drivers/usbdev/pl2303.c and the function in the file drivers/usbdev/usbmsc.c

    2. These initialization functions called the driver API, usbdev_register(). This driver function will bind the USB class driver to the USB device controller driver, completing the initialization.