RISCV_RPMsg使用参考
REVISION HISTORY¶
Revision No. | Description |
Date |
---|---|---|
1.0 | 12/10/2022 |
1. RPMSG说明¶
1.1. 概述¶
RPMSG英文全称是(Remote processor Messaging),是一种开放式的多核间通讯协议。Linux Kernel内置RPMSG协议的实现。
1.2. 流程框图¶
Sigmastar RPMsg userspace driver 对接kernel内置RPMsg协议实现,并提供了创建RPMsg endpoint的接口,Linux userspace app可以使用这些endpoints和FreeRTOS下的app进行通信。
Rpmsg userspace driver会生成设备节点/dev/rpmsg_ctrl0,userspace app通过其ioctl接口,可以创建用于通信的Endpoint设备节点(如:/dev/rpmsg0),创建节点时,需要指定本地地址和远端的通信地址,如在Linux userspace app上指定src address Src1和dst address Dst1,在FreeRTOS下就要使用Dst1作为src address创建endpoint,并且发送数据时制定Src1作为dst address,这样Linux userspace app创建的Endpoint就可以和FreeRTOS创建的Endpoint进行通信了。
1.2.1. Linux端的架构图¶
1.2.2. FreeRTOS端的架构图¶
2. RPMsg Userspace API参考¶
API名 | 功能 |
---|---|
SS_RPMSG_CREATE_EPT_IOCTL | 创建RPMsg Endpoint设备节点 |
SS_RPMSG_DESTROY_EPT_IOCTL | 销毁RPMsg Endpoint设备节点 |
SS_RPMSG_DEVICES_INFO_IOCTL | 获取当前连接上的RISCV列表 |
read | 接收数据 |
write | 发送数据 |
2.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节点(如:rpmsg0),表示一个虚拟的通信通道。
-
每个RC口或EP口长出来的RPMsg bus,其地址空间是独立的,不同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节点。
-
-
举例
-
相关主题
2.2. SS_RPMSG_DESTROY_EPT_IOCTL¶
-
功能
销毁掉对应的RPMsg Endpoint设备节点。
-
语法
ioctl(fd, SS_RPMSG_DESTROY_EPT_IOCTL);
-
形参
参数名称 参数含义 输入/输出 fd /dev/rpmsgX(如rpmsg0)的文件句柄。 输入 -
返回值
-
0: 表示成功。
-
-1: 表示失败,查看errno获取原因。
-
-
依赖
-
头文件:drivers/sstar/include/sstar_rpmsg.h & errno.h
-
库文件
-
-
注意
-
创建RPMsg Endpoint(/dev/rpmsgX)使用的是/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进行销毁。
-
-
举例
-
相关主题
2.3. SS_RPMSG_DEVICES_INFO_IOCTL¶
-
功能
查询当前通过RPMsg有和Linux系统连接的RISCV列表,返回个数和target_id列表。
-
语法
int ioctl(int fd, SS_RPMSG_DEVICES_INFO_IOCTL, struct rpmsg_devices_info *info);
-
形参
参数名称 参数含义 输入/输出 fd /dev/rpmsg_ctrl0的文件句柄。 输入 info 指定查询的设备类型,并提供存储返回值的buffer。 输入,输出 -
返回值
-
0: 表示成功。
-
-1: 表示失败,查看errno获取原因。
-
-
依赖
-
头文件:drivers/sstar/include/sstar_rpmsg.h & errno.h
-
库文件
-
-
注意
- 查询RISCV是否有通过RPMsg连接到Linux。
-
举例(RC端)
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 int)); 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);
-
相关主题
无
2.4. read¶
-
功能
接收数据。
-
语法
ssize_t read(int fd, void *buf, size_t count);
-
形参
参数名称 参数含义 输入/输出 fd /dev/rpmsgX(如:rpmsg0)的文件句柄。 输入 buf buffer地址,用于存放读取到的数据 输出 count buffer的size 输入 -
返回值
-
>= 0: 读取到的bytes个数。
-
-1: 失败,检查errno获取原因。
-
-
依赖
-
头文件:unistd.h & errno.h
-
库文件
-
-
注意
- 支持使用Linux select/poll/epoll接口监视/dev/rpmsgX的文件句柄(fd),检查有待读取的数据。
-
举例
-
相关主题
2.5. write¶
-
功能
发送数据。
-
语法
ssize_t write(int fd, const void *buf, size_t count);
-
形参
参数名称 参数含义 输入/输出 fd /dev/rpmsgX(如:rpmsg0)的文件句柄。 输入 buf buffer地址,存放要发送的数据。 输入 count 待发送数据的字节数。 输入 -
返回值
-
>= 0: 已发送的字节数。
-
-1: 失败,检查errno获取原因。
-
-
依赖
-
头文件:unistd.h & errno.h
-
库文件
-
-
注意
- 一次write,会使用一个RPMsg 数据包发送,最多携带496个bytes,所以需要检查返回值,当返回值不是-1时,并且小于count时,可以多次调用write来完成剩下数据的发送。
-
举例
-
相关主题
3. RPMsg 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 | 定义连接设备查询条件的结构体 |
3.1. RPMSG_MODE_RISCV¶
-
说明
表示target所在的RPMsg bus是由RISCV virtio device driver 创建的。
-
定义
#define RPMSG_MODE_RISCV 5
-
成员
无
-
注意事项
无
-
相关数据类型及接口
无
3.2. RPMSG_MODE_UNKNOWN¶
-
说明
表示未知的RPMsg bus模式。
-
定义
#define RPMSG_MODE_UNKNOWN 255
-
成员
无
-
注意事项
无
-
相关数据类型及接口
无
3.3. EPT_TYPE_CUSTOMER¶
-
说明
表示客户使用的(非Sigmastar使用)RPMsg Endpoint地址。
-
定义
#define EPT_TYPE_CUSTOMER 0x1
-
成员
无
-
注意事项
无
-
相关数据类型及接口
无
3.4. EPT_TYPE_SIGMASTAR¶
-
说明
表示非客户使用的(Sigmastar使用)RPMsg Endpoint地址。
-
定义
#define EPT_TYPE_SIGMASTAR 0x0
-
成员
无
-
注意事项
客户进行应用开发时,不得使用这种类型的RPMsg Endpoint。
-
相关数据类型及接口
无
3.5. EPT_TYPE¶
-
说明
定义RPMsg Endpoint地址所属类型的宏。
-
定义
#define EPT_TYPE(x) ((x & 0x1) << 30)
-
成员
无
-
注意事项
参数成员为EPT_TYPE_CUSTOMER或者EPT_TYPE_SIGMASTAR
-
相关数据类型及接口
3.6. EPT_ADDR_MACRO¶
-
说明
定义RPMsg Endpoint地址的宏。
-
定义
#define EPT_ADDR_MACRO(t, c) (EPT_TYPE(t) | (c & 0x3fffffff))
-
成员
无
-
注意事项
t表示地址类型,EPT_TYPE_CUSTOERM或者EPT_TYPE_SIGMASTAR
c表示地址的值,在使用EPT_TYPE_CUSTOMER时,可以是任意数字
-
相关数据类型及接口
3.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 -
注意事项
无。
-
相关数据类型及接口
3.8. struct ss_rpmsg_devices_info¶
-
说明
定义devices列表的查询条件。
-
定义
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列表。 -
注意事项
无。
-
相关数据类型及接口
4. RPMsg FreeRTOS API参考¶
API名 | 功能 |
---|---|
rpmsg_dualos_get_instance | 获取RPMsg设备实例指针 |
rpmsg_queue_create | 创建queue用于存储RPMsg驱动接收到的数据 |
rpmsg_lite_create_ept | 创建RPMsg endpoint |
rpmsg_queue_recv | 从RPMsg endpoint中获取接收到的数据 |
rpmsg_lite_send | 通过RPMsg endpoint把数据发给远端的RPMsg endpoint |
rpmsg_queue_destroy | 销毁queue |
rpmsg_lite_destroy_ept | 销毁RPMsg endpoint |
4.1. rpmsg_dualos_get_instance¶
-
功能
获取FreeRTOS下RPMsg设备实例指针,这个句柄是许多其他API(如:rpmsg_create_queue, 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
-
库文件
-
4.2. rpmsg_queue_create¶
-
功能
创建存放消息的queue,RPMsg driver会将接收到的,发往对应endpoint的数据存放创建endpoint时指定的queue中。
-
语法
rpmsg_queue_handle rpmsg_queue_create(struct rpmsg_lite_instance *rpmsg_lite_dev);
-
形参
参数名称 参数含义 输入/输出 rpmsg_lite_dev RPMsg设备实例指针 输入 -
返回值
-
非RL_NULL: 成功。
-
RL_NULL: 表示创建失败
-
-
依赖
-
头文件:rpmsg_dualos.h
-
库文件
-
4.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的source address地址 输入 rx_cb RPMsg驱动收到发往addr的数据包,会调用这callback函数,进行处理 输入 data RPMsg驱动在调用rx_cb函数时,作为priv参数的值传给rx_cb 输入 -
返回值
-
非RL_NULL: 成功。
-
RL_NULL: 表示创建失败。
-
-
依赖
-
头文件:rpmsg_dualos.h
-
库文件
-
4.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
-
库文件
-
4.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 输入 len 返回接收到的数据包size 输入 timeout 超时时间 输入 -
返回值
-
RL_SUCCESS: 成功
-
RL_ERR_PARAM: 错误参数
-
RL_ERR_BUF_SIZE: 数据大于单个RPMsg数据包允许的最大payload size(496 bytes)
-
RL_ERR_NO_MEM: 没有空间用于发送数据
-
-
依赖
-
头文件:rpmsg_dualos.h
-
库文件
-
4.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
-
库文件
-
4.7. rpmsg_lite_destroy_ept¶
-
功能
销毁rpmsg_lite_create_create()创建的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
-
库文件
-
5. RPMsg Demo说明¶
5.1. RPMsg Demo简介¶
RPMsg demo由两部分组成,一个是linux userspace app,一个是FreeRTOS下的app,两个app间使用RPMsg进行通信。
Linux userspace app向FreeRTOS app不断的发送带上编号的”hello,world”信息,然后读取FreeRTOS app的应答并打印出来。
FreeRTOS app则是循环等待linux userspace app发送数据过来,然后把数据不做修改的发送回去。
5.2. Linux userspace app¶
5.2.1. 代码¶
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. }
5.2.2. 注释¶
1) 第42行sleep()函数调用,是为了避免mdev还没有创建好rpmsg endpoint节点
5.3. FreeRTOS app¶
5.3.1. 代码¶
RISCV sdk中的路径:
sc/application/rpmsg_app
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);
5.3.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()函数把数据发回给发送方