目录
核间通信
AMP 通信方案采用中断 + 共享内存的方式实现,发送端在更新共享内存中数据后,通过触发中断通知接收端进行处理
三种核间中断触发方式,分别是 Mailbox 中断触发、软件中断触发及SGI 触发。
rpmsg物理层
- kernel/drivers/rpmsg/rockchip_rpmsg_mbox.c 是注册在 Platform Bus 上的 driver,同时向 VirtIO Bus 注册
device。它是基于 mailbox 核间中断加 Shared Memory 底层驱动接口实现的物理层(Physical Layer) - kernel/drivers/rpmsg/rockchip_rpmsg_softirq.c 也是注册在 Platform Bus 上的 driver,同时向 VirtIO Bus 注册
device。它是基于 softirq 核间中断加 Shared Memory 底层驱动接口实现的物理层(Physical Layer)。
mailbox 方式的物理层源码
结构体
-
struct rk_virtio_dev { struct virtio_device vdev; // Virtio设备结构 unsigned int vring[2]; // 两个vring的物理地址 struct virtqueue *vq[2]; // 两个virtqueue指针 unsigned int base_queue_id; // 基础队列ID int num_of_vqs; // Virtqueue数量 struct rk_rpmsg_dev *rpdev; // 指向父RPMSG设备 };
-
struct rk_rpmsg_dev { struct platform_device *pdev; // 平台设备 int vdev_nums; // virtio设备数量 unsigned int link_id; // 链路ID int first_notify; // 首次通知标志 u32 flags; // 状态标志 struct mbox_client mbox_cl; // 邮箱客户端 struct mbox_chan *mbox_rx_chan; // 接收邮箱通道 struct mbox_chan *mbox_tx_chan; // 发送邮箱通道 struct rk_virtio_dev *rpvdev[RPMSG_MAX_INSTANCE_NUM]; // virtio设备数组 };
-
struct rk_rpmsg_vq_info { u32 queue_id; // 队列ID void *vring_addr; // vring虚拟地址 struct rk_rpmsg_dev *rpdev; // 指向RPMSG设备 };
probe函数
/**
* rockchip_rpmsg_probe - Rockchip RPMSG 平台驱动探测函数
* @pdev: 关联的平台设备
*
* 这是Rockchip RPMSG驱动的主要初始化函数,负责:
* 1. 初始化RPMSG设备结构
* 2. 设置邮箱通信通道
* 3. 从设备树读取配置参数
* 4. 设置virtio环形缓冲区
* 5. 注册virtio设备
*
* 返回: 0 成功,负值表示错误
rpmsg: rpmsg@7c00000 {
compatible = "rockchip,rpmsg";
mbox-names = "rpmsg-rx", "rpmsg-tx";
mboxes = <&mailbox0 0 &mailbox0 3>;
rockchip,vdev-nums = <1>;
rockchip,link-id = <0x03>;
reg = <0x0 0x7c00000 0x0 0x20000>;
memory-region = <&rpmsg_dma_reserved>;
status = "okay";
};
*/
static int rockchip_rpmsg_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev; // 获取平台设备的设备结构
struct rk_rpmsg_dev *rpdev = NULL; // RPMSG主设备结构指针
struct mbox_client *cl; // 邮箱客户端指针
int i, ret = 0; // 循环变量和返回值
/* 1. 分配并初始化RPMSG主设备结构 */
rpdev = devm_kzalloc(dev, sizeof(*rpdev), GFP_KERNEL);
if (!rpdev)
return -ENOMEM; // 内存分配失败
dev_info(dev, "rockchip rpmsg platform probe.\n");
rpdev->pdev = pdev; // 保存平台设备指针
rpdev->first_notify = 0; // 初始化首次通知标志
/* 2. 设置邮箱客户端 */
cl = &rpdev->mbox_cl; // 获取邮箱客户端结构
cl->dev = dev; // 设置关联设备
cl->rx_callback = rk_rpmsg_rx_callback; // 设置接收回调函数
/* 3. 申请邮箱通道 */
// 请求接收通道
rpdev->mbox_rx_chan = mbox_request_channel_byname(cl, "rpmsg-rx");
if (IS_ERR(rpdev->mbox_rx_chan)) {
ret = PTR_ERR(rpdev->mbox_rx_chan);
dev_err(dev, "failed to request mbox rx chan, ret %d\n", ret);
return ret; // 接收通道申请失败
}
// 请求发送通道
rpdev->mbox_tx_chan = mbox_request_channel_byname(cl, "rpmsg-tx");
if (IS_ERR(rpdev->mbox_tx_chan)) {
ret = PTR_ERR(rpdev->mbox_tx_chan);
dev_err(dev, "failed to request mbox tx chan, ret %d\n", ret);
return ret; // 发送通道申请失败
}
/* 4. 从设备树读取配置参数 */
// 读取链路ID
ret = device_property_read_u32(dev, "rockchip,link-id", &rpdev->link_id);
if (ret) {
dev_err(dev, "failed to get link_id, ret %d\n", ret);
goto free_channel; // 读取失败跳转到清理
}
// 读取virtio设备数量,默认为1
ret = device_property_read_u32(dev, "rockchip,vdev-nums", &rpdev->vdev_nums);
if (ret) {
dev_info(dev, "vdev-nums default 1\n");
rpdev->vdev_nums = 1;
}
// 检查设备数量是否超过最大值
if (rpdev->vdev_nums > RPMSG_MAX_INSTANCE_NUM) {
dev_err(dev, "vdev-nums exceed the max %d\n", RPMSG_MAX_INSTANCE_NUM);
ret = -EINVAL;
goto free_channel;
}
/* 5. 设置virtio环形缓冲区 */
ret = rk_set_vring_phy_buf(pdev, rpdev, rpdev->vdev_nums);
if (ret) {
dev_err(dev, "No vring buffer.\n");
ret = -ENOMEM;
goto free_channel;
}
/* 6. 初始化保留内存(共享DMA池) */
if (of_reserved_mem_device_init(dev)) {
dev_info(dev, "No shared DMA pool.\n");
rpdev->flags &= (~RPMSG_SHARED_DMA_POOL); // 清除共享内存标志
} else {
rpdev->flags |= RPMSG_SHARED_DMA_POOL; // 设置共享内存标志
}
/* 7. 注册virtio设备 */
for (i = 0; i < rpdev->vdev_nums; i++) {
// 打印vring信息
dev_info(dev, "rpdev vdev%d: vring0 0x%x, vring1 0x%x\n",
i, rpdev->rpvdev[i]->vring[0], rpdev->rpvdev[i]->vring[1]);
// 配置virtio设备
rpdev->rpvdev[i]->vdev.id.device = VIRTIO_ID_RPMSG; // 设备类型为RPMSG
rpdev->rpvdev[i]->vdev.config = &rk_rpmsg_config_ops; // 配置操作集
rpdev->rpvdev[i]->vdev.dev.parent = dev; // 父设备
rpdev->rpvdev[i]->vdev.dev.release = rk_rpmsg_vdev_release; // 释放函数
rpdev->rpvdev[i]->base_queue_id = i * 2; // 基础队列ID
rpdev->rpvdev[i]->rpdev = rpdev; // 回指主设备
// 注册virtio设备
ret = register_virtio_device(&rpdev->rpvdev[i]->vdev);
if (ret) {
dev_err(dev, "fail to register rpvdev: %d\n", ret);
goto free_reserved_mem; // 注册失败跳转到清理
}
}
// 保存驱动数据
platform_set_drvdata(pdev, rpdev);
return ret; // 返回成功或错误码
/* 错误处理路径 */
free_reserved_mem:
// 如果配置了共享内存,释放之
if (rpdev->flags & RPMSG_SHARED_DMA_POOL)
of_reserved_mem_device_release(dev);
free_channel:
// 释放邮箱通道
mbox_free_channel(rpdev->mbox_rx_chan);
mbox_free_channel(rpdev->mbox_tx_chan);
return ret;
}
-
使用了 struct mbox_client 接收,接收处理函数 rk_rpmsg_rx_callback
-
ops 功能结构体
-
static struct virtio_config_ops rk_rpmsg_config_ops = { .get_status = rk_rpmsg_get_status, .set_status = rk_rpmsg_set_status, .reset = rk_rpmsg_reset, .find_vqs = rk_rpmsg_find_vqs, .del_vqs = rk_rpmsg_del_vqs, .get_features = rk_rpmsg_get_features, .finalize_features = rk_rpmsg_finalize_features, };
-
-
rpdev->rpvdev[i]->vdev.id.device = VIRTIO_ID_RPMSG; // 设备类型为RPMSG
- VIRTIO_ID_RPMSG 设备注册,之后匹配了virtio_rpmsg_bus.c 的驱动MAC层
-
mailbox为外层提供了controller和client(remoteproc层)两者
MAC层
kernel/drivers/rpmsg/virtio_rpmsg_bus.c 是注册在 VirtIO Bus 上的 driver,同时向 RPMsg Bus 注册 device。
VirtIO 和Virtqueue 是通用 RPMsg 协议选择的MAC层(MAC Layer)
源码
结构体
-
/** * struct virtproc_info - 虚拟远程处理器状态 */ struct virtproc_info { struct virtio_device *vdev; // 关联的virtio设备 struct virtqueue *rvq, *svq; // 接收/发送virtqueue void *rbufs, *sbufs; // 接收/发送缓冲区的内核虚拟地址 unsigned int num_bufs; // 收发缓冲区总数 unsigned int buf_size; // 单个缓冲区大小 int last_sbuf; // 最后使用的发送缓冲区索引 dma_addr_t bufs_dma; // 缓冲区的DMA基地址 struct mutex tx_lock; // 保护svq/sbufs/sleepers的互斥锁 struct idr endpoints; // 本地端点ID映射表 struct mutex endpoints_lock; // 保护endpoints的锁 wait_queue_head_t sendq; // 发送等待队列 atomic_t sleepers; // 等待发送缓冲区的任务计数 struct rpmsg_endpoint *ns_ept; // 名称服务端点 };
probe函数
/**
* rpmsg_probe - virtio RPMSG 设备探测函数
* @vdev: 关联的virtio设备
*
* 这是virtio RPMSG驱动的主要初始化函数,负责:
* 1. 初始化virtio RPMSG设备结构
* 2. 查找并设置virtqueue
* 3. 分配和初始化通信缓冲区
* 4. 设置接收缓冲区
* 5. 初始化名称服务端点
* 6. 启动设备
*
* 返回: 0 成功,负值表示错误
*/
static int rpmsg_probe(struct virtio_device *vdev)
{
// 定义virtqueue回调函数数组
vq_callback_t *vq_cbs[] = { rpmsg_recv_done, rpmsg_xmit_done };
// 定义virtqueue名称数组
static const char * const names[] = { "input", "output" };
struct virtqueue *vqs[2]; // virtqueue指针数组
struct virtproc_info *vrp; // RPMSG处理信息结构
void *bufs_va; // 缓冲区的虚拟地址
int err = 0, i; // 错误码和循环变量
size_t total_buf_space; // 总缓冲区大小
bool notify; // 是否需要通知的标志
/* 1. 分配并初始化virtproc_info结构 */
vrp = kzalloc(sizeof(*vrp), GFP_KERNEL);
if (!vrp)
return -ENOMEM;
vrp->vdev = vdev; // 保存virtio设备指针
// 初始化端点ID管理
idr_init(&vrp->endpoints);
// 初始化端点锁
mutex_init(&vrp->endpoints_lock);
// 初始化传输锁
mutex_init(&vrp->tx_lock);
// 初始化发送等待队列
init_waitqueue_head(&vrp->sendq);
/* 2. 查找并设置virtqueue */
// 查找两个virtqueue(rx和tx)
err = virtio_find_vqs(vdev, 2, vqs, vq_cbs, names, NULL);
if (err)
goto free_vrp;
vrp->rvq = vqs[0]; // 接收virtqueue
vrp->svq = vqs[1]; // 发送virtqueue
// 验证rx和tx的vring大小是否相同
WARN_ON(virtqueue_get_vring_size(vrp->rvq) !=
virtqueue_get_vring_size(vrp->svq));
/* 3. 确定缓冲区数量 */
// 如果vring较小,则减少缓冲区数量
if (virtqueue_get_vring_size(vrp->rvq) < MAX_RPMSG_NUM_BUFS / 2)
vrp->num_bufs = virtqueue_get_vring_size(vrp->rvq) * 2;
else
vrp->num_bufs = MAX_RPMSG_NUM_BUFS;
// 设置每个缓冲区的大小
vrp->buf_size = MAX_RPMSG_BUF_SIZE;
// 计算总缓冲区空间
total_buf_space = vrp->num_bufs * vrp->buf_size;
/* 4. 分配DMA一致性内存用于缓冲区 */
bufs_va = dma_alloc_coherent(vdev->dev.parent,
total_buf_space, &vrp->bufs_dma,
GFP_KERNEL);
if (!bufs_va) {
err = -ENOMEM;
goto vqs_del;
}
dev_dbg(&vdev->dev, "buffers: va %pK, dma %pad\n",
bufs_va, &vrp->bufs_dma);
/* 5. 划分接收和发送缓冲区 */
// 一半用于接收
vrp->rbufs = bufs_va;
// 另一半用于发送
vrp->sbufs = bufs_va + total_buf_space / 2;
/* 6. 设置接收缓冲区 */
for (i = 0; i < vrp->num_bufs / 2; i++) {
struct scatterlist sg;
void *cpu_addr = vrp->rbufs + i * vrp->buf_size;
// 初始化scatterlist
rpmsg_sg_init(&sg, cpu_addr, vrp->buf_size);
// 将接收缓冲区添加到virtqueue
err = virtqueue_add_inbuf(vrp->rvq, &sg, 1, cpu_addr,
GFP_KERNEL);
WARN_ON(err); // 正常情况下不应发生错误
}
/* 7. 禁用发送完成中断 */
virtqueue_disable_cb(vrp->svq);
// 将vrp保存到virtio设备私有数据
vdev->priv = vrp;
/* 8. 如果支持名称服务,初始化名称服务端点 */
if (virtio_has_feature(vdev, VIRTIO_RPMSG_F_NS)) {
vrp->ns_ept = __rpmsg_create_ept(vrp, NULL, rpmsg_ns_cb,
vrp, RPMSG_NS_ADDR);
if (!vrp->ns_ept) {
dev_err(&vdev->dev, "failed to create the ns ept\n");
err = -ENOMEM;
goto free_coherent;
}
}
/* 9. 准备启动设备 */
// 检查是否需要通知
notify = virtqueue_kick_prepare(vrp->rvq);
// 标记设备就绪
virtio_device_ready(vdev);
/* 10. 通知远程处理器可以开始发送消息 */
if (notify)
virtqueue_notify(vrp->rvq);
dev_info(&vdev->dev, "rpmsg host is online\n");
return 0;
/* 错误处理路径 */
free_coherent:
// 释放DMA一致性内存
dma_free_coherent(vdev->dev.parent, total_buf_space,
bufs_va, vrp->bufs_dma);
vqs_del:
// 删除virtqueue
vdev->config->del_vqs(vrp->vdev);
free_vrp:
// 释放virtproc_info结构
kfree(vrp);
return err;
}
static struct virtio_device_id id_table[] = {
{ VIRTIO_ID_RPMSG, VIRTIO_DEV_ANY_ID },
{ 0 },
};
static unsigned int features[] = {
VIRTIO_RPMSG_F_NS,
};
static struct virtio_driver virtio_ipc_driver = {
.feature_table = features,
.feature_table_size = ARRAY_SIZE(features),
.driver.name = KBUILD_MODNAME,
.driver.owner = THIS_MODULE,
.id_table = id_table,
.probe = rpmsg_probe,
.remove = rpmsg_remove,
};
- 收发函数 rpmsg_recv_done, rpmsg_xmit_done
- 512个缓冲区 MAX_RPMSG_NUM_BUFS,每一个大小512字节MAX_RPMSG_BUF_SIZE
- 初始化scatterlist ,按照页操作的可以看出
- VIRTIO_RPMSG_F_NS 是执行的,注册了rpmsg_core.c提供的rpmsg_register_device
- 这里是一个回调函数实现的,当m0核发送了端点信息后,Linux会注册dev设备,供上层去dri匹配(m没有发送就不会注册设备,也就不会有rpmsg_ctrl0)
- 注册 virtio 的dri
- 根据名称创建两个virtio_queue,调用device提供的find_vqs回调(find_vqs是在物理层注册的virtio dev设备)
- 根据当前buf数量,配置到vring中,这里配置的一般是mailbox使用的共享内存
- 该文件另外最大的功能,是提供device以及ept端点的回调给上层
传输层
kernel/drivers/rpmsg/rpmsg_core.c 则是创建 RPMsg Bus,并提供传输层(Transport Layer)接口
源码
probe函数
/*
* 当 rpmsg 驱动与通道(channel)绑定(probe)时,自动为其创建端点(endpoint),
* 并将其接收回调(rx callback)绑定到一个唯一的本地 rpmsg 地址。
*
* 如果需要,还会向远程处理器(remote processor)宣告该通道的存在
*(适用于驱动提供 rpmsg 服务的情况)。
*/
static int rpmsg_dev_probe(struct device *dev)
{
// 1. 获取 rpmsg 设备结构和驱动结构
struct rpmsg_device *rpdev = to_rpmsg_device(dev);
struct rpmsg_driver *rpdrv = to_rpmsg_driver(rpdev->dev.driver);
struct rpmsg_channel_info chinfo = {}; // 初始化通道信息
struct rpmsg_endpoint *ept = NULL; // 端点初始化为 NULL
int err; // 错误码
// 2. 绑定设备的电源管理域(Power Management Domain)
err = dev_pm_domain_attach(dev, true);
if (err)
goto out; // 失败则跳转到 out 标签
// 3. 检查驱动是否定义了回调函数(必须有回调才能接收消息)
if (rpdrv->callback) {
// 填充通道信息:名称、源地址(src)、目标地址(dst=ANY表示自动分配)
strncpy(chinfo.name, rpdev->id.name, RPMSG_NAME_SIZE);
chinfo.src = rpdev->src;
chinfo.dst = RPMSG_ADDR_ANY; // 由内核动态分配端点地址
// 4. 创建端点(endpoint),绑定驱动的回调函数
ept = rpmsg_create_ept(rpdev, rpdrv->callback, NULL, chinfo);
if (!ept) {
dev_err(dev, "failed to create endpoint\n");
err = -ENOMEM;
goto out; // 创建失败则跳转到 out
}
// 5. 将端点关联到 rpmsg 设备,并更新源地址为端点分配的地址
rpdev->ept = ept;
rpdev->src = ept->addr; // 动态分配的地址更新到设备
// 6. 如果驱动支持信号(signals),注册信号回调
if (rpdrv->signals)
ept->sig_cb = rpdrv->signals;
}
// 7. 调用驱动的 probe 函数(驱动自定义的初始化逻辑)
err = rpdrv->probe(rpdev);
if (err) {
dev_err(dev, "%s: failed: %d\n", __func__, err);
goto destroy_ept; // probe 失败则销毁端点
}
// 8. 如果端点创建成功且设备支持 announce_create,向远程处理器宣告通道
if (ept && rpdev->ops->announce_create) {
err = rpdev->ops->announce_create(rpdev);
if (err) {
dev_err(dev, "failed to announce creation\n");
goto remove_rpdev; // 宣告失败则回滚驱动 probe
}
}
// 9. 成功返回
return 0;
// 错误处理标签(按执行顺序反向排列)
remove_rpdev:
// 10. 如果宣告失败,调用驱动的 remove 函数回滚
if (rpdrv->remove)
rpdrv->remove(rpdev);
destroy_ept:
// 11. 如果 probe 失败,销毁已创建的端点
if (ept)
rpmsg_destroy_ept(ept);
out:
// 12. 返回错误码
return err;
}
EXPORT_SYMBOL(rpmsg_unregister_device);
EXPORT_SYMBOL(__register_rpmsg_driver);
- 创建了一个bus总线,驱动应用层(rpmsg_ctrl.c、rpmsg_ns.c、rpmsg_char.c)向下注册rpmsg_driver,同时注册字符设备暴露接口给应用层,virtio_rpmsg_bus.c文件向下注册了virtio driver,向上则注册rpmsg device,一旦和上层驱动应用的driver匹配,上层驱动应用即可通过device提供的接口访问到device层,即virtio_rpmsg_bus.c这一层,这是rpmsg bus主要功能,同时也支持多个device和driver的接入。(相当于是一个中间层)
- 提供注册函数
- rpmsg_register_device
- register_rpmsg_driver
- 当匹配成功后,probe执行,然后执行dri下的 rpdrv->probe(rpdev)
上层驱动应用
rpmsg_char.c
源码
probe函数
/**
* rpmsg_chrdev_probe - RPMsg字符设备探测函数
* @rpdev: 关联的RPMsg设备
*
* 当RPMsg总线发现匹配的设备时调用,负责:
* 1. 创建设备节点
* 2. 初始化字符设备
* 3. 注册到系统
* 返回:0成功,负值表示错误
*/
static int rpmsg_chrdev_probe(struct rpmsg_device *rpdev)
{
struct rpmsg_ctrldev *ctrldev;
struct device *dev;
int ret;
/* 1. 分配控制设备结构体内存 */
ctrldev = kzalloc(sizeof(*ctrldev), GFP_KERNEL);
if (!ctrldev)
return -ENOMEM; // 内存不足错误
/* 关联RPMsg设备 */
ctrldev->rpdev = rpdev;
/* 2. 初始化设备结构 */
dev = &ctrldev->dev;
device_initialize(dev); // 初始化device结构
dev->parent = &rpdev->dev; // 设置父设备
dev->class = rpmsg_class; // 指定设备类
/* 3. 初始化字符设备 */
cdev_init(&ctrldev->cdev, &rpmsg_ctrldev_fops); // 关联文件操作集
ctrldev->cdev.owner = THIS_MODULE; // 设置模块所有者
/* 4. 分配次设备号 */
ret = ida_simple_get(&rpmsg_minor_ida, 0, RPMSG_DEV_MAX, GFP_KERNEL);
if (ret < 0)
goto free_ctrldev;
dev->devt = MKDEV(MAJOR(rpmsg_major), ret); // 组合主次设备号
/* 5. 分配控制ID */
ret = ida_simple_get(&rpmsg_ctrl_ida, 0, 0, GFP_KERNEL);
if (ret < 0)
goto free_minor_ida;
dev->id = ret;
dev_set_name(&ctrldev->dev, "rpmsg_ctrl%d", ret); // 设置设备名称
/* 6. 注册字符设备到系统 */
ret = cdev_device_add(&ctrldev->cdev, &ctrldev->dev);
if (ret)
goto free_ctrl_ida;
/* 7. 设置释放回调 */
dev->release = rpmsg_ctrldev_release_device; // 设备注销时的清理函数
/* 8. 保存私有数据 */
dev_set_drvdata(&rpdev->dev, ctrldev);
return ret;
/* 错误处理路径(逆向释放资源) */
free_ctrl_ida:
ida_simple_remove(&rpmsg_ctrl_ida, dev->id); // 释放控制ID
free_minor_ida:
ida_simple_remove(&rpmsg_minor_ida, MINOR(dev->devt)); // 释放次设备号
free_ctrldev:
put_device(dev); // 减少设备引用计数
kfree(ctrldev); // 释放设备内存
return ret;
}
- 创建rpmsg_ctrl 设备文件
- 向下注册了driver
rpmsg_ctrl 操作集
/**
* rpmsg_ctrldev_open - 打开控制设备文件
* @inode: 设备文件的inode结构
* @filp: 文件结构指针
*
* 功能:
* 1. 获取关联的rpmsg控制设备
* 2. 增加设备引用计数
* 3. 存储设备指针到文件私有数据
* 返回:始终返回0(成功)
*/
static int rpmsg_ctrldev_open(struct inode *inode, struct file *filp)
{
/* 通过inode中的cdev指针获取控制设备结构 */
struct rpmsg_ctrldev *ctrldev = cdev_to_ctrldev(inode->i_cdev);
/* 增加设备引用计数,防止设备在使用期间被注销 */
get_device(&ctrldev->dev);
/* 将设备指针存入文件私有数据,供后续操作使用 */
filp->private_data = ctrldev;
return 0;
}
/**
* rpmsg_ctrldev_release - 关闭设备文件
* @inode: 设备文件的inode结构
* @filp: 文件结构指针
*
* 功能:
* 1. 减少设备引用计数
* 2. 当引用计数为0时触发设备的release回调
*/
static int rpmsg_ctrldev_release(struct inode *inode, struct file *filp)
{
/* 获取文件关联的控制设备 */
struct rpmsg_ctrldev *ctrldev = cdev_to_ctrldev(inode->i_cdev);
/* 减少设备引用计数,可能触发设备释放 */
put_device(&ctrldev->dev);
return 0;
}
/**
* rpmsg_ctrldev_ioctl - 设备控制接口
* @fp: 文件结构指针
* @cmd: ioctl命令字
* @arg: 用户空间参数指针
*
* 支持的命令:
* - RPMSG_CREATE_EPT_IOCTL:创建RPMsg端点
*
* 返回:成功返回0,失败返回错误码
*/
static long rpmsg_ctrldev_ioctl(struct file *fp, unsigned int cmd,
unsigned long arg)
{
/* 从文件私有数据获取控制设备 */
struct rpmsg_ctrldev *ctrldev = fp->private_data;
/* 用户空间参数指针(需要做安全拷贝) */
void __user *argp = (void __user *)arg;
/* 端点信息结构(用户空间)*/
struct rpmsg_endpoint_info eptinfo;
/* 通道信息结构(内核空间)*/
struct rpmsg_channel_info chinfo;
/* 唯一支持的ioctl命令 */
if (cmd != RPMSG_CREATE_EPT_IOCTL)
return -EINVAL; // 非法命令
/* 从用户空间安全拷贝端点信息 */
if (copy_from_user(&eptinfo, argp, sizeof(eptinfo)))
return -EFAULT; // 拷贝失败
/* 准备通道信息 */
memcpy(chinfo.name, eptinfo.name, RPMSG_NAME_SIZE); // 拷贝端点名
chinfo.name[RPMSG_NAME_SIZE-1] = '\0'; // 确保字符串终止
chinfo.src = eptinfo.src; // 源地址 需要大于1024,Linux端
chinfo.dst = eptinfo.dst; // 目标地址,与m核对应
/* 创建实际的端点设备 */
return rpmsg_eptdev_create(ctrldev, chinfo);
}
/**
* rpmsg_eptdev_create - 创建并注册RPMSG端点设备
* @ctrldev: 所属的控制设备
* @chinfo: 通道信息(名称/src/dst)
*
* 功能流程:
* 1. 分配和初始化端点设备结构
* 2. 设置同步机制(互斥锁/自旋锁)
* 3. 初始化skb接收队列
* 4. 注册字符设备到内核
* 返回:0成功,负错误码表示失败
*/
static int rpmsg_eptdev_create(struct rpmsg_ctrldev *ctrldev,
struct rpmsg_channel_info chinfo)
{
struct rpmsg_device *rpdev = ctrldev->rpdev; // 获取底层RPMSG设备
struct rpmsg_eptdev *eptdev; // 端点设备结构
struct device *dev; // 基础设备结构
int ret;
/* 1. 内存分配 */
eptdev = kzalloc(sizeof(*eptdev), GFP_KERNEL);
if (!eptdev)
return -ENOMEM; // 内核内存不足
/* 2. 基础结构初始化 */
dev = &eptdev->dev;
eptdev->rpdev = rpdev; // 关联RPMSG设备
eptdev->chinfo = chinfo; // 保存通道信息
/* 3. 同步机制初始化 */
mutex_init(&eptdev->ept_lock); // 初始化互斥锁(保护端点操作)
spin_lock_init(&eptdev->queue_lock); // 初始化自旋锁(保护队列)
skb_queue_head_init(&eptdev->queue); // 初始化skb接收队列
init_waitqueue_head(&eptdev->readq); // 初始化读等待队列
/* 4. 设备模型设置 */
device_initialize(dev); // 初始化设备结构
dev->class = rpmsg_class; // 指定设备类
dev->parent = &ctrldev->dev; // 设置父设备
dev->groups = rpmsg_eptdev_groups; // 属性文件组
dev_set_drvdata(dev, eptdev); // 存储私有数据
/* 5. 字符设备设置 */
cdev_init(&eptdev->cdev, &rpmsg_eptdev_fops); // 关联文件操作集
eptdev->cdev.owner = THIS_MODULE; // 设置模块所有者
/* 6. 设备号分配 */
ret = ida_simple_get(&rpmsg_minor_ida, 0, RPMSG_DEV_MAX, GFP_KERNEL);
if (ret < 0)
goto free_eptdev;
dev->devt = MKDEV(MAJOR(rpmsg_major), ret); // 组合主次设备号
/* 7. 端点ID分配 */
ret = ida_simple_get(&rpmsg_ept_ida, 0, 0, GFP_KERNEL);
if (ret < 0)
goto free_minor_ida;
dev->id = ret;
dev_set_name(dev, "rpmsg%d", ret); // 设置设备名称格式
/* 8. 设备注册 */
ret = cdev_device_add(&eptdev->cdev, &eptdev->dev);
if (ret)
goto free_ept_ida;
/* 9. 设置释放回调 */
dev->release = rpmsg_eptdev_release_device; // 注销时的清理函数
return ret; // 返回状态
/* 错误处理路径(按资源申请逆序释放)*/
free_ept_ida:
ida_simple_remove(&rpmsg_ept_ida, dev->id); // 释放端点ID
free_minor_ida:
ida_simple_remove(&rpmsg_minor_ida, MINOR(dev->devt)); // 释放次设备号
free_eptdev:
put_device(dev); // 减少设备引用
kfree(eptdev); // 释放内存
return ret;
}
- 应用层传入2个地址以及名称,驱动去创建节点 rpmsg_eptdev_create
rpmsg 操作集
static const struct file_operations rpmsg_eptdev_fops = {
.owner = THIS_MODULE,
.open = rpmsg_eptdev_open,
.release = rpmsg_eptdev_release,
.read_iter = rpmsg_eptdev_read_iter,
.write_iter = rpmsg_eptdev_write_iter,
.poll = rpmsg_eptdev_poll,
.unlocked_ioctl = rpmsg_eptdev_ioctl,
.compat_ioctl = compat_ptr_ioctl,
};
写:
rpmsg_eptdev_write_iter
rpmsg_sendto:ept->ops->sendto(ept, data, len, dst);//调用端点绑定的回调
端点ept的来源
rpmsg_eptdev_open
{
ept = rpmsg_create_ept(rpdev, rpmsg_ept_cb, eptdev, eptdev->chinfo);
eptdev->ept = ept;
}
而在rpmsg_create_ept: rpdev->ops->create_ept(rpdev, cb, priv, chinfo)(drivers/rpmsg/rpmsg_core.c)调用的是device的ops,该device之前以分析在kernel/drivers/rpmsg/virtio_rpmsg_bus.c生成
static struct rpmsg_device *rpmsg_virtio_add_ctrl_dev(struct virtio_device *vdev)
{
struct rpmsg_device *rpdev_ctrl;
rpdev_ctrl->ops = &virtio_rpmsg_ops;
}
static const struct rpmsg_device_ops virtio_rpmsg_ops = {
.create_channel = virtio_rpmsg_create_channel,
.release_channel = virtio_rpmsg_release_channel,
.create_ept = virtio_rpmsg_create_ept,
.announce_create = virtio_rpmsg_announce_create,
.announce_destroy = virtio_rpmsg_announce_destroy,
};
virtio_rpmsg_create_ept
static struct rpmsg_endpoint *__rpmsg_create_ept(struct virtproc_info *vrp,
struct rpmsg_device *rpdev,
rpmsg_rx_cb_t cb,
void *priv, u32 addr)
{
int id_min, id_max, id;
struct rpmsg_endpoint *ept;
ept->ops = &virtio_endpoint_ops;
...
return ept;
}
static const struct rpmsg_endpoint_ops virtio_endpoint_ops = {
.destroy_ept = virtio_rpmsg_destroy_ept,
.send = virtio_rpmsg_send,
.sendto = virtio_rpmsg_sendto,
.send_offchannel = virtio_rpmsg_send_offchannel,
.trysend = virtio_rpmsg_trysend,
.trysendto = virtio_rpmsg_trysendto,
.trysend_offchannel = virtio_rpmsg_trysend_offchannel,
.get_mtu = virtio_rpmsg_get_mtu,
};
回到最开始的写函数
rpmsg_sendto:ept->ops->sendto(ept, data, len, dst);
即此时写调用的是drivers/rpmsg/virtio_rpmsg_bus.c下的virtio_rpmsg_sendto
其他send的函数同理
读
rpmsg_eptdev_read_iter
{
skb = skb_dequeue(&eptdev->queue);
}
static int rpmsg_ept_cb(struct rpmsg_device *rpdev, void *buf, int len,
void *priv, u32 addr)
{
skb_queue_tail(&eptdev->queue, skb);
}
ept = rpmsg_create_ept(rpdev, rpmsg_ept_cb, eptdev, eptdev->chinfo);
根据上述写的分析,这个rpmsg_create_ept最终调用drivers/rpmsg/virtio_rpmsg_bus.c下的
static struct rpmsg_endpoint *__rpmsg_create_ept(struct virtproc_info *vrp,
struct rpmsg_device *rpdev,
rpmsg_rx_cb_t cb,
void *priv, u32 addr)
{
ept->cb = cb;
}
该回调被绑定到端点上,等待其他地方调用。
该cb回调由以下流程调用
rpmsg_recv_done(注册到下层virtio,由下层调用,后面章节分析)
rpmsg_recv_single
{
ept->cb(ept->rpdev, msg->data, msg_len, ept->priv, __rpmsg32_to_cpu(little_endian, msg->src));
}
- 上层调用ioctrl创建ept时,会新创建一个rpmsgx设备
写流程
-
rpmsg_eptdev_write_iter->rpmsg_trysend | rpmsg_send(非阻塞和阻塞)
-
rpmsg_send-> ept->ops->send(ept, data, len)
-
端点ept的来源
rpmsg_eptdev_open
{
ept = rpmsg_create_ept(rpdev, rpmsg_ept_cb, eptdev, eptdev->chinfo);
eptdev->ept = ept;
} -
ops的来源
-
在bus也就是mac层,ns名称服务的时候创建的dev
-
__rpmsg_create_ept -》ept->ops = &virtio_endpoint_ops
static const struct rpmsg_endpoint_ops virtio_endpoint_ops = { .destroy_ept = virtio_rpmsg_destroy_ept, .send = virtio_rpmsg_send, .sendto = virtio_rpmsg_sendto, .send_offchannel = virtio_rpmsg_send_offchannel, .trysend = virtio_rpmsg_trysend, .trysendto = virtio_rpmsg_trysendto, .trysend_offchannel = virtio_rpmsg_trysend_offchannel, .get_mtu = virtio_rpmsg_get_mtu, };
-
-
-
virtio_rpmsg_send-》rpmsg_send_offchannel_raw(rpdev, src, dst, data, len, true)
-
将buffer添加vring中
virtqueue_add_outbuf(vrp->svq, &sg, 1, msg, GFP_KERNEL); -
通知下层进行数据发送
virtqueue_kick(vrp->svq); -
通知到remoteproc层,而remoteproc层则调用下层mailbox硬件接口进行远端通知
-
virtqueue_notify 调用vq->notify回调,该回调在初始化struct virtqueue时传入 rproc_virtio_notify(rpmsg_probe->virtio_find_vqs->(vdev->config->find_vqs) rproc_virtio_find_vqs->rp_find_vq ->vring_new_virtqueue->传入rproc_virtio_notify),该函数最终调用struct rproc *rproc的ops下kick回调。 hobot_vdsp_rproc_kick(hobot_remoteproc_probe->rproc_alloc传入hobot_vdsp_rproc_ops->rproc_alloc_ops->rproc->ops = kmemdup(ops, sizeof(*ops), GFP_KERNEL);) trigger_interrupt:vdsp_trigger_interrupt
-
-
mbox_send_message 下层mailbox硬件
- msg_submit:chan->mbox->ops->send_data
读流程
- get_mbox_dev_resource 注册中断服务hb_mbox_softirq
- mbox_chan_received_data 回调client 的rx_callback(rk_rpmsg_rx_callback)
- rpmsg_recv_done-》rpmsg_recv_single -》ept->cb,
- ept->cb
- rpmsg_ns_cb(就是ns名称服务时候的,通道rpmesg0完成后就不执行了)该回调在端点初始化时传入
- rpmsg_ept_cb(rpmsg_eptdev_open->rpmsg_create_ept时传入了该回调),后续读进入的是这个函数
- 最终将buf放入队列skb_queue_tail(&eptdev->queue, skb);
- 上层调用read时,rpmsg_eptdev_read_iter被调用
- skb = skb_dequeue(&eptdev->queue);//冲skb队列取数据
总结
-
RPMsg是利用通道进行数据通信,先用 /dev/rpmsg_ctrl0 设备通过ioctrl生成端点,然后使用生成的端点进行通讯
-
名称服务,VIRTIO_RPMSG_F_NS 宏,不开启就不使用名称服务,使用就需要m核心先发送信息才可以注册设备
-
m核心
-
Linux
-
-
RPMsg内核驱动框架涉及到RPMsg框架、virtio框架、remoteproc子系统和mailbox子系统
-
mailbox,驱动开发者可自我注册client生成设备提供APP层直接进行核间通信
-
分析
- 物理层,创建virtio dev设备(物理和mac层中间有一个 remoteproc 这2层的桥梁 ,dev设备注册由virtio与remoteproc的适配层实现drivers/remoteproc/remoteproc_virtio.c)
- mac层,创建virtio dri匹配物理层的dev,然后去创建rpmsg dev设备
- 传输层core,注册rpmsg bus总线,提供dev和dri函数,mac注册dev,应用层注册dri,匹配后执行probe
- 应用层,注册rpmsg dri