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 */