Просмотр исходного кода

[dm][rpmsg] support Remote Processor Messaging (RPMSG)

Signed-off-by: GuEe-GUI <2991707448@qq.com>
GuEe-GUI 2 дней назад
Родитель
Сommit
46b90df247

+ 1 - 0
components/drivers/Kconfig

@@ -25,6 +25,7 @@ rsource "led/Kconfig"
 rsource "input/Kconfig"
 rsource "mailbox/Kconfig"
 rsource "hwspinlock/Kconfig"
+rsource "rpmsg/Kconfig"
 rsource "phye/Kconfig"
 rsource "ata/Kconfig"
 rsource "nvme/Kconfig"

+ 142 - 0
components/drivers/include/drivers/rpmsg.h

@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2023-02-25     GuEe-GUI     the first version
+ */
+
+#ifndef __RPMSG_H__
+#define __RPMSG_H__
+
+#include <rthw.h>
+#include <rtthread.h>
+
+#include <drivers/core/dm.h>
+#include <drivers/core/driver.h>
+#include <drivers/byteorder.h>
+
+#define RT_DEVICE_CTRL_RPMSG_CREATE_EPT     (RT_DEVICE_CTRL_BASE(Char) + 'R' + 1)
+#define RT_DEVICE_CTRL_RPMSG_DESTROY_EPT    (RT_DEVICE_CTRL_BASE(Char) + 'R' + 2)
+#define RT_DEVICE_CTRL_RPMSG_DATA_OVERWRITE (RT_DEVICE_CTRL_BASE(Char) + 'R' + 3)
+
+struct rt_rpmsg_device_id
+{
+#define RT_RPMSG_NAME_SIZE 32
+    char name[RT_RPMSG_NAME_SIZE];
+
+    const void *data;
+};
+
+struct rt_rpmsg_ops;
+struct rt_rpmsg_endpoint;
+struct rt_rpmsg_endpoint_info;
+
+struct rt_rpmsg_device
+{
+    struct rt_device parent;
+
+    struct rt_rpmsg_device_id id;
+    rt_list_t ept_nodes;
+    struct rt_spinlock lock;
+
+    const struct rt_rpmsg_ops *ops;
+    void *priv;
+};
+
+struct rt_rpmsg_driver
+{
+    struct rt_driver parent;
+
+    const struct rt_rpmsg_device_id *ids;
+
+    rt_err_t (*probe)(struct rt_rpmsg_device *rdev);
+    rt_err_t (*remove)(struct rt_rpmsg_device *rdev);
+    rt_err_t (*rx_callback)(struct rt_rpmsg_device *rdev,
+            rt_uint32_t src, void *data, rt_size_t len);
+};
+
+typedef rt_err_t (*rt_rpmsg_rx_callback)(struct rt_rpmsg_device *rdev,
+        rt_uint32_t src, void *data, rt_size_t len);
+
+struct rt_rpmsg_ops
+{
+    rt_err_t (*create_endpoint)(struct rt_rpmsg_device *, struct rt_rpmsg_endpoint *,
+            struct rt_rpmsg_endpoint_info *info);
+    rt_err_t (*destroy_endpoint)(struct rt_rpmsg_device *, struct rt_rpmsg_endpoint *);
+    rt_err_t (*send)(struct rt_rpmsg_device *, rt_uint32_t src, rt_uint32_t dst,
+            const void *data, rt_size_t len, rt_int32_t timeout);
+};
+
+struct rt_rpmsg_endpoint_info
+{
+    char name[RT_RPMSG_NAME_SIZE];
+
+#define RT_RPMSG_ADDR_ANY   0xffffffff
+    rt_uint32_t src;
+    rt_uint32_t dst;
+};
+
+struct rt_rpmsg_endpoint
+{
+    rt_list_t list;
+    struct rt_rpmsg_device *rdev;
+
+    struct rt_rpmsg_endpoint_info info;
+    rt_rpmsg_rx_callback rx_callback;
+
+    struct rt_spinlock lock;
+    void *sysdata;
+    void *priv;
+};
+
+enum rt_rpmsg_ns_flags
+{
+    RT_RPMSG_NS_CREATE = 0,
+    RT_RPMSG_NS_DESTROY = 1,
+};
+
+rt_packed(struct rt_rpmsg_ns_msg
+{
+    char name[RT_RPMSG_NAME_SIZE];
+
+#define RT_RPMSG_NS_ADDR    0x35        /* 0x35 -> 53 */
+    rt_uint32_t addr;
+    rt_uint32_t flags;
+});
+
+enum
+{
+    RT_RPMSG_MODE_MASTER,
+    RT_RPMSG_MODE_SLAVE,
+
+    RT_RPMSG_MODE_MAX,
+};
+
+rt_uint32_t rt_rpmsg_mode(void);
+
+struct rt_rpmsg_endpoint *rt_rpmsg_create_endpoint(struct rt_rpmsg_device *,
+        struct rt_rpmsg_endpoint_info *info, rt_rpmsg_rx_callback rx_cb);
+rt_err_t rt_rpmsg_destroy_endpoint(struct rt_rpmsg_device *,
+        struct rt_rpmsg_endpoint *);
+struct rt_rpmsg_endpoint *rt_rpmsg_find_endpoint(struct rt_rpmsg_device *,
+        struct rt_rpmsg_endpoint_info *info);
+
+rt_err_t rt_rpmsg_send(struct rt_rpmsg_endpoint *,
+        const void *data, rt_size_t len);
+rt_err_t rt_rpmsg_sendto(struct rt_rpmsg_endpoint *, rt_uint32_t dst,
+        const void *data, rt_size_t len);
+
+rt_err_t rt_rpmsg_send_wait(struct rt_rpmsg_endpoint *,
+        const void *data, rt_size_t len, rt_int32_t timeout);
+rt_err_t rt_rpmsg_sendto_wait(struct rt_rpmsg_endpoint *, rt_uint32_t dst,
+        const void *data, rt_size_t len, rt_int32_t timeout);
+
+rt_err_t rt_rpmsg_driver_register(struct rt_rpmsg_driver *rdrv);
+rt_err_t rt_rpmsg_device_register(struct rt_rpmsg_device *rdev);
+
+#define RT_RPMSG_DRIVER_EXPORT(driver)  RT_DRIVER_EXPORT(driver, rpmsg, BUILIN)
+
+#endif /* __RPMSG_H__ */

+ 4 - 0
components/drivers/include/rtdevice.h

@@ -79,6 +79,10 @@ extern "C" {
 #include "drivers/hwspinlock.h"
 #endif /* RT_USING_HWSPINLOCK */
 
+#ifdef RT_USING_RPMSG
+#include "drivers/rpmsg.h"
+#endif /* RT_USING_RPMSG */
+
 #ifdef RT_USING_BLK
 #include "drivers/blk.h"
 #endif /* RT_USING_BLK */

+ 19 - 0
components/drivers/rpmsg/Kconfig

@@ -0,0 +1,19 @@
+menuconfig RT_USING_RPMSG
+    bool "Using Remote Processor Messaging (RPMSG)"
+    select RT_USING_DEVICE_IPC
+    select RT_USING_SYSTEM_WORKQUEUE
+    default n
+
+config RT_RPMSG_CHAR_MSG_MAX
+    int "Char device message receive max"
+    depends on RT_USING_RPMSG
+    default 64
+
+config RT_RPMSG_CHAR_MSG_SIZE_MAX
+    int "Char device message size max"
+    depends on RT_USING_RPMSG
+    default 256
+
+if RT_USING_RPMSG
+    osource "$(SOC_DM_RPMSG_DIR)/Kconfig"
+endif

+ 15 - 0
components/drivers/rpmsg/SConscript

@@ -0,0 +1,15 @@
+from building import *
+
+group = []
+
+if not GetDepend(['RT_USING_RPMSG']):
+    Return('group')
+
+cwd = GetCurrentDir()
+CPPPATH = [cwd + '/../include']
+
+src = ['rpmsg.c', 'rpmsg_char.c', 'rpmsg_ns.c']
+
+group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH)
+
+Return('group')

+ 327 - 0
components/drivers/rpmsg/rpmsg.c

@@ -0,0 +1,327 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2023-02-25     GuEe-GUI     the first version
+ */
+
+#include <rthw.h>
+#include <rtthread.h>
+
+#include <drivers/ofw.h>
+#include <drivers/rpmsg.h>
+#include <drivers/core/bus.h>
+#include <drivers/core/power_domain.h>
+
+#define DBG_TAG "rtdm.rpmsg"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+static rt_uint32_t rpmsg_mode = RT_RPMSG_MODE_SLAVE;
+
+static int rpmsg_mode_setup(void)
+{
+    const char *mode = RT_NULL;
+
+#ifdef RT_USING_OFW
+    mode = rt_ofw_bootargs_select("rpmsg.mode=", 0);
+#endif
+
+    if (!mode)
+    {
+        goto _end;
+    }
+
+    if (!rt_strcmp(mode, "master"))
+    {
+        rpmsg_mode = RT_RPMSG_MODE_MASTER;
+    }
+    else if (!rt_strcmp(mode, "slave"))
+    {
+        rpmsg_mode = RT_RPMSG_MODE_SLAVE;
+    }
+    else
+    {
+        LOG_W("Unknown mode of RPMsg: %s", mode);
+
+        return (int)-RT_EINVAL;
+    }
+
+_end:
+    LOG_D("RPMsg mode: %s", rpmsg_mode == RT_RPMSG_MODE_MASTER ? "master" : "slave");
+
+    return 0;
+}
+INIT_CORE_EXPORT(rpmsg_mode_setup);
+
+rt_uint32_t rt_rpmsg_mode(void)
+{
+    return rpmsg_mode;
+}
+
+struct rt_rpmsg_endpoint *rt_rpmsg_create_endpoint(struct rt_rpmsg_device *rdev,
+        struct rt_rpmsg_endpoint_info *info, rt_rpmsg_rx_callback rx_cb)
+{
+    rt_err_t err;
+    rt_ubase_t level;
+    struct rt_rpmsg_endpoint *ept;
+
+    RT_ASSERT(rdev != RT_NULL);
+    RT_ASSERT(info != RT_NULL);
+
+    ept = rt_calloc(1, sizeof(*ept));
+
+    if (!ept)
+    {
+        return rt_err_ptr(-RT_ENOMEM);
+    }
+    ept->rdev = rdev;
+
+    rt_memcpy(&ept->info, info, sizeof(ept->info));
+    ept->rx_callback = rx_cb ? : rt_container_of(rdev->parent.drv,
+            struct rt_rpmsg_driver, parent)->rx_callback;
+
+    RT_ASSERT(ept->rx_callback != RT_NULL);
+
+    err = rdev->ops->create_endpoint(rdev, ept, info);
+
+    if (err)
+    {
+        rt_free(ept);
+        return rt_err_ptr(err);
+    }
+
+    rt_spin_lock_init(&ept->lock);
+
+    rt_list_init(&ept->list);
+    level = rt_spin_lock_irqsave(&rdev->lock);
+    rt_list_insert_before(&rdev->ept_nodes, &ept->list);
+    rt_spin_unlock_irqrestore(&rdev->lock, level);
+
+    return ept;
+}
+
+rt_err_t rt_rpmsg_destroy_endpoint(struct rt_rpmsg_device *rdev,
+        struct rt_rpmsg_endpoint *ept)
+{
+    rt_err_t err;
+    rt_ubase_t level;
+
+    RT_ASSERT(rdev != RT_NULL);
+    RT_ASSERT(ept != RT_NULL);
+
+    err = rdev->ops->destroy_endpoint(rdev, ept);
+
+    if (err)
+    {
+        return err;
+    }
+
+    level = rt_spin_lock_irqsave(&rdev->lock);
+    rt_list_remove(&ept->list);
+    rt_spin_unlock_irqrestore(&rdev->lock, level);
+
+    rt_free(ept);
+
+    return RT_EOK;
+}
+
+struct rt_rpmsg_endpoint *rt_rpmsg_find_endpoint(struct rt_rpmsg_device *rdev,
+        struct rt_rpmsg_endpoint_info *info)
+{
+    rt_ubase_t level;
+    struct rt_rpmsg_endpoint *ept = RT_NULL, *ept_tmp;
+
+    RT_ASSERT(rdev != RT_NULL);
+    RT_ASSERT(info != RT_NULL);
+
+    level = rt_spin_lock_irqsave(&rdev->lock);
+
+    rt_list_for_each_entry(ept_tmp, &rdev->ept_nodes, list)
+    {
+        if (info->src != RT_RPMSG_ADDR_ANY && info->src != ept_tmp->info.src)
+        {
+            continue;
+        }
+
+        if (info->dst != RT_RPMSG_ADDR_ANY && info->dst != ept_tmp->info.dst)
+        {
+            continue;
+        }
+
+        if (info->name[0] &&
+            rt_strncmp(info->name, ept_tmp->info.name, RT_RPMSG_NAME_SIZE))
+        {
+            continue;
+        }
+
+        ept = ept_tmp;
+        break;
+    }
+
+    rt_spin_unlock_irqrestore(&rdev->lock, level);
+
+    return ept;
+}
+
+rt_err_t rt_rpmsg_send(struct rt_rpmsg_endpoint *ept,
+        const void *data, rt_size_t len)
+{
+    RT_ASSERT(ept != RT_NULL);
+
+    return rt_rpmsg_sendto(ept, ept->info.dst, data, len);
+}
+
+rt_err_t rt_rpmsg_sendto(struct rt_rpmsg_endpoint *ept, rt_uint32_t dst,
+        const void *data, rt_size_t len)
+{
+    RT_ASSERT(ept != RT_NULL);
+
+    return rt_rpmsg_sendto_wait(ept, dst, data, len, 0);
+}
+
+rt_err_t rt_rpmsg_send_wait(struct rt_rpmsg_endpoint *ept,
+        const void *data, rt_size_t len, rt_int32_t timeout)
+{
+    RT_ASSERT(ept != RT_NULL);
+
+    return rt_rpmsg_sendto_wait(ept, ept->info.dst, data, len, timeout);
+}
+
+rt_err_t rt_rpmsg_sendto_wait(struct rt_rpmsg_endpoint *ept, rt_uint32_t dst,
+        const void *data, rt_size_t len, rt_int32_t timeout)
+{
+    rt_err_t err;
+    struct rt_rpmsg_device *rdev;
+
+    RT_ASSERT(ept != RT_NULL);
+    rdev = ept->rdev;
+
+    rt_hw_spin_lock(&ept->lock.lock);
+
+    err = rdev->ops->send(rdev, ept->info.src, dst, data, len, timeout);
+
+    rt_hw_spin_unlock(&ept->lock.lock);
+
+    return err;
+}
+
+static struct rt_bus rpmsg_bus;
+
+rt_err_t rt_rpmsg_driver_register(struct rt_rpmsg_driver *rdrv)
+{
+    RT_ASSERT(rdrv != RT_NULL);
+
+    rdrv->parent.bus = &rpmsg_bus;
+
+    return rt_driver_register(&rdrv->parent);
+}
+
+rt_err_t rt_rpmsg_device_register(struct rt_rpmsg_device *rdev)
+{
+    rt_err_t err;
+
+    if ((err = rt_dm_dev_set_name_auto(&rdev->parent, rdev->id.name)) < 0)
+    {
+        return err;
+    }
+
+    rt_list_init(&rdev->ept_nodes);
+    rt_spin_lock_init(&rdev->lock);
+
+    return rt_bus_add_device(&rpmsg_bus, &rdev->parent);
+}
+
+static rt_bool_t rpmsg_match(rt_driver_t drv, rt_device_t dev)
+{
+    const struct rt_rpmsg_device_id *id;
+    struct rt_rpmsg_driver *rdrv = rt_container_of(drv, struct rt_rpmsg_driver, parent);
+    struct rt_rpmsg_device *rdev = rt_container_of(dev, struct rt_rpmsg_device, parent);
+
+    for (id = rdrv->ids; id->name[0]; ++id)
+    {
+        if (!rt_strncmp(id->name, rdev->id.name, RT_RPMSG_NAME_SIZE))
+        {
+            rdev->id.data = id->data;
+
+            return RT_TRUE;
+        }
+    }
+
+    return RT_FALSE;
+}
+
+static rt_err_t rpmsg_probe(rt_device_t dev)
+{
+    rt_err_t err;
+    struct rt_rpmsg_driver *rdrv = rt_container_of(dev->drv, struct rt_rpmsg_driver, parent);
+    struct rt_rpmsg_device *rdev = rt_container_of(dev, struct rt_rpmsg_device, parent);
+
+    err = rt_dm_power_domain_attach(dev, RT_TRUE);
+
+    if (err && err != -RT_EEMPTY)
+    {
+        LOG_E("Attach power domain error = %s in device %s",
+                rt_strerror(err), rt_dm_dev_get_name(dev));
+
+        return err;
+    }
+
+    err = rdrv->probe(rdev);
+
+    if (err)
+    {
+        rt_dm_power_domain_detach(dev, RT_TRUE);
+    }
+
+    return err;
+}
+
+static rt_err_t rpmsg_remove(rt_device_t dev)
+{
+    rt_ubase_t level;
+    struct rt_rpmsg_endpoint *ept, *ept_next;
+    struct rt_rpmsg_driver *rdrv = rt_container_of(dev->drv, struct rt_rpmsg_driver, parent);
+    struct rt_rpmsg_device *rdev = rt_container_of(dev, struct rt_rpmsg_device, parent);
+
+    level = rt_spin_lock_irqsave(&rdev->lock);
+
+    rt_list_for_each_entry_safe(ept, ept_next, &rdev->ept_nodes, list)
+    {
+        rt_spin_unlock_irqrestore(&rdev->lock, level);
+
+        rt_rpmsg_destroy_endpoint(rdev, ept);
+
+        level = rt_spin_lock_irqsave(&rdev->lock);
+    }
+
+    rt_spin_unlock_irqrestore(&rdev->lock, level);
+
+    if (rdrv && rdrv->remove)
+    {
+        rdrv->remove(rdev);
+    }
+
+    rt_dm_power_domain_detach(dev, RT_TRUE);
+
+    return RT_EOK;
+}
+
+static struct rt_bus rpmsg_bus =
+{
+    .name = "rpmsg",
+    .match = rpmsg_match,
+    .probe = rpmsg_probe,
+    .remove = rpmsg_remove,
+};
+
+static int rpmsg_bus_init(void)
+{
+    rt_bus_register(&rpmsg_bus);
+
+    return 0;
+}
+INIT_CORE_EXPORT(rpmsg_bus_init);

+ 412 - 0
components/drivers/rpmsg/rpmsg_char.c

@@ -0,0 +1,412 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2023-02-25     GuEe-GUI     the first version
+ */
+
+#include <rtthread.h>
+#include <rtdevice.h>
+
+#define DBG_TAG "rpmsg.char"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+struct rpmsg_char_ctrl;
+
+struct rpmsg_char
+{
+    struct rt_device parent;
+    rt_list_t list;
+
+    struct rpmsg_char_ctrl *rchc;
+    struct rt_rpmsg_endpoint *ept;
+
+    rt_bool_t is_overwrite;
+    struct rt_ringbuffer msg_ring;
+    rt_uint8_t msg_pool[RT_RPMSG_CHAR_MSG_SIZE_MAX * RT_RPMSG_CHAR_MSG_MAX];
+};
+
+struct rpmsg_char_ctrl
+{
+    struct rt_device parent;
+
+    struct rt_rpmsg_device *rdev;
+
+    rt_list_t ept_nodes;
+    rt_list_t del_ept_nodes;
+    struct rt_spinlock lock;
+    struct rt_work del_ept_work;
+};
+
+#define raw_to_rpmsg_char(raw)      rt_container_of(raw, struct rpmsg_char, parent)
+#define raw_to_rpmsg_char_ctrl(raw) rt_container_of(raw, struct rpmsg_char_ctrl, parent)
+
+static struct rt_dm_ida rpmsg_ept_ida = RT_DM_IDA_INIT(RPMSG_EPT);
+static struct rt_dm_ida rpmsg_char_ida = RT_DM_IDA_INIT(RPMSG_CHAR);
+
+static rt_err_t rpmsg_char_open(rt_device_t dev, rt_uint16_t oflag)
+{
+    rt_ubase_t level;
+    rt_err_t err = RT_EOK;
+    struct rpmsg_char_ctrl *rchc;
+    struct rpmsg_char *this_rch = raw_to_rpmsg_char(dev), *rch, *rch_next;
+
+    rchc = this_rch->rchc;
+
+    level = rt_spin_lock_irqsave(&rchc->lock);
+
+    rt_list_for_each_entry_safe(rch, rch_next, &rchc->del_ept_nodes, list)
+    {
+        if (rch == this_rch)
+        {
+            /* It's been cleaned. Don't open it. */
+            err = -RT_EIO;
+            break;
+        }
+    }
+
+    rt_spin_unlock_irqrestore(&rchc->lock, level);
+
+    return err;
+}
+
+static rt_ssize_t rpmsg_char_read(rt_device_t dev,
+        rt_off_t pos, void *buffer, rt_size_t size)
+{
+    struct rpmsg_char *rch = raw_to_rpmsg_char(dev);
+
+    return rt_ringbuffer_get(&rch->msg_ring, buffer, size);
+}
+
+static rt_ssize_t rpmsg_char_write(rt_device_t dev,
+        rt_off_t pos, const void *buffer, rt_size_t size)
+{
+    struct rpmsg_char *rch = raw_to_rpmsg_char(dev);
+
+    return rt_rpmsg_send(rch->ept, buffer, size) ? : size;
+}
+
+static rt_err_t rpmsg_char_control(rt_device_t dev, int cmd, void *args)
+{
+    struct rpmsg_char *rch = raw_to_rpmsg_char(dev);
+
+    if (cmd == RT_DEVICE_CTRL_RPMSG_DESTROY_EPT)
+    {
+        if (dev->ref_count == 1)
+        {
+            rt_ubase_t level;
+
+            level = rt_spin_lock_irqsave(&rch->rchc->lock);
+            rt_list_remove(&rch->list);
+            rt_list_insert_before(&rch->rchc->del_ept_nodes, &rch->list);
+            rt_spin_unlock_irqrestore(&rch->rchc->lock, level);
+
+            rt_work_submit(&rch->rchc->del_ept_work,
+                    RT_SCHED_PRIV(rt_thread_self()).remaining_tick);
+        }
+
+        return RT_EOK;
+    }
+
+    if (cmd == RT_DEVICE_CTRL_RPMSG_DATA_OVERWRITE)
+    {
+        rch->is_overwrite = !!args;
+
+        return RT_EOK;
+    }
+
+    return -RT_EINVAL;
+}
+
+#ifdef RT_USING_DEVICE_OPS
+const static struct rt_device_ops rpmsg_char_ops =
+{
+    .open = rpmsg_char_open,
+    .read = rpmsg_char_read,
+    .write = rpmsg_char_write,
+    .control = rpmsg_char_control,
+};
+#endif
+
+static rt_err_t rpmsg_char_rx_callback(struct rt_rpmsg_device *rdev,
+        rt_uint32_t src, void *data, rt_size_t len)
+{
+    rt_size_t res_size;
+    struct rpmsg_char *rch;
+    struct rt_rpmsg_endpoint *ept;
+    struct rt_rpmsg_endpoint_info info;
+
+    RT_ASSERT(len <= RT_RPMSG_CHAR_MSG_SIZE_MAX);
+
+    info.src = RT_RPMSG_ADDR_ANY;
+    info.dst = src;
+    info.name[0] = '\0';
+    ept = rt_rpmsg_find_endpoint(rdev, &info);
+
+    if (ept)
+    {
+        rch = ept->priv;
+
+        if (rch->is_overwrite)
+        {
+            res_size = rt_ringbuffer_put_force(&rch->msg_ring, data, len);
+        }
+        else
+        {
+            res_size = rt_ringbuffer_put(&rch->msg_ring, data, len);
+        }
+    }
+    else
+    {
+        return -RT_EINVAL;
+    }
+
+    return res_size ? RT_EOK : -RT_ENOMEM;
+}
+
+static void rpmsg_char_ctrl_del_ept_work(struct rt_work *work, void *work_data)
+{
+    rt_ubase_t level;
+    rt_size_t clean_count = 0;
+    struct rpmsg_char *rch, *rch_next;
+    struct rpmsg_char_ctrl *rchc = work_data;
+
+    level = rt_spin_lock_irqsave(&rchc->lock);
+
+    rt_list_for_each_entry_safe(rch, rch_next, &rchc->del_ept_nodes, list)
+    {
+        if (rch->parent.open_flag == RT_DEVICE_OFLAG_CLOSE)
+        {
+            rt_list_remove(&rch->list);
+
+            rt_spin_unlock_irqrestore(&rchc->lock, level);
+
+            rt_rpmsg_destroy_endpoint(rchc->rdev, rch->ept);
+
+            rt_dm_ida_free(&rpmsg_ept_ida, rch->parent.device_id);
+
+            rt_device_unregister(&rch->parent);
+            rt_free(rch);
+
+            level = rt_spin_lock_irqsave(&rchc->lock);
+
+            ++clean_count;
+        }
+    }
+
+    rt_spin_unlock_irqrestore(&rchc->lock, level);
+
+    if (!clean_count)
+    {
+        /* Try again */
+        rt_work_submit(&rchc->del_ept_work, RT_TICK_PER_SECOND);
+    }
+}
+
+static rt_err_t rpmsg_char_ctrl_control(rt_device_t dev, int cmd, void *args)
+{
+    struct rpmsg_char_ctrl *rchc = raw_to_rpmsg_char_ctrl(dev);
+
+    if (cmd == RT_DEVICE_CTRL_RPMSG_CREATE_EPT && args)
+    {
+        int device_id;
+        rt_ubase_t level;
+        struct rpmsg_char *rch;
+        struct rt_rpmsg_endpoint *ept;
+        struct rt_rpmsg_endpoint_info *info = args;
+
+        if (!info->name[0])
+        {
+            rt_strncpy(info->name, "rpmsg-raw", RT_RPMSG_NAME_SIZE);
+        }
+
+        ept = rt_rpmsg_create_endpoint(rchc->rdev, info, &rpmsg_char_rx_callback);
+
+        if (rt_is_err(ept))
+        {
+            return rt_ptr_err(ept);
+        }
+
+        rch = rt_calloc(1, sizeof(*rch));
+
+        if (!rch)
+        {
+            rt_rpmsg_destroy_endpoint(rchc->rdev, ept);
+            return -RT_ENOMEM;
+        }
+
+        if ((device_id = rt_dm_ida_alloc(&rpmsg_ept_ida)) < 0)
+        {
+            rt_free(rch);
+            rt_rpmsg_destroy_endpoint(rchc->rdev, ept);
+            return -RT_EFULL;
+        }
+
+        ept->priv = rch;
+        rch->ept = ept;
+        rch->rchc = rchc;
+
+        rch->parent.type = RT_Device_Class_Char;
+    #ifdef RT_USING_DEVICE_OPS
+        rch->parent.ops = &rpmsg_char_ops;
+    #else
+        rch->parent.read = rpmsg_char_read;
+        rch->parent.write = rpmsg_char_write;
+        rch->parent.control = rpmsg_char_control;
+    #endif
+        rch->parent.master_id = rpmsg_ept_ida.master_id;
+        rch->parent.device_id = device_id;
+
+        rt_ringbuffer_init(&rch->msg_ring, rch->msg_pool, sizeof(rch->msg_pool));
+        rt_dm_dev_set_name(&rch->parent, "rpmsg_%ux%u", ept->info.src, ept->info.dst);
+
+        rt_list_init(&rch->list);
+
+        level = rt_spin_lock_irqsave(&rchc->lock);
+        rt_list_insert_before(&rchc->ept_nodes, &rch->list);
+        rt_spin_unlock_irqrestore(&rchc->lock, level);
+
+        rt_device_register(&rch->parent, rt_dm_dev_get_name(&rch->parent),
+                RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_REMOVABLE);
+
+        return RT_EOK;
+    }
+
+    return -RT_EINVAL;
+}
+
+#ifdef RT_USING_DEVICE_OPS
+const static struct rt_device_ops rpmsg_char_ctrl_ops =
+{
+    .control = rpmsg_char_ctrl_control,
+};
+#endif
+
+static rt_err_t rpmsg_char_probe(struct rt_rpmsg_device *rdev)
+{
+    rt_err_t err;
+    int device_id;
+    struct rpmsg_char_ctrl *rchc = rt_calloc(1, sizeof(*rchc));
+
+    if (!rchc)
+    {
+        return -RT_ENOMEM;
+    }
+
+    if ((device_id = rt_dm_ida_alloc(&rpmsg_char_ida)) < 0)
+    {
+        err = -RT_EFULL;
+        goto _free_dev;
+    }
+
+    rchc->rdev = rdev;
+    rdev->parent.user_data = rchc;
+
+    rt_list_init(&rchc->ept_nodes);
+    rt_list_init(&rchc->del_ept_nodes);
+    rt_spin_lock_init(&rchc->lock);
+    rt_work_init(&rchc->del_ept_work, rpmsg_char_ctrl_del_ept_work, rchc);
+
+    rt_dm_dev_set_name(&rchc->parent, "rpmsg_char%u", device_id);
+
+    rchc->parent.type = RT_Device_Class_Char;
+#ifdef RT_USING_DEVICE_OPS
+    rchc->parent.ops = &rpmsg_char_ctrl_ops;
+#else
+    rchc->parent.control = rpmsg_char_ctrl_control;
+#endif
+    rchc->parent.master_id = rpmsg_char_ida.master_id;
+    rchc->parent.device_id = device_id;
+
+    if ((err = rt_device_register(&rchc->parent, rt_dm_dev_get_name(&rchc->parent),
+            RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_REMOVABLE)))
+    {
+        goto _fail;
+    }
+
+    return RT_EOK;
+
+_fail:
+    rt_dm_ida_free(&rpmsg_char_ida, device_id);
+_free_dev:
+    rt_free(rchc);
+
+    return err;
+}
+
+static rt_err_t rpmsg_char_remove(struct rt_rpmsg_device *rdev)
+{
+    rt_ubase_t level;
+    struct rpmsg_char *rch, *rch_next;
+    struct rpmsg_char_ctrl *rchc = rdev->parent.user_data;
+
+    rt_work_cancel(&rchc->del_ept_work);
+
+    level = rt_spin_lock_irqsave(&rchc->lock);
+
+    rt_list_for_each_entry_safe(rch, rch_next, &rchc->ept_nodes, list)
+    {
+        rt_list_remove(&rch->list);
+
+        rt_spin_unlock_irqrestore(&rchc->lock, level);
+
+        rt_rpmsg_destroy_endpoint(rchc->rdev, rch->ept);
+
+        rt_dm_ida_free(&rpmsg_ept_ida, rch->parent.device_id);
+
+        rt_device_unregister(&rch->parent);
+        rt_free(rch);
+
+        level = rt_spin_lock_irqsave(&rchc->lock);
+    }
+
+    rt_list_for_each_entry_safe(rch, rch_next, &rchc->del_ept_nodes, list)
+    {
+        rt_list_remove(&rch->list);
+
+        rt_spin_unlock_irqrestore(&rchc->lock, level);
+
+        rt_rpmsg_destroy_endpoint(rchc->rdev, rch->ept);
+
+        rt_dm_ida_free(&rpmsg_ept_ida, rch->parent.device_id);
+
+        rt_device_unregister(&rch->parent);
+        rt_free(rch);
+
+        level = rt_spin_lock_irqsave(&rchc->lock);
+    }
+
+    rt_spin_unlock_irqrestore(&rchc->lock, level);
+
+    rt_dm_ida_free(&rpmsg_char_ida, rchc->parent.device_id);
+
+    rt_device_unregister(&rchc->parent);
+
+    rt_free(rchc);
+
+    return RT_EOK;
+}
+
+static struct rt_rpmsg_device_id rpmsg_char_ids[] =
+{
+    { .name = "rpmsg-raw" },
+    { .name = "rpmsg-char" },
+    { /* sentinel */ }
+};
+
+static struct rt_rpmsg_driver rpmsg_char_driver =
+{
+    .parent.parent =
+    {
+        .name = "rpmsg-char",
+    },
+    .ids = rpmsg_char_ids,
+
+    .probe = rpmsg_char_probe,
+    .remove = rpmsg_char_remove,
+};
+RT_RPMSG_DRIVER_EXPORT(rpmsg_char_driver);

+ 122 - 0
components/drivers/rpmsg/rpmsg_ns.c

@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2023-02-25     GuEe-GUI     the first version
+ */
+
+#include <rtthread.h>
+#include <rtdevice.h>
+
+#define DBG_TAG "rpmsg.ns"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+/*
+ * Used when rt_rpmsg_create_endpoint(..., RT_NULL) (e.g. remote NS announce);
+ * application traffic should be bound via a higher layer later.
+ */
+static rt_err_t rpmsg_ns_remote_default_rx(struct rt_rpmsg_device *rdev,
+        rt_uint32_t src, void *data, rt_size_t len)
+{
+    LOG_D("%s: remote endpoint rx (no user cb), src=%u len=%u", rt_dm_dev_get_name(&rdev->parent), src, len);
+
+    return RT_EOK;
+}
+
+static rt_err_t rpmsg_ns_rx_callback(struct rt_rpmsg_device *rdev,
+        rt_uint32_t src, void *data, rt_size_t len)
+{
+    rt_err_t err = RT_EOK;
+    struct rt_rpmsg_ns_msg *msg = data;
+    struct rt_rpmsg_endpoint *ept;
+    struct rt_rpmsg_endpoint_info info;
+
+    if (len != sizeof(*msg))
+    {
+        LOG_E("Invalid MSG size = %d", len);
+
+        return -RT_EINVAL;
+    }
+
+    /* Fixup the name */
+    msg->name[RT_RPMSG_NAME_SIZE - 1] = '\0';
+    rt_strncpy(info.name, msg->name, RT_RPMSG_NAME_SIZE);
+    info.src = RT_RPMSG_ADDR_ANY;
+    info.dst = rt_le32_to_cpu(msg->addr);
+
+    LOG_D("%s: name: %s, src: %u, dst: %u", rt_dm_dev_get_name(&rdev->parent),
+            info.name, info.src, info.dst);
+
+    if (rt_le32_to_cpu(msg->flags) & RT_RPMSG_NS_DESTROY)
+    {
+        ept = rt_rpmsg_find_endpoint(rdev, &info);
+
+        if (ept)
+        {
+            err = rt_rpmsg_destroy_endpoint(rdev, ept);
+        }
+        else
+        {
+            err = -RT_EEMPTY;
+        }
+    }
+    else if (rt_le32_to_cpu(msg->flags) == RT_RPMSG_NS_CREATE)
+    {
+        ept = rt_rpmsg_create_endpoint(rdev, &info, RT_NULL);
+
+        if (rt_is_err(ept))
+        {
+            err = rt_ptr_err(ept);
+        }
+    }
+    else
+    {
+        LOG_E("Unsupported flags = %x", rt_le32_to_cpu(msg->flags));
+    }
+
+    if (err)
+    {
+        LOG_E("%s: name = %s, addr = %x flags = %d error = %s",
+                rt_dm_dev_get_name(&rdev->parent),
+                msg->name, msg->addr, msg->flags, rt_strerror(err));
+    }
+
+    return err;
+}
+
+static rt_err_t rpmsg_ns_probe(struct rt_rpmsg_device *rdev)
+{
+    struct rt_rpmsg_endpoint *ep;
+    struct rt_rpmsg_endpoint_info info;
+
+    rt_strncpy(info.name, "name-service", RT_RPMSG_NAME_SIZE);
+    info.src = RT_RPMSG_NS_ADDR;
+    info.dst = RT_RPMSG_NS_ADDR;
+
+    ep = rt_rpmsg_create_endpoint(rdev, &info, &rpmsg_ns_rx_callback);
+
+    return rt_is_err(ep) ? rt_ptr_err(ep) : RT_EOK;
+}
+
+static struct rt_rpmsg_device_id rpmsg_ns_ids[] =
+{
+    { .name = "rpmsg-name-service" },
+    { /* sentinel */ }
+};
+
+static struct rt_rpmsg_driver rpmsg_ns_driver =
+{
+    .parent.parent =
+    {
+        .name = "rpmsg-ns",
+    },
+    .ids = rpmsg_ns_ids,
+
+    .probe = rpmsg_ns_probe,
+    .rx_callback = rpmsg_ns_remote_default_rx,
+};
+RT_RPMSG_DRIVER_EXPORT(rpmsg_ns_driver);