Linux rpmsg源码分析
目录
- 核间通信
- rpmsg物理层
- mailbox 方式的物理层源码
- 结构体
- probe函数
- MAC层
- 源码
- 结构体
- probe函数
- 传输层
- 源码
- probe函数
- 上层驱动应用
- 源码
- probe函数
- rpmsg_ctrl 操作集
- rpmsg 操作集
- 写流程
- 读流程
- 总结
核间通信
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; // 基础队列IDint 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; // 链路IDint 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; // 队列IDvoid *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. 从设备树读取配置参数 */// 读取链路IDret = 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设备数量,默认为1ret = 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; // 设备类型为RPMSGrpdev->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; // 基础队列IDrpdev->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; // 接收/发送virtqueuevoid *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]; // 接收virtqueuevrp->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;elsevrp->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;// 初始化scatterlistrpmsg_sg_init(&sg, cpu_addr, vrp->buf_size);// 将接收缓冲区添加到virtqueueerr = 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:// 删除virtqueuevdev->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; // 端点初始化为 NULLint 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
参考博文