This is an automated email from the ASF dual-hosted git repository.

xiaoxiang781216 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git


The following commit(s) were added to refs/heads/master by this push:
     new 1528b470b9e drivers/ioexpander: List the registered pins in /proc/gpio.
1528b470b9e is described below

commit 1528b470b9e3e490830e739926055ded9a7f127b
Author: Justin Hammond <[email protected]>
AuthorDate: Sun Aug 2 18:54:39 2026 +0800

    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 <[email protected]>
---
 .../components/drivers/character/gpio.rst          |  59 ++-
 drivers/ioexpander/Kconfig                         |  17 +
 drivers/ioexpander/gpio.c                          | 446 ++++++++++++++++++++-
 include/nuttx/ioexpander/gpio.h                    |  17 +
 4 files changed, 537 insertions(+), 2 deletions(-)

diff --git a/Documentation/components/drivers/character/gpio.rst 
b/Documentation/components/drivers/character/gpio.rst
index fe3bf735ce4..53f3ee6e5aa 100644
--- a/Documentation/components/drivers/character/gpio.rst
+++ b/Documentation/components/drivers/character/gpio.rst
@@ -166,6 +166,62 @@ 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
 =============
 
@@ -176,7 +232,8 @@ 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``.
+is configured with ``CONFIG_DEV_NSIGNALS``. ``CONFIG_GPIO_PROCFS`` adds
+``/proc/gpio``, described above.
 
 IO Expander Device Drivers
 ==========================
diff --git a/drivers/ioexpander/Kconfig b/drivers/ioexpander/Kconfig
index 0de2ee4e56e..489ca1b0c7c 100644
--- a/drivers/ioexpander/Kconfig
+++ b/drivers/ioexpander/Kconfig
@@ -615,6 +615,23 @@ config DEV_GPIO
                Enables a simple GPIO input/output driver to support 
application-
                space testing of hardware.
 
+config GPIO_PROCFS
+       bool "GPIO procfs entry"
+       default n
+       depends on DEV_GPIO && FS_PROCFS && FS_PROCFS_REGISTER
+       ---help---
+               Create /proc/gpio, listing every registered pin with its type,
+               its current value, and how many times it has been registered for
+               signals and taken an interrupt.
+
+               The pins are visible in /dev and each can be read through its 
own
+               node, but surveying a board that way means an open and two 
ioctls
+               per pin, and the counters are not reachable through any of them.
+
+               A lower half that supplies the optional go_describe method also
+               contributes whatever only it can say, such as which pad carries
+               the line.
+
 config DEV_GPIO_NPOLLWAITERS
        int "Max number of polls"
        default 1
diff --git a/drivers/ioexpander/gpio.c b/drivers/ioexpander/gpio.c
index 512a0e19f41..e00d6a60a74 100644
--- a/drivers/ioexpander/gpio.c
+++ b/drivers/ioexpander/gpio.c
@@ -27,6 +27,7 @@
 #include <nuttx/config.h>
 
 #include <sys/types.h>
+#include <inttypes.h>
 #include <stdio.h>
 #include <string.h>
 #include <signal.h>
@@ -40,8 +41,43 @@
 #include <nuttx/spinlock.h>
 #include <nuttx/ioexpander/gpio.h>
 
+#ifdef CONFIG_GPIO_PROCFS
+#  include <sys/stat.h>
+#  include <sys/param.h>
+#  include <fcntl.h>
+#  include <nuttx/kmalloc.h>
+#  include <nuttx/list.h>
+#  include <nuttx/mutex.h>
+#  include <nuttx/fs/procfs.h>
+#endif
+
 #ifdef CONFIG_DEV_GPIO
 
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+#ifdef CONFIG_GPIO_PROCFS
+
+/* One registered pin.  struct gpio_dev_s belongs to the lower half, which
+ * usually embeds it in a larger private structure of its own, so the list
+ * node lives here rather than being added to it.
+ */
+
+/* Long enough for any name gpio_pin_register() accepts: it builds
+ * "/dev/" plus the name in a 32 byte buffer.
+ */
+
+#define GPIO_PROCFS_NAMELEN 28
+
+struct gpio_entry_s
+{
+  struct list_node node;
+  FAR struct gpio_dev_s *dev;
+  char name[GPIO_PROCFS_NAMELEN];
+};
+#endif
+
 /****************************************************************************
  * Private Function Prototypes
  ****************************************************************************/
@@ -55,6 +91,21 @@ static ssize_t gpio_write(FAR struct file *filep, FAR const 
char *buffer,
 static off_t   gpio_seek(FAR struct file *filep, off_t offset, int whence);
 static int     gpio_ioctl(FAR struct file *filep, int cmd,
                           unsigned long arg);
+#ifdef CONFIG_GPIO_PROCFS
+static int     gpio_procfs_open(FAR struct file *filep,
+                                FAR const char *relpath,
+                                int oflags, mode_t mode);
+static int     gpio_procfs_close(FAR struct file *filep);
+static ssize_t gpio_procfs_read(FAR struct file *filep, FAR char *buffer,
+                                size_t buflen);
+static int     gpio_procfs_dup(FAR const struct file *oldp,
+                               FAR struct file *newp);
+static int     gpio_procfs_stat(FAR const char *relpath,
+                                FAR struct stat *buf);
+static void    gpio_procfs_add(FAR struct gpio_dev_s *dev,
+                               FAR const char *pinname);
+static void    gpio_procfs_remove(FAR struct gpio_dev_s *dev);
+#endif
 static int     gpio_poll(FAR struct file *filep,
                          FAR struct pollfd *fds, bool setup);
 
@@ -75,10 +126,390 @@ static const struct file_operations g_gpio_drvrops =
   gpio_poll,   /* poll */
 };
 
+#ifdef CONFIG_GPIO_PROCFS
+
+static struct list_node g_gpio_list = LIST_INITIAL_VALUE(g_gpio_list);
+static mutex_t g_gpio_lock = NXMUTEX_INITIALIZER;
+static bool g_gpio_procfs_added;
+
+/* Indexed by enum gpio_pintype_e.  The enum's own comment warns that a
+ * table like this has to be extended with it; the assertion below turns
+ * forgetting into a build error rather than a pin type with no name.
+ */
+
+static const FAR char *g_gpio_typename[] =
+{
+  "INPUT",            /* GPIO_INPUT_PIN                  */
+  "INPUT_PU",         /* GPIO_INPUT_PIN_PULLUP           */
+  "INPUT_PD",         /* GPIO_INPUT_PIN_PULLDOWN         */
+  "OUTPUT",           /* GPIO_OUTPUT_PIN                 */
+  "OUTPUT_OD",        /* GPIO_OUTPUT_PIN_OPENDRAIN       */
+  "INT",              /* GPIO_INTERRUPT_PIN              */
+  "INT_HIGH",         /* GPIO_INTERRUPT_HIGH_PIN         */
+  "INT_LOW",          /* GPIO_INTERRUPT_LOW_PIN          */
+  "INT_RISING",       /* GPIO_INTERRUPT_RISING_PIN       */
+  "INT_FALLING",      /* GPIO_INTERRUPT_FALLING_PIN      */
+  "INT_BOTH",         /* GPIO_INTERRUPT_BOTH_PIN         */
+  "INT_WAKE",         /* GPIO_INTERRUPT_PIN_WAKEUP       */
+  "INT_HIGH_WAKE",    /* GPIO_INTERRUPT_HIGH_PIN_WAKEUP  */
+  "INT_LOW_WAKE",     /* GPIO_INTERRUPT_LOW_PIN_WAKEUP   */
+  "INT_RISING_WAKE",  /* GPIO_INTERRUPT_RISING_PIN_WAKEUP */
+  "INT_FALLING_WAKE", /* GPIO_INTERRUPT_FALLING_PIN_WAKEUP */
+  "INT_BOTH_WAKE",    /* GPIO_INTERRUPT_BOTH_PIN_WAKEUP  */
+};
+
+static_assert(nitems(g_gpio_typename) == GPIO_NPINTYPES,
+              "pin type name table does not match enum gpio_pintype_e");
+
+static const struct procfs_operations g_gpio_procfs_ops =
+{
+  gpio_procfs_open,   /* open */
+  gpio_procfs_close,  /* close */
+  gpio_procfs_read,   /* read */
+  NULL,               /* write */
+  NULL,               /* poll */
+
+  gpio_procfs_dup,    /* dup */
+
+  NULL,               /* opendir */
+  NULL,               /* closedir */
+  NULL,               /* readdir */
+  NULL,               /* rewinddir */
+
+  gpio_procfs_stat,   /* stat */
+};
+
+static const struct procfs_entry_s g_gpio_procfs =
+{
+  "gpio", &g_gpio_procfs_ops, PROCFS_FILE_TYPE
+};
+
+#endif /* CONFIG_GPIO_PROCFS */
+
 /****************************************************************************
  * Private Functions
  ****************************************************************************/
 
+#ifdef CONFIG_GPIO_PROCFS
+
+/****************************************************************************
+ * Name: gpio_procfs_open
+ *
+ * Description:
+ *   Open /proc/gpio.  The entry is read only, and holds no state of its
+ *   own beyond the position accounting procfs does for every file.
+ *
+ * Input Parameters:
+ *   filep   - The file structure to attach the open file to
+ *   relpath - The path below /proc being opened
+ *   oflags  - Open flags; anything but read only is refused
+ *   mode    - Ignored, the entry cannot be created
+ *
+ * Returned Value:
+ *   Zero on success, or a negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int gpio_procfs_open(FAR struct file *filep, FAR const char *relpath,
+                            int oflags, mode_t mode)
+{
+  FAR struct procfs_file_s *priv;
+
+  if ((oflags & O_ACCMODE) != O_RDONLY)
+    {
+      return -EACCES;
+    }
+
+  priv = kmm_zalloc(sizeof(struct procfs_file_s));
+  if (priv == NULL)
+    {
+      return -ENOMEM;
+    }
+
+  filep->f_priv = priv;
+  return OK;
+}
+
+/****************************************************************************
+ * Name: gpio_procfs_close
+ *
+ * Description:
+ *   Close /proc/gpio and free what open() allocated.
+ *
+ * Input Parameters:
+ *   filep - The open file
+ *
+ * Returned Value:
+ *   Zero on success, or a negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int gpio_procfs_close(FAR struct file *filep)
+{
+  kmm_free(filep->f_priv);
+  filep->f_priv = NULL;
+  return OK;
+}
+
+/****************************************************************************
+ * Name: gpio_procfs_read
+ *
+ * Description:
+ *   List every registered pin, in registration order, with what the upper
+ *   half knows about it.  A lower half that supplies go_describe adds its
+ *   own fields to the same line.
+ *
+ *   Each read renders from the start and skips what earlier reads already
+ *   returned, so a file longer than the caller's buffer still comes out
+ *   whole across successive reads.
+ *
+ * Input Parameters:
+ *   filep  - The open file, carrying the offset reached so far
+ *   buffer - Where to return the text
+ *   buflen - Size of buffer
+ *
+ * Returned Value:
+ *   The number of bytes returned, zero at end of file, or a negated errno
+ *   on failure.
+ *
+ ****************************************************************************/
+
+static ssize_t gpio_procfs_read(FAR struct file *filep, FAR char *buffer,
+                                size_t buflen)
+{
+  FAR struct gpio_entry_s *entry;
+  size_t remaining = buflen;
+  FAR char *dest = buffer;
+  off_t pos = filep->f_pos;
+  char extra[48];
+  char line[128];
+  bool value;
+  size_t n;
+  int ret;
+
+  ret = nxmutex_lock(&g_gpio_lock);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  list_for_every_entry(&g_gpio_list, entry, struct gpio_entry_s, node)
+    {
+      FAR const char *type = "-";
+
+      if (remaining == 0)
+        {
+          break;
+        }
+
+      /* A lower half is free to invent a pin type this table has never
+       * heard of, so the index is bounded here rather than trusted.
+       */
+
+      if (entry->dev->gp_pintype < nitems(g_gpio_typename))
+        {
+          type = g_gpio_typename[entry->dev->gp_pintype];
+        }
+
+      /* A pin that cannot be read reports -, so that a failed read is
+       * not shown as a low level.
+       */
+
+      if (entry->dev->gp_ops->go_read != NULL &&
+          entry->dev->gp_ops->go_read(entry->dev, &value) >= 0)
+        {
+          n = snprintf(line, sizeof(line), "%-12s type:%-18s val:%u",
+                       entry->name, type, value);
+        }
+      else
+        {
+          n = snprintf(line, sizeof(line), "%-12s type:%-18s val:-",
+                       entry->name, type);
+        }
+
+      n += snprintf(line + n, sizeof(line) - n,
+                    " regs:%u ints:%" PRIuPTR,
+                    entry->dev->register_count, entry->dev->int_count);
+
+      extra[0] = '\0';
+      if (entry->dev->gp_ops->go_describe != NULL)
+        {
+          entry->dev->gp_ops->go_describe(entry->dev, extra, sizeof(extra));
+          extra[sizeof(extra) - 1] = '\0';
+        }
+
+      if (extra[0] != '\0')
+        {
+          n += snprintf(line + n, sizeof(line) - n, " %s", extra);
+        }
+
+      n += snprintf(line + n, sizeof(line) - n, "\n");
+
+      /* snprintf() reports the length it wanted, so a line longer than the
+       * buffer would otherwise carry n past it.
+       */
+
+      if (n >= sizeof(line))
+        {
+          n = sizeof(line) - 1;
+          line[n - 1] = '\n';
+        }
+
+      n = procfs_memcpy(line, n, dest, remaining, &pos);
+      dest += n;
+      remaining -= n;
+    }
+
+  nxmutex_unlock(&g_gpio_lock);
+
+  filep->f_pos += (dest - buffer);
+  return dest - buffer;
+}
+
+/****************************************************************************
+ * Name: gpio_procfs_dup
+ *
+ * Description:
+ *   Duplicate an open /proc/gpio, copying the position reached so that the
+ *   new file continues where the old one had got to.
+ *
+ * Input Parameters:
+ *   oldp - The open file being duplicated
+ *   newp - The file structure to attach the duplicate to
+ *
+ * Returned Value:
+ *   Zero on success, or a negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int gpio_procfs_dup(FAR const struct file *oldp,
+                           FAR struct file *newp)
+{
+  FAR struct procfs_file_s *priv;
+
+  priv = kmm_zalloc(sizeof(struct procfs_file_s));
+  if (priv == NULL)
+    {
+      return -ENOMEM;
+    }
+
+  memcpy(priv, oldp->f_priv, sizeof(struct procfs_file_s));
+  newp->f_priv = priv;
+  return OK;
+}
+
+/****************************************************************************
+ * Name: gpio_procfs_stat
+ *
+ * Description:
+ *   Report /proc/gpio as a read only regular file.
+ *
+ * Input Parameters:
+ *   relpath - The path below /proc being queried
+ *   buf     - Where to return the status
+ *
+ * Returned Value:
+ *   Zero on success, or a negated errno on failure.
+ *
+ ****************************************************************************/
+
+static int gpio_procfs_stat(FAR const char *relpath, FAR struct stat *buf)
+{
+  buf->st_mode    = S_IFREG | S_IROTH | S_IRGRP | S_IRUSR;
+  buf->st_size    = 0;
+  buf->st_blksize = 0;
+  buf->st_blocks  = 0;
+  return OK;
+}
+
+/****************************************************************************
+ * Name: gpio_procfs_add
+ *
+ * Description:
+ *   Remember a pin, and create /proc/gpio when the first one appears.
+ *   procfs_register() has to run before procfs is mounted, which holds
+ *   for the pins a board registers during start up.  The entry is claimed
+ *   once for the lifetime of the system, since procfs_register() appends
+ *   without checking for duplicates.
+ *
+ * Input Parameters:
+ *   dev     - The pin being registered
+ *   pinname - The name it was registered under, used as the /proc/gpio
+ *             label; the caller's string must outlive the pin
+ *
+ * Returned Value:
+ *   None.  A pin that cannot be listed is still a working pin, so a
+ *   failure here does not fail the registration.
+ *
+ ****************************************************************************/
+
+static void gpio_procfs_add(FAR struct gpio_dev_s *dev,
+                            FAR const char *pinname)
+{
+  FAR struct gpio_entry_s *entry;
+
+  entry = kmm_zalloc(sizeof(struct gpio_entry_s));
+  if (entry == NULL)
+    {
+      return;
+    }
+
+  entry->dev = dev;
+  strlcpy(entry->name, pinname, sizeof(entry->name));
+
+  nxmutex_lock(&g_gpio_lock);
+
+  /* procfs_register() appends without checking for a duplicate, 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.
+   */
+
+  if (!g_gpio_procfs_added)
+    {
+      procfs_register(&g_gpio_procfs);
+      g_gpio_procfs_added = true;
+    }
+
+  list_add_tail(&g_gpio_list, &entry->node);
+  nxmutex_unlock(&g_gpio_lock);
+}
+
+/****************************************************************************
+ * Name: gpio_procfs_remove
+ *
+ * Description:
+ *   Forget a pin.  /proc/gpio stays, since procfs has no way to withdraw
+ *   an entry, and lists nothing once the last pin has gone.
+ *
+ * Input Parameters:
+ *   dev - The pin being unregistered
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+static void gpio_procfs_remove(FAR struct gpio_dev_s *dev)
+{
+  FAR struct gpio_entry_s *entry;
+
+  nxmutex_lock(&g_gpio_lock);
+
+  list_for_every_entry(&g_gpio_list, entry, struct gpio_entry_s, node)
+    {
+      if (entry->dev == dev)
+        {
+          list_delete(&entry->node);
+          kmm_free(entry);
+          break;
+        }
+    }
+
+  nxmutex_unlock(&g_gpio_lock);
+}
+
+#endif /* CONFIG_GPIO_PROCFS */
+
 /****************************************************************************
  * Name: gpio_handler
  *
@@ -791,7 +1222,16 @@ int gpio_pin_register_byname(FAR struct gpio_dev_s *dev,
 
   gpioinfo("Registering %s\n", devname);
 
-  return register_driver(devname, &g_gpio_drvrops, 0600, dev);
+  ret = register_driver(devname, &g_gpio_drvrops, 0600, dev);
+
+#ifdef CONFIG_GPIO_PROCFS
+  if (ret >= 0)
+    {
+      gpio_procfs_add(dev, pinname);
+    }
+#endif
+
+  return ret;
 }
 
 /****************************************************************************
@@ -851,6 +1291,10 @@ int gpio_pin_unregister_byname(FAR struct gpio_dev_s *dev,
 
   gpioinfo("Unregistering %s\n", devname);
 
+#ifdef CONFIG_GPIO_PROCFS
+  gpio_procfs_remove(dev);
+#endif
+
   return unregister_driver(devname);
 }
 
diff --git a/include/nuttx/ioexpander/gpio.h b/include/nuttx/ioexpander/gpio.h
index cd85ae888bd..a34d3b2ecd9 100644
--- a/include/nuttx/ioexpander/gpio.h
+++ b/include/nuttx/ioexpander/gpio.h
@@ -156,6 +156,23 @@ struct gpio_operations_s
   CODE int (*go_setdebounce)(FAR struct gpio_dev_s *gpio,
                              unsigned long duration);
   CODE int (*go_setmask)(FAR struct gpio_dev_s *dev, bool enable);
+
+  /* Describe whatever only this pin's controller can say, for
+   * /proc/gpio: which pad carries the line, how its trigger is
+   * configured, anything the upper half has no way to ask for.
+   *
+   * Optional.  A pin whose lower half omits it is still listed with
+   * everything the upper half knows: its type, its value, and how many
+   * times it has been registered for signals and taken an interrupt.
+   *
+   * Write at most len bytes into extra as further key:value fields, in
+   * the form the renderer uses, and return OK.  The renderer owns the
+   * line and appends this to it, so a lower half needs no procfs
+   * knowledge of its own.
+   */
+
+  CODE int (*go_describe)(FAR struct gpio_dev_s *dev, FAR char *extra,
+                          size_t len);
 };
 
 /* Signal information */

Reply via email to