Cover image for Rpmsg Core

Rpmsg Core

字数 9.3k
阅读
访客

时间轴

时间轴

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.drawio
rpmsg.drawio

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.h#define __initcall(fn) device_initcall(fn)#define pure_initcall(fn)		__define_initcall(fn, 0)#define core_initcall(fn)		__define_initcall(fn, 1)#define core_initcall_sync(fn)		__define_initcall(fn, 1s)#define postcore_initcall(fn)		__define_initcall(fn, 2)#define postcore_initcall_sync(fn)	__define_initcall(fn, 2s)#define arch_initcall(fn)		__define_initcall(fn, 3)#define arch_initcall_sync(fn)		__define_initcall(fn, 3s)#define subsys_initcall(fn)		__define_initcall(fn, 4)#define subsys_initcall_sync(fn)	__define_initcall(fn, 4s)#define fs_initcall(fn)			__define_initcall(fn, 5)#define fs_initcall_sync(fn)		__define_initcall(fn, 5s)#define rootfs_initcall(fn)		__define_initcall(fn, rootfs)#define device_initcall(fn)		__define_initcall(fn, 6)#define device_initcall_sync(fn)	__define_initcall(fn, 6s)#define late_initcall(fn)		__define_initcall(fn, 7)#define late_initcall_sync(fn)		__define_initcall(fn, 7s)#define __define_initcall(fn, id) ___define_initcall(fn, id, .initcall##id)typedef int (*initcall_t)(void);#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS#define ___define_initcall(fn, id, __sec)			\	__ADDRESSABLE(fn)					\	asm(".section	\"" #__sec ".init\", \"a\"	\n"	\	"__initcall_" #fn #id ":			\n"	\	    ".long	" #fn " - .			\n"	\	    ".previous					\n");#else#define ___define_initcall(fn, id, __sec) \	static initcall_t __initcall_##fn##id __used \		__attribute__((__section__(#__sec ".init"))) = fn;#endif

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;}

Base=INT_MAX/2Base = INT\_MAX / 2

优先级驱动约束组合匹配细节条件分数计算公式最终得分举例
1specificcompat&&type&&namecompat匹配且index=0;type匹配;name匹配Base−(0×4)+2+1Base - (0 \times 4) + 2 + 1Base+3Base + 3 (最高分)
2specificcompat&&typecompat匹配且index=0;type匹配;无name约束Base−(0×4)+2+0Base - (0 \times 4) + 2 + 0Base+2Base + 2
3specificcompat&&namecompat匹配且index=0;无type约束;name匹配Base−(0×4)+0+1Base - (0 \times 4) + 0 + 1Base+1Base + 1
4specificcompatcompat匹配且index=0;无type和name约束Base−(0×4)+0+0Base - (0 \times 4) + 0 + 0BaseBase
5generalcompat&&type&&namecompat匹配且index=1;type匹配;name匹配Base−(1×4)+2+1Base - (1 \times 4) + 2 + 1Base−1Base - 1
6generalcompat&&typecompat匹配且index=1;type匹配;无name约束Base−(1×4)+2+0Base - (1 \times 4) + 2 + 0Base−2Base - 2
7generalcompat&&namecompat匹配且index=1;无type约束;name匹配Base−(1×4)+0+1Base - (1 \times 4) + 0 + 1Base−3Base - 3
8generalcompatcompat匹配且index=1;无type和name约束Base−(1×4)+0+0Base - (1 \times 4) + 0 + 0Base−4Base - 4
—更泛化的 compat…compat匹配且index=2(更靠后的兼容字符串)Base−(2×4)+…Base - (2 \times 4) + \dots随着 index 增大继续递减
9type&&name无compat约束;type匹配;name匹配0+2+10 + 2 + 133
10type无compat约束;type匹配;无name约束0+2+00 + 2 + 022
11name无compat约束;无type约束;name匹配0+0+10 + 0 + 111 (最低有效分)
—不匹配 / 淘汰任何一项驱动指定的约束未能在节点中找到直接返回000

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;}
  1. 电源域关联
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 失败(如电源域不存在),后续初始化没有意义,直接退出

放第一步因为后续的端点创建、驱动初始化可能都依赖硬件电源已经上电。如果电源域没准备好,这些操作可能失败甚至导致硬件异常。


  1. 自动创建 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,确保设备结构体记录的是真实地址

这意味着:驱动的接收回调会被绑定到这个新分配的地址上,远端发送到这个地址的消息就会触发该回调。


  1. 调用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 函数


  1. 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 根据这些变量决定:

  • 创建设备节点
  • 自动加载驱动模块
  • 执行规则脚本

代码逐行分析

  1. 第一优先:设备树格式 modalias
123
ret = of_device_uevent_modalias(dev, env);if (ret != -ENODEV)    return ret;

如果设备关联了设备树节点(dev->of_node),of_device_uevent_modalias会:

  1. 读取设备树的 compatible 属性
  2. 生成标准的 OF modalias 格式:of:N<name>T<type>C<compatible>
  3. 添加到 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
#define RPMSG_DEVICE_MODALIAS_FMT   "rpmsg:%s"

最终生成的 uevent 环境变量示例:MODALIAS=rpmsg:rpmsg-tty


  1. 完整的 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
#define to_rpmsg_device(d) container_of(d, struct rpmsg_device, dev)

内核总线回调只拿到struct device *,通过这个宏转换为struct rpmsg_device *。

  • struct rpmsg_device_id id

include/linux/mod_devicetable.h

12345678
/* rpmsg */#define RPMSG_NAME_SIZE			32#define RPMSG_DEVICE_MODALIAS_FMT	"rpmsg:%s"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() 这个指针。正确用法:

1
driver_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->srcrpmsg_endpoint->addr说明
设备刚创建RPMSG_ADDR_ANY无(还没创建)等待后端分配
rpmsg_dev_probe()中被更新,设置为ept->addrept->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框架行为适用场景
非 NULLrpmsg_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_eptrpmsg_create_ept()后端分配资源:为本地地址绑定后端缓冲区和中断
announce_createrpmsg_dev_probe()后端发布服务:通知远端"这个地址有服务在监听"
announce_destroyrpmsg_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 Devicerpmsg_device_opsrpmsg_device(channel)创建 endpoint、宣告 channel 存在
Rpmsg Endpointrpmsg_endpoint_opsrpmsg_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。

总结

opsrequiredcore 行为(未实现时)virtio backend
destroy_ept✅ requiredN/A(不会为 NULL,初始化时必设)virtio_rpmsg_destroy_ept
send✅ required-ENXIOvirtio_rpmsg_send
trysend✅ required-ENXIOvirtio_rpmsg_trysend
sendtooptional-ENXIOvirtio_rpmsg_sendto
send_offchanneloptional-ENXIOvirtio_rpmsg_send_offchannel
trysendtooptional-ENXIOvirtio_rpmsg_trysendto
trysend_offchanneloptional-ENXIOvirtio_rpmsg_trysend_offchannel
polloptional返回 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)
评论加载中…