RISCV_RPMsg使用参考
REVISION HISTORY¶
Revision No. | Description |
Date |
---|---|---|
1.0 | Initial release | 04/17/2025 |
1. 概述¶
RPMsg的英文全称是Remote Processor Messaging,是一种开放的多核间通讯协议。Linux Kernel内置了RPMsg协议的实现。
1.1. 流程框图¶
SigmaStar RPMsg userspace driver对接了Linux内置的RPMsg协议实现,并提供了创建RPMsg Endpoint的接口,Linux userspace app可以使用这些endpoint和FreeRTOS下的app进行通信。
SigmaStar RPMsg userspace driver会生成设备节点/dev/rpmsg_ctrl0,Linux userspace app通过/dev/rpmsg_ctrl0提供的ioctl接口,可以创建用于通信的endpoint设备节点(例如:/dev/rpmsg0)。创建节点时,需要指定本地地址以及远端的通信地址,例如,在Linux userspace app上创建endpoint时指定了src address Src1和dst address Dst1,那么在FreeRTOS上就要使用Dst1作为src address来创建endpoint,并且发送数据时要使用Src1作为dst address,这样Linux userspace app创建的endpoint就可以和FreeRTOS创建的endpoint进行通信了。
1.1.1. Linux端的架构图¶
1.1.2. FreeRTOS端的架构图¶
2. 关键字说明¶
-
master
rpmsg通信的双方中主导的一方。master就绪之后,对方才能就绪。 当前是linux端作master。
-
remote
rpmsg通信中居次要地位的一方。remote需要master通知才能就绪(协议自行完成)。 当前是FreeRTOS端作remote。
-
端点(end point)
用于rpmsg信息的接收与发送的对象,用一个32bit的地址表示。 rpmsg信息的发送和接收都是基于端点进行的。 地址是本地分配的,也就是master和remote的端点可以使用相同地址。 端点数目没有限制,只要端点地址和内存空间足够。
-
通道(channel)
master上的一个端点和remote上的一个端点所组成的通信组。
3. 功能描述¶
要使用RPMsg进行通信,需要在master和remote双方创建端点,接收端需要监听本地端点,发送端需要使用本地的端点向另一端的端点发送数据。
由于对RPMsg的封装不同,RPMsg在linux上和FreeRTOS上的使用方法很大差异。
3.1 端点地址分配¶
我们将0x40000000 - 0x7FFFFFFF的地址空间保留给用户使用。用户地址的生成请使用地址生成接口。
3.2 linux上的使用方法¶
linux上开放给用户使用的是通道,将通道封装成端点设备,端点设备是一个标准字符设备,设备的开、关、读、写都使用字符设备的通用操作接口。
发送和接收数据的操作变成对端点设备的写和读。
创建端点需要使用RPMsg控制设备/dev/rpmsg_ctrl0。
创建端点设备时,不仅要指定本地端点地址,还需要指定对方端点的地址。
由于通道已经包含了对方端点的地址,所以要发送数据只要向端点设备写数据即可。
在linux上要使用rpmsg进行通信的流程如下
-
发送信息:创建端点设备,打开端点设备,向这个端点设备写数据,关闭端点设备。
-
接收信息:创建端点设备,打开端点设备,从这个端点设备读数据,关闭端点设备。
3.3 FreeRTOS上的使用方法¶
FreeRTOS上要使用RPMsg进行通信,需要先获取RPMsg实例,再使用实例创建端点的接收队列和端点本身
接收则使用接收接口去从接收队列获取数据
发送则使用发送接口通过本地端点向指定对方端点发送数据
4. RPMsg Demo¶
本章介绍两个demo,RPMsg demo和RPMsg speed demo,其中RPMsg demo的代码需要用户自行添加到工程中进行编译,RPMsg speed demo的代码已经集成在工程中,emmc类型的defconfig已经默认打开,其他类型需要用户自行编译。
4.1. RPMsg Demo简介¶
RPMsg demo由两部分组成,一个是Linux userspace app,一个是FreeRTOS下的app,两个app间使用RPMsg进行通信。
在这个demo里,Linux userspace app向FreeRTOS app不断地发送带有编号的“hello,world”信息,然后读取FreeRTOS app的应答并打印出来。FreeRTOS app则是循环等待Linux userspace app发送数据过来,然后把数据不做修改地发送回去。
4.1.1 Linux userspace app¶
4.1.1.1 代码(app_demo.c)
1 #include <stdio.h> 2 #include <string.h> 3 #include <unistd.h> 4 #include <sys/types.h> 5 #include <sys/stat.h> 6 #include <sys/ioctl.h> 7 #include <fcntl.h> 8 #include <stdint.h> 9 10 #include "sstar_rpmsg.h" 11 12 int main(void) 13 { 14 struct ss_rpmsg_endpoint_info info; 15 char buffer[512]; 16 char data[512]; 17 int ret; 18 char devPath[256]; 19 int fd, eptFd; 20 unsigned int index = 0x0; 21 22 memset(&info, 0, sizeof(info)); 23 info.src = EPT_ADDR_MACRO(EPT_TYPE_CUSTOMER, 1); 24 info.dst = EPT_ADDR_MACRO(EPT_TYPE_CUSTOMER, 2); 25 snprintf(info.name, sizeof(info.name), "demo"); 26 info.mode = RPMSG_MODE_RISCV; 27 info.target_id = 0; 28 29 fd = open("/dev/rpmsg_ctrl0", O_RDWR); 30 if (fd < 0) 31 { 32 perror("open"); 33 return 0; 34 } 35 36 if (ioctl(fd, SS_RPMSG_CREATE_EPT_IOCTL, &info) < 0) 37 { 38 perror("ioctl"); 39 return 0; 40 } 41 42 sleep(2); 43 44 snprintf(devPath, sizeof(devPath), "/dev/rpmsg%d", info.id); 45 eptFd = open(devPath, O_RDWR); 46 47 if (eptFd < 0) 48 { 49 fprintf(stderr, "Failed to open endpoint!\n"); 50 return 0; 51 } 52 53 while (1) 54 { 55 snprintf(buffer, sizeof(buffer), "hello,world:0x%x\n", index++); 56 ret = write(eptFd, buffer, strlen(buffer) + 1); 57 58 memset(data, 0, sizeof(data)); 59 ret = read(eptFd, data, sizeof(data)); 60 if (ret > 0) 61 printf("read ept:%d, %s\n", ret, data); 62 else 63 printf("read ept error:%d\n", ret); 64 } 65 return 0; 66 }
4.1.1.2 注释
1) 第42行调用了sleep()函数,这是为了避免mdev还没有创建好RPMsg Endpoint节点而主动delay了一小段时间。具体请参见ss_rpmsg_create_ept_ioctl。
4.1.1.3 编译
配置好编译工具链,直接编译即可:
aarch64-linux-gnu-gcc -static -I ${KERNELPATH}/drivers/sstar/include/ -o app_demo app_demo.c
4.1.2 FreeRTOS app¶
4.1.2.1 代码
1 #include "ms_platform.h" 2 #include "cam_os_wrapper.h" 3 #include "initcall.h" 4 #include "rpmsg_dualos.h" 5 6 static struct rpmsg_lite_instance *rpmsg_instance; 7 static CamOsThread rpmsg_test_thread; 8 9 static void* rpmsg_test(void *arg) 10 { 11 struct rpmsg_lite_endpoint *test_ept; 12 rpmsg_queue_handle test_ept_q; 13 int test_ept_addr; 14 15 int recved = 0; 16 int ret; 17 unsigned long src; 18 char buf[256]; 19 20 CamOsPrintf("Running rpmsg_test_task ...\n"); 21 22 while (1) { 23 rpmsg_instance = rpmsg_dualos_get_instance(EPT_SOC_DEFAULT, EPT_OS_LINUX); 24 if (rpmsg_instance) 25 break; 26 CamOsMsSleep(1); 27 } 28 29 test_ept_q = rpmsg_queue_create(rpmsg_instance); 30 if (test_ept_q == NULL) { 31 CamOsPrintf("rpmsg_test: failed to create queue\n"); 32 return NULL; 33 } 34 35 test_ept_addr = EPT_ADDR_MACRO(EPT_TYPE_CUSTOMER, 2); 36 test_ept = rpmsg_lite_create_ept(rpmsg_instance, test_ept_addr, rpmsg_queue_rx_cb, test_ept_q); 37 if (test_ept == NULL) { 38 CamOsPrintf("rpmsg_test: failed to create ept\n"); 39 rpmsg_queue_destroy(rpmsg_instance, test_ept_q); 40 return NULL; 41 } 42 43 while (1) { 44 recved = 0x0; 45 rpmsg_queue_recv(rpmsg_instance, test_ept_q, &src, (char *)&buf, 256, &recved, RL_BLOCK); 46 47 if (recved > 0) { 48 ret = rpmsg_lite_send(rpmsg_instance, test_ept, src, (char *)buf, recved, 5*1000); 49 if (ret != RL_SUCCESS) { 50 CamOsPrintf("rpmsg_test: rpmsg_lite_send return %d\n", ret); 51 } 52 } 53 } 54 return NULL; 55 } 56 57 void RPMsgAppMainEntry(void) 58 { 59 CamOsThreadAttrb_t attr = { 60 .nStackSize = 2048 61 }; 62 63 attr.szName = "rpmsg_test"; 64 CamOsThreadCreate(&rpmsg_test_thread, &attr, rpmsg_test, NULL); 65 } 66 67 rtos_application_initcall(RPMsgAppMainEntry, 0);
4.1.2.2 注释
1) 22-27行循环调用rpmsg_dualos_get_instance()获取rpmsg instance,是为了等待FreeRTOS中的RPMsg驱动准备好。
2) 29行创建一个queue,RPMsg驱动在收到发往对应endpoint的数据时,会调用回调函数rpmsg_queue_rx_cb,将数据放到这个queue中。
3) 36行调用rpmsg_lite_create_ept()创建RPMsg Endpoint,这里会传入src address,回调函数rpmsg_queue_rx_cb,以及29行创建的queue。
4) 45行调用rpmsg_queue_recv()接收数据。创建好RPMsg Endpoint后,RPMsg驱动就可以接收发往对应src address的数据了。rpmsg_queue_recv()的最后一个参数RL_BLOCK表示blocked,即如果没有数据就阻塞,直到有数据才返回。如果收到数据,其第二个参数会返回发送方的地址。
5) 48行调用rpmsg_lite_send()把数据发回给发送方。
4.1.2.3 编译
复制上述代码,替换到RISCV sdk中sc/application/rpmsg_app/src/rpmsg_speed_demo.c里(这将会导致RPMsg speed demo不可用),重新编译RISCV rtos即可。
4.2 RPMsg speed demo¶
这个demo是一个rpmsg的速度测试,详细的测试内容可以看sdk\verify\sample_code\source\pcupid\rpmsg\speed_demo\Readme.md。 它也分为rtos部分和linux部分,其中RTOS部分是默认打开的,linux部分只在emmc的defconfig默认打开。 要运行这个demo需要先运行rtos部分,再运行linux部分。
4.2.1 RTOS部分¶
RTOS的部分位于riscv\kernel\rtk\proj\sc\application\rpmsg_app\src\rpmsg_speed_demo.c。 这个部分是默认打开的。
要运行它需要在FreeRTOS下输入命令:rpmsg_speed_test
RTOS # rpmsg_speed_test rpmsg_channel1_thread: start rpmsg channel1 thread... rpmsg_channel2_thread: start rpmsg channel1 thread...
4.2.2 linux部分¶
这部分位于sdk\verify\sample_code\source\pcupid\rpmsg\speed_demo\rpmsg_speed_demo.cpp,这部分只在emmc的defconfig默认打开,其他defconfig要使用请单独编译
4.2.2.1 emmc的运行方式
emmc是默认打开的,要运行只需要输入如下命令即可
/ # /customer/sample_code/rpmsg_speed_demo/prog_rpmsg_speed_demo Create rpmsg channnel success ! Please input follow key to enter rpmsg demo test item: '1' : Single-channel simple send-receive test '2' : Dual-channel simple send-receive test '3' : Single-channel single-send speed test '4' : Dual-channel single-send speed test '5' : Single-channel send-receive speed test '6' : Dual-channel send-receive speed test '7' : Async single-channel send-receive speed test '8' : Async dual-channel send-receive speed test 'q' : Quit test
4.2.2.2 非emmc的运行方式
非emmc时,比如spinand,需要用户自行编译
cd sdk/verify/sample_code
make source/pcupid/rpmsg/speed_demo
生成的可执行文件位于
sample_code/out/arm(64)/app/prog_rpmsg_speed_demo
然后将其上传到板子上比如/custom/目录下,添加可执行权限,即可执行
/ # cp mnt/prog_rpmsg_speed_demo /customer/ / # chmod 777 /customer/prog_rpmsg_speed_demo / # /customer/prog_rpmsg_speed_demo Create rpmsg channnel success ! Please input follow key to enter rpmsg demo test item: '1' : Single-channel simple send-receive test '2' : Dual-channel simple send-receive test '3' : Single-channel single-send speed test '4' : Dual-channel single-send speed test '5' : Single-channel send-receive speed test '6' : Dual-channel send-receive speed test '7' : Async single-channel send-receive speed test '8' : Async dual-channel send-receive speed test 'q' : Quit test
4.2.3 运行结果¶
Create rpmsg channnel success ! Please input follow key to enter rpmsg demo test item: '1' : Single-channel simple send-receive test '2' : Dual-channel simple send-receive test '3' : Single-channel single-send speed test '4' : Dual-channel single-send speed test '5' : Single-channel send-receive speed test '6' : Dual-channel send-receive speed test '7' : Async single-channel send-receive speed test '8' : Async dual-channel send-receive speed test 'q' : Quit test 1 Start single-channel simple send-receive test Channel 4 send 25 bytes data to endpoint Channel 4 receive 25 bytes data : simple send receive test Single-channel simple send-receive test success 2 Start dual-channel simple send-receive test Channel 4 send 25 bytes data to endpoint Channel 5 send 25 bytes data to endpoint Channel 4 receive 25 bytes data : simple send receive test Channel 5 receive 25 bytes data : simple send receive test Dual-channel simple send-receive test success 3 Start single-channel single-send speed test Channel 4 single send speed test result : 3303360 bytes/s 4 Start dual-channel single-send speed test Channel 5 single send speed test result : 1622912 bytes/s Channel 4 single send speed test result : 1687888 bytes/s 5 Start single-channel send-receive speed test Channel 4 send receive speed test result : send : 1569344 bytes/s, receive : 1569344 bytes/s 6 Start dual-channel send-receive speed test Channel 4 send receive speed test result : send : 965712 bytes/s, receive : 965712 bytes/s Channel 5 send receive speed test result : send : 962240 bytes/s, receive : 962240 bytes/s 7 Start async single-channel send-receive speed test Channel 4 single receive speed test result : 2711136 bytes/s Channel 4 single send speed test result : 1599104 bytes/s Destroy rpmsg channnel success ! Create rpmsg channnel success ! 8 Start async dual-channel send-receive speed test Channel 4 single receive speed test result : 1729056 bytes/s Channel 5 single receive speed test result : 1475104 bytes/s Channel 4 single send speed test result : 767808 bytes/s Channel 5 single send speed test result : 605616 bytes/s Destroy rpmsg channnel success ! Create rpmsg channnel success !
5. API参考¶
5.1. RPMsg Linux Userspace API参考¶
本章介绍linux上的api和数据。
API名称 | 功能 |
---|---|
SS_RPMSG_CREATE_EPT_IOCTL | 创建RPMsg Endpoint设备节点。 |
SS_RPMSG_DESTROY_EPT_IOCTL | 销毁RPMsg Endpoint设备节点。 |
SS_RPMSG_DEVICES_INFO_IOCTL | 获取当前已连接的RISCV列表。 |
read | 接收数据。 |
write | 发送数据。 |
5.1.1. SS_RPMSG_CREATE_EPT_IOCTL¶
-
功能
创建和target chip通信的RPMsg Endpoint设备节点。
-
语法
struct ss_rpmsg_endpoint_info info; ioctl(fd, SS_RPMSG_CREATE_EPT_IOCTL, &info);
-
形参
参数名称 参数含义 输入/输出 fd /dev/rpmsg_ctrl0的文件句柄。 输入 info RPMsg Endpoint的属性。 输入,输出 -
返回值
返回值 描述 0 成功 -1 失败,查看errno获取原因 -
依赖
-
头文件:drivers/sstar/include/sstar_rpmsg.h & errno.h
-
库文件
-
-
注意
-
每次ioctl都会新建一个/dev/rpmsgX节点(例如:/dev/rpmsg0),表示一个虚拟的通信通道。
-
struct ss_rpmsg_endpoint_info中不同的mode&target_id组合表示不同的RPMsg bus,其地址空间是独立的,所以不同的mode&target_id组合可以使用一样的src address。
-
可以使用完全相同的struct ss_rpmsg_endpoint_info进行多次调用来创建多个/dev/rpmsgX节点,但是当这些节点中的一个被open后,再去open其他的节点就会失败,即同一时间只允许一个处于open状态。
-
/dev/rpmsgX里面的X由struct ss_rpmsg_endpoint_info里面的id返回。
-
如使用mdev,则ioctl返回后,/dev/rpmsgX节点可能还没有被mdev建立,会有延迟。
-
在合法的src&dst address空间内可以创建任意多个/dev/rpmsgX节点。
-
-
举例
-
相关主题
5.1.2. SS_RPMSG_DESTROY_EPT_IOCTL¶
-
功能
销毁对应的RPMsg Endpoint设备节点。
-
语法
ioctl(fd, SS_RPMSG_DESTROY_EPT_IOCTL);
-
形参
参数名称 参数含义 输入/输出 fd /dev/rpmsgX(例如:/dev/rpmsg0)的文件句柄。 输入 -
返回值
返回值 描述 0 成功 -1 失败,查看errno获取原因 -
依赖
-
头文件:drivers/sstar/include/sstar_rpmsg.h & errno.h
-
库文件
-
-
注意
-
创建RPMsg Endpoint(即/dev/rpmsgX)的ioctl使用的是/dev/rpmsg_ctrl0文件句柄,而销毁/dev/rpmsgX节点的ioctl使用的是/dev/rpmsgX文件句柄。在调用SS_RPMSG_DESTROY_EPT_IOCTL后,对应的RPMsg Endpoint设备节点会在其最后一个文件句柄被close后销毁。
-
RPMsg Endpoint的创建/销毁和open/close是独立的:open后可以进行read/write操作;close后可以重新open再次使用(read/write)。如无其他需求,不必每次都重新创建。当不再需要了,可以调用SS_RPMSG_DESTROY_EPT_IOCTL进行销毁。
-
-
举例
-
相关主题
5.1.3. SS_RPMSG_DEVICES_INFO_IOCTL¶
-
功能
查询当前通过RPMsg有和Linux系统连接的RISCV列表,返回个数和target_id列表。
-
语法
struct rpmsg_devices_info info; ioctl(fd, SS_RPMSG_DEVICES_INFO_IOCTL, &info);
-
形参
参数名称 参数含义 输入/输出 fd /dev/rpmsg_ctrl0的文件句柄。 输入 info 指定查询的设备类型,并提供存储返回值的buffer。 输入,输出 -
返回值
返回值 描述 0 成功 -1 失败,查看errno获取原因 -
依赖
-
头文件:drivers/sstar/include/sstar_rpmsg.h & errno.h
-
库文件
-
-
注意
- 查询RISCV是否有通过RPMsg连接到Linux。
-
举例(Linux端)
struct rpmsg_devices_info info; unsigned short *pTargetIDs; info.mode = RPMSG_MODE_RISCV; info.count = 1; info.buffer = (unsigned long long)malloc(1 * sizeof(unsigned short)); fd = open("/dev/rpmsg_ctrl0", O_RDWR); if (fd < 0) return -1; if (ioctl(fd, SS_RPMSG_DEVICES_INFO_IOCTL, &info) < 0) { close(fd); return -1; } printf(“Current %d connected RISCV: \n”, info.count); pTargetIDs = (unsigned short *)info.buffer; for (int i = 0; i < info.count; i++) printf(“target_id %hu\n”, pTargetIDs[i]); free(info.buffer); close(fd);
-
相关主题
无
5.1.4. read¶
-
功能
接收数据。
-
语法
ssize_t read(int fd, void *buf, size_t count);
-
形参
参数名称 参数含义 输入/输出 fd /dev/rpmsgX(例如:/dev/rpmsg0)的文件句柄。 输入 buf buffer地址,用于存放读取到的数据。 输出 count buffer的size。 输入 -
返回值
返回值 描述 >= 0 表示成功,读取到的字节数 -1 失败,查看errno获取原因 -
依赖
-
头文件:unistd.h & errno.h
-
库文件
-
-
注意
- 支持使用Linux select/poll/epoll接口监视/dev/rpmsgX的文件句柄(fd),检查是否有待读取的数据。
-
举例
-
相关主题
5.1.5. write¶
-
功能
发送数据。
-
语法
ssize_t write(int fd, const void *buf, size_t count);
-
形参
参数名称 参数含义 输入/输出 fd /dev/rpmsgX(例如:/dev/rpmsg0)的文件句柄。 输入 buf buffer地址,用于存放要发送的数据。 输入 count 待发送数据的字节数。 输入 -
返回值
返回值 描述 >= 0 表示成功,读取到的字节数 -1 失败,查看errno获取原因 -
依赖
-
头文件:unistd.h & errno.h
-
库文件
-
-
注意
- 一次write,只会发送一个RPMsg数据包,而一个RPMsg数据包最多可携带496bytes的有效数据,所以一次write最多可发送496bytes的数据。当count大于496时,可以通过多次调用write来完成全部数据的发送。
-
举例
-
相关主题
5.2. RPMsg Linux Userspace接口数据类型¶
相关数据类型、数据结构定义如下:
数据类型 | 定义 |
---|---|
RPMSG_MODE_RISCV | 表示target所在的RPMsg bus是由RISCV virtio device driver创建的。 |
RPMSG_MODE_UNKNOWN | 表示未知的RPMsg bus模式。 |
EPT_TYPE_CUSTOMER | 表示客户使用的(非SigmaStar使用)RPMsg Endpoint地址。 |
EPT_TYPE_SIGMASTAR | 表示非客户使用的(SigmaStar使用)RPMsg Endpoint地址。 |
EPT_TYPE | 定义RPMsg Endpoint地址所属类型的宏。 |
EPT_ADDR_MACRO | 定义RPMsg Endpoint地址的宏。 |
struct ss_rpmsg_endpoint_info | 创建的RPMsg Endpoint属性。 |
struct ss_rpmsg_devices_info | 已连接设备的查询条件。 |
5.2.1. RPMSG_MODE_RISCV¶
-
说明
表示target所在的RPMsg bus是由RISCV virtio device driver创建的。
-
定义
#define RPMSG_MODE_RISCV 6
-
成员
无
-
注意事项
无
-
相关数据类型及接口
无
5.2.2. RPMSG_MODE_UNKNOWN¶
-
说明
表示未知的RPMsg bus模式。
-
定义
#define RPMSG_MODE_UNKNOWN 255
-
成员
无
-
注意事项
无
-
相关数据类型及接口
无
5.2.3. EPT_TYPE_CUSTOMER¶
-
说明
表示客户使用的(非SigmaStar使用)RPMsg Endpoint地址。
-
定义
#define EPT_TYPE_CUSTOMER 0x1
-
成员
无
-
注意事项
无
-
相关数据类型及接口
无
5.2.4. EPT_TYPE_SIGMASTAR¶
-
说明
表示非客户使用的(SigmaStar使用)RPMsg Endpoint地址。
-
定义
#define EPT_TYPE_SIGMASTAR 0x0
-
成员
无
-
注意事项
客户进行应用开发时,不得使用这种类型的RPMsg Endpoint。
-
相关数据类型及接口
无
5.2.5. EPT_TYPE¶
-
说明
定义RPMsg Endpoint地址所属类型的宏。
-
定义
#define EPT_TYPE(x) ((x & 0x1) << 30)
-
成员
无
-
注意事项
参数成员为EPT_TYPE_CUSTOMER或者EPT_TYPE_SIGMASTAR。
-
相关数据类型及接口
5.2.6. EPT_ADDR_MACRO¶
-
说明
定义RPMsg Endpoint地址的宏。
-
定义
#define EPT_ADDR_MACRO(t, c) (EPT_TYPE(t) | (c & 0x3fffffff))
-
成员
无
-
注意事项
t表示地址类型,EPT_TYPE_CUSTOMER或者EPT_TYPE_SIGMASTAR。
c表示地址的值,在使用EPT_TYPE_CUSTOMER时,可以是任意数字。
-
相关数据类型及接口
5.2.7. struct ss_rpmsg_endpoint_info¶
-
说明
创建的RPMsg Endpoint属性。
-
定义
struct ss_rpmsg_endpoint_info { char name[32]; __u32 src; __u32 dst; __u32 id; __u32 mode; __u16 target_id; };
-
成员
成员名称 描述 name RPMsg Endpoint的名字。 src RPMsg Endpoint的本地地址。远端若要发信息给这个Endpoint,其dst地址就要填写这个值。 dst RPMsg Endpoint的信息发送目标地址。write Endpoint时,信息会被发送到这个地址。 id ioctl的返回值,表示RPMsg Endpoint节点/dev/rpmsgX中X的值。 mode Target所在RPMsg bus的模式,其值固定为: RPMSG_MODE_RISCV。 target_id 只有一个RISCV核,所以固定为0。 -
注意事项
无
-
相关数据类型及接口
5.2.8. struct ss_rpmsg_devices_info¶
-
说明
已连接设备的查询条件。
-
定义
struct ss_rpmsg_devices_info { __u32 mode; __u32 count; __u64 buffer; };
-
成员
成员名称 描述 mode Target所在RPMsg bus的模式,其值固定为: RPMSG_MODE_RISCV。 count 作为输入,表示buffer最多可以存几个target_id。
作为输出,返回值表示buffer里有几个target_id。buffer Userspace buffer地址,Kernel会往里面填写当前已连接的RISCV的target_id列表。 -
注意事项
无
-
相关数据类型及接口
5.3. RPMsg FreeRTOS API参考¶
本章介绍FreeRTOS上的API
API名 | 功能 |
---|---|
rpmsg_dualos_get_instance | 获取RPMsg设备实例指针。 |
rpmsg_queue_create | 创建存放消息的queue。 |
rpmsg_lite_create_ept | 创建RPMsg Endpoint。 |
rpmsg_queue_recv | 从queue中获取接收到的数据。 |
rpmsg_lite_send | 通过RPMsg Endpoint把数据发给远端的RPMsg Endpoint。 |
rpmsg_queue_destroy | 销毁queue。 |
rpmsg_lite_destroy_ept | 销毁RPMsg Endpoint。 |
5.3.1. rpmsg_dualos_get_instance¶
-
功能
获取FreeRTOS下RPMsg设备实例指针(句柄)。这个句柄是许多其他API(例如:rpmsg_queue_create,rpsmg_lite_create_ept等)所必须的参数。
-
语法
struct rpmsg_lite_instance *rpmsg_dualos_get_instance(int soc_id, int os_id);
-
形参
参数名称 参数含义 输入/输出 soc_id 固定传入EPT_SOC_DEFAULT。 输入 os_id 固定传入EPT_OS_LINUX。 输入 -
返回值
返回值 描述 非NULL 成功 NULL 表示RPMsg driver还没有初始化好. -
依赖
-
头文件:rpmsg_dualos.h
-
库文件
-
5.3.2. rpmsg_queue_create¶
-
功能
创建存放消息的queue。RPMsg driver会将接收到的,以及待发送的数据存放到创建endpoint时指定的queue中。
-
语法
rpmsg_queue_handle rpmsg_queue_create(struct rpmsg_lite_instance *rpmsg_lite_dev);
-
形参
参数名称 参数含义 输入/输出 rpmsg_lite_dev RPMsg设备实例指针。 输入 -
返回值
返回值 描述 非RL_NULL 成功,创建的queue RL_NULL 失败 -
依赖
-
头文件:rpmsg_dualos.h
-
库文件
-
5.3.3. rpmsg_lite_create_ept¶
-
功能
创建用于RPMsg通信的endpoint。
-
语法
struct rpmsg_lite_endpoint *rpmsg_lite_create_ept(struct rpmsg_lite_instance *rpmsg_lite_dev, unsigned long addr, rl_ept_rx_cb_t rx_cb, void *rx_cb_data);
-
形参
参数名称 参数含义 输入/输出 rpmsg_lite_dev RPMsg设备实例指针。 输入 addr endpoint的src address。 输入 rx_cb RPMsg驱动收到发往addr的数据包后,会调用这个callback函数进行处理。 输入 data RPMsg驱动在调用rx_cb函数时,会将data作为priv参数的值传给rx_cb。 输入 -
返回值
返回值 描述 非RL_NULL 成功,创建的端点 RL_NULL 失败 -
依赖
-
头文件:rpmsg_dualos.h
-
库文件
-
5.3.4. rpmsg_queue_recv¶
-
功能
从queue中获取接收到的数据。
-
语法
int rpmsg_queue_recv(struct rpmsg_lite_instance *rpmsg_lite_dev, rpmsg_queue_handle q, unsigned long *src, char *data, int maxlen, int *len, unsigned long timeout);
-
形参
参数名称 参数含义 输入/输出 rpmsg_lite_dev RPMsg设备实例指针。 输入 q queue的句柄。 输入 src 返回数据发送方的endpoint地址。 输出 data buffer地址,用于存储数据包。 输入 maxlen buffer的size。 输入 len 返回接收到的数据包size。 输出 timeout 超时时间。 输入 -
返回值
返回值 描述 RL_SUCCESS 成功 RL_ERR_PARAM 表示错误参数 RL_ERR_BUF_SIZE 表示数据大于buffer size RL_ERR_NO_BUFF 表示没有数据 -
依赖
-
头文件:rpmsg_dualos.h
-
库文件
-
5.3.5. rpmsg_lite_send¶
-
功能
发送数据到(指定地址的)远端RPMsg endpoint。
-
语法
int rpmsg_lite_send(struct rpmsg_lite_instance *rpmsg_lite_dev, struct rpmsg_lite_endpoint *ept, unsigned long dst, char *data, unsigned long size, unsigned long timeout);
-
形参
参数名称 参数含义 输入/输出 rpmsg_lite_dev RPMsg设备实例指针。 输入 ept RPMsg Endpoint实例指针。 输入 dst 远端RPMsg Endpoint的地址。 输入 data 需要发送数据的buffer地址。 输入 size 需要发送数据的size。 输入 timeout 超时时间。 输入 -
返回值
返回值 描述 RL_SUCCESS 成功 RL_ERR_PARAM 表示错误参数 RL_ERR_BUF_SIZE 表示数据size大于单个RPMsg数据包允许的最大payload size(即496bytes) RL_ERR_NO_BUFF 表示没有空间用于发送数据 -
依赖
-
头文件:rpmsg_dualos.h
-
库文件
-
5.3.6. rpmsg_queue_destroy¶
-
功能
销毁rpmsg_queue_create()所创建的queue。
-
语法
int rpmsg_queue_destroy(struct rpmsg_lite_instance *rpmsg_lite_dev, rpmsg_queue_handle q);
-
形参
参数名称 参数含义 输入/输出 rpmsg_lite_dev RPMsg设备实例指针。 输入 q 待销毁的queue句柄。 输入 -
返回值
返回值 描述 RL_SUCCESS 成功 RL_ERR_PARAM 表示错误参数 -
依赖
-
头文件:rpmsg_dualos.h
-
库文件
-
5.3.7. rpmsg_lite_destroy_ept¶
-
功能
销毁rpmsg_lite_create_ept()所创建的RPMsg Endpoint。
-
语法
int rpmsg_lite_destroy_ept(struct rpmsg_lite_instance *rpmsg_lite_dev, struct rpmsg_lite_endpoint *rl_ept);
-
形参
参数名称 参数含义 输入/输出 rpmsg_lite_dev RPMsg设备实例指针。 输入 rl_ept 待销毁的RPMsg Endpoint指针。 输入 -
返回值
返回值 描述 RL_SUCCESS 成功 RL_ERR_PARAM 表示错误参数 -
依赖
-
头文件:rpmsg_dualos.h
-
库文件
-
6. FAQ¶
以下日志来源于SSU938X、SSD238X,在不同芯片日志可能不同,但是问题定位的思路是一样的。
Q:kenrel报错Failed to get address of rpmsg share area,通信时arm端的/dev/rpmsgX节点创建失败报错rpmsg rpmsg0: Remote side(0x5,0x0) of demo0 is gone!
A:一般是arm端和riscv端没有完成rpmsg握手导致无法正常通信,可按以下步骤排查
①确认riscv是否正常跑起来,可接riscv端串口确认,若没有跑起来则确认是否是IPL_CUST load riscv固件失败
下图红框打印表示没有加载riscv固件,需要检查riscv加载地址是否和存储地址匹配或固件是否有损坏
②确认riscv能正常跑起来,检查riscv应用初始化顺序,应用是否在rpmsg驱动初始化前死等 是否在rpmsg的驱动初始化之前,有其他更高优先级任务占用了太长时间 是否给初始化过程中的某些地方添加了太多的日志导致启动过慢
Q:通信时arm端的/dev/rpmsgX节点能成功创建但是无法正常通信
A:检查是否riscv和kernel版本不匹配
riscv和kernel版本不匹配会有如下报错信息,需要确保riscv和linux的SDK版本一致
Q:通信时rpmsg延迟会越来越大
A:一般是riscv端的收发间隔和arm端的收发间隔不匹配,需要检查arm端是否在接收或者发送时有多添加休眠
Q:kernel应用层/dev/rpmsgX通信节点反复销毁再创建有概率无法正常创建/dev/rpmsgX节点
A:一般是rpmsg节点创建和销毁时间间隔太短,应用层mdev去创建和销毁rpmsg节点的时候原来先销毁再创建的时序变成了先创建再销毁,open rpmsg0节点的时候节点实际上不存在导致问题,需要应用层在创建rpmsg0节点后添加access判断rpmsg0节点存在后再进行open操作, 在销毁rpmsg0节点后添加access判断rpmsg0节点不存在后再进行重新创建操作
Q:kernel打印rpmsg_dualos virtio0.rpmsg_dualos.-1.33554480: timeout waiting for a tx buffer
A:kernel发送给riscv端的rpmsg tx buffer有上限,当riscv没有及时接收时会出现该打印,需要检查riscv端是否有高优先级耗时任务占用CPU,或者RISCV异常挂死