nuttx/Documentation/components/drivers/character/gpio.rst
Justin Hammond 1528b470b9 drivers/ioexpander: List the registered pins in /proc/gpio.
The pins a board publishes are visible in /dev and each can be read
through its own node, but surveying a whole board that way means an open
and two ioctls per pin, and the signal and interrupt counters the upper
half keeps are not reachable through any of them.

Adds a list of registered pins and publishes it as /proc/gpio, behind
GPIO_PROCFS: a quality of life view of the same kind as /proc/pinctrl
and /proc/reset.  Every common field comes from state the upper half
already holds: the pin type, the value through go_read(), how many times
the pin has been registered for signals, and how many interrupts it has
taken.  Lines carry the same key:value tokens in the same order, so the
file is machine parseable.

Lower halves may supply an optional go_describe() adding what only they
can say, such as which pad carries the line or how its trigger is
configured.  It writes text into a caller supplied buffer and the upper
half owns the line, so a lower half needs no procfs knowledge.  A lower
half without it is listed with the common tokens alone.

The pin type index is bounded before use: it comes from the lower half
and the name table cannot cover a type the enumeration does not define.
A pin that cannot be read reports val:- rather than a zero that would
read as a real level.

procfs_register() appends without checking for duplicates, so the entry
is claimed once for the lifetime of the system rather than whenever the
list is empty; pins come and go at run time.

The name is held in a buffer as long as the one gpio_pin_register()
accepts, so a listing always names the same string as /dev.

The pin type name table is declared without an explicit size so that the
assertion beside it compares against the enumeration and can fail; sized
as [GPIO_NPINTYPES] it would have been tautological.

Documents the entry, its tokens, and how a lower half describes a pin.

Off by default: with GPIO_PROCFS unset the list, the lock and the procfs
entry are compiled out, and go_describe() is one more member at the end
of a structure existing lower halves do not reach.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
2026-08-19 00:46:56 +08:00

246 lines
8.4 KiB
ReStructuredText

============
GPIO Drivers
============
- ``include/nuttx/ioexpander/gpio.h``. All structures and APIs needed
to work with GPIO pins as drivers are provided in this header file. This
header file includes:
#. Structures and interface descriptions needed to develop a
low-level, board-specific, GPIO driver.
#. To register the GPIO driver with a common GPIO character
driver.
#. Interfaces needed for interfacing user programs with the
common GPIO character driver.
- ``drivers/ioexpander/gpio.c``. The implementation of the common GPIO
character driver.
Application Programming Interface
=================================
The Application Programming Interface is included in the application with
the following header file.
.. code-block:: c
#include <nuttx/ioexpander/gpio.h>
A GPIO pin is either an input pin or an output pin.
One GPIO pin is registered as a POSIX character device file into ``/dev``
namespace. It is necessary to open the device to get a file descriptor for
further operations. This can be done with standard POSIX ``open()`` call.
Only one pir per driver is supported by the peripheral.
Standard POSIX ``read()`` and ``write()`` operations are not allowed for GPIO
based drivers. All interface is routed through IOCTL calls. Following commands
are supported:
* :c:macro:`GPIOC_WRITE`
* :c:macro:`GPIOC_READ`
* :c:macro:`GPIOC_PINTYPE`
* :c:macro:`GPIOC_REGISTER`
* :c:macro:`GPIOC_UNREGISTER`
* :c:macro:`GPIOC_SETPINTYPE`
* :c:macro:`GPIOC_SETDEBOUNCE`
* :c:macro:`GPIOC_IRQ_SETMASK`
.. c:macro:: GPIOC_WRITE
The ``GPIOC_WRITE`` command sets the value of an output GPIO. The argument
is either 0 (set low value) or 1 (set high value). It is possible to write
to output pins only. Typical use case is:
.. code-block:: c
bool value = true;
int ret = ioctl(fd, GPIOC_WRITE, value);
.. c:macro:: GPIOC_READ
The ``GPIOC_READ`` command reads the value of a GPIO. The argument
is a pointer to a bool value to receive the result. The result is either 0
(low value) or 1 (high value). It is possible to read from both input and
output pins. The currently set value is returned in case the pin is output
pin. Typical use case is:
.. code-block:: c
bool value;
int ret = ioctl(fd, GPIOC_READ, (unsigned long)(uintptr_t)&value);
.. c:macro:: GPIOC_PINTYPE
The ``GPIOC_PINTYPE`` command gets the type of GPIO pin. The argument
is a pointer to an instance of type :c:enum:`gpio_pintype_e`.
.. code-block:: c
enum gpio_pintype_e
{
GPIO_INPUT_PIN = 0, /* float */
GPIO_INPUT_PIN_PULLUP,
GPIO_INPUT_PIN_PULLDOWN,
GPIO_OUTPUT_PIN, /* push-pull */
GPIO_OUTPUT_PIN_OPENDRAIN,
GPIO_INTERRUPT_PIN,
GPIO_INTERRUPT_HIGH_PIN,
GPIO_INTERRUPT_LOW_PIN,
GPIO_INTERRUPT_RISING_PIN,
GPIO_INTERRUPT_FALLING_PIN,
GPIO_INTERRUPT_BOTH_PIN,
GPIO_INTERRUPT_PIN_WAKEUP,
GPIO_INTERRUPT_HIGH_PIN_WAKEUP,
GPIO_INTERRUPT_LOW_PIN_WAKEUP,
GPIO_INTERRUPT_RISING_PIN_WAKEUP,
GPIO_INTERRUPT_FALLING_PIN_WAKEUP,
GPIO_INTERRUPT_BOTH_PIN_WAKEUP,
GPIO_NPINTYPES
};
.. c:macro:: GPIOC_REGISTER
The ``GPIOC_REGISTER`` command registers a pin to receive a signal whenever
there is an interrupt received on an input GPIO pin. This feature, of course,
depends upon interrupt GPIO support in the platform specific code. Please
refer to the documentation describing your target platform for further
information. The argument is the pointer to :c:type:`sigevent` value, a signal
to be generated when the interrupt occurs.
Typical use case is following:
.. code-block:: c
struct sigevent notify;
notify.sigev_notify = SIGEV_SIGNAL;
notify.sigev_signo = SIGUSR1;
int ret = ioctl(fd, GPIOC_REGISTER, (unsigned long)&notify);
.. c:macro:: GPIOC_UNREGISTER
The ``GPIOC_UNREGISTER`` command unresigters a pin and stop receiving signals
for pin interrupt.
.. c:macro:: GPIOC_SETPINTYPE
The ``GPIOC_SETPINTYPE`` command can be used to change the GPIO pin type
(from input pin to output pin, changing interrupt edges and similar). The
types to set are listed in :c:enum:`gpio_pintype_e`.
.. c:macro:: GPIOC_SETDEBOUNCE
The ``GPIOC_SETDEBOUNCE`` command sets the debounce time for a GPIO input pin.
The argument is a pointer to an integer value, which specifies the debounce time in milliseconds.
This helps to filter out spurious transitions (noise) on the input pin.
Typical use case:
.. code-block:: c
int debounce_ms = 10;
int ret = ioctl(fd, GPIOC_SETDEBOUNCE, (unsigned long)(uintptr_t)&debounce_ms);
.. c:macro:: GPIOC_IRQ_SETMASK
The ``GPIOC_IRQ_SETMASK`` command sets the interrupt mask for a GPIO pin.
The argument is a pointer to an integer value, which specifies the mask to enable or disable specific interrupt types (such as rising/falling edge, level, etc).
The exact meaning of the mask depends on the platform implementation.
Typical use case:
.. code-block:: c
int irq_mask = /* platform-specific mask value */;
int ret = ioctl(fd, GPIOC_IRQ_SETMASK, (unsigned long)(uintptr_t)&irq_mask);
Application Example
~~~~~~~~~~~~~~~~~~~
An example application can be found in ``nuttx-apps`` repository under
path ``examples/gpio``. It is an example application that allows you
to read, write or configure GPIO pins.
/proc/gpio
==========
The pins are visible in ``/dev`` and each can be read through its own node
with the commands above, but doing that for a whole board means an open and
two ioctls per pin, and the signal and interrupt counters are not exposed
that way at all. ``CONFIG_GPIO_PROCFS`` adds ``/proc/gpio``, which puts
every registered pin in one place:
.. code-block:: text
gpio0 type:INPUT val:1 regs:0 ints:0 pad:GPIO27 port:A.27
gpio1 type:OUTPUT val:0 regs:0 ints:0 pad:GPIO28 port:A.28
gpio2 type:INT_BOTH val:1 regs:1 ints:42 pad:GPIO29 port:A.29 armed:both
Every line carries the same ``key:value`` tokens in the same order, so the
file can be parsed as well as read:
========== =============================================================
Token Meaning
========== =============================================================
``type`` The pin type, from ``enum gpio_pintype_e``
``val`` The pin's current value, or ``-`` if it cannot be read
``regs`` How many times the pin has been registered for signals
``ints`` How many interrupts the pin has taken
========== =============================================================
Anything after those comes from the pin's lower half.
Describing a pin from the lower half
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The upper half already knows a pin's type, value and counters, so a lower
half only supplies what it alone can say: which pad carries the line, how
the trigger is configured, and so on. That is the optional
``go_describe`` method, which writes further ``key:value`` fields and
leaves the rest of the line to the renderer:
.. code-block:: c
static int mychip_describe(FAR struct gpio_dev_s *dev, FAR char *extra,
size_t len)
{
FAR struct mychip_gpio_s *priv = (FAR struct mychip_gpio_s *)dev;
snprintf(extra, len, "pad:%u port:%c.%u", priv->pad,
'A' + priv->port, priv->pin);
return OK;
}
It is optional: a pin whose lower half omits it is listed with the common
tokens and nothing more. A lower half needs no procfs knowledge of its
own, since the renderer owns the line.
The option depends on ``FS_PROCFS_REGISTER`` and is off by default.
Configuration
=============
This section describes GPIO driver configuration in ``Kconfig``. The reader
should refer to target documentation for target specific configuration.
GPIO peripheral is enabled by ``CONFIG_DEV_GPIO``. Option
``CONFIG_DEV_NPOLLWAITERS`` is used to specify the maximum number of threads
that can be waiting on poll with default set to one. It is also possible
to register signals with the GPIO driver. The number of allowed signals
is configured with ``CONFIG_DEV_NSIGNALS``. ``CONFIG_GPIO_PROCFS`` adds
``/proc/gpio``, described above.
IO Expander Device Drivers
==========================
IO Expander device drivers are chips that provides more GPIO pins, usually
connected to the MCU with SPI or I2C bus. It is possible to register
individual GPIO pins of the expander as a separate pins if needed. This
option is enabled by ``CONFIG_GPIO_LOWER_HALF`` option. Please refer
to `ioexpander documentation <https://nuttx.apache.org/docs/latest/components/drivers/special/ioexpander.html>`_
for more description.