mirror of
https://github.com/RT-Thread/rt-thread.git
synced 2026-05-15 19:19:06 +08:00
[dm][rpmsg] support Remote Processor Messaging (RPMSG)
Signed-off-by: GuEe-GUI <2991707448@qq.com>
This commit is contained in:
@@ -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"
|
||||
|
||||
Executable
+142
@@ -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__ */
|
||||
@@ -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 */
|
||||
|
||||
Executable
+19
@@ -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
|
||||
Executable
+15
@@ -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')
|
||||
Executable
+327
@@ -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);
|
||||
Executable
+412
@@ -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);
|
||||
Executable
+122
@@ -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);
|
||||
Reference in New Issue
Block a user