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 f4a234613e8d4d5b89d6f63adf5d34ec324620f8
Author: Shoukui Zhang <[email protected]>
AuthorDate: Tue Oct 24 17:14:21 2023 +0800

    Add I2C Slave driver
    
    Signed-off-by: Shoukui Zhang <[email protected]>
---
 drivers/i2c/CMakeLists.txt     |   4 +
 drivers/i2c/Kconfig            |  24 ++
 drivers/i2c/Make.defs          |   4 +
 drivers/i2c/i2c_slave_driver.c | 523 +++++++++++++++++++++++++++++++++++++++++
 include/nuttx/i2c/i2c_slave.h  |  27 +++
 5 files changed, 582 insertions(+)

diff --git a/drivers/i2c/CMakeLists.txt b/drivers/i2c/CMakeLists.txt
index d3f32ac006..b365491fee 100644
--- a/drivers/i2c/CMakeLists.txt
+++ b/drivers/i2c/CMakeLists.txt
@@ -30,6 +30,10 @@ if(CONFIG_I2C)
     list(APPEND SRCS i2c_bitbang.c)
   endif()
 
+  if(CONFIG_I2C_SLAVE_DRIVER)
+    list(APPEND SRCS i2c_slave_driver.c)
+  endif()
+
   # Include the selected I2C multiplexer drivers
 
   if(CONFIG_I2CMULTIPLEXER_PCA9540BDP)
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
index 764fc7473d..a0883f5a20 100644
--- a/drivers/i2c/Kconfig
+++ b/drivers/i2c/Kconfig
@@ -24,6 +24,30 @@ config I2C_SLAVE
        bool "I2C Slave"
        default n
 
+menu "I2C Slave Support"
+
+config I2C_SLAVE_DRIVER
+       bool "I2C Slave driver"
+       default n
+       ---help---
+               Enable I2C Slave driver
+
+config I2C_SLAVE_READBUFSIZE
+       int "I2C Slave driver read buffer size"
+       default 32
+       depends on I2C_SLAVE_DRIVER
+       ---help---
+               I2C Slave read buffer size
+
+config I2C_SLAVE_WRITEBUFSIZE
+       int "I2C Slave driver write buffer size"
+       default 32
+       depends on I2C_SLAVE_DRIVER
+       ---help---
+               I2C Slave write buffer size
+
+endmenu # I2C Slave Support
+
 config I2C_POLLED
        bool "Polled I2C (no interrupts)"
        default n
diff --git a/drivers/i2c/Make.defs b/drivers/i2c/Make.defs
index d069ad80dc..e823327de0 100644
--- a/drivers/i2c/Make.defs
+++ b/drivers/i2c/Make.defs
@@ -32,6 +32,10 @@ ifeq ($(CONFIG_I2C_BITBANG),y)
 CSRCS += i2c_bitbang.c
 endif
 
+ifeq ($(CONFIG_I2C_SLAVE_DRIVER),y)
+CSRCS += i2c_slave_driver.c
+endif
+
 # Include the selected I2C multiplexer drivers
 
 ifeq ($(CONFIG_I2CMULTIPLEXER_PCA9540BDP),y)
diff --git a/drivers/i2c/i2c_slave_driver.c b/drivers/i2c/i2c_slave_driver.c
new file mode 100644
index 0000000000..1cb71262e1
--- /dev/null
+++ b/drivers/i2c/i2c_slave_driver.c
@@ -0,0 +1,523 @@
+/****************************************************************************
+ * drivers/i2c/i2c_slave_driver.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 <nuttx/config.h>
+
+#include <assert.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <nuttx/fs/fs.h>
+#include <nuttx/mutex.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/i2c/i2c_slave.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define DEVNAME_FMT    "/dev/i2cslv%d"
+#define DEVNAME_FMTLEN (11 + 3 + 1)
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int i2c_slave_open(FAR struct file *filep);
+static int i2c_slave_close(FAR struct file *filep);
+static ssize_t i2c_slave_read(FAR struct file *filep, FAR char *buffer,
+                              size_t buflen);
+static ssize_t i2c_slave_write(FAR struct file *filep,
+                               FAR const char *buffer, size_t buflen);
+static int i2c_slave_poll(FAR struct file *filep, FAR struct pollfd *fds,
+                          bool setup);
+
+#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
+static int i2c_slave_unlink(FAR struct inode *inode);
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct i2c_slave_driver_s
+{
+  /* Slave private data */
+
+  FAR struct i2c_slave_s *dev;
+
+  /* Slave send buffer */
+
+  uint8_t write_buffer[CONFIG_I2C_SLAVE_WRITEBUFSIZE];
+
+  /* Slave receive buffer */
+
+  uint8_t read_buffer[CONFIG_I2C_SLAVE_READBUFSIZE];
+
+  /* Slave receive buffer length */
+
+  size_t read_length;
+
+  /* Read buffer index */
+
+  size_t read_index;
+
+  /* Wait for transfer to complete */
+
+  sem_t wait;
+
+  /* Mutual exclusion */
+
+  mutex_t lock;
+
+  /* The poll waiter */
+
+  FAR struct pollfd *fds;
+
+  /* Number of open references */
+
+  int16_t crefs;
+
+#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
+  bool unlinked; /* Indicates if the driver has been unlinked */
+#endif
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct file_operations g_i2cslavefops =
+{
+  i2c_slave_open,     /* open */
+  i2c_slave_close,    /* close */
+  i2c_slave_read,     /* read */
+  i2c_slave_write,    /* write */
+  NULL,               /* seek */
+  NULL,               /* ioctl */
+  NULL,               /* mmap */
+  NULL,               /* truncate */
+  i2c_slave_poll      /* poll */
+#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
+  , i2c_slave_unlink  /* unlink */
+#endif
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: i2c_slave_open
+ *
+ * Description:
+ *   This function is called whenever the I2C Slave device is opened.
+ *
+ * Input Parameters:
+ *   filep  - File structure instance
+ *
+ * Returned Value:
+ *   Zero (OK) is returned on success. A negated errno value is returned on
+ *   any failure to indicate the nature of the failure.
+ *
+ ****************************************************************************/
+
+static int i2c_slave_open(FAR struct file *filep)
+{
+  FAR struct i2c_slave_driver_s *priv;
+
+  DEBUGASSERT(filep->f_inode->i_private != NULL);
+
+  /* Get our private data structure */
+
+  priv = filep->f_inode->i_private;
+
+  /* Get exclusive access to the I2C Slave driver state structure */
+
+  nxmutex_lock(&priv->lock);
+
+  /* Increment the count of open references on the driver */
+
+  priv->crefs++;
+  DEBUGASSERT(priv->crefs > 0);
+
+  nxmutex_unlock(&priv->lock);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: i2c_slave_close
+ *
+ * Description:
+ *   This routine is called when the I2C Slave device is closed.
+ *
+ * Input Parameters:
+ *   filep  - File structure instance
+ *
+ * Returned Value:
+ *   Zero (OK) is returned on success; A negated errno value is returned on
+ *   any failure to indicate the nature of the failure.
+ *
+ ****************************************************************************/
+
+static int i2c_slave_close(FAR struct file *filep)
+{
+  FAR struct i2c_slave_driver_s *priv;
+
+  DEBUGASSERT(filep->f_inode->i_private != NULL);
+
+  /* Get our private data structure */
+
+  priv = filep->f_inode->i_private;
+
+  /* Get exclusive access to the I2C slave driver state structure */
+
+  nxmutex_lock(&priv->lock);
+
+  /* Decrement the count of open references on the driver */
+
+  DEBUGASSERT(priv->crefs > 0);
+  priv->crefs--;
+#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
+  if (priv->crefs <= 0 && priv->unlinked)
+    {
+      nxmutex_destroy(&priv->lock);
+      kmm_free(priv);
+      filep->f_inode->i_private = NULL;
+      return OK;
+    }
+#endif
+
+  nxmutex_unlock(&priv->lock);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: i2c_slave_read
+ *
+ * Description:
+ *   This routine is called when the application requires to read the data
+ *   received by the I2C Slave device.
+ *
+ * Input Parameters:
+ *   filep  - File structure instance
+ *   buffer - User-provided to save the data
+ *   buflen - The maximum size of the user-provided buffer
+ *
+ * Returned Value:
+ *   The positive non-zero number of bytes read on success, 0 on if an
+ *   end-of-file condition, or a negated errno value on any failure.
+ *
+ ****************************************************************************/
+
+static ssize_t i2c_slave_read(FAR struct file *filep, FAR char *buffer,
+                              size_t buflen)
+{
+  FAR struct i2c_slave_driver_s *priv;
+  int ret;
+
+  DEBUGASSERT(filep->f_inode->i_private != NULL);
+
+  /* Get our private data structure */
+
+  priv = filep->f_inode->i_private;
+
+  /* Get exclusive access to the I2C Slave driver state structure */
+
+  nxmutex_lock(&priv->lock);
+
+  while (priv->read_length == 0)
+    {
+      nxmutex_unlock(&priv->lock);
+      if (filep->f_oflags & O_NONBLOCK)
+        {
+          return -EAGAIN;
+        }
+
+      ret = nxsem_wait(&priv->wait);
+      if (ret < 0)
+        {
+          return ret;
+        }
+
+      nxmutex_lock(&priv->lock);
+    }
+
+  buflen = MIN(buflen, priv->read_length);
+  memcpy(buffer, priv->read_buffer + priv->read_index, buflen);
+  priv->read_index += buflen;
+  priv->read_length -= buflen;
+  nxmutex_unlock(&priv->lock);
+  return buflen;
+}
+
+/****************************************************************************
+ * Name: i2c_slave_write
+ *
+ * Description:
+ *   This routine is called when the application needs to enqueue data to be
+ *   transferred at the next leading clock edge of the I2C Slave controller.
+ *
+ * Input Parameters:
+ *   filep  - Instance of struct file to use with the write
+ *   buffer - Data to write
+ *   buflen - Length of data to write in bytes
+ *
+ * Returned Value:
+ *   On success, the number of bytes written are returned (zero indicates
+ *   nothing was written). On any failure, a negated errno value is returned.
+ *
+ ****************************************************************************/
+
+static ssize_t i2c_slave_write(FAR struct file *filep,
+                               FAR const char *buffer, size_t buflen)
+{
+  FAR struct i2c_slave_driver_s *priv;
+  size_t write_bytes;
+  int ret;
+
+  DEBUGASSERT(filep->f_inode->i_private != NULL);
+
+  /* Get our private data structure */
+
+  priv = filep->f_inode->i_private;
+
+  /* Get exclusive access to the I2C Slave driver state structure */
+
+  nxmutex_lock(&priv->lock);
+
+  write_bytes = MIN(buflen, CONFIG_I2C_SLAVE_WRITEBUFSIZE);
+  memcpy(priv->write_buffer, buffer, write_bytes);
+  ret = I2CS_WRITE(priv->dev, priv->write_buffer, write_bytes);
+  nxmutex_unlock(&priv->lock);
+  return ret < 0 ? ret : write_bytes;
+}
+
+/****************************************************************************
+ * Name: i2c_slave_poll
+ ****************************************************************************/
+
+static int i2c_slave_poll(FAR struct file *filep, FAR struct pollfd *fds,
+                          bool setup)
+{
+  FAR struct i2c_slave_driver_s *priv;
+  int ret;
+
+  DEBUGASSERT(filep->f_inode->i_private != NULL);
+
+  /* Get our private data structure */
+
+  priv = filep->f_inode->i_private;
+
+  /* Get exclusive access to the I2C Slave driver state structure */
+
+  nxmutex_lock(&priv->lock);
+  if (setup)
+    {
+      pollevent_t eventset = 0;
+
+      if (priv->fds == NULL)
+        {
+          priv->fds = fds;
+          fds->priv = &priv->fds;
+        }
+      else
+        {
+          ret = -EBUSY;
+          goto out;
+        }
+
+      if (priv->read_length > 0)
+        {
+          eventset |= POLLIN;
+        }
+
+      poll_notify(&priv->fds, 1, eventset);
+    }
+  else if (fds->priv != NULL)
+    {
+      priv->fds = NULL;
+      fds->priv = NULL;
+    }
+
+out:
+  nxmutex_unlock(&priv->lock);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: i2c_slave_unlink
+ *
+ * Description:
+ *   This routine is called when the I2C Slave device is unlinked.
+ *
+ * Input Parameters:
+ *   inode  - The inode associated with the I2C Slave device
+ *
+ * Returned Value:
+ *   Zero is returned on success; a negated value is returned on any failure.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
+static int i2c_slave_unlink(FAR struct inode *inode)
+{
+  FAR struct i2c_slave_driver_s *priv;
+
+  DEBUGASSERT(inode->i_private != NULL);
+
+  /* Get our private data structure */
+
+  priv = inode->i_private;
+
+  /* Get exclusive access to the I2C Slave driver state structure */
+
+  nxmutex_lock(&priv->lock);
+
+  /* Are there open references to the driver data structure? */
+
+  if (priv->crefs <= 0)
+    {
+      nxmutex_destroy(&priv->lock);
+      kmm_free(priv);
+      inode->i_private = NULL;
+      return OK;
+    }
+
+  /* No... just mark the driver as unlinked and free the resources when the
+   * last client closes their reference to the driver.
+   */
+
+  priv->unlinked = true;
+  nxmutex_unlock(&priv->lock);
+  return OK;
+}
+#endif
+
+static int i2c_slave_callback(FAR void *arg, size_t length)
+{
+  FAR struct i2c_slave_driver_s *priv = arg;
+  int semcount;
+
+  /* Get exclusive access to the I2C Slave driver state structure */
+
+  nxmutex_lock(&priv->lock);
+
+  priv->read_index = 0;
+  priv->read_length = length;
+
+  while (nxsem_get_value(&priv->wait, &semcount) >= 0 && semcount <= 1)
+    {
+      nxsem_post(&priv->wait);
+    }
+
+  nxmutex_unlock(&priv->lock);
+
+  poll_notify(&priv->fds, 1, POLLIN);
+  return OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: i2c_slave_register
+ *
+ * Description:
+ *   Register the I2C Slave character device driver as 'devpath'.
+ *
+ * Input Parameters:
+ *   dev   - An instance of the I2C Slave interface to use to communicate
+ *           with the I2C Slave device
+ *   bus   - The I2C Slave bus number. This will be used as the I2C device
+ *           minor number. The I2C Slave character device will be
+ *           registered as /dev/i2cslvN where N is the minor number
+ *   addr  - I2C Slave address
+ *   nbits - The number of address bits provided (7 or 10)
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int i2c_slave_register(FAR struct i2c_slave_s *dev, int bus, int addr,
+                       int nbits)
+{
+  FAR struct i2c_slave_driver_s *priv;
+  char devname[DEVNAME_FMTLEN];
+  int ret;
+
+  /* Sanity check */
+
+  DEBUGASSERT(dev != NULL && (unsigned int)bus < 1000);
+
+  priv = kmm_zalloc(sizeof(struct i2c_slave_driver_s));
+  if (priv == NULL)
+    {
+      return -ENOMEM;
+    }
+
+  snprintf(devname, DEVNAME_FMTLEN, DEVNAME_FMT, bus);
+  ret = register_driver(devname, &g_i2cslavefops, 0666, priv);
+  if (ret < 0)
+    {
+      kmm_free(priv);
+      return ret;
+    }
+
+  nxsem_init(&priv->wait, 0, 0);
+  nxmutex_init(&priv->lock);
+  priv->dev = dev;
+
+  ret = I2CS_SETOWNADDRESS(priv->dev, addr, nbits);
+  if (ret < 0)
+    {
+      goto out;
+    }
+
+  ret = I2CS_READ(priv->dev, priv->read_buffer,
+                  CONFIG_I2C_SLAVE_READBUFSIZE);
+  if (ret < 0)
+    {
+      goto out;
+    }
+
+  ret = I2CS_REGISTERCALLBACK(priv->dev, i2c_slave_callback, priv);
+  if (ret > 0)
+    {
+      return OK;
+    }
+
+out:
+  nxmutex_destroy(&priv->lock);
+  nxsem_destroy(&priv->wait);
+  unregister_driver(devname);
+  kmm_free(priv);
+  return ret;
+}
diff --git a/include/nuttx/i2c/i2c_slave.h b/include/nuttx/i2c/i2c_slave.h
index ef29ab6ced..ee33157958 100644
--- a/include/nuttx/i2c/i2c_slave.h
+++ b/include/nuttx/i2c/i2c_slave.h
@@ -223,6 +223,33 @@ extern "C"
 #define EXTERN extern
 #endif
 
+#ifdef CONFIG_I2C_SLAVE_DRIVER
+
+/****************************************************************************
+ * Name: i2c_slave_register
+ *
+ * Description:
+ *   Register the I2C Slave character device driver as 'devpath'.
+ *
+ * Input Parameters:
+ *   dev   - An instance of the I2C Slave interface to use to communicate
+ *           with the I2C Slave device
+ *   bus   - The I2C Slave bus number. This will be used as the I2C device
+ *           minor number. The I2C Slave character device will be
+ *           registered as /dev/i2cslvN where N is the minor number
+ *   addr  - I2C Slave address
+ *   nbits - The number of address bits provided (7 or 10)
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int i2c_slave_register(FAR struct i2c_slave_s *dev, int bus, int addr,
+                       int nbit);
+
+#endif
+
 #undef EXTERN
 #if defined(__cplusplus)
 }

Reply via email to