时间轴
时间轴
2026-06-27
init
本文介绍了Linux内核中rpmsg core的设计与实现。rpmsg作为一种总线抽象,不绑定任何具体传输,可支持基于共享内存、mailbox中断或PCIe doorbell等多种后端实现,每个后端只需提供相应操作即可接入核心。文章详细阐述了rpmsg设备匹配机制(rpmsg_dev_match)的优先级评分规则,通过比较specific/general驱动约束、compat匹配及of_node匹配等条件计算分数,以确定最优驱动。同时,文章总结了rpmsg设备探测流程(rpmsg_dev_probe),包括电源域关联(genpd)、自动创建端点(endpoint)以及调用驱动probe函数等关键步骤,并说明了端点地址动态分配与回调绑定的机制。
linux 5.10.23

rpmsg 是一个总线抽象,不绑定任何具体传输。virtio 是目前 Linux 中最常见的实现,但理论上可以有:
- 基于 shared memory 的直接映射实现
- 基于 mailbox 中断的实现
- 基于 PCIe doorbell 的实现
每个 backend 只需要提供自己的rpmsg_endpoint_ops和rpmsg_device_ops,就能接入 rpmsg core。而且不是所有 backend 都需要支持所有 send 变体。例如:
- 最简单的 backend 只需要实现 send 和 trysend。
- 如果 backend 不支持显式指定 src/dst(offchannel),可以不支持
sendto/send_offchannel - 如果 backend 不支持 poll,用户空间写操作仍然可用(阻塞/非阻塞模式由
rpmsg_send/rpmsg_trysend语义保证)。
module_init/module_exit
1234567891011121314151617181920 | static int __init rpmsg_init(void){ int ret; ret = bus_register(&rpmsg_bus); if (ret) pr_err("failed to register rpmsg bus: %d\n", ret); return ret;}postcore_initcall(rpmsg_init);static void __exit rpmsg_fini(void){ bus_unregister(&rpmsg_bus);}module_exit(rpmsg_fini);MODULE_DESCRIPTION("remote processor messaging bus");MODULE_LICENSE("GPL v2"); |
rpmsg_init函数使用postcore_initcall, 调用bus_register注册一个总线
1234567891011121314151617181920212223242526272829303132333435363738 | // include/linux/init.htypedef int (*initcall_t)(void); |
struct bust_type rpmsg_bus
struct bus_type rpmsg_bus定义如下:
12345678 | static struct bus_type rpmsg_bus = { .name = "rpmsg", .match = rpmsg_dev_match, .dev_groups = rpmsg_dev_groups, .uevent = rpmsg_uevent, .probe = rpmsg_dev_probe, .remove = rpmsg_dev_remove,}; |
rpmsg_dev_match
123456789101112131415161718 | /* match rpmsg channel and rpmsg driver */static int rpmsg_dev_match(struct device *dev, struct device_driver *drv){ struct rpmsg_device *rpdev = to_rpmsg_device(dev); struct rpmsg_driver *rpdrv = to_rpmsg_driver(drv); const struct rpmsg_device_id *ids = rpdrv->id_table; unsigned int i; if (rpdev->driver_override) return !strcmp(rpdev->driver_override, drv->name); if (ids) for (i = 0; ids[i].name[0]; i++) if (rpmsg_id_match(rpdev, &ids[i])) return 1; return of_driver_match_device(dev, drv);} |
如果
rpdev->driver_override则只需要比较rpdev->driver_override和drv->name, 即 指定该rpdev强制匹配某个对应名字的driver如果
rpdev->id_table存在从
struct rpmsg_driver *rpdrv中取出const struct rpmsg_device_id *ids,然后遍历 ids 表,通过ids[i].name匹配rpmsg_device,匹配成功就返回 1,否则调用
of_driver_match_device匹配struct device *dev和struct device_driver *drv
of_driver_match_device的匹配即通过struct device_driver *drv中的drv->of_match_table和struct device *dev中的dev->of_node作为参数,调用__of_match_node匹配:
12345678910111213141516171819202122 | staticconst struct of_device_id *__of_match_node(const struct of_device_id *matches, const struct device_node *node){ const struct of_device_id *best_match = NULL; int score, best_score = 0; if (!matches) return NULL; for (; matches->name[0] || matches->type[0] || matches->compatible[0]; matches++) { score = __of_device_is_compatible(node, matches->compatible, matches->type, matches->name); if (score > best_score) { best_match = matches; best_score = score; } } return best_match;} |
通过__of_device_is_compatible计算分数,找到分数最大的那个const struct of_device_id *best_match, 而__of_device_is_compatible定义如下:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667 | /** * __of_device_is_compatible() - Check if the node matches given constraints * @device: pointer to node * @compat: required compatible string, NULL or "" for any match * @type: required device_type value, NULL or "" for any match * @name: required node name, NULL or "" for any match * * Checks if the given @compat, @type and @name strings match the * properties of the given @device. A constraints can be skipped by * passing NULL or an empty string as the constraint. * * Returns 0 for no match, and a positive integer on match. The return * value is a relative score with larger values indicating better * matches. The score is weighted for the most specific compatible value * to get the highest score. Matching type is next, followed by matching * name. Practically speaking, this results in the following priority * order for matches: * * 1. specific compatible && type && name * 2. specific compatible && type * 3. specific compatible && name * 4. specific compatible * 5. general compatible && type && name * 6. general compatible && type * 7. general compatible && name * 8. general compatible * 9. type && name * 10. type * 11. name */static int __of_device_is_compatible(const struct device_node *device, const char *compat, const char *type, const char *name){ struct property *prop; const char *cp; int index = 0, score = 0; /* Compatible match has highest priority */ if (compat && compat[0]) { prop = __of_find_property(device, "compatible", NULL); for (cp = of_prop_next_string(prop, NULL); cp; cp = of_prop_next_string(prop, cp), index++) { if (of_compat_cmp(cp, compat, strlen(compat)) == 0) { score = INT_MAX/2 - (index << 2); break; } } if (!score) return 0; } /* Matching type is better than matching name */ if (type && type[0]) { if (!__of_node_is_type(device, type)) return 0; score += 2; } /* Matching name is a bit better than not */ if (name && name[0]) { if (!of_node_name_eq(device, name)) return 0; score++; } return score;} |
| 优先级 | 驱动约束组合 | 匹配细节条件 | 分数计算公式 | 最终得分举例 |
|---|---|---|---|---|
| 1 | specificcompat&&type&&name | compat匹配且index=0;type匹配;name匹配 | (最高分) | |
| 2 | specificcompat&&type | compat匹配且index=0;type匹配;无name约束 | ||
| 3 | specificcompat&&name | compat匹配且index=0;无type约束;name匹配 | ||
| 4 | specificcompat | compat匹配且index=0;无type和name约束 | ||
| 5 | generalcompat&&type&&name | compat匹配且index=1;type匹配;name匹配 | ||
| 6 | generalcompat&&type | compat匹配且index=1;type匹配;无name约束 | ||
| 7 | generalcompat&&name | compat匹配且index=1;无type约束;name匹配 | ||
| 8 | generalcompat | compat匹配且index=1;无type和name约束 | ||
| — | 更泛化的 compat… | compat匹配且index=2(更靠后的兼容字符串) | 随着 index 增大继续递减 | |
| 9 | type&&name | 无compat约束;type匹配;name匹配 | ||
| 10 | type | 无compat约束;type匹配;无name约束 | ||
| 11 | name | 无compat约束;无type约束;name匹配 | (最低有效分) | |
| — | 不匹配 / 淘汰 | 任何一项驱动指定的约束未能在节点中找到 | 直接返回0 |
rpmsg_dev_probe
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061 | /* * when an rpmsg driver is probed with a channel, we seamlessly create * it an endpoint, binding its rx callback to a unique local rpmsg * address. * * if we need to, we also announce about this channel to the remote * processor (needed in case the driver is exposing an rpmsg service). */static int rpmsg_dev_probe(struct device *dev){ 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; int err; err = dev_pm_domain_attach(dev, true); if (err) goto out; if (rpdrv->callback) { strncpy(chinfo.name, rpdev->id.name, RPMSG_NAME_SIZE); chinfo.src = rpdev->src; chinfo.dst = RPMSG_ADDR_ANY; ept = rpmsg_create_ept(rpdev, rpdrv->callback, NULL, chinfo); if (!ept) { dev_err(dev, "failed to create endpoint\n"); err = -ENOMEM; goto out; } rpdev->ept = ept; rpdev->src = ept->addr; } err = rpdrv->probe(rpdev); if (err) { dev_err(dev, "%s: failed: %d\n", __func__, err); goto destroy_ept; } 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; } } return 0;remove_rpdev: if (rpdrv->remove) rpdrv->remove(rpdev);destroy_ept: if (ept) rpmsg_destroy_ept(ept);out: return err;} |
- 电源域关联
123 | err = dev_pm_domain_attach(dev, true); if (err) goto out; |
作用:将设备关联到电源管理域(Power Management Domain,genpd)。
- 现代 SoC 中,不同外设可能属于不同电源域,可以独立开关
dev_pm_domain_attach(dev, true)中的 true 表示:如果设备树中指定了该设备的power-domains属性,内核会尝试自动 attach- 如果 attach 失败(如电源域不存在),后续初始化没有意义,直接退出
放第一步因为后续的端点创建、驱动初始化可能都依赖硬件电源已经上电。如果电源域没准备好,这些操作可能失败甚至导致硬件异常。
- 自动创建 Endpoint
123456789101112131415 | if (rpdrv->callback) { strncpy(chinfo.name, rpdev->id.name, RPMSG_NAME_SIZE); chinfo.src = rpdev->src; chinfo.dst = RPMSG_ADDR_ANY; ept = rpmsg_create_ept(rpdev, rpdrv->callback, NULL, chinfo); if (!ept) { dev_err(dev, "failed to create endpoint\n"); err = -ENOMEM; goto out; } rpdev->ept = ept; rpdev->src = ept->addr;} |
首先判断if (rpdrv->callback)
- 简单驱动:只需要一个接收回调,注册时提供 callback,框架自动为其创建端点
- 复杂驱动:可能需要多个端点、动态管理地址,这类驱动的 callback 可能为 NULL,它们会在自己的 probe 中手动调用
rpmsg_create_ept(),
然后构造struct rpmsg_channel_info chipinfo
123 | strncpy(chinfo.name, rpdev->id.name, RPMSG_NAME_SIZE); // 服务名chinfo.src = rpdev->src; // 本地地址chinfo.dst = RPMSG_ADDR_ANY; // 目的地址任意 |
注意
dst = RPMSG_ADDR_ANY的含义:端点创建时不绑定固定对端地址,可以接受来自任何远端地址的消息。
调用rpmsg_create_ept()传入的参数分别是struct rpmsg_device *rpdev、rpdrv->callback、priv = NULL、struct rpmsg_channel_info chinfo,创建 ept 后,关键赋值:rpdev->src = ept->addr,这是一个非常关键的操作!
- 如果创建前
rpdev->src = RPMSG_ADDR_ANY,后端会动态分配一个可用地址 ept->addr是后端分配后的实际本地地址- 把
ept->addr写回rpdev->src,确保设备结构体记录的是真实地址
这意味着:驱动的接收回调会被绑定到这个新分配的地址上,远端发送到这个地址的消息就会触发该回调。
- 调用
rpdrv的probe函数
123456 | err = rpdrv->probe(rpdev);if (err) { dev_err(dev, "%s: failed: %d\n", __func__, err); goto destroy_ept;} |
调用struct rpmsg_driver *rpdrv的 probe 函数
- announce_create
1234567 | 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; }} |
如果struct rpmsg_device *rpdev的const struct rpmsg_device_ops *ops的announce_create被设置,则调用该announce_create,即在 ept 被创建后调用announce_create这个 callback
rpmsg_dev_remove
12345678910111213141516171819 | static int rpmsg_dev_remove(struct device *dev){ struct rpmsg_device *rpdev = to_rpmsg_device(dev); struct rpmsg_driver *rpdrv = to_rpmsg_driver(rpdev->dev.driver); int err = 0; if (rpdev->ops->announce_destroy) err = rpdev->ops->announce_destroy(rpdev); if (rpdrv->remove) rpdrv->remove(rpdev); dev_pm_domain_detach(dev, true); if (rpdev->ept) rpmsg_destroy_ept(rpdev->ept); return err;} |
remove 函数和 probe 函数顺序相反,先announce_destroy,然后调用struct rpmsg_driver *rpdrv中的 remove 函数,然后调用dev_pm_domain_detach分离电源域,最后调用rpmsg_destroy_ept销毁rpdev->ept
rpmsg_uevent
123456789101112 | static int rpmsg_uevent(struct device *dev, struct kobj_uevent_env *env){ struct rpmsg_device *rpdev = to_rpmsg_device(dev); int ret; ret = of_device_uevent_modalias(dev, env); if (ret != -ENODEV) return ret; return add_uevent_var(env, "MODALIAS=" RPMSG_DEVICE_MODALIAS_FMT, rpdev->id.name);} |
这个函数是 RPMSG 总线设备产生 uevent(用户空间事件/热插拔事件) 时的回调。它是连接内核设备模型和用户态 udev 的桥梁。函数定位与调用链:
12345678 | 设备注册到 rpmsg_bus │ ▼device_add() └── bus_add_device() / bus_probe_device() └── kobject_uevent(KOBJ_ADD) // 触发 uevent └── dev_uevent() // 设备的 uevent 回调 └── rpmsg_uevent() // ← 就是这里(通过 bus_type.uevent) |
uevent 会携带一组环境变量送到用户态,udev 根据这些变量决定:
- 创建设备节点
- 自动加载驱动模块
- 执行规则脚本
代码逐行分析
- 第一优先:设备树格式 modalias
123 | ret = of_device_uevent_modalias(dev, env);if (ret != -ENODEV) return ret; |
如果设备关联了设备树节点(dev->of_node),of_device_uevent_modalias会:
- 读取设备树的 compatible 属性
- 生成标准的 OF modalias 格式:
of:N<name>T<type>C<compatible> - 添加到 uevent 环境变量
返回值含义:
0:成功添加了 OF modalias,直接返回-ENODEV:设备没有设备树节点,继续走 RPMSG 自己的逻辑
2. **第二优先:RPMSG 自定义 modalias**
12 | return add_uevent_var(env, "MODALIAS=" RPMSG_DEVICE_MODALIAS_FMT, rpdev->id.name); |
如果设备没有设备树节点,RPMSG 生成自己的 modalias:
RPMSG_DEVICE_MODALIAS_FMT定义(在include/linux/rpmsg.h或mod_devicetable.h中):
1 | |
最终生成的 uevent 环境变量示例:MODALIAS=rpmsg:rpmsg-tty
- 完整的 uevent 输出示例
当一个新的 RPMSG 设备注册时,uevent 可能长这样:
12345678 | ACTION=addBUS=rpmsgSUBSYSTEM=rpmsgMODALIAS=rpmsg:rpmsg-tty ← 这里由 rpmsg_uevent 生成NAME=rpmsg-ttySRC=0x401DST=0x0DEVPATH=/bus/rpmsg/devices/virtio0.rpmsg-tty.-1.0 |
udev 如何利用 modalias?
- 自动加载驱动模块: udev 规则通常包含:
123 | # /lib/udev/rules.d/80-drivers.rulesENV{MODALIAS}=="?*", RUN{builtin}+="kmod load $env{MODALIAS}" |
当 uevent 携带MODALIAS=rpmsg:rpmsg-tty时,udev 会执行:modprobe rpmsg:rpmsg-tty
但 modprobe 不认识带冒号的格式,需要模块本身通过别名来匹配。
- 驱动模块中的别名声明: 驱动源码中:
123456 | static struct rpmsg_device_id rpmsg_tty_id_table[] = { { .name = "rpmsg-tty" }, { },}MODULE_DEVICE_TABLE(rpmsg, rpmsg_tty_id_table); // ← 生成模块别名 |
编译后,模块文件中会包含别名信息:
12 | $ modinfo rpmsg_tty alias: rpmsg:tty* |
注意:
MODULE_DEVICE_TABLE宏在编译时生成 mod_rpmsg… 符号,depmod 会将其写入/lib/modules/$(uname -r)/modules.alias。
udev→modprobe的完整链条
内核:rpmsg_uevent()
│
▼ 生成
MODALIAS=rpmsg:rpmsg-tty
│
▼ 通过netlink/socket发送到用户态udevd收到uevent
│
▼ 解析环境变量MODALIAS=rpmsg:rpmsg-tty
│
▼ 执行规则modprobe rpmsg:rpmsg-tty
│
▼ 匹配/lib/modules/.../modules.alias
找到rpmsg_tty.ko
│
▼insmod rpmsg_tty.ko
这样,RPMSG 通道一创建,对应的驱动模块就能自动加载,无需用户手动 modprobe。
如果用户态想手动测试:
查看设备 uevent
1234 $ cat /sys/bus/rpmsg/devices/virtio0.rpmsg-tty.-1.0/ueventBUS=rpmsgDRIVER=rpmsg_ttyMODALIAS=rpmsg:rpmsg-tty手动触发 uevent
1$ echo change > /sys/bus/rpmsg/devices/virtio0.rpmsg-tty.-1.0/uevent这会重新调用 rpmsg_uevent(),udev 会再次处理。查看模块别名
123 $ grep rpmsg /lib/modules/$(uname -r)/modules.aliasalias rpmsg:* rpmsg_corealias rpmsg:rpmsg-tty rpmsg_tty
rpmsg_dev_groups
dev_groups 是 Linux 设备模型中 struct bus_type 的一个字段,它的作用是:为注册到这条总线上的每个设备,自动创建一组 sysfs 属性文件。
在rpmsg_core.c中:
12345678910 | static struct attribute *rpmsg_dev_attrs[] = { &dev_attr_name.attr, &dev_attr_modalias.attr, &dev_attr_dst.attr, &dev_attr_src.attr, &dev_attr_announce.attr, &dev_attr_driver_override.attr, NULL,};ATTRIBUTE_GROUPS(rpmsg_dev); |
ATTRIBUTE_GROUPS(rpmsg_dev) 是内核宏,展开后变成:
12345678 | static struct attribute_group rpmsg_dev_group = { .attrs = rpmsg_dev_attrs,};static struct attribute_group *rpmsg_dev_groups[] = { &rpmsg_dev_group, NULL,}; |
然后挂到总线上:
12345 | static struct bus_type rpmsg_bus = { .name = "rpmsg", .dev_groups = rpmsg_dev_groups, // ← 这里 ...}; |
关键数据结构
struct rpmsg_channel_info
1234567891011 | /** * struct rpmsg_channel_info - channel info representation * @name: name of service * @src: local address * @dst: destination address */struct rpmsg_channel_info { char name[RPMSG_NAME_SIZE]; // 服务名称 u32 src; // 本地地址(源地址) u32 dst; // 目的地址}; |
常用场景:
- 场景 1:创建端点时传递通道信息
12345678 | // rpmsg_dev_probe() 中struct rpmsg_channel_info chinfo = {};strncpy(chinfo.name, rpdev->id.name, RPMSG_NAME_SIZE); // "rpmsg-tty"chinfo.src = rpdev->src; // 初始地址chinfo.dst = RPMSG_ADDR_ANY; // 接受任意远端ept = rpmsg_create_ept(rpdev, rpdrv->callback, NULL, chinfo); |
这里的 chinfo 告诉后端:
我是什么服务:
name = "rpmsg-tty"我想要什么本地地址:src(可能是
RPMSG_ADDR_ANY,让后端分配)我接受谁来访问我:
dst = RPMSG_ADDR_ANY(任意远端)场景 2:标识通道
12345678910111213141516171819202122 | // rpmsg_find_device() 中struct device *rpmsg_find_device(struct device *parent, struct rpmsg_channel_info *chinfo){ return device_find_child(parent, chinfo, rpmsg_device_match);}// rpmsg_device_match() 中static int rpmsg_device_match(struct device *dev, void *data){ struct rpmsg_channel_info *chinfo = data; struct rpmsg_device *rpdev = to_rpmsg_device(dev); if (chinfo->src != RPMSG_ADDR_ANY && chinfo->src != rpdev->src) return 0; if (chinfo->dst != RPMSG_ADDR_ANY && chinfo->dst != rpdev->dst) return 0; if (strncmp(chinfo->name, rpdev->id.name, RPMSG_NAME_SIZE)) return 0; return 1; // 匹配成功} |
这里的 chinfo 是一个"查询条件":
- 可以通过
name+src+dst精确查找一个已存在的通道 - 也可以用
RPMSG_ADDR_ANY作为通配符,忽略 src 或 dst 进行匹配
struct rpmsg_device
12345678910111213141516171819202122 | /** * rpmsg_device - device that belong to the rpmsg bus * @dev: the device struct * @id: device id (used to match between rpmsg drivers and devices) * @driver_override: driver name to force a match; do not set directly, * because core frees it; use driver_set_override() to * set or clear it. * @src: local address * @dst: destination address * @ept: the rpmsg endpoint of this channel * @announce: if set, rpmsg will announce the creation/removal of this channel */struct rpmsg_device { struct device dev; // Linux 设备模型的基类 struct rpmsg_device_id id; // 设备标识(匹配用) const char *driver_override; // 强制绑定指定驱动 u32 src; // 本地地址 u32 dst; // 目的地址 struct rpmsg_endpoint *ept; // 端点(接收回调绑定于此) bool announce; // 是否向远端宣告生命周期 const struct rpmsg_device_ops *ops; // 后端操作表}; |
struct device dev
这是 Linux 设备模型的嵌入基类。rpmsg_device通过组合而非继承的方式接入内核设备模型。
关键宏(在rpmsg_internal.h中):
1 | |
内核总线回调只拿到struct device *,通过这个宏转换为struct rpmsg_device *。
struct rpmsg_device_id id
include/linux/mod_devicetable.h
12345678 | /* rpmsg */struct rpmsg_device_id { char name[RPMSG_NAME_SIZE];}; |
这就是总线匹配时比较的服务名。例如 “rpmsg-tty”、“rpmsg-client-sample”。
为什么单独包一层结构体?
- 遵循 Linux 设备模型的
mod_devicetable.h标准MODULE_DEVICE_TABLE(rpmsg, ...)需要统一的xxx_device_id格式- 未来可以扩展字段而不破坏 ABI
const char *driver_override
强制指定驱动名。当设置后,总线匹配逻辑会绕过 id_table 和 OF 匹配,直接比较驱动名。
重要:注释说 do not set directly,是因为内核会在设备销毁时 kfree() 这个指针。正确用法:
1driver_set_override(dev, &rpdev->driver_override, "my_drv", strlen("my_drv"));
u32 src/u32 dst
| 字段 | 含义 | 变化时机 |
|---|---|---|
| src | 本地地址。设备创建时可能是RPMSG_ADDR_ANY,后端分配后更新为实际值 | rpmsg_dev_probe()中rpdev->src = ept->addr |
| dst | 对端地址。通常是已知的远端服务地址,或RPMSG_ADDR_ANY | 创建时由后端设置 |
src 是我监听/接收的地址,dst 是我要发给谁。rpmsg_device 代表一条逻辑通道,所以同时包含两端地址。
struct rpmsg_endpoint *ept
指向该通道的默认端点。当驱动提供了 callback 时,rpmsg_dev_probe() 会自动创建端点并赋值给这里。
注意:一个 rpmsg_device 只能有一个默认 ept,但驱动在 probe() 中可以手动创建额外的端点(比如需要多个监听地址时)。
bool announce
控制是否在通道创建/销毁时向远端发送**名字服务(Name Service, NS)**宣告消息。announce = true:创建时发 “我上线了”,销毁时发 “我下线了”。远端处理器收到后可以更新自己的服务表,或触发对应的客户端连接
const struct rpmsg_device_ops *ops
后端操作表,定义在rpmsg_internal.h:
123456789101112131415161718 | /** * struct rpmsg_device_ops - indirection table for the rpmsg_device operations * @create_ept: create backend-specific endpoint, required * @announce_create: announce presence of new channel, optional * @announce_destroy: announce destruction of channel, optional * * Indirection table for the operations that a rpmsg backend should implement. * @announce_create and @announce_destroy are optional as the backend might * advertise new channels implicitly by creating the endpoints. */struct rpmsg_device_ops { struct rpmsg_endpoint *(*create_ept)(struct rpmsg_device *rpdev, rpmsg_rx_cb_t cb, void *priv, struct rpmsg_channel_info chinfo); int (*announce_create)(struct rpmsg_device *ept); int (*announce_destroy)(struct rpmsg_device *ept);}; |
这是 RPMSG 核心层与具体后端的分界线:
rpmsg_core.c只调用这些接口virtio_rpmsg_bus.c(或其他后端)实现这些接口- 允许 RPMSG 框架支持多种底层传输(虽然当前主要是 virtio)
struct rpmsg_endpoint
1234567891011121314151617181920212223242526272829303132333435 | typedef int (*rpmsg_rx_cb_t)(struct rpmsg_device *, void *, int, void *, u32);/** * struct rpmsg_endpoint - binds a local rpmsg address to its user * @rpdev: rpmsg channel device * @refcount: when this drops to zero, the ept is deallocated * @cb: rx callback handler * @cb_lock: must be taken before accessing/changing @cb * @addr: local rpmsg address * @priv: private data for the driver's use * * In essence, an rpmsg endpoint represents a listener on the rpmsg bus, as * it binds an rpmsg address with an rx callback handler. * * Simple rpmsg drivers shouldn't use this struct directly, because * things just work: every rpmsg driver provides an rx callback upon * registering to the bus, and that callback is then bound to its rpmsg * address when the driver is probed. When relevant inbound messages arrive * (i.e. messages which their dst address equals to the src address of * the rpmsg channel), the driver's handler is invoked to process it. * * More complicated drivers though, that do need to allocate additional rpmsg * addresses, and bind them to different rx callbacks, must explicitly * create additional endpoints by themselves (see rpmsg_create_ept()). */struct rpmsg_endpoint { struct rpmsg_device *rpdev; struct kref refcount; rpmsg_rx_cb_t cb; struct mutex cb_lock; u32 addr; void *priv; const struct rpmsg_endpoint_ops *ops;}; |
endpoint 是发送/接收的第一类对象
rpmsg core 把所有通信操作都挂在rpmsg_endpoint上,而不是rpmsg_device上。这意味着:
- 一个
rpmsg_device(channel)可以拥有多个 endpoint。 - 不同 endpoint 可以有不同的 callback 和地址。
- 发送操作通过 endpoint 进行,自然地携带了 source address。
这与 TCP socket 的设计类似:device 像 socket fd,endpoint 像具体的连接端点。
rpmsg_rx_cb_t回调类型
1 | typedef int (*rpmsg_rx_cb_t)(struct rpmsg_device *, void *, int, void *, u32); |
参数详解
| 类型 | 含义 |
|---|---|
struct rpmsg_device * | 收到消息的 RPMSG 设备(通道) |
void * | 消息数据指针(payload) |
int | 消息长度(payload len) |
void * | 私有数据(rpmsg_create_ept时传入的priv) |
u32 | 消息来源地址(sender address) |
通常返回 0 表示成功处理。具体含义由后端定义,一般:
- 0:消息已处理,可以释放缓冲区
- 负值:处理出错
使用场景
- 场景 A:简单驱动,注册时提供 callback
12345678910111213 | static int my_rpmsg_cb(struct rpmsg_device *rpdev, void *data, int len, void *priv, u32 src){ pr_info("received %d bytes from 0x%x: %.*s\n", len, src, len, (char *)data); return 0;}static struct rpmsg_driver my_drv = { .drv.name = "my_rpmsg", .id_table = my_id_table, .probe = my_probe, .callback = my_rpmsg_cb, // ← 这里}; |
框架在rpmsg_dev_probe()时自动创建端点,调用在后端实现的rpdev->ops->create_ept()中绑定此回调(virtio_rpmsg_bus.c)。
- 场景 B:复杂驱动,手动创建多个端点
123456789101112 | static int my_probe(struct rpmsg_device *rpdev){ struct rpmsg_channel_info chinfo = {}; struct rpmsg_endpoint *ept2; // 默认端点已由框架创建(rpdev->ept) // 再创建一个额外端点用于控制消息 strncpy(chinfo.name, "ctrl", RPMSG_NAME_SIZE); chinfo.src = RPMSG_ADDR_ANY; ept2 = rpmsg_create_ept(rpdev, ctrl_msg_cb, my_priv, chinfo); // ctrl_msg_cb 会收到发往这个新地址的消息} |
rpmsg_device与rpmsg_endpoint的关系
这是理解 RPMSG 架构的关键:
123456789101112131415161718 | ┌─────────────────────┐ ┌──────────────────────┐│ struct rpmsg_device│ │ struct rpmsg_endpoint││ (代表一条通道) │ │ (代表一个监听地址) │├─────────────────────┤ ├──────────────────────┤│ dev │◄──────────rpdev ││ id.name = "tty" │ │ refcount ││ src = 0x401 │ │ cb = my_callback ││ dst = 0x0 │ │ addr = 0x401 ││ ept ─────────────────────────►│ priv ││ announce = true │ │ ops ││ ops │ └──────────────────────┘└─────────────────────┘ │ │ 1:N ▼ ┌──────────────┐ │ 额外的端点们 │ (驱动手动创建) └──────────────┘ |
关系总结:
- 1 个
rpmsg_device代表一条逻辑通道(关联一个远端处理器) - 1 个
rpmsg_device至少有 1 个默认rpmsg_endpoint(rpdev->ept) - 1 个
rpmsg_device可以有 N 个额外端点(驱动手动创建) - 每个
rpmsg_endpoint绑定一个唯一的addr,收到发往该地址的消息时触发自己的 cb
地址的生命周期映射
| 阶段 | rpmsg_device->src | rpmsg_endpoint->addr | 说明 |
|---|---|---|---|
| 设备刚创建 | RPMSG_ADDR_ANY | 无(还没创建) | 等待后端分配 |
rpmsg_dev_probe()中 | 被更新,设置为ept->addr | ept->addr | 后端分配的实际地址 |
| 运行时 | 保持不变 | 保持不变 | 用于消息路由 |
关键赋值链:
12345678 | // rpmsg_dev_probe()ept = rpmsg_create_ept(rpdev, rpdrv->callback, NULL, chinfo); │ └── 后端分配 addr(如 0x401) │ ▼rpdev->ept = ept;rpdev->src = ept->addr; // 同步到设备结构体! |
struct rpmsg_driver
123456789101112131415 | /** * struct rpmsg_driver - rpmsg driver struct * @drv: underlying device driver * @id_table: rpmsg ids serviced by this driver * @probe: invoked when a matching rpmsg channel (i.e. device) is found * @remove: invoked when the rpmsg channel is removed * @callback: invoked when an inbound message is received on the channel */struct rpmsg_driver { struct device_driver drv; const struct rpmsg_device_id *id_table; int (*probe)(struct rpmsg_device *dev); void (*remove)(struct rpmsg_device *dev); int (*callback)(struct rpmsg_device *, void *, int, void *, u32);}; |
struct rpmsg_driver是 RPMSG 框架中驱动开发者需要填充的核心结构体,它遵循 Linux 标准设备驱动模型,同时封装了 RPMSG 特有的消息收发语义。
struct device_driver drv
Linux 设备模型基类,用于挂接到rpmsg_bus
const struct rpmsg_device_id *id_table
驱动支持的设备 ID 表(按服务名匹配)
int (*probe)(struct rpmsg_device *)
匹配成功时调用,执行驱动初始化,参考rpmsg_dev_probe函数
void (*remove)(struct rpmsg_device *dev);
设备移除/驱动卸载时调用,执行清理
int (*callback)(struct rpmsg_device *, void *, int, void *, u32);
消息接收回调(会触发框架自动创建默认端点),callback与probe的分工
callback 触发自动端点创建
| callback | 框架行为 | 适用场景 |
|---|---|---|
| 非 NULL | rpmsg_dev_probe()自动创建默认端点,绑定 callback 到rpdev->src | 简单服务,单地址监听 |
| NULL | 框架不创建默认端点,驱动需在probe()中手动调用rpmsg_create_ept() | 复杂服务,多地址、动态端点管理 |
与rpmsg_device/rpmsg_endpoint的三角关系
| 对象 | 由谁创建 | 由谁管理 | 生命周期 |
|---|---|---|---|
rpmsg_device | 后端(virtio)收到 NS 消息 | 内核设备模型 | 通道存在期间 |
rpmsg_endpoint | 框架自动创建(rpdev->ept)或驱动手动创建 | kref引用计数 | 与设备或驱动需求绑定 |
rpmsg_driver | 驱动作者静态定义 | module_init/module_exit | 模块加载期间 |
调用链
12345678910 | rpmsg_bus.match() 比较 rpdev->id.name vs rpdrv->id_table[].name ↓ 匹配成功rpmsg_bus.probe() ├── dev_pm_domain_attach() ├── rpmsg_create_ept() ← virtio_rpmsg_bus.c 中赋值 ept->cb = rpdrv->callback ├── rpdrv->probe() ← 驱动初始化 └── announce_create() ↓ 远端发消息rpdev->ops->announce_create() ← virtio_rpmsg_bus.c 中调用ept->cb |
struct rpmsg_device_ops
123456789101112131415161718 | /** * struct rpmsg_device_ops - indirection table for the rpmsg_device operations * @create_ept: create backend-specific endpoint, required * @announce_create: announce presence of new channel, optional * @announce_destroy: announce destruction of channel, optional * * Indirection table for the operations that a rpmsg backend should implement. * @announce_create and @announce_destroy are optional as the backend might * advertise new channels implicitly by creating the endpoints. */struct rpmsg_device_ops { struct rpmsg_endpoint *(*create_ept)(struct rpmsg_device *rpdev, rpmsg_rx_cb_t cb, void *priv, struct rpmsg_channel_info chinfo); int (*announce_create)(struct rpmsg_device *ept); int (*announce_destroy)(struct rpmsg_device *ept);}; |
调用时机
| 成员 | 是否必须 | 调用函数 | 调用时机 | 前置条件 |
|---|---|---|---|---|
| create_ept | 必须 | rpmsg_create_ept() | ① 框架自动创建默认端点; ② 驱动手动创建端点 | rpdev->ops非空 |
| announce_create | 可选 | rpmsg_dev_probe() | 驱动probe()成功之后 | ept 创建成功且ops->announce_create非空 |
| announce_destroy | 可选 | rpmsg_dev_remove() | 设备移除/驱动卸载 最开始 | ops->announce_destroy非空 |
rpdev->ops->create_ept()
- 框架自动创建
rpmsg_core.c:rpmsg_dev_probe()
12345 | // rpmsg_core.c: rpmsg_dev_probe()if (rpdrv->callback) { ept = rpmsg_create_ept(rpdev, rpdrv->callback, NULL, chinfo); // 内部调用: rpdev->ops->create_ept(rpdev, cb, priv, chinfo)} |
- 驱动手动创建
123456789101112 | // 驱动代码示例static int my_probe(struct rpmsg_device *rpdev){ struct rpmsg_endpoint *ept2; struct rpmsg_channel_info chinfo = { .name = "ctrl", .src = RPMSG_ADDR_ANY, .dst = RPMSG_ADDR_ANY, }; ept2 = rpmsg_create_ept(rpdev, ctrl_cb, my_priv_data, chinfo); // 内部调用: rpdev->ops->create_ept(rpdev, ctrl_cb, my_priv_data, chinfo)} |
rpdev->ops->announce_create()
12345678910 | // rpmsg_core.c: rpmsg_dev_probe()err = rpdrv->probe(rpdev); // ← ① 先让驱动完成初始化if (err) goto destroy_ept;if (ept && rpdev->ops->announce_create) { // ← ② 驱动就绪后再宣告 err = rpdev->ops->announce_create(rpdev); if (err) goto remove_rpdev;} |
后端:
- 通过 RPMSG 名字服务(Name Service)协议,向远端处理器发送一条 “通道创建” 消息
- 消息内容通常包含:服务名
rpdev->id.name、本地地址rpdev->src
rpdev->ops->announce_destroy()
123456789101112131415161718192021 | // rpmsg_core.c: rpmsg_dev_remove()static int rpmsg_dev_remove(struct device *dev){ struct rpmsg_device *rpdev = to_rpmsg_device(dev); struct rpmsg_driver *rpdrv = to_rpmsg_driver(rpdev->dev.driver); int err = 0; if (rpdev->ops->announce_destroy) // ← ① 最先执行:通知远端 err = rpdev->ops->announce_destroy(rpdev); if (rpdrv->remove) // ← ② 再调用驱动清理 rpdrv->remove(rpdev); dev_pm_domain_detach(dev, true); // ← ③ 电源分离 if (rpdev->ept) rpmsg_destroy_ept(rpdev->ept); // ← ④ 最后销毁端点 return err;} |
后端:
- 通过名字服务协议,向远端发送 “通道销毁” 消息
- 远端收到后,会从自己的服务表中删除该通道,后续发往该地址的消息会被丢弃或返回错误
总结
| 操作 | 调用者 | 被调用者 | 核心语义 |
|---|---|---|---|
| create_ept | rpmsg_create_ept() | 后端 | 分配资源:为本地地址绑定后端缓冲区和中断 |
| announce_create | rpmsg_dev_probe() | 后端 | 发布服务:通知远端"这个地址有服务在监听" |
| announce_destroy | rpmsg_dev_remove() | 后端 | 撤销服务:通知远端"这个地址的服务即将停止" |
这三个钩子共同实现了 RPMSG “创建-发布-撤销” 的完整生命周期管理,是核心层与后端之间最关键的契约接口
struct rpmsg_endpoint_ops
12345678910111213141516171819202122232425262728293031 | /** * struct rpmsg_endpoint_ops - indirection table for rpmsg_endpoint operations * @destroy_ept: see @rpmsg_destroy_ept(), required * @send: see @rpmsg_send(), required * @sendto: see @rpmsg_sendto(), optional * @send_offchannel: see @rpmsg_send_offchannel(), optional * @trysend: see @rpmsg_trysend(), required * @trysendto: see @rpmsg_trysendto(), optional * @trysend_offchannel: see @rpmsg_trysend_offchannel(), optional * @poll: see @rpmsg_poll(), optional * * Indirection table for the operations that a rpmsg backend should implement. * In addition to @destroy_ept, the backend must at least implement @send and * @trysend, while the variants sending data off-channel are optional. */struct rpmsg_endpoint_ops { void (*destroy_ept)(struct rpmsg_endpoint *ept); int (*send)(struct rpmsg_endpoint *ept, void *data, int len); int (*sendto)(struct rpmsg_endpoint *ept, void *data, int len, u32 dst); int (*send_offchannel)(struct rpmsg_endpoint *ept, u32 src, u32 dst, void *data, int len); int (*trysend)(struct rpmsg_endpoint *ept, void *data, int len); int (*trysendto)(struct rpmsg_endpoint *ept, void *data, int len, u32 dst); int (*trysend_offchannel)(struct rpmsg_endpoint *ept, u32 src, u32 dst, void *data, int len); __poll_t (*poll)(struct rpmsg_endpoint *ept, struct file *filp, poll_table *wait);}; |
rpmsg core 与 backend 解耦
rpmsg 子系统的架构可以看作两层:
1234567891011121314 | +---------------------------------------------------+| rpmsg client driver (user code) || - imx_rproc, ti_pruss, etc. || - rpmsg_send(), rpmsg_create_ept(), ... |+---------------------------------------------------+| rpmsg core (drivers/rpmsg/rpmsg_core.c) || - 提供 EXPORT_SYMBOL 的 API || - 通过 ops 表转发到 backend |+---------------------------------------------------+| rpmsg transport backend || - virtio_rpmsg_bus.c (virtio 传输) || - 未来可能有其他 backend || - 实现 rpmsg_endpoint_ops / rpmsg_device_ops |+--------------------------------------------------- |
| 层级 | ops | 操作对象 | 典型操作 |
|---|---|---|---|
| Rpmsg Device | rpmsg_device_ops | rpmsg_device(channel) | 创建 endpoint、宣告 channel 存在 |
| Rpmsg Endpoint | rpmsg_endpoint_ops | rpmsg_endpoint(通信端点) | 发送数据、销毁端点、poll |
这种分层让 core 可以在 device 注册时做 name service 宣告(通过 rpmsg_device_ops.announce_create),而具体的数据收发走 endpoint(通过 rpmsg_endpoint_ops.send)。
分层关系:
12345678910111213141516 | rpmsg_device (代表一个 channel) | |-- rpmsg_device_ops | | | |-- create_ept() --> rpmsg_endpoint | |-- announce_create/destroy() | vrpmsg_endpoint (代表 channel 上的一个通信端点) | |-- rpmsg_endpoint_ops | | | |-- send/sendto/send_offchannel | |-- trysend/trysendto/trysend_offchannel | |-- destroy_ept | |-- poll |
rpmsg core 的转发逻辑
drivers/rpmsg/rpmsg_core.c中每个 API 都是薄薄的一层封装, 几个典型函数为例:
12345678910 | int rpmsg_send(struct rpmsg_endpoint *ept, void *data, int len){ if (WARN_ON(!ept)) return -EINVAL; if (!ept->ops->send) return -ENXIO; return ept->ops->send(ept, data, len);} |
因为 send 是 required,所以 core 没有 fallback。如果XIO。
12345678 | int rpmsg_sendto(struct rpmsg_endpoint *ept, void *data, int len, u32 dst){ if (WARN_ON(!ept)) return -EINVAL; if (!ept->ops->sendto) return -ENXIO; return ept->ops->sendto(ept, data, len, dst);} |
见rpmsg_core.c虽然注释说 sendto 是 optional,但 optional 的语义是"backend 可以选择不支持该操作",而不是"core 会帮你兼容"。如果某个 backend 只实现了 send 和 trysend,调用 sendto 就会失败。
123456789 | __poll_t rpmsg_poll(struct rpmsg_endpoint *ept, struct file *filp, poll_table *wait) { if (WARN_ON(!ept)) return 0; if (!ept->ops->poll) return 0; return ept->ops->poll(ept, filp, wait);} |
virtio_rpmsg_bus.c没实现 poll,所以通过rpmsg_poll()调用时返回 0。而drivers/rpmsg/rpmsg_char.c
1 | mask |= rpmsg_poll(eptdev->ept, filp, wait); |
rpmsg_char.c提供用户空间的/dev/rpmsgX接口。rpmsg_poll()的结果被 OR 到poll mask中。如果 backend 没实现 poll,用户空间对/dev/rpmsgX做poll()时只能检测读事件(EPOLLIN),无法通过rpmsg_poll()检测写就绪状态。不过由于 virtio 的rpmsg_send系列要么阻塞要么立即返回,实际上写路径不依赖 poll。
总结
| ops | required | core 行为(未实现时) | virtio backend |
|---|---|---|---|
| destroy_ept | ✅ required | N/A(不会为 NULL,初始化时必设) | virtio_rpmsg_destroy_ept |
| send | ✅ required | -ENXIO | virtio_rpmsg_send |
| trysend | ✅ required | -ENXIO | virtio_rpmsg_trysend |
| sendto | optional | -ENXIO | virtio_rpmsg_sendto |
| send_offchannel | optional | -ENXIO | virtio_rpmsg_send_offchannel |
| trysendto | optional | -ENXIO | virtio_rpmsg_trysendto |
| trysend_offchannel | optional | -ENXIO | virtio_rpmsg_trysend_offchannel |
| poll | optional | 返回 0 | 未定义 |
EXPORT_SYMBOLS
rpmsg_create_ept()
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950 | /** * rpmsg_create_ept() - create a new rpmsg_endpoint * @rpdev: rpmsg channel device * @cb: rx callback handler * @priv: private data for the driver's use * @chinfo: channel_info with the local rpmsg address to bind with @cb * * Every rpmsg address in the system is bound to an rx callback (so when * inbound messages arrive, they are dispatched by the rpmsg bus using the * appropriate callback handler) by means of an rpmsg_endpoint struct. * * This function allows drivers to create such an endpoint, and by that, * bind a callback, and possibly some private data too, to an rpmsg address * (either one that is known in advance, or one that will be dynamically * assigned for them). * * Simple rpmsg drivers need not call rpmsg_create_ept, because an endpoint * is already created for them when they are probed by the rpmsg bus * (using the rx callback provided when they registered to the rpmsg bus). * * So things should just work for simple drivers: they already have an * endpoint, their rx callback is bound to their rpmsg address, and when * relevant inbound messages arrive (i.e. messages which their dst address * equals to the src address of their rpmsg channel), the driver's handler * is invoked to process it. * * That said, more complicated drivers might need to allocate * additional rpmsg addresses, and bind them to different rx callbacks. * To accomplish that, those drivers need to call this function. * * Drivers should provide their @rpdev channel (so the new endpoint would belong * to the same remote processor their channel belongs to), an rx callback * function, an optional private data (which is provided back when the * rx callback is invoked), and an address they want to bind with the * callback. If @addr is RPMSG_ADDR_ANY, then rpmsg_create_ept will * dynamically assign them an available rpmsg address (drivers should have * a very good reason why not to always use RPMSG_ADDR_ANY here). * * Returns a pointer to the endpoint on success, or NULL on error. */struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_device *rpdev, rpmsg_rx_cb_t cb, void *priv, struct rpmsg_channel_info chinfo){ if (WARN_ON(!rpdev)) return NULL; return rpdev->ops->create_ept(rpdev, cb, priv, chinfo);}EXPORT_SYMBOL(rpmsg_create_ept); |
调用rpdev->ops中的create_ept
rpmsg_destroy_ept()
1234567891011121314 | /** * rpmsg_destroy_ept() - destroy an existing rpmsg endpoint * @ept: endpoing to destroy * * Should be used by drivers to destroy an rpmsg endpoint previously * created with rpmsg_create_ept(). As with other types of "free" NULL * is a valid parameter. */void rpmsg_destroy_ept(struct rpmsg_endpoint *ept){ if (ept && ept->ops) ept->ops->destroy_ept(ept);}EXPORT_SYMBOL(rpmsg_destroy_ept); |
rpmsg_send()
12345678910111213141516171819202122232425262728 | /** * rpmsg_send() - send a message across to the remote processor * @ept: the rpmsg endpoint * @data: payload of message * @len: length of payload * * This function sends @data of length @len on the @ept endpoint. * The message will be sent to the remote processor which the @ept * endpoint belongs to, using @ept's address and its associated rpmsg * device destination addresses. * In case there are no TX buffers available, the function will block until * one becomes available, or a timeout of 15 seconds elapses. When the latter * happens, -ERESTARTSYS is returned. * * Can only be called from process context (for now). * * Returns 0 on success and an appropriate error value on failure. */int rpmsg_send(struct rpmsg_endpoint *ept, void *data, int len){ if (WARN_ON(!ept)) return -EINVAL; if (!ept->ops->send) return -ENXIO; return ept->ops->send(ept, data, len);}EXPORT_SYMBOL(rpmsg_send); |
rpmsg_sendto()
12345678910111213141516171819202122232425262728 | /** * rpmsg_sendto() - send a message across to the remote processor, specify dst * @ept: the rpmsg endpoint * @data: payload of message * @len: length of payload * @dst: destination address * * This function sends @data of length @len to the remote @dst address. * The message will be sent to the remote processor which the @ept * endpoint belongs to, using @ept's address as source. * In case there are no TX buffers available, the function will block until * one becomes available, or a timeout of 15 seconds elapses. When the latter * happens, -ERESTARTSYS is returned. * * Can only be called from process context (for now). * * Returns 0 on success and an appropriate error value on failure. */int rpmsg_sendto(struct rpmsg_endpoint *ept, void *data, int len, u32 dst){ if (WARN_ON(!ept)) return -EINVAL; if (!ept->ops->sendto) return -ENXIO; return ept->ops->sendto(ept, data, len, dst);}EXPORT_SYMBOL(rpmsg_sendto); |
rpmsg_send_offchannel
12345678910111213141516171819202122232425262728293031 | /** * rpmsg_send_offchannel() - send a message using explicit src/dst addresses * @ept: the rpmsg endpoint * @src: source address * @dst: destination address * @data: payload of message * @len: length of payload * * This function sends @data of length @len to the remote @dst address, * and uses @src as the source address. * The message will be sent to the remote processor which the @ept * endpoint belongs to. * In case there are no TX buffers available, the function will block until * one becomes available, or a timeout of 15 seconds elapses. When the latter * happens, -ERESTARTSYS is returned. * * Can only be called from process context (for now). * * Returns 0 on success and an appropriate error value on failure. */int rpmsg_send_offchannel(struct rpmsg_endpoint *ept, u32 src, u32 dst, void *data, int len){ if (WARN_ON(!ept)) return -EINVAL; if (!ept->ops->send_offchannel) return -ENXIO; return ept->ops->send_offchannel(ept, src, dst, data, len);}EXPORT_SYMBOL(rpmsg_send_offchannel); |
rpmsg_trysend()
123456789101112131415161718192021222324252627 | /** * rpmsg_trysend() - send a message across to the remote processor * @ept: the rpmsg endpoint * @data: payload of message * @len: length of payload * * This function sends @data of length @len on the @ept endpoint. * The message will be sent to the remote processor which the @ept * endpoint belongs to, using @ept's address as source and its associated * rpdev's address as destination. * In case there are no TX buffers available, the function will immediately * return -ENOMEM without waiting until one becomes available. * * Can only be called from process context (for now). * * Returns 0 on success and an appropriate error value on failure. */int rpmsg_trysend(struct rpmsg_endpoint *ept, void *data, int len){ if (WARN_ON(!ept)) return -EINVAL; if (!ept->ops->trysend) return -ENXIO; return ept->ops->trysend(ept, data, len);}EXPORT_SYMBOL(rpmsg_trysend); |
rpmsg_trysendto()
123456789101112131415161718192021222324252627 | /** * rpmsg_trysendto() - send a message across to the remote processor, specify dst * @ept: the rpmsg endpoint * @data: payload of message * @len: length of payload * @dst: destination address * * This function sends @data of length @len to the remote @dst address. * The message will be sent to the remote processor which the @ept * endpoint belongs to, using @ept's address as source. * In case there are no TX buffers available, the function will immediately * return -ENOMEM without waiting until one becomes available. * * Can only be called from process context (for now). * * Returns 0 on success and an appropriate error value on failure. */int rpmsg_trysendto(struct rpmsg_endpoint *ept, void *data, int len, u32 dst){ if (WARN_ON(!ept)) return -EINVAL; if (!ept->ops->trysendto) return -ENXIO; return ept->ops->trysendto(ept, data, len, dst);}EXPORT_SYMBOL(rpmsg_trysendto); |
rpmsg_poll()
12345678910111213141516171819 | /** * rpmsg_poll() - poll the endpoint's send buffers * @ept: the rpmsg endpoint * @filp: file for poll_wait() * @wait: poll_table for poll_wait() * * Returns mask representing the current state of the endpoint's send buffers */__poll_t rpmsg_poll(struct rpmsg_endpoint *ept, struct file *filp, poll_table *wait){ if (WARN_ON(!ept)) return 0; if (!ept->ops->poll) return 0; return ept->ops->poll(ept, filp, wait);}EXPORT_SYMBOL(rpmsg_poll); |
rpmsg_trysend_offchannel()
123456789101112131415161718192021222324252627282930 | /** * rpmsg_trysend_offchannel() - send a message using explicit src/dst addresses * @ept: the rpmsg endpoint * @src: source address * @dst: destination address * @data: payload of message * @len: length of payload * * This function sends @data of length @len to the remote @dst address, * and uses @src as the source address. * The message will be sent to the remote processor which the @ept * endpoint belongs to. * In case there are no TX buffers available, the function will immediately * return -ENOMEM without waiting until one becomes available. * * Can only be called from process context (for now). * * Returns 0 on success and an appropriate error value on failure. */int rpmsg_trysend_offchannel(struct rpmsg_endpoint *ept, u32 src, u32 dst, void *data, int len){ if (WARN_ON(!ept)) return -EINVAL; if (!ept->ops->trysend_offchannel) return -ENXIO; return ept->ops->trysend_offchannel(ept, src, dst, data, len);}EXPORT_SYMBOL(rpmsg_trysend_offchannel); |
rpmsg_find_device()
123456789101112131415161718192021222324252627282930 | /* * match a rpmsg channel with a channel info struct. * this is used to make sure we're not creating rpmsg devices for channels * that already exist. */static int rpmsg_device_match(struct device *dev, void *data){ struct rpmsg_channel_info *chinfo = data; struct rpmsg_device *rpdev = to_rpmsg_device(dev); if (chinfo->src != RPMSG_ADDR_ANY && chinfo->src != rpdev->src) return 0; if (chinfo->dst != RPMSG_ADDR_ANY && chinfo->dst != rpdev->dst) return 0; if (strncmp(chinfo->name, rpdev->id.name, RPMSG_NAME_SIZE)) return 0; /* found a match ! */ return 1;}struct device *rpmsg_find_device(struct device *parent, struct rpmsg_channel_info *chinfo){ return device_find_child(parent, chinfo, rpmsg_device_match);}EXPORT_SYMBOL(rpmsg_find_device); |
完整流程图
点击代码块展开
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556 | local processors remote processor │ [dtb] virtio_device match virtio_driver │[dtb] virtio_device match virtio_driver │ rpmsg_probe (virtio_rpmsg_bus.c) │rpmsg_probe (virtio_rpmsg_bus.c) │ vrp->ns_ept = __rpmsg_create_ept(vrp, NULL, │vrp->ns_ept = __rpmsg_create_ept(vrp, NULL, │ rpmsg_ns_cb, vrp, RPMSG_NS_ADDR); │ rpmsg_ns_cb, vrp, RPMSG_NS_ADDR); │ │ │ │ │ │ │ [dtb] rpmsg_device register rpmsg_bus │[dtb] rpmsg_device register rpmsg_bus │ rpmsg_dev_probe(rpdev->dev) (rpmsg_core.c) │rpmsg_dev_probe(rpdev->dev) (rpmsg_core.c) │ rpdrv->callback = NULL, don't create ept │ rpdrv->callback = NULL, don't create ept │ rpdrv->probe = rpmsg_chrdev_probe (rpmsg_char.c) │ rpdrv->probe = rpmsg_chrdev_probe (rpmsg_char.c) │ create cdev "rpmsg_ctrl0" │ create cdev "rpmsg_ctrl0" │ n rpdev->ops->announce_create(rpdev); │ rpdev->ops->announce_create(rpdev); │ = virtio_rpmsg_announce_create(rpdev) │ = virtio_rpmsg_announce_create(rpdev) │ rpdev->ept = NULL, don't announce │ rpdev->ept = NULL, don't announce │ │ │ │ │ open("/dev/rpmsg_ctrl0") │open("/dev/rpmsg_ctrl0") │ ioctl(fd, RPMSG_CREATE_EPT_IOCTL, &eptinfo) │ioctl(fd, RPMSG_CREATE_EPT_IOCTL, &eptinfo) │ eptinfo.name="tty", src=0x300, dst=0x400 │ eptinfo.name="tty", src=0x400, dst=0x300 │ if src = RPMSG_ADDR_ANY, ept->addr = idr_alloc()│ if src = RPMSG_ADDR_ANY, ept->addr = idr_alloc() │ rpmsg_eptdev_create(ctrldev, chinfo); │ rpmsg_eptdev_create(ctrldev, chinfo); │ create cdev "rpmsg%d" │ create cdev "rpmsg%d" │ open("/dev/rpmsg0") │open("/dev/rpmsg0") │ rpmsg_create_ept(rpdev, rpmsg_ept_cb, │ rpmsg_create_ept(rpdev, rpmsg_ept_cb, │ eptdev, eptdev->chinfo); │ eptdev, eptdev->chinfo); │ __rpmsg_create_ept │ __rpmsg_create_ept │ ept->addr = 0x300 │ ept->addr = 0x400 │ ept->cb = rpmsg_ept_cb │ ept->cb = rpmsg_ept_cb │ │ │ write_iter("/dev/rpmsg0") │read_iter("/dev/rpmsg0") │ rpmsg_eptdev_write_iter(iocb, from) │ rpmsg_eptdev_read_iter(iocb, to) │ rpmsg_send/trysend(eptdev->ept, kbuf, len); │ wait_event_interruptible(eptdev->readq, │ ept->ops->send │ !skb_queue_empty(&eptdev->queue) || │ src=ept->addr, dst=rpdev->dst │ !eptdev->ept) │ rpmsg_send_offchannel_raw() │ │ get_a_tx_buf() │ │ fill rpmsg_hdr │ │ virtqueue_add_outbuf() │ │ virtqueue_kick() │ │───────────────────────────────────────────────────►│ │ │rpmsg_recv_done(rvq) │ │ rpmsg_recv_single(vrp, dev, msg, len) │ │ ept->cb() = rpmsg_ept_cb() │ │ skb_put_data(skb, buf, len); │ │ skb_queue_tail(&eptdev->queue, skb); │ │ wake_up_interruptible(&eptdev->readq); │ │ │ │ │ │ wait_event_interruptible(eptdev->readq, │ │ !skb_queue_empty(&eptdev->queue) || │ │ !eptdev->ept) │ │ skb = skb_dequeue(&eptdev->queue) │ │ copy_to_iter(skb->data, use, to) │ │ kfree_skb(skb) |
