时间轴

时间轴

2026-06-27

init


linux 5.10.238

rpmsg 是一个总线抽象,不绑定任何具体传输。virtio 是目前 Linux 中最常见的实现,但理论上可以有:

  • 基于 shared memory 的直接映射实现
  • 基于 mailbox 中断的实现
  • 基于 PCIe doorbell 的实现

每个 backend 只需要提供自己的 rpmsg_endpoint_opsrpmsg_device_ops,就能接入 rpmsg core。而且不是所有 backend 都需要支持所有 send 变体。例如:

  • 最简单的 backend 只需要实现 send 和 trysend。
  • 如果 backend 不支持显式指定 src/dst(offchannel),可
  • 如果 backend 不支持 poll,用户空间写操作仍然可用(阻塞/非阻塞模式由 rpmsg_send/rpmsg_trysend 语义保证)。

module_init/module_exit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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注册一个总线

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 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 定义如下:

1
2
3
4
5
6
7
8
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* 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_overridedrv->name, 即 指定该rpdev强制匹配某个对应名字的driver

  • 如果rpdev->id_talbe 存在

    • struct rpmsg_driver *rpdrv中取出const struct rpmsg_device_id *ids 然后,遍历 ids 表,通过 ids[i].name 匹配rpmsg_device ,匹配成功就返回1,

    • 否则调用of_driver_match_device匹配struct device *devstruct device_driver *drv

of_driver_match_device 的匹配即通过struct device_driver *drv 中的drv->of_match_tablestruct device *dev 中的 dev->of_node 作为参数,调用__of_match_node匹配:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
static
const 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 定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
/**
* __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

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

rpmsg_dev_probe

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
/*
* 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. 电源域关联
1
2
3
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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

1
2
3
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 *rpdevrpdrv->callbackpriv = NULLstruct rpmsg_channel_info chinfo,创建ept后,关键赋值:rpdev->src = ept->addr, 这是一个非常关键的操作!

  • 如果创建前 rpdev->src = RPMSG_ADDR_ANY,后端会动态分配一个可用地址
  • ept->addr 是后端分配后的实际本地地址
  • ept->addr 写回 rpdev->src,确保设备结构体记录的是真实地址

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


  1. 调用 rpdrvprobe 函数
1
2
3
4
5
6
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
1
2
3
4
5
6
7
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 *rpdevconst struct rpmsg_device_ops *ops;announce_create 被设置,则调用该announce_create,即在ept 被创建后调用 announce_create 这个callback

rpmsg_dev_remove

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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

1
2
3
4
5
6
7
8
9
10
11
12
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 的桥梁。函数定位与调用链:

1
2
3
4
5
6
7
8
设备注册到 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
1
2
3
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**
1
2
return add_uevent_var(env, "MODALIAS=" RPMSG_DEVICE_MODALIAS_FMT,
rpdev->id.name);

如果设备没有设备树节点,RPMSG 生成自己的 modalias:

RPMSG_DEVICE_MODALIAS_FMT 定义(在 include/linux/rpmsg.hmod_devicetable.h 中):

1
#define RPMSG_DEVICE_MODALIAS_FMT   "rpmsg:%s"

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


  1. 完整的 uevent 输出示例

当一个新的 RPMSG 设备注册时,uevent 可能长这样:

1
2
3
4
5
6
7
8
ACTION=add
BUS=rpmsg
SUBSYSTEM=rpmsg
MODALIAS=rpmsg:rpmsg-tty ← 这里由 rpmsg_uevent 生成
NAME=rpmsg-tty
SRC=0x401
DST=0x0
DEVPATH=/bus/rpmsg/devices/virtio0.rpmsg-tty.-1.0

udev 如何利用 modalias?

  • 自动加载驱动模块: udev 规则通常包含:
1
2
3
# /lib/udev/rules.d/80-drivers.rules

ENV{MODALIAS}=="?*", RUN{builtin}+="kmod load $env{MODALIAS}"

当 uevent 携带 MODALIAS=rpmsg:rpmsg-tty 时,udev 会执行:modprobe rpmsg:rpmsg-tty

但 modprobe 不认识带冒号的格式,需要模块本身通过别名来匹配。

  • 驱动模块中的别名声明: 驱动源码中:
1
2
3
4
5
6
static struct rpmsg_device_id rpmsg_tty_id_table[] = {
{ .name = "rpmsg-tty" },
{ },
}

MODULE_DEVICE_TABLE(rpmsg, rpmsg_tty_id_table); // ← 生成模块别名

编译后,模块文件中会包含别名信息:

1
2
$ modinfo rpmsg_tty
alias: rpmsg:tty*

注意:MODULE_DEVICE_TABLE 宏在编译时生成 mod_rpmsg… 符号,depmod 会将其写入 /lib/modules/$(uname -r)/modules.alias

udevmodprobe 的完整链条

内核: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

1
2
3
4
$ cat /sys/bus/rpmsg/devices/virtio0.rpmsg-tty.-1.0/uevent
BUS=rpmsg
DRIVER=rpmsg_tty
MODALIAS=rpmsg:rpmsg-tty

手动触发 uevent

1
$ echo change > /sys/bus/rpmsg/devices/virtio0.rpmsg-tty.-1.0/uevent

这会重新调用 rpmsg_uevent(),udev 会再次处理。查看模块别名

1
2
3
$ grep rpmsg /lib/modules/$(uname -r)/modules.alias
alias rpmsg:* rpmsg_core
alias rpmsg:rpmsg-tty rpmsg_tty

rpmsg_dev_groups

dev_groups 是 Linux 设备模型中 struct bus_type 的一个字段,它的作用是:为注册到这条总线上的每个设备,自动创建一组 sysfs 属性文件。

rpmsg_core.c 中:

1
2
3
4
5
6
7
8
9
10
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) 是内核宏,展开后变成:

1
2
3
4
5
6
7
8
static struct attribute_group rpmsg_dev_group = {
.attrs = rpmsg_dev_attrs,
};

static struct attribute_group *rpmsg_dev_groups[] = {
&rpmsg_dev_group,
NULL,
};

然后挂到总线上:

1
2
3
4
5
static struct bus_type rpmsg_bus = {
.name = "rpmsg",
.dev_groups = rpmsg_dev_groups, // ← 这里
...
};

关键数据结构

struct rpmsg_channel_info

1
2
3
4
5
6
7
8
9
10
11
/**
* 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:创建端点时传递通道信息
1
2
3
4
5
6
7
8
// 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:标识通道

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 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

1
2
3
4
5
6
7
8
/* 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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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
1
2
3
4
5
6
7
8
9
10
11
12
13
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:复杂驱动,手动创建多个端点
1
2
3
4
5
6
7
8
9
10
11
12
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_devicerpmsg_endpoint 的关系

这是理解 RPMSG 架构的关键:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
┌─────────────────────┐         ┌──────────────────────┐
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_endpointrpdev->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 后端分配的实际地址
运行时 保持不变 保持不变 用于消息路由

关键赋值链:

1
2
3
4
5
6
7
8
// rpmsg_dev_probe()
ept = rpmsg_create_ept(rpdev, rpdrv->callback, NULL, chinfo);

└── 后端分配 addr(如 0x401


rpdev->ept = ept;
rpdev->src = ept->addr; // 同步到设备结构体!

struct rpmsg_driver

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 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);

消息接收回调(会触发框架自动创建默认端点),callbackprobe 的分工

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 模块加载期间

调用链

1
2
3
4
5
6
7
8
9
10
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 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()
1
2
3
4
5
// 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)
}
  • 驱动手动创建
1
2
3
4
5
6
7
8
9
10
11
12
// 驱动代码示例
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()

1
2
3
4
5
6
7
8
9
10
// 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()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
* 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 子系统的架构可以看作两层:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
+---------------------------------------------------+
| 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)。

分层关系:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
rpmsg_device (代表一个 channel)
|
|-- rpmsg_device_ops
| |
| |-- create_ept() --> rpmsg_endpoint
| |-- announce_create/destroy()
|
v
rpmsg_endpoint (代表 channel 上的一个通信端点)
|
|-- rpmsg_endpoint_ops
| |
| |-- send/sendto/send_offchannel
| |-- trysend/trysendto/trysend_offchannel
| |-- destroy_ept
| |-- poll

rpmsg core 的转发逻辑

drivers/rpmsg/rpmsg_core.c 中每个 API 都是薄薄的一层封装, 几个典型函数为例:

1
2
3
4
5
6
7
8
9
10
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。

1
2
3
4
5
6
7
8
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 就会失败。

1
2
3
4
5
6
7
8
9
__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/rpmsgXpoll() 时只能检测读事件(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()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/**
* 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()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 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()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
* 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()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
* 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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
* 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()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
* 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()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
* 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()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 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()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/**
* 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()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/*
* 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);

完整流程图

点击代码块展开

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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)