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

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

commit 36f91643ae2e85492c0b5a9ad46e5fdb01f3db47
Author: yezhonghui <[email protected]>
AuthorDate: Wed Apr 2 09:49:34 2025 +0800

    sim/gpiochip: add sim gpiochip for gpio autotest
    
    add sim gpiochip driver
    
    Signed-off-by: yezhonghui <[email protected]>
---
 Documentation/platforms/sim/index.rst        |   1 +
 Documentation/platforms/sim/sim_gpiochip.rst | 308 ++++++++++++++++++
 arch/sim/Kconfig                             |  32 ++
 arch/sim/src/Makefile                        |   5 +
 arch/sim/src/sim/CMakeLists.txt              |   5 +
 arch/sim/src/sim/posix/sim_linux_gpiochip.c  | 419 ++++++++++++++++++++++++
 arch/sim/src/sim/sim_gpiochip.c              | 454 +++++++++++++++++++++++++++
 arch/sim/src/sim/sim_gpiochip.h              |  71 +++++
 arch/sim/src/sim/sim_internal.h              |   4 +
 9 files changed, 1299 insertions(+)

diff --git a/Documentation/platforms/sim/index.rst 
b/Documentation/platforms/sim/index.rst
index ddd06b6606a..84adbf17c8a 100644
--- a/Documentation/platforms/sim/index.rst
+++ b/Documentation/platforms/sim/index.rst
@@ -14,3 +14,4 @@ The following Simulator/Emulators are supported:
    */*
    network_linux
    network_vpnkit
+   sim_gpiochip
diff --git a/Documentation/platforms/sim/sim_gpiochip.rst 
b/Documentation/platforms/sim/sim_gpiochip.rst
new file mode 100644
index 00000000000..57097ffb95d
--- /dev/null
+++ b/Documentation/platforms/sim/sim_gpiochip.rst
@@ -0,0 +1,308 @@
+======================================
+Sim GPIO Chip Driver (Linux Host GPIO)
+======================================
+
+Overview
+========
+
+The Sim GPIO Chip driver provides a mechanism for NuttX simulation (sim) to 
access
+the Linux host's GPIO chip devices (``/dev/gpiochipN``). This allows NuttX 
applications
+running in simulation mode to interact with real hardware GPIO pins connected 
to the
+Linux host system.
+
+This driver is particularly useful for:
+
+- Testing GPIO-based applications in a simulated environment with real hardware
+- Interfacing with USB-to-GPIO adapters (e.g., CH341A) from NuttX simulation
+- Developing and debugging GPIO drivers without dedicated embedded hardware
+
+Host Prepare
+============
+
+Preparation required on the host side:
+
+- Hardware module required: USB-CH341A module
+- Refer to https://github.com/frank-zago/ch341-i2c-spi-gpio, and install the 
driver
+- Verify existence of /dev/gpiochipN device file
+
+Architecture
+============
+
+The driver consists of two layers:
+
+1. **NuttX Layer** (``sim_gpiochip.c``): Implements the NuttX 
``ioexpander_dev_s``
+   interface, providing standard GPIO operations to upper-layer NuttX drivers.
+
+2. **Host Layer** (``sim_linux_gpiochip.c``): Interfaces directly with Linux 
kernel's
+   GPIO character device (``/dev/gpiochipN``) using the GPIO v2 ABI.
+
+::
+
+    +---------------------+
+    |  NuttX Application  |
+    +---------------------+
+            |
+            v
+    +---------------------+
+    |  GPIO Lower Half    |
+    |  (gpio_lower_half)  |
+    +---------------------+
+            |
+            v
+    +---------------------+
+    |   sim_gpiochip.c    |  <-- NuttX ioexpander interface
+    | (ioexpander_dev_s)  |
+    +---------------------+
+            |
+            v
+    +---------------------+
+    | sim_linux_gpiochip.c|  <-- Linux host GPIO interface
+    |  (GPIO v2 ABI)      |
+    +---------------------+
+            |
+            v
+    +---------------------+
+    |  /dev/gpiochipN     |  <-- Linux GPIO character device
+    +---------------------+
+
+Header Files
+============
+
+-  ``arch/sim/src/sim/sim_hostgpiochip.h``: Host GPIO chip interface 
definitions
+   and function prototypes.
+
+-  ``include/nuttx/ioexpander/ioexpander.h``: Standard NuttX IO expander 
interface.
+
+-  ``include/nuttx/ioexpander/gpio.h``: NuttX GPIO interface definitions.
+
+Configuration Options
+=====================
+
+The following configuration options are relevant to this driver:
+
+- ``CONFIG_SIM_GPIOCHIP``: Enable the sim GPIO chip driver.
+- ``CONFIG_IOEXPANDER_NPINS``: Maximum number of GPIO pins supported (default: 
64).
+- ``CONFIG_IOEXPANDER_INT_ENABLE``: Enable interrupt support for GPIO pins.
+
+Supported Operations
+====================
+
+The driver supports the following GPIO operations:
+
+Direction Control
+-----------------
+
+.. code-block:: c
+
+   int sim_gpiochip_direction(struct ioexpander_dev_s *dev,
+                              uint8_t pin, int direction);
+
+Set GPIO pin direction. Supported directions:
+
+- ``IOEXPANDER_DIRECTION_IN``: Configure as input
+- ``IOEXPANDER_DIRECTION_OUT``: Configure as output
+- ``IOEXPANDER_DIRECTION_OUT_OPENDRAIN``: Configure as open-drain output
+
+Read/Write Pin
+--------------
+
+.. code-block:: c
+
+   int sim_gpiochip_readpin(struct ioexpander_dev_s *dev, uint8_t pin,
+                            bool *value);
+   int sim_gpiochip_writepin(struct ioexpander_dev_s *dev, uint8_t pin,
+                             bool value);
+
+Read or write the value of a GPIO pin.
+
+Interrupt Configuration
+-----------------------
+
+.. code-block:: c
+
+   int sim_gpiochip_option(struct ioexpander_dev_s *dev, uint8_t pin,
+                           int option, void *val);
+
+Configure GPIO pin options. Supported interrupt edge configurations:
+
+- ``IOEXPANDER_VAL_RISING``: Trigger on rising edge
+- ``IOEXPANDER_VAL_FALLING``: Trigger on falling edge
+- ``IOEXPANDER_VAL_BOTH``: Trigger on both edges
+- ``IOEXPANDER_VAL_DISABLE``: Disable interrupt
+
+Interrupt Callback
+------------------
+
+.. code-block:: c
+
+   void *sim_gpiochip_attach(struct ioexpander_dev_s *dev,
+                             ioe_pinset_t pinset,
+                             ioe_callback_t callback,
+                             void *arg);
+   int sim_gpiochip_detach(struct ioexpander_dev_s *dev, void *handle);
+
+Attach or detach an interrupt callback function for GPIO pins.
+
+Host Layer API
+==============
+
+The host layer (``sim_linux_gpiochip.c``) provides the following functions:
+
+.. code-block:: c
+
+   /* Allocate and initialize a host GPIO chip device */
+   struct host_gpiochip_dev *host_gpiochip_alloc(const char *filename);
+
+   /* Free a host GPIO chip device */
+   void host_gpiochip_free(struct host_gpiochip_dev *dev);
+
+   /* Set GPIO pin direction */
+   int host_gpiochip_direction(struct host_gpiochip_dev *dev,
+                               uint8_t pin, bool input);
+
+   /* Read GPIO pin value */
+   int host_gpiochip_readpin(struct host_gpiochip_dev *dev,
+                             uint8_t pin, bool *value);
+
+   /* Write GPIO pin value */
+   int host_gpiochip_writepin(struct host_gpiochip_dev *dev,
+                              uint8_t pin, bool value);
+
+   /* Request GPIO interrupt */
+   int host_gpiochip_irq_request(struct host_gpiochip_dev *dev,
+                                 uint8_t pin, uint16_t cfgset);
+
+   /* Check if GPIO interrupt is active */
+   bool host_gpiochip_irq_active(struct host_gpiochip_dev *dev, uint8_t pin);
+
+   /* Get GPIO line information */
+   int host_gpiochip_get_line(struct host_gpiochip_dev *priv,
+                              uint8_t pin, bool *input);
+
+Linux Kernel Version Requirements
+=================================
+
+The driver uses Linux GPIO v2 ABI, which requires:
+
+- **Linux kernel >= 6.8.0**: Full functionality with GPIO v2 API support.
+- **Linux kernel < 6.8.0**: The driver compiles but provides stub 
implementations
+  that return 0 or NULL.
+
+Usage Example
+=============
+
+Initialization
+--------------
+
+.. code-block:: c
+
+   #include <nuttx/ioexpander/gpio.h>
+   #include "sim_internal.h"
+
+   int board_gpio_initialize(void)
+   {
+     struct ioexpander_dev_s *ioe;
+     int ret;
+
+     /* Initialize the GPIO chip device */
+     ioe = sim_gpiochip_initialize("/dev/gpiochip0");
+     if (ioe == NULL)
+       {
+         return -ENODEV;
+       }
+
+     /* Register GPIO pins using gpio_lower_half */
+     ret = gpio_lower_half(ioe, 0, GPIO_INPUT_PIN, 60);  /* Pin 0 as input, 
minor 60 */
+     if (ret < 0)
+       {
+         return ret;
+       }
+
+     ret = gpio_lower_half(ioe, 1, GPIO_OUTPUT_PIN, 61); /* Pin 1 as output, 
minor 61 */
+     if (ret < 0)
+       {
+         return ret;
+       }
+
+     return OK;
+   }
+
+Application Usage
+-----------------
+
+After initialization, GPIO pins can be accessed through standard NuttX GPIO 
interface:
+
+.. code-block:: c
+
+   #include <fcntl.h>
+   #include <sys/ioctl.h>
+   #include <nuttx/ioexpander/gpio.h>
+
+   int main(void)
+   {
+     int fd;
+     bool value;
+
+     /* Open GPIO device */
+     fd = open("/dev/gpio60", O_RDWR);
+     if (fd < 0)
+       {
+         return -1;
+       }
+
+     /* Read GPIO value */
+     ioctl(fd, GPIOC_READ, &value);
+     printf("GPIO value: %d\n", value);
+
+     close(fd);
+     return 0;
+   }
+
+Interrupt Handling
+==================
+
+The driver uses a work queue to poll for GPIO events. The polling interval is
+defined by ``SIM_GPIOCHIP_WORK_DELAY`` (default: 500 microseconds).
+
+When an interrupt event is detected on a GPIO pin, the registered callback
+function is invoked with the pin number and user-provided argument.
+
+.. code-block:: c
+
+   static int gpio_interrupt_handler(struct ioexpander_dev_s *dev,
+                                     ioe_pinset_t pinset, void *arg)
+   {
+     printf("GPIO interrupt on pin %d\n", pinset);
+     return OK;
+   }
+
+   /* Attach interrupt handler */
+   void *handle = IOEP_ATTACH(ioe, (1 << pin), gpio_interrupt_handler, NULL);
+
+   /* Configure interrupt edge */
+   IOEP_SETOPTION(ioe, pin, IOEXPANDER_OPTION_INTCFG,
+                  (void *)IOEXPANDER_VAL_RISING);
+
+Files
+=====
+
+-  ``arch/sim/src/sim/sim_gpiochip.c``: NuttX IO expander implementation
+-  ``arch/sim/src/sim/posix/sim_linux_gpiochip.c``: Linux host GPIO interface
+-  ``arch/sim/src/sim/sim_hostgpiochip.h``: Host GPIO chip header file
+
+Limitations
+===========
+
+1. **Polling-based interrupts**: Due to simulation constraints, interrupts are
+   implemented using polling rather than true hardware interrupts.
+
+2. **Linux kernel version**: Full functionality requires Linux kernel >= 6.8.0.
+
+3. **Pin count**: Limited by ``CONFIG_IOEXPANDER_NPINS`` configuration.
+
+4. **Invert option**: The ``IOEXPANDER_OPTION_INVERT`` option is not yet 
implemented.
+
+See Also
+========
+
+-  Linux GPIO documentation: 
https://www.kernel.org/doc/html/latest/driver-api/gpio/
diff --git a/arch/sim/Kconfig b/arch/sim/Kconfig
index 08c12e66492..944a12c6c7e 100644
--- a/arch/sim/Kconfig
+++ b/arch/sim/Kconfig
@@ -661,6 +661,38 @@ config SIM_SPIDEV_NAME
 
 endif
 
+config SIM_GPIOCHIP
+       bool "Simulated gpiochip"
+       default n
+       depends on IOEXPANDER
+       ---help---
+               Build in support for simulated gpiochip
+
+if SIM_GPIOCHIP
+
+choice
+       prompt "Simulated GPIOCHIP Type"
+       default SIM_GPIOCHIP_LINUX
+
+config SIM_GPIOCHIP_LINUX
+       bool "Linux Gpiochip Character Dev"
+       depends on HOST_LINUX
+       ---help---
+               Attach a Linux Gpiochip via the character device
+               interface. To achieve a SPI port on Linux host, it is
+               recommended to use a USB<>GPIO device such as CH341A/B.
+
+endchoice
+
+config SIM_GPIOCHIP_NAME
+       string "the name of host gpiochip dev to attach to simulator"
+       default "/dev/gpiochip0"
+       ---help---
+               This is the name of the gpiochip device on the host 
implementation to
+               attach to the simulator driver.
+
+endif
+
 menu "Simulated UART"
 
 config SIM_UART_DMA
diff --git a/arch/sim/src/Makefile b/arch/sim/src/Makefile
index d0ef55dae4b..542a0567470 100644
--- a/arch/sim/src/Makefile
+++ b/arch/sim/src/Makefile
@@ -246,6 +246,11 @@ ifeq ($(CONFIG_SIM_SPI_LINUX),y)
   HOSTSRCS += sim_linuxspi.c
 endif
 
+ifeq ($(CONFIG_SIM_GPIOCHIP_LINUX),y)
+  HOSTSRCS += sim_linux_gpiochip.c
+  CSRCS += sim_gpiochip.c
+endif
+
 ifeq ($(CONFIG_SIM_USB_DEV),y)
   CSRCS += sim_usbdev.c
 ifeq ($(CONFIG_SIM_USB_RAW_GADGET),y)
diff --git a/arch/sim/src/sim/CMakeLists.txt b/arch/sim/src/sim/CMakeLists.txt
index 66dc3db8944..3b3da83e189 100644
--- a/arch/sim/src/sim/CMakeLists.txt
+++ b/arch/sim/src/sim/CMakeLists.txt
@@ -249,6 +249,11 @@ if(CONFIG_SIM_SPI_LINUX)
   list(APPEND HOSTSRCS sim_linuxspi.c)
 endif()
 
+if(CONFIG_SIM_GPIOCHIP_LINUX)
+  list(APPEND SRCS sim_gpiochip.c)
+  list(APPEND HOSTSRCS sim_linux_gpiochip.c)
+endif()
+
 if(CONFIG_SIM_USB_DEV)
   list(APPEND SRCS sim_usbdev.c)
   if(CONFIG_SIM_USB_RAW_GADGET)
diff --git a/arch/sim/src/sim/posix/sim_linux_gpiochip.c 
b/arch/sim/src/sim/posix/sim_linux_gpiochip.c
new file mode 100644
index 00000000000..e6bdd252647
--- /dev/null
+++ b/arch/sim/src/sim/posix/sim_linux_gpiochip.c
@@ -0,0 +1,419 @@
+/****************************************************************************
+ * arch/sim/src/sim/posix/sim_linux_gpiochip.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/const.h>
+#include <linux/ioctl.h>
+#include <linux/types.h>
+#include <linux/gpio.h>
+
+#include "sim_gpiochip.h"
+#include "sim_internal.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define gpioerr(fmt, ...) \
+        syslog(LOG_ERR, "sim_linux_gpiochip: " fmt "\n", ##__VA_ARGS__)
+#define gpioinfo(fmt, ...) \
+        syslog(LOG_ERR, "sim_linux_gpiochip: " fmt "\n", ##__VA_ARGS__)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: host_gpiochip_direction
+ *
+ * Description:
+ *   Provide gpiochip pin direction config.
+ *
+ * Input Parameters:
+ *   priv  - A pointer to instance of Linux gpiochip.
+ *   pin   - The pin number.
+ *   input - The direction of the pin.
+ *
+ * Returned Value:
+ *   0 for success, other for fail.
+ ****************************************************************************/
+
+int host_gpiochip_direction(struct host_gpiochip_dev *priv, uint8_t pin,
+                            bool input)
+{
+  struct gpio_v2_line_request req;
+  int nonblock = 1;
+  int ret;
+
+  memset(&req, 0, sizeof(req));
+  req.num_lines = 1;
+  req.offsets[0] = pin;
+
+  if (priv->line_fd[pin] > 0)
+    {
+      close(priv->line_fd[pin]);
+      priv->line_fd[pin] = -1;
+    }
+
+  if (input)
+    {
+      req.config.flags = GPIO_V2_LINE_FLAG_INPUT;
+    }
+  else
+    {
+      req.config.flags = GPIO_V2_LINE_FLAG_OUTPUT;
+    }
+
+  snprintf(req.consumer, sizeof(req.consumer) - 1, "gpio%d", pin);
+  ret = host_uninterruptible(ioctl, priv->file, GPIO_V2_GET_LINE_IOCTL,
+                             &req);
+  if (ret < 0)
+    {
+      gpioerr("ERROR: pin %d set direction failed\n", pin);
+      return -errno;
+    }
+
+  ret = host_uninterruptible(ioctl, req.fd, FIONBIO, &nonblock);
+  if (ret < 0)
+    {
+      gpioerr("ERROR: Failed to set non-blocking: %s\n", strerror(errno));
+      return -errno;
+    }
+
+  priv->line_fd[pin] = req.fd;
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: host_gpiochip_irq_request
+ *
+ * Input Parameters:
+ *   priv   - A pointer to instance of Linux gpiochip.
+ *   pin    - The pin number.
+ *   cfgset - The config set of the pin.
+ *
+ * Returned Value:
+ *   0 for success, other for fail.
+ ****************************************************************************/
+
+int host_gpiochip_irq_request(struct host_gpiochip_dev *priv, uint8_t pin,
+                              uint16_t cfg)
+{
+  struct gpio_v2_line_request req;
+  int nonblock = 1;
+  int ret;
+
+  if (priv->line_fd[pin] > 0)
+    {
+      close(priv->line_fd[pin]);
+      priv->line_fd[pin] = -1;
+    }
+
+  memset(&req, 0, sizeof(req));
+  switch (cfg)
+    {
+      case GPIOCHIP_LINE_FLAG_FALLING:
+        req.config.flags = GPIO_V2_LINE_FLAG_EDGE_FALLING;
+        break;
+      case GPIOCHIP_LINE_FLAG_RISING:
+        req.config.flags = GPIO_V2_LINE_FLAG_EDGE_RISING;
+        break;
+      case GPIOCHIP_LINE_FLAG_BOTH:
+        req.config.flags = GPIO_V2_LINE_FLAG_EDGE_FALLING |
+                           GPIO_V2_LINE_FLAG_EDGE_RISING;
+        break;
+      default:
+        req.config.flags = 0;
+        break;
+    }
+
+  req.offsets[0] = pin;
+  req.num_lines = 1;
+  req.config.flags |= GPIO_V2_LINE_FLAG_INPUT;
+  if (req.config.flags != GPIO_V2_LINE_FLAG_INPUT)
+    {
+      snprintf(req.consumer, sizeof(req.consumer) - 1, "gpio-irq%d", pin);
+    }
+
+  /* Warn only pin 10 can register in ch341A */
+
+  ret = host_uninterruptible(ioctl, priv->file, GPIO_V2_GET_LINE_IOCTL,
+                             &req);
+  if (ret < 0)
+    {
+      gpioerr("ERROR: ioctl failed: %s \n", strerror(errno));
+      return -errno;
+    }
+
+  ret = host_uninterruptible(ioctl, req.fd, FIONBIO, &nonblock);
+  if (ret < 0)
+    {
+      gpioerr("ERROR: Failed to set non-blocking: %s\n", strerror(errno));
+      return -errno;
+    }
+
+  priv->line_fd[pin] = req.fd;
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: host_gpiochip_writepin
+ *
+ * Description:
+ *   Write gpiochip pin value.
+ *
+ * Input Parameters:
+ *   priv  - A pointer to instance of Linux gpiochip.
+ *   pin   - The pin number.
+ *   value - The value write to the pin.
+ *
+ * Returned Value:
+ *   0 for success, other for fail.
+ ****************************************************************************/
+
+int host_gpiochip_writepin(struct host_gpiochip_dev *priv, uint8_t pin,
+                           bool value)
+{
+  struct gpio_v2_line_values vals;
+  int ret;
+
+  if (priv->line_fd[pin] <= 0)
+    {
+      gpioerr("ERROR: Invalid pin %d config\n", pin);
+      return -EINVAL;
+    }
+
+  memset(&vals, 0, sizeof(vals));
+  vals.mask = 1;
+  vals.bits = !!value;
+
+  ret = host_uninterruptible(ioctl, priv->line_fd[pin],
+                             GPIO_V2_LINE_SET_VALUES_IOCTL, &vals);
+  if (ret < 0)
+    {
+      gpioerr("ERROR: Failed to set pin %d value %d\n", pin, value);
+      return -errno;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: host_gpiochip_readpin
+ *
+ * Description:
+ *   Read gpiochip pin value.
+ *
+ * Input Parameters:
+ *   priv  - A pointer to instance of Linux gpiochip.
+ *   pin   - The pin number.
+ *   value - The value write to the pin.
+ *
+ * Returned Value:
+ *   0 for success, other for fail.
+ ****************************************************************************/
+
+int host_gpiochip_readpin(struct host_gpiochip_dev *priv, uint8_t pin,
+                          bool *value)
+{
+  struct gpio_v2_line_values vals;
+  int ret;
+
+  if (priv->line_fd[pin] <= 0)
+    {
+      gpioerr("ERROR: Invalid pin %d config\n", pin);
+      return -EINVAL;
+    }
+
+  memset(&vals, 0, sizeof(vals));
+  vals.mask = 1;
+  ret = host_uninterruptible(ioctl, priv->line_fd[pin],
+                             GPIO_V2_LINE_GET_VALUES_IOCTL, &vals);
+  if (ret < 0)
+    {
+      gpioerr("ERROR: Failed to get pin%d value, errno[%d]\n", pin, errno);
+      return -errno;
+    }
+
+  *value = !!(vals.bits & 0x01);
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: host_gpiochip_irq_active
+ *
+ * Description:
+ *   register gpio for gpiochip device
+ *
+ * Input Parameters:
+ *   priv - A pointer to instance of Linux gpiochip.
+ *   pin  - gpio pin of Linux gpiochip device.
+ *
+ * Returned Value:
+ *   0 for OK.
+ *
+ ****************************************************************************/
+
+bool host_gpiochip_irq_active(struct host_gpiochip_dev *priv, uint8_t pin)
+{
+  if (priv->line_fd[pin] > 0)
+    {
+      struct gpio_v2_line_event ev;
+      int fd = priv->line_fd[pin];
+      memset(&ev, 0, sizeof(ev));
+      if (host_uninterruptible(read, fd, &ev, sizeof(ev)) == sizeof(ev))
+        {
+          return true;
+        }
+    }
+
+  return false;
+}
+
+/****************************************************************************
+ * Name: host_gpiochip_get_line
+ *
+ * Description:
+ *   Get line info from gpiochip device
+ *
+ * Input Parameters:
+ *   priv  - A pointer to instance of Linux gpiochip.
+ *   pin   - gpio line of Linux gpiochip.
+ *   input - A pointer to direction of gpioline.
+ *
+ * Returned Value:
+ *   0 for OK.
+ *
+ ****************************************************************************/
+
+int host_gpiochip_get_line(struct host_gpiochip_dev *priv, uint8_t pin,
+                           bool *input)
+{
+  struct gpio_v2_line_info info;
+  int ret;
+
+  memset(&info, 0, sizeof(info));
+  info.offset = pin;
+  ret = host_uninterruptible(ioctl, priv->file, GPIO_V2_GET_LINEINFO_IOCTL,
+                             &info);
+  if (ret < 0)
+    {
+      gpioerr("Failed to get line info: %d", ret);
+      return -errno;
+    }
+
+  if (info.flags & GPIO_V2_LINE_FLAG_USED)
+    {
+      return 1;
+    }
+
+  if (info.flags & GPIO_V2_LINE_FLAG_OUTPUT)
+    {
+      *input = false;
+    }
+  else
+    {
+      *input = true;
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: host_gpiochip_alloc
+ *
+ * Description:
+ *   Initialize one gpiochip device
+ *
+ * Input Parameters:
+ *   filename - the name of gpiochip device in Linux, e.g. "/dev/gpiochipN".
+ *
+ * Returned Value:
+ *   The pointer to the instance of Linux gpiochip device.
+ *
+ ****************************************************************************/
+
+struct host_gpiochip_dev *host_gpiochip_alloc(const char *filename)
+{
+  struct host_gpiochip_dev *dev;
+
+  dev = malloc(sizeof(struct host_gpiochip_dev));
+  if (!dev)
+    {
+      gpioerr("Failed to allocate memory for gpiochip device");
+      return NULL;
+    }
+
+  dev->file = host_uninterruptible(open, filename, O_RDWR | O_CLOEXEC);
+  if (dev->file < 0)
+    {
+      gpioerr("Failed to open %s: %d", filename, dev->file);
+      free(dev);
+      return NULL;
+    }
+
+  return dev;
+}
+
+/****************************************************************************
+ * Name: host_gpiochip_free
+ *
+ * Description:
+ *   Uninitialize an gpiochip device
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+void host_gpiochip_free(struct host_gpiochip_dev *priv)
+{
+  host_uninterruptible(close, priv->file);
+  free(priv);
+}
diff --git a/arch/sim/src/sim/sim_gpiochip.c b/arch/sim/src/sim/sim_gpiochip.c
new file mode 100644
index 00000000000..8ce4bba6df1
--- /dev/null
+++ b/arch/sim/src/sim/sim_gpiochip.c
@@ -0,0 +1,454 @@
+/****************************************************************************
+ * arch/sim/src/sim/sim_gpiochip.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <debug.h>
+#include <nuttx/wdog.h>
+#include <nuttx/ioexpander/gpio.h>
+#include <nuttx/ioexpander/ioexpander.h>
+
+#include "sim_gpiochip.h"
+#include "sim_internal.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define GPIOCHIP_LINE_BASE        60
+#define SIM_GPIOCHIP_WDOG_DELAY   USEC2TICK(500)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct sim_gpiochip_callback_s
+{
+  ioe_callback_t cbfunc;
+  void *cbarg;
+};
+
+struct sim_gpiochip_dev_s
+{
+  const struct ioexpander_ops_s *ops;   /* gpiochip vtable */
+  struct sim_gpiochip_callback_s cb[CONFIG_IOEXPANDER_NPINS];
+  struct host_gpiochip_dev *dev;
+  struct wdog_s wdog;                   /* poll work */
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int sim_gpiochip_direction(struct ioexpander_dev_s *dev,
+                                  uint8_t pin, int direction);
+static int sim_gpiochip_option(struct ioexpander_dev_s *dev, uint8_t pin,
+                               int option, void *val);
+static int sim_gpiochip_writepin(struct ioexpander_dev_s *dev, uint8_t pin,
+                                 bool value);
+static int sim_gpiochip_readpin(struct ioexpander_dev_s *dev, uint8_t pin,
+                                bool *value);
+#ifdef CONFIG_IOEXPANDER_INT_ENABLE
+static void *sim_gpiochip_attach(struct ioexpander_dev_s *dev,
+                                 uint16_t pinset,
+                                 ioe_callback_t callback,
+                                 void *arg);
+static int sim_gpiochip_detach(struct ioexpander_dev_s *dev,
+                               void *handle);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static struct ioexpander_ops_s g_sim_gpiochip_ops =
+{
+  .ioe_direction = sim_gpiochip_direction,
+  .ioe_option    = sim_gpiochip_option,
+  .ioe_writepin  = sim_gpiochip_writepin,
+  .ioe_readpin   = sim_gpiochip_readpin,
+  .ioe_readbuf   = sim_gpiochip_readpin,
+#ifdef CONFIG_IOEXPANDER_INT_ENABLE
+  .ioe_attach    = sim_gpiochip_attach,
+  .ioe_detach    = sim_gpiochip_detach,
+#endif
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: sim_gpiochip_direction
+ *
+ * Description:
+ *   Provide gpiochip pin direction config.
+ *
+ * Input Parameters:
+ *   dev       - A pointer to instance of sim gpiochip device.
+ *   pin       - The pin number.
+ *   direction - The direction of the pin.
+ *
+ * Returned Value:
+ *   0 for success, other for fail.
+ ****************************************************************************/
+
+static int sim_gpiochip_direction(struct ioexpander_dev_s *dev,
+                                  uint8_t pin, int direction)
+{
+  struct sim_gpiochip_dev_s *priv = (struct sim_gpiochip_dev_s *)dev;
+
+  if (direction == IOEXPANDER_DIRECTION_OUT ||
+      direction == IOEXPANDER_DIRECTION_OUT_OPENDRAIN)
+    {
+      return host_gpiochip_direction(priv->dev, pin, false);
+    }
+  else
+    {
+      return host_gpiochip_direction(priv->dev, pin, true);
+    }
+}
+
+/****************************************************************************
+ * Name: sim_gpiochip_option
+ *
+ * Description:
+ *   Provide gpiochip pin option.
+ *
+ * Input Parameters:
+ *   dev    - A pointer to instance of sim gpiochip device.
+ *   pin    - The pin number.
+ *   option.- The option type.
+ *   val    - A pointer to val of the pin.
+ *
+ * Returned Value:
+ *   0 for success, other for fail.
+ ****************************************************************************/
+
+static int sim_gpiochip_option(struct ioexpander_dev_s *dev, uint8_t pin,
+                               int option, void *val)
+{
+  struct sim_gpiochip_dev_s *priv = (struct sim_gpiochip_dev_s *)dev;
+  uint16_t cfgset = 0;
+  int ret = 0;
+
+  if (option == IOEXPANDER_OPTION_INTCFG)
+    {
+      switch ((uintptr_t)val)
+        {
+          case IOEXPANDER_VAL_FALLING:
+            cfgset = GPIOCHIP_LINE_FLAG_FALLING;
+            break;
+
+          case IOEXPANDER_VAL_RISING:
+            cfgset = GPIOCHIP_LINE_FLAG_RISING;
+            break;
+
+          case IOEXPANDER_VAL_BOTH:
+            cfgset = GPIOCHIP_LINE_FLAG_BOTH;
+            break;
+
+          case IOEXPANDER_VAL_DISABLE:
+            cfgset = GPIOCHIP_LINE_FLAG_DISABLE;
+            break;
+
+          default:
+            return -ENOTSUP;
+        }
+
+      ret = host_gpiochip_irq_request(priv->dev, pin, cfgset);
+      if (ret < 0)
+        {
+          gpioerr("ERROR: Failed to request event: %s\n", strerror(errno));
+        }
+    }
+  else
+    {
+      gpioinfo("gpiochip io option not support\n");
+      return -ENOTSUP;
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: sim_gpiochip_writepin
+ *
+ * Description:
+ *   Write gpiochip pin value.
+ *
+ * Input Parameters:
+ *   dev   - A pointer to instance of sim gpiochip device.
+ *   pin   - The pin number.
+ *   value - The value write to the pin.
+ *
+ * Returned Value:
+ *   0 for success, other for fail.
+ ****************************************************************************/
+
+static int sim_gpiochip_writepin(struct ioexpander_dev_s *dev, uint8_t pin,
+                                 bool value)
+{
+  struct sim_gpiochip_dev_s *priv = (struct sim_gpiochip_dev_s *)dev;
+
+  return host_gpiochip_writepin(priv->dev, pin, value);
+}
+
+/****************************************************************************
+ * Name: sim_gpiochip_readpin
+ *
+ * Description:
+ *   Read gpiochip pin value.
+ *
+ * Input Parameters:
+ *   dev   - A pointer to instance of sim gpiochip device.
+ *   pin   - The pin number.
+ *   value - The value write to the pin.
+ *
+ * Returned Value:
+ *   0 for success, other for fail.
+ ****************************************************************************/
+
+static int sim_gpiochip_readpin(struct ioexpander_dev_s *dev, uint8_t pin,
+                                bool *value)
+{
+  struct sim_gpiochip_dev_s *priv = (struct sim_gpiochip_dev_s *)dev;
+
+  return host_gpiochip_readpin(priv->dev, pin, value);
+}
+
+#ifdef CONFIG_IOEXPANDER_INT_ENABLE
+
+/****************************************************************************
+ * Name: sim_gpiochip_attach
+ *
+ * Returned Value:
+ *   0 for success, other for fail.
+ ****************************************************************************/
+
+static void *sim_gpiochip_attach(struct ioexpander_dev_s *dev,
+                                 ioe_pinset_t pinset,
+                                 ioe_callback_t callback,
+                                 void *arg)
+{
+  struct sim_gpiochip_dev_s *priv = (struct sim_gpiochip_dev_s *)dev;
+  void *handle = NULL;
+  int i;
+
+  for (i = 0; i < CONFIG_IOEXPANDER_NPINS; i++)
+    {
+      if (pinset & (1 << i))
+        {
+          priv->cb[i].cbarg = arg;
+          handle = &priv->cb[i];
+          priv->cb[i].cbfunc = callback;
+        }
+    }
+
+  return handle;
+}
+
+/****************************************************************************
+ * Name: sim_gpiochip_detach
+ *
+ * Returned Value:
+ *   0 for success, other for fail.
+ ****************************************************************************/
+
+static int sim_gpiochip_detach(struct ioexpander_dev_s *dev, void *handle)
+{
+  struct sim_gpiochip_dev_s *priv = (struct sim_gpiochip_dev_s *)dev;
+  struct sim_gpiochip_callback_s *cb = handle;
+
+  if (priv == NULL || cb == NULL)
+    {
+      gpioerr("ERROR: Invalid handle\n");
+      return -EINVAL;
+    }
+
+  cb->cbfunc = NULL;
+  cb->cbarg = NULL;
+  return 0;
+}
+#endif
+
+/****************************************************************************
+ * Name: sim_gpiochip_register_gpio
+ *
+ * Description:
+ *   register gpio for gpiochip device
+ *
+ * Input Parameters:
+ *   priv - A pointer to instance of sim gpiochip device.
+ *
+ * Returned Value:
+ *   0 for OK.
+ *
+ ****************************************************************************/
+
+static int sim_gpiochip_register_gpio(struct sim_gpiochip_dev_s *priv)
+{
+  struct ioexpander_dev_s *ioe = (struct ioexpander_dev_s *)priv;
+  bool input;
+  int line;
+  int ret;
+
+  for (line = 0; line < CONFIG_IOEXPANDER_NPINS; line++)
+    {
+      ret = host_gpiochip_get_line(priv->dev, line, &input);
+      if (ret != 0)
+        {
+          continue;
+        }
+
+      if (input)
+        {
+          ret = gpio_lower_half(ioe, line, GPIO_INPUT_PIN,
+                                GPIOCHIP_LINE_BASE + line);
+        }
+      else
+        {
+          ret = gpio_lower_half(ioe, line, GPIO_OUTPUT_PIN,
+                                GPIOCHIP_LINE_BASE + line);
+        }
+
+      if (ret < 0)
+        {
+          return ret;
+        }
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: sim_gpiochip_irq_process
+ *
+ * Description:
+ *   work to poll irq event for gpiochip device
+ *
+ * Input Parameters:
+ *   priv - A pointer to instance of sim gpiochip device.
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+static void sim_gpiochip_irq_process(struct sim_gpiochip_dev_s *priv)
+{
+  int line;
+
+  for (line = 0; line < CONFIG_IOEXPANDER_NPINS; line++)
+    {
+      if (host_gpiochip_irq_active(priv->dev, line))
+        {
+          if (priv->cb[line].cbfunc)
+            {
+              priv->cb[line].cbfunc((struct ioexpander_dev_s *)priv,
+                                    line, priv->cb[line].cbarg);
+            }
+        }
+    }
+}
+
+/****************************************************************************
+ * Name: sim_gpiochip_interrupt
+ *
+ * Description:
+ *   wdog timer function for gpiochip device
+ *
+ * Input Parameters:
+ *   arg - A pointer to instance of sim gpiochip device.
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+static void sim_gpiochip_interrupt(wdparm_t arg)
+{
+  struct sim_gpiochip_dev_s *priv = (struct sim_gpiochip_dev_s *)arg;
+
+  if (priv)
+    {
+      sim_gpiochip_irq_process(priv);
+
+      wd_start(&priv->wdog, SIM_GPIOCHIP_WDOG_DELAY,
+               sim_gpiochip_interrupt, (wdparm_t)priv);
+    }
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: sim_gpiochip_initialize
+ *
+ * Description:
+ *   Initialize one gpiochip device
+ *
+ * Input Parameters:
+ *   path - the name of gpiochip device in sim, e.g. "/dev/gpiochipN".
+ *
+ * Returned Value:
+ *   The pointer to the instance of sim gpiochip device.
+ *
+ ****************************************************************************/
+
+int sim_gpiochip_initialize(const char *path)
+{
+  struct sim_gpiochip_dev_s *priv;
+  int ret;
+
+  priv = kmm_zalloc(sizeof(struct sim_gpiochip_dev_s));
+  if (priv == NULL)
+    {
+      gpioerr("Failed to allocate memory for gpiochip device");
+      return -ENOMEM;
+    }
+
+  priv->ops = &g_sim_gpiochip_ops;
+
+  priv->dev = host_gpiochip_alloc(path);
+  if (priv->dev == NULL)
+    {
+      gpioerr("Failed to init gpiochip: %d", ret);
+      kmm_free(priv);
+      return -ENODEV;
+    }
+
+  ret = sim_gpiochip_register_gpio(priv);
+  if (ret < 0)
+    {
+      gpioerr("Failed to register gpio: %d", ret);
+      host_gpiochip_free(priv->dev);
+      kmm_free(priv);
+      return ret;
+    }
+
+  wd_start(&priv->wdog, SIM_GPIOCHIP_WDOG_DELAY,
+           sim_gpiochip_interrupt, (wdparm_t)priv);
+
+  return 0;
+}
diff --git a/arch/sim/src/sim/sim_gpiochip.h b/arch/sim/src/sim/sim_gpiochip.h
new file mode 100644
index 00000000000..43bdf3b672f
--- /dev/null
+++ b/arch/sim/src/sim/sim_gpiochip.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+ * arch/sim/src/sim/sim_gpiochip.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_SIM_SRC_SIM_GPIOCHIP_H
+#define __ARCH_SIM_SRC_SIM_GPIOCHIP_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#ifdef __SIM__
+#  include "config.h"
+#endif
+
+#include <stdint.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define GPIOCHIP_LINE_FLAG_DISABLE  (1 << 0)
+#define GPIOCHIP_LINE_FLAG_RISING   (1 << 1)
+#define GPIOCHIP_LINE_FLAG_FALLING  (1 << 2)
+#define GPIOCHIP_LINE_FLAG_BOTH     (1 << 3)
+
+/****************************************************************************
+ * Type Definitions
+ ****************************************************************************/
+
+struct host_gpiochip_dev
+{
+  int file;
+  int line_fd[CONFIG_IOEXPANDER_NPINS];
+};
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+struct host_gpiochip_dev *host_gpiochip_alloc(const char *filename);
+void host_gpiochip_free(struct host_gpiochip_dev *dev);
+int host_gpiochip_get_line(struct host_gpiochip_dev *priv, uint8_t pin,
+                           bool *input);
+int host_gpiochip_readpin(struct host_gpiochip_dev *dev,
+                          uint8_t line, bool *value);
+int host_gpiochip_writepin(struct host_gpiochip_dev *dev,
+                           uint8_t pin, bool value);
+int host_gpiochip_direction(struct host_gpiochip_dev *dev,
+                            uint8_t pin, bool input);
+int host_gpiochip_irq_request(struct host_gpiochip_dev *dev, uint8_t pin,
+                              uint16_t cfgset);
+bool host_gpiochip_irq_active(struct host_gpiochip_dev *dev, uint8_t pin);
+
+#endif /* __ARCH_SIM_SRC_SIM_GPIOCHIP_H */
diff --git a/arch/sim/src/sim/sim_internal.h b/arch/sim/src/sim/sim_internal.h
index 45d74064852..f278222772f 100644
--- a/arch/sim/src/sim/sim_internal.h
+++ b/arch/sim/src/sim/sim_internal.h
@@ -516,5 +516,9 @@ size_t sim_stack_check(void *alloc, size_t size);
 void sim_stack_color(void *stackbase, size_t nbytes);
 #endif
 
+#ifdef CONFIG_SIM_GPIOCHIP
+int sim_gpiochip_initialize(const char *filename);
+#endif
+
 #endif /* __ASSEMBLY__ */
 #endif /* __ARCH_SIM_SRC_SIM_INTERNAL_H */


Reply via email to