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节点。

  • 举例

    Linux userspace app

  • 相关主题

    SS_RPMSG_DESTROY_EPT_IOCTL

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进行销毁。

  • 举例

    参见SS_RPMSG_CREATE_EPT_IOCTL举例。

  • 相关主题

    SS_RPMSG_CREATE_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),检查是否有待读取的数据。
  • 举例

    参见SS_RPMSG_CREATE_EPT_IOCTL举例。

  • 相关主题

    SS_RPMSG_CREATE_EPT_IOCTL

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来完成全部数据的发送。
  • 举例

    参见SS_RPMSG_CREATE_EPT_IOCTL举例。

  • 相关主题

    SS_RPMSG_CREATE_EPT_IOCTL

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。

  • 相关数据类型及接口

    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时,可以是任意数字。

  • 相关数据类型及接口

    EPT_TYPE

    EPT_TYPE_CUSTOMER

    EPT_TYPE_SIGMASTAR

    SS_RPMSG_CREATE_EPT_IOCTL

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。
  • 注意事项

  • 相关数据类型及接口

    EPT_TYPE_CUSTOMER

    EPT_TYPE_SIGMASTAR

    EPT_TYPE

    EPT_ADDR_MACRO

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列表。
  • 注意事项

  • 相关数据类型及接口

    RPMSG_MODE_RISCV

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异常挂死