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 a7dd865d0d3 drivers/vhost: add vhost (device-side) I2C backend
a7dd865d0d3 is described below

commit a7dd865d0d3600bd0d2ddb44589b1277fa3d3f89
Author: p-szafonimateusz <[email protected]>
AuthorDate: Wed Jun 3 11:30:17 2026 +0200

    drivers/vhost: add vhost (device-side) I2C backend
    
    Add the device side of virtio-i2c (device ID 34). The driver reads the
    path of a local I2C character device from the device config space
    (struct vhost_i2c_config_s), opens it, and serves the peer's request
    virtqueue: each request is decoded with the OASIS virtio-i2c wire
    format and forwarded through I2CIOC_TRANSFER.
    
    Signed-off-by: p-szafonimateusz <[email protected]>
    Signed-off-by: yangsong8 <[email protected]>
---
 drivers/vhost/CMakeLists.txt      |   4 +
 drivers/vhost/Kconfig             |  14 ++
 drivers/vhost/Make.defs           |   4 +
 drivers/vhost/vhost-i2c.c         | 301 ++++++++++++++++++++++++++++++++++++++
 drivers/vhost/vhost-i2c.h         |  67 +++++++++
 drivers/vhost/vhost.c             |   9 ++
 include/nuttx/virtio/virtio-i2c.h | 118 +++++++++++++++
 7 files changed, 517 insertions(+)

diff --git a/drivers/vhost/CMakeLists.txt b/drivers/vhost/CMakeLists.txt
index 1bc142af927..cbd65288fcb 100644
--- a/drivers/vhost/CMakeLists.txt
+++ b/drivers/vhost/CMakeLists.txt
@@ -29,6 +29,10 @@ if(CONFIG_DRIVERS_VHOST_CAN)
   list(APPEND SRCS vhost-can.c)
 endif()
 
+if(CONFIG_DRIVERS_VHOST_I2C)
+  list(APPEND SRCS vhost-i2c.c)
+endif()
+
 if(CONFIG_DRIVERS_VHOST_RNG)
   list(APPEND SRCS vhost-rng.c)
 endif()
diff --git a/drivers/vhost/Kconfig b/drivers/vhost/Kconfig
index 69bc6abb079..341cb32bf81 100644
--- a/drivers/vhost/Kconfig
+++ b/drivers/vhost/Kconfig
@@ -19,6 +19,20 @@ config DRIVERS_VHOST
                resource table through this bus; without it such vdevs are
                rejected with -ENODEV.
 
+config DRIVERS_VHOST_I2C
+       bool "Virtual Host I2C Device Support"
+       default n
+       select I2C
+       select DRIVERS_VHOST
+       ---help---
+               Enable the vhost (device-side) I2C backend.  When an rptun 
resource
+               table exposes a VirtIO I2C adapter device (device ID 34) in the
+               device role, this driver serves the request virtqueue, decodes 
each
+               request with the OASIS VirtIO I2C wire format, and forwards it 
to the
+               local I2C device node named in the device config space.  This 
lets
+               one NuttX instance export an I2C bus to a remote VirtIO I2C 
frontend,
+               the native-VirtIO counterpart of the RPMSG I2C server.
+
 config DRIVERS_VHOST_RNG
        bool "Virtual Host Rng Device Support"
        default n
diff --git a/drivers/vhost/Make.defs b/drivers/vhost/Make.defs
index c27ac8ee077..65908111c3c 100644
--- a/drivers/vhost/Make.defs
+++ b/drivers/vhost/Make.defs
@@ -30,6 +30,10 @@ ifeq ($(CONFIG_DRIVERS_VHOST_CAN),y)
   CSRCS += vhost-can.c
 endif
 
+ifeq ($(CONFIG_DRIVERS_VHOST_I2C),y)
+  CSRCS += vhost-i2c.c
+endif
+
 ifeq ($(CONFIG_DRIVERS_VHOST_RNG),y)
   CSRCS += vhost-rng.c
 endif
diff --git a/drivers/vhost/vhost-i2c.c b/drivers/vhost/vhost-i2c.c
new file mode 100644
index 00000000000..3c46aa53a18
--- /dev/null
+++ b/drivers/vhost/vhost-i2c.c
@@ -0,0 +1,301 @@
+/****************************************************************************
+ * drivers/vhost/vhost-i2c.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * 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 <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/endian.h>
+#include <sys/param.h>
+
+#include <nuttx/fs/fs.h>
+#include <nuttx/i2c/i2c_master.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/vhost/vhost.h>
+#include <nuttx/virtio/virtio-i2c.h>
+#include <nuttx/wqueue.h>
+
+#include "vhost-i2c.h"
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct vhost_i2c_priv_s
+{
+  FAR struct vhost_device *hdev;
+  struct file              bus;      /* Local I2C bus forwarded to */
+  struct work_s            work;
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int vhost_i2c_probe(FAR struct vhost_device *hdev);
+static void vhost_i2c_remove(FAR struct vhost_device *hdev);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static struct vhost_driver g_vhost_i2c_driver =
+{
+  LIST_INITIAL_VALUE(g_vhost_i2c_driver.node),
+  VIRTIO_ID_I2C_ADAPTER, /* Device id */
+  vhost_i2c_probe,       /* Probe */
+  vhost_i2c_remove,      /* Remove */
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: virtio_i2c_decode
+ *
+ * Description:
+ *   Decode a VirtIO I2C request into a NuttX I2C message.
+ *
+ ****************************************************************************/
+
+static int virtio_i2c_decode(FAR const struct virtio_i2c_out_hdr_s *out,
+                             FAR void *data, size_t buflen,
+                             FAR struct i2c_msg_s *msg)
+{
+  uint32_t flags;
+  uint16_t raw;
+
+  if (out == NULL || (buflen > 0 && data == NULL))
+    {
+      return -EINVAL;
+    }
+
+  raw = le16toh(out->addr);
+  if ((raw & 1) != 0 || le16toh(out->padding) != 0)
+    {
+      return -EINVAL;
+    }
+
+  flags = le32toh(out->flags);
+  if ((flags & ~(VIRTIO_I2C_FLAGS_FAIL_NEXT | VIRTIO_I2C_FLAGS_M_RD)) != 0)
+    {
+      return -EINVAL;
+    }
+
+  msg->frequency = 0;
+  msg->flags = (flags & VIRTIO_I2C_FLAGS_M_RD) != 0 ? I2C_M_READ : 0;
+  if ((raw & 0x00f8) == 0x00f0)
+    {
+      /* 11110 marker in bits 7-3 => 10-bit address mode. */
+
+      msg->flags |= I2C_M_TEN;
+      msg->addr = (((raw >> 1) & 0x03) << 8) | ((raw >> 8) & 0xff);
+    }
+  else
+    {
+      msg->addr = (raw >> 1) & 0x7f;
+    }
+
+  msg->buffer = data;
+  msg->length = buflen;
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: vhost_i2c_work
+ *
+ * Description:
+ *   Process available I2C requests.  Each descriptor chain carries a single
+ *   OASIS VirtIO I2C request laid out as an out header, an optional data
+ *   buffer, and an in header.  Each role is its own descriptor, so a
+ *   frontend that maps the buffers separately (non-contiguous) works too.
+ *
+ ****************************************************************************/
+
+static void vhost_i2c_work(FAR void *arg)
+{
+  FAR struct vhost_i2c_priv_s *priv = arg;
+  FAR struct virtqueue *vq = priv->hdev->vrings_info[0].vq;
+
+  for (; ; )
+    {
+      FAR struct virtio_i2c_in_hdr_s *in = NULL;
+      struct virtqueue_buf vb[3];
+      struct i2c_msg_s msg;
+      size_t vbcnt = 0;
+      size_t reqlen = 0;
+      int head;
+      int ret;
+      size_t i;
+
+      head = vhost_get_vq_buffers(vq, vb, nitems(vb), &vbcnt);
+      if (head < 0)
+        {
+          break;
+        }
+
+      for (i = 0; i < vbcnt; i++)
+        {
+          reqlen += vb[i].len;
+        }
+
+      /* Layout: out header, optional data, in header.  Grab the in header
+       * (the last descriptor) first so a rejected request can still report
+       * an error.  vhost_get_vq_buffers caps vbcnt at nitems(vb), so a valid
+       * request has two or three descriptors.
+       */
+
+      if (vbcnt >= 2 && vb[vbcnt - 1].len >= sizeof(*in))
+        {
+          in = vb[vbcnt - 1].buf;
+        }
+
+      if (in != NULL && vb[0].len >= sizeof(struct virtio_i2c_out_hdr_s))
+        {
+          FAR void *data = vbcnt == 3 ? vb[1].buf : NULL;
+          size_t buflen = vbcnt == 3 ? vb[1].len : 0;
+
+          ret = virtio_i2c_decode(vb[0].buf, data, buflen, &msg);
+          if (ret >= 0)
+            {
+              struct i2c_transfer_s xfer;
+
+              xfer.msgv = &msg;
+              xfer.msgc = 1;
+              ret = file_ioctl(&priv->bus, I2CIOC_TRANSFER, &xfer);
+            }
+
+          in->status = ret >= 0 ? VIRTIO_I2C_MSG_OK : VIRTIO_I2C_MSG_ERR;
+        }
+      else if (in != NULL)
+        {
+          in->status = VIRTIO_I2C_MSG_ERR;
+        }
+
+      virtqueue_add_consumed_buffer(vq, head, reqlen);
+    }
+
+  virtqueue_kick(vq);
+}
+
+/****************************************************************************
+ * Name: vhost_i2c_handler
+ ****************************************************************************/
+
+static void vhost_i2c_handler(FAR struct virtqueue *vq)
+{
+  FAR struct vhost_i2c_priv_s *priv = vq->vq_dev->priv;
+
+  if (work_available(&priv->work))
+    {
+      work_queue(HPWORK, &priv->work, vhost_i2c_work, priv, 0);
+    }
+}
+
+/****************************************************************************
+ * Name: vhost_i2c_probe
+ ****************************************************************************/
+
+static int vhost_i2c_probe(FAR struct vhost_device *hdev)
+{
+  FAR struct vhost_i2c_priv_s *priv;
+  struct vhost_i2c_config_s cfg;
+  FAR const char *vqnames[1];
+  vq_callback callback[1];
+  int ret;
+
+  priv = kmm_zalloc(sizeof(*priv));
+  if (priv == NULL)
+    {
+      vhosterr("No enough memory\n");
+      return -ENOMEM;
+    }
+
+  priv->hdev = hdev;
+
+  /* Open the local I2C device node named in the device config space. */
+
+  vhost_read_config(hdev, 0, &cfg, sizeof(cfg));
+  cfg.path[sizeof(cfg.path) - 1] = '\0';
+
+  ret = file_open(&priv->bus, cfg.path, O_RDWR | O_CLOEXEC);
+  if (ret < 0)
+    {
+      vhosterr("Failed to open %s: %d\n", cfg.path, ret);
+      kmm_free(priv);
+      return ret;
+    }
+
+  hdev->priv = priv;
+
+  vqnames[0]  = "virtio_i2c_req";
+  callback[0] = vhost_i2c_handler;
+  ret = vhost_create_virtqueues(hdev, 0, 1, vqnames, callback, NULL);
+  if (ret < 0)
+    {
+      vhosterr("vhost_create_virtqueues failed, ret=%d\n", ret);
+      file_close(&priv->bus);
+      kmm_free(priv);
+      return ret;
+    }
+
+  virtqueue_enable_cb(hdev->vrings_info[0].vq);
+
+  /* Process any requests already queued by the remote frontend. */
+
+  vhost_i2c_work(priv);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: vhost_i2c_remove
+ ****************************************************************************/
+
+static void vhost_i2c_remove(FAR struct vhost_device *hdev)
+{
+  FAR struct vhost_i2c_priv_s *priv = hdev->priv;
+
+  virtqueue_disable_cb(hdev->vrings_info[0].vq);
+  work_cancel_sync(HPWORK, &priv->work);
+  vhost_delete_virtqueues(hdev);
+  file_close(&priv->bus);
+  kmm_free(priv);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: vhost_register_i2c_driver
+ ****************************************************************************/
+
+int vhost_register_i2c_driver(void)
+{
+  return vhost_register_driver(&g_vhost_i2c_driver);
+}
diff --git a/drivers/vhost/vhost-i2c.h b/drivers/vhost/vhost-i2c.h
new file mode 100644
index 00000000000..9366c770405
--- /dev/null
+++ b/drivers/vhost/vhost-i2c.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+ * drivers/vhost/vhost-i2c.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * 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 __DRIVERS_VHOST_VHOST_I2C_H
+#define __DRIVERS_VHOST_VHOST_I2C_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#ifdef CONFIG_DRIVERS_VHOST_I2C
+
+#ifdef __cplusplus
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: vhost_register_i2c_driver
+ *
+ * Description:
+ *   Register the vhost (device-side) I2C driver with the vhost core so it
+ *   can be bound to a matching VirtIO I2C vdev.
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int vhost_register_i2c_driver(void);
+
+#undef EXTERN
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CONFIG_DRIVERS_VHOST_I2C */
+
+#endif /* __DRIVERS_VHOST_VHOST_I2C_H */
diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c
index 70afcb0cc9d..0c177ff1df3 100644
--- a/drivers/vhost/vhost.c
+++ b/drivers/vhost/vhost.c
@@ -36,6 +36,7 @@
 #endif
 
 #include "vhost-can.h"
+#include "vhost-i2c.h"
 #include "vhost-rng.h"
 #include "vhost-rpmsg.h"
 
@@ -528,6 +529,14 @@ void vhost_register_drivers(void)
     }
 #endif
 
+#ifdef CONFIG_DRIVERS_VHOST_I2C
+  ret = vhost_register_i2c_driver();
+  if (ret < 0)
+    {
+      vhosterr("vhost_register_i2c_driver failed, ret=%d\n", ret);
+    }
+#endif
+
 #ifdef CONFIG_DRIVERS_VHOST_RNG
   ret = vhost_register_rng_driver();
   if (ret < 0)
diff --git a/include/nuttx/virtio/virtio-i2c.h 
b/include/nuttx/virtio/virtio-i2c.h
new file mode 100644
index 00000000000..7892c8a7fa8
--- /dev/null
+++ b/include/nuttx/virtio/virtio-i2c.h
@@ -0,0 +1,118 @@
+/****************************************************************************
+ * include/nuttx/virtio/virtio-i2c.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * 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 __INCLUDE_NUTTX_VIRTIO_VIRTIO_I2C_H
+#define __INCLUDE_NUTTX_VIRTIO_VIRTIO_I2C_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/compiler.h>
+
+#include <errno.h>
+#include <stdint.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define VIRTIO_I2C_FLAGS_FAIL_NEXT       (1 << 0)
+#define VIRTIO_I2C_FLAGS_M_RD            (1 << 1)
+
+/* Feature bits */
+
+#define VIRTIO_I2C_F_ZERO_LENGTH_REQUEST 0
+
+#define VIRTIO_I2C_MSG_OK                0
+#define VIRTIO_I2C_MSG_ERR               1
+
+/* Max length of the local I2C device node path in the device config space */
+
+#define VHOST_I2C_PATH_MAX               64
+
+#define VIRTIO_I2C_REQ_SIZE(n) \
+  (sizeof(struct virtio_i2c_out_hdr_s) + (n) + \
+   sizeof(struct virtio_i2c_in_hdr_s))
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/* 5.16.6.1 Device Operation: Request Queue */
+
+/* Device-side private config space (the I2C spec defines none): the path of
+ * the local I2C device node (e.g. "/dev/i2c0") this device forwards to,
+ * filled by the resource table author and read back by the vhost I2C
+ * backend, which opens it via file_open().
+ */
+
+begin_packed_struct struct vhost_i2c_config_s
+{
+  char path[VHOST_I2C_PATH_MAX];
+} end_packed_struct;
+
+begin_packed_struct struct virtio_i2c_out_hdr_s
+{
+  uint16_t addr;
+  uint16_t padding;
+  uint32_t flags;
+} end_packed_struct;
+
+begin_packed_struct struct virtio_i2c_in_hdr_s
+{
+  uint8_t status;
+} end_packed_struct;
+
+/****************************************************************************
+ * Inline Functions
+ ****************************************************************************/
+
+/* Shared request-buffer layout helpers and status conversion used by both
+ * the VirtIO I2C frontend (drivers/virtio) and the vhost I2C backend
+ * (drivers/vhost).  The encode (frontend) and decode (backend) routines are
+ * private to each driver and built on these primitives.
+ */
+
+/* Return the data buffer inside a VirtIO I2C request buffer. */
+
+static inline FAR uint8_t *virtio_i2c_buf(FAR void *req)
+{
+  return (FAR uint8_t *)req + sizeof(struct virtio_i2c_out_hdr_s);
+}
+
+/* Return the VirtIO I2C input status header after buflen data bytes. */
+
+static inline FAR struct virtio_i2c_in_hdr_s *
+virtio_i2c_in_hdr(FAR void *req, size_t buflen)
+{
+  return (FAR struct virtio_i2c_in_hdr_s *)(virtio_i2c_buf(req) + buflen);
+}
+
+/* Convert a VirtIO I2C request status into a NuttX errno. */
+
+static inline int virtio_i2c_result(int status)
+{
+  return status == VIRTIO_I2C_MSG_OK ? OK : -EIO;
+}
+
+#endif /* __INCLUDE_NUTTX_VIRTIO_VIRTIO_I2C_H */

Reply via email to