目录
1 文档目的
2 相关概念
2.1 术语
2.2 RPMsg相关概念
3 RPMsg核间通信软硬件模块框架
3.1 硬件原理
3.2 软件框架
4 使用RPMsg进行核间通信
4.1 RPMsg通信建立
4.1.1 使用名称服务建立通信
4.1.2 不用名称服务
4.2 RPMsg应用过程
4.3 应用层示例
5 RPMsg内核驱动源码解析
5.1 RPMsg框架源码
5.1.1 源代码与功能
5.1.2 源码分析
5.2 virtio介绍
5.2.1 virtio源码及virtio_queue原理介绍
5.2.2 源码分析
5.3 remoteproc介绍
5.3.1 remoteproc子系统
5.3.2 remoteproc源码解析
5.4 mailbox 框架
5.4.1 mailbox源码介绍
5.4.2 mailbox框架源码解析
6 收发数据流程总结
6.1 接收数据过程调用栈
6.2 发送数据过程调用栈
6.3 收发数据buffer处理过程
6.3.1 J6缓冲区处理流程
6.3.2 stm32-mp157缓冲区处理流程
6.3.3 缓冲区处理
7 附录
1 文档目的
该文档目的介绍RPMsg基础概念知识、RPMsg整体使用流程,rpmsg、virtio、remotepro、mailbox软件框架、硬件原理相关介绍和RPMsg适配新平台的开发流程,以帮助开发者更好理解RPMsg核间通信框架,进行核间通信开发。
2 相关概念
2.1 术语
RPMsg: remote processor messages,Linux下用于核间通信顶层框架,面向驱动开发者。
Virtio: virtual IO,linux平台下一种IO半虚拟化框架。
Remoteproc: Remote Processor Framework,用于管理异构远程处理器设备,既是一个硬件模块,Linux也实现了对应的remoteproc框架,remoteproc框架允许不同平台或架构控制远程处理器。
Mailbox:Linux一种软件框架,通过消息队列和中断驱动信号处理多处理器间的通讯,也有人把用于核间通信模块称为mailbox,软件上是个框架,硬件也是个IP。
MHU:mailbox and handshake uint,是个硬件模块,包含mailbox功能和握手功能,用来确认双方收发情况,减少丢包等。
IPCC:功能上跟mailbox一样,用于核间通信的硬件IP模块。
2.2 RPMsg相关概念
RPMsg-Lite 组件: Remote Processor Messaging(RPMsg)协议的一个轻量级实现。
通道:用于数据传输,通信前需要建立通道,跟实际硬件通道(mailbox/MHU/IPCC)不是同一个概念(后面详细解析),是RPMsg的一样虚拟概念。
端点:用于核间通信的具体控制点,利用通道进行数据通信。
3 RPMsg核间通信软硬件模块框架
3.1 硬件原理
RPMsg硬件上依赖mailbox或者MHU(mailbox and handshake uint)和共享内存。基于原理是利用mailbox/MHU模块,触发接收端对应通道中断,接收端收到中断,从共享内存中取出发送放放在共享内存中的数据,如下图:
3.2 软件框架
RPMsg是在基于虚拟化框架virtio上层实现软件框架,RPMsg总线是一种基于virtio的消息总线。Linux下的RPMsg核间通信,涉及多个Linux内核多个软件框架,包括virtio、remoteproc和mailbox,下图大致描述了各个软件框架在核间通信的关系:
APP通过调用RPMsg提供的设备来进行核间通信,RPMsg driver向RPMsg bus总线注册driver,同时由virtio和RPMsg的中间适配层RPMsg virtio layer向注册对应device,匹配对应的driver,同时该层又向下注册了virtio driver,将virtio和RPMsg关联起来。Virtio的下层是remoteproc框架,该框架向上注册了virtio device,以和virtio driver匹配向上提供device硬件相关接口(这里硬件接口,包括remoteproc这个IP的接口和下层mailbox接口)。Mailbox框架向外提供client,remoteproce框架同时注册上层了一个mailbox client以和mailbox交互,mailbox向下同时提供了controller,让具体的硬件模块提供硬件操作接口。
此外,有些软件框架可以脱离其他层单独使用,例如mailbox,驱动开发者可自我注册client生成设备提供APP层直接进行核间通信,remoteproc通过sysfs暴露接口给APP层,用户可以直接控制远端处理器的状态(启动、停止、固件加载等)。而virtio则是一个与硬件无关的框架,可用于其他外设作为数据管理使用。
最新Linux内核,GLINK和SMD也向RPMsg bus注册了device,这两个协议是用于高通平台,本文不展述。
4 使用RPMsg进行核间通信
本节从实际使用角度,描述user空间,APP如何使用RPMsg进行核间通信。
4.1 RPMsg通信建立
4.1.1 使用名称服务建立通信
RPMsg使用前,有时需要建立一定条件,即在使用远端服务时,需要依赖远端发送服务到本地,本地建立服务(即是设备名称)后,user空间app才可打开设备进行通信,如下图是通信建立的大致流程:
SOC在使用前,需要先收到远端服务请求,在本地建立RPMsg通道、端点和设备等信息,再反馈初始化信息给远端,双方才可进行通信。建立后,APP可主动发送数据,被动收数据或者主动沦陷,最后主动销毁通道。
在Linux内核代码,服务建立是kernel/drivers/rpmsg/rpmsg_ns.c文件完成,RPMsg device 层向上注册了ns的device,后续章节将深入代码解析该流程。
4.1.2 不用名称服务
最新的内核代码,也能在不使用名称服务情况下,直接建立端点进行通信,甚至脱离具体RPMsg通道建立端点来通信,这种情况下,需要双方手动登记通信的端点地址,如下图:
SOC侧在驱动加载时,直接建立通道和端点信息,之后进行正常通信,此时需要手动和远端登记好地址。
最新内核代码中,drivers/rpmsg/rpmsg_ctrl.c和drivers/rpmsg/rpmsg_char.c都是在不适用名称服务情况下直接进行通信,同时许多其他利用RPMsg框架进行核间通信的模块,都直接使用该方式,例如camera的核间通信等。
4.2 RPMsg应用过程
利用RPMsg核间通信过程:建立通道和端点同时绑定本地地址和远端地址,发送时带上本地地址,远端发送数据到本地时,也需要携带远端地址才能被本地端点使用,上送数据给应用层。
比较典型的应用,是一个通道两端各一个端点进行通信:
还可以一个通道多个端点
或者多通道多端点
4.3 应用层示例
在底层准备好后,应用层就能打开底层提供的设备进行核间通信。以/dev/rpmsg_ctrl0设备为例子,就是4.1.2提到的drivers/rpmsg/rpmsg_ctrl.c生成的设备,该RPMsg驱动支持生成多端点进行通信或者多通道,下面应用程序是使用流程:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <stdint.h>
#include "sstar_rpmsg.h"
int main(void)
{
struct ss_rpmsg_endpoint_info info;
char buffer[512];
char data[512];
int ret;
char devPath[256];
int fd, eptFd;
unsigned int index = 0x0;
memset(&info, 0, sizeof(info));
info.src = EPT_ADDR_MACRO(EPT_TYPE_CUSTOMER, 1);
info.dst = EPT_ADDR_MACRO(EPT_TYPE_CUSTOMER, 2);
snprintf(info.name, sizeof(info.name), "demo");
info.mode = RPMSG_MODE_RISCV;
info.target_id = 0;
fd = open("/dev/rpmsg_ctrl0", O_RDWR);
if (fd < 0)
{
perror("open");
return 0;
}
if (ioctl(fd, SS_RPMSG_CREATE_EPT_IOCTL, &info) < 0)//创建通道时携带端点地址
{
perror("ioctl");
return 0;
}
sleep(2);
snprintf(devPath, sizeof(devPath), "/dev/rpmsg%d", info.id);//打开创建端点时生成的设备
eptFd = open(devPath, O_RDWR);
if (eptFd < 0)
{
fprintf(stderr, "Failed to open endpoint!\n");
return 0;
}
while (1)
{
snprintf(buffer, sizeof(buffer), "hello,world:0x%x\n", index++);
ret = write(eptFd, buffer, strlen(buffer) + 1);//写数据
memset(data, 0, sizeof(data));
ret = read(eptFd, data, sizeof(data));//读数据
if (ret > 0)
printf("read ept:%d, %s\n", ret, data);
else
printf("read ept error:%d\n", ret);
}
return 0;
}
使用由rpmsg_ctrl.c注册生产的设备来使用rpmsg进行核间通信;先用/dev/rpmsg_ctrl0 设备通过ioctrl生成端点SS_RPMSG_CREATE_EPT_IOCT ,此时驱动将为进程生成/dev/rpmsgX 设备用来核间通信使用,后续可以使用常用的文件操作方式访问该设备进行核间通信。
- "/dev/rpmg_ctrl0" to instantiate /dev/rpmsg0 sysfs device interface,
- "/dev/rpmg0" sysfs device interface for communication.
在后面章节,将介绍分析驱动如何提供接口给应用层。
5 RPMsg内核驱动源码解析
RPMsg内核驱动框架涉及到RPMsg框架、virtio框架、remoteproc子系统和mailbox子系统,下面逐一介绍这些框架。
5.1 RPMsg框架源码
5.1.1 源代码与功能
RPMsg源码位于Linux源码目录kernel/drivers/rpmsg/下,下图是一个RPMsg大致软件功能框图,主要涉及源文件为:<