From eae2eccf082756c1a814ce71e8c2a8d7c13dee6f Mon Sep 17 00:00:00 2001 From: Tanmay Shah Date: Thu, 22 Sep 2022 16:23:08 -0700 Subject: [PATCH] Add rpmsg-echo-test example rpmsg-echo-test example demonstrates how to write linux userspace application that uses rpmsg devices in kernel space. This application sends pre-defined payload to remote processor firmware and waits for remote processor to send same payload back. It then verifies received payload integrity. Number of packets to send to remote processor can be configured from command line Signed-off-by: Tanmay Shah --- examples/linux/rpmsg-echo-test/LICENSE | 33 ++ examples/linux/rpmsg-echo-test/Makefile | 18 + examples/linux/rpmsg-echo-test/README | 10 + examples/linux/rpmsg-echo-test/echo_test.c | 412 +++++++++++++++++++++ 4 files changed, 473 insertions(+) create mode 100644 examples/linux/rpmsg-echo-test/LICENSE create mode 100644 examples/linux/rpmsg-echo-test/Makefile create mode 100644 examples/linux/rpmsg-echo-test/README create mode 100644 examples/linux/rpmsg-echo-test/echo_test.c diff --git a/examples/linux/rpmsg-echo-test/LICENSE b/examples/linux/rpmsg-echo-test/LICENSE new file mode 100644 index 0000000..7496965 --- /dev/null +++ b/examples/linux/rpmsg-echo-test/LICENSE @@ -0,0 +1,33 @@ +Software License Agreement (BSD License) +======================================== + +Copyright (c) 2014, Mentor Graphics Corporation. All rights reserved. +Copyright (c) 2015 - 2016 Xilinx, Inc. All rights reserved. +Copyright (c) 2016 Freescale Semiconductor, Inc. All rights reserved + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. The names of its contributors may not be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + diff --git a/examples/linux/rpmsg-echo-test/Makefile b/examples/linux/rpmsg-echo-test/Makefile new file mode 100644 index 0000000..31943f3 --- /dev/null +++ b/examples/linux/rpmsg-echo-test/Makefile @@ -0,0 +1,18 @@ + +APP = echo_test +APP_OBJS = echo_test.o + +# Add any other object files to this list below + + +all: $(APP) + +$(APP): $(APP_OBJS) + $(CC) $(LDFLAGS) -o $@ $(APP_OBJS) $(LDLIBS) + +clean: + rm -rf $(APP) *.o + +%.o: %.c + $(CC) -c $(CFLAGS) -o $@ $< + diff --git a/examples/linux/rpmsg-echo-test/README b/examples/linux/rpmsg-echo-test/README new file mode 100644 index 0000000..fa7fadd --- /dev/null +++ b/examples/linux/rpmsg-echo-test/README @@ -0,0 +1,10 @@ +echo_test + +Description: + This demo uses linux kernel rpmsg framework to send large data buffer to remote + processor and validates integrity of received buffer from remote processor. + If buffer data does not match, then number of different bytes are reported on + console + +Remoteproc firmware for Xilinx ZynqMP platform: + https://github.com/Xilinx/embeddedsw/tree/xlnx_rel_v2022.1/lib/sw_apps/openamp_echo_test diff --git a/examples/linux/rpmsg-echo-test/echo_test.c b/examples/linux/rpmsg-echo-test/echo_test.c new file mode 100644 index 0000000..bb5a20b --- /dev/null +++ b/examples/linux/rpmsg-echo-test/echo_test.c @@ -0,0 +1,412 @@ +/* + * echo_test.c + * + * Created on: Oct 4, 2014 + * Author: etsam + */ + +/* + * Test application that data integraty of inter processor + * communication from linux userspace to a remote software + * context. The application sends chunks of data to the + * remote processor. The remote side echoes the data back + * to application which then validates the data returned. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct _payload { + unsigned long num; + unsigned long size; + char data[]; +}; + +struct _payload *i_payload; +struct _payload *r_payload; + +#define RPMSG_GET_KFIFO_SIZE 1 +#define RPMSG_GET_AVAIL_DATA_SIZE 2 +#define RPMSG_GET_FREE_SPACE 3 + +#define RPMSG_HEADER_LEN 16 +#define MAX_RPMSG_BUFF_SIZE (512 - RPMSG_HEADER_LEN) +#define PAYLOAD_MIN_SIZE 1 +#define PAYLOAD_MAX_SIZE (MAX_RPMSG_BUFF_SIZE - 24) +#define NUM_PAYLOADS (PAYLOAD_MAX_SIZE/PAYLOAD_MIN_SIZE) + +#define RPMSG_BUS_SYS "/sys/bus/rpmsg" + +#define PR_DBG(fmt, args ...) printf("%s():%u "fmt, __func__, __LINE__, ##args) +#define SHUTDOWN_MSG 0xEF56A55A +void send_shutdown(int fd) +{ + union { + unsigned int n[8]; + struct _payload sdown; + } umsg = { + .n = { + SHUTDOWN_MSG, SHUTDOWN_MSG, SHUTDOWN_MSG, SHUTDOWN_MSG, + SHUTDOWN_MSG, SHUTDOWN_MSG, SHUTDOWN_MSG, SHUTDOWN_MSG, + } + }; + + umsg.sdown.size = sizeof(umsg); + if (write(fd, &umsg, sizeof(umsg)) < 0) + perror("write SHUTDOWN_MSG\n"); +} + +int rpmsg_create_ept(int rpfd, struct rpmsg_endpoint_info *eptinfo) +{ + int ret; + + ret = ioctl(rpfd, RPMSG_CREATE_EPT_IOCTL, eptinfo); + if (ret) + perror("Failed to create endpoint.\n"); + return ret; +} + +static char *get_rpmsg_ept_dev_name(const char *rpmsg_char_name, + const char *ept_name, + char *ept_dev_name) +{ + char sys_rpmsg_ept_name_path[64]; + char svc_name[64]; + char *sys_rpmsg_path = "/sys/class/rpmsg"; + FILE *fp; + int i; + int ept_name_len; + + for (i = 0; i < 128; i++) { + sprintf(sys_rpmsg_ept_name_path, "%s/%s/rpmsg%d/name", + sys_rpmsg_path, rpmsg_char_name, i); + printf("checking %s\n", sys_rpmsg_ept_name_path); + if (access(sys_rpmsg_ept_name_path, F_OK) < 0) + continue; + fp = fopen(sys_rpmsg_ept_name_path, "r"); + if (!fp) { + printf("failed to open %s\n", sys_rpmsg_ept_name_path); + break; + } + fgets(svc_name, sizeof(svc_name), fp); + fclose(fp); + printf("svc_name: %s.\n",svc_name); + ept_name_len = strlen(ept_name); + if (ept_name_len > sizeof(svc_name)) + ept_name_len = sizeof(svc_name); + if (!strncmp(svc_name, ept_name, ept_name_len)) { + sprintf(ept_dev_name, "rpmsg%d", i); + return ept_dev_name; + } + } + + printf("Not able to RPMsg endpoint file for %s:%s.\n", + rpmsg_char_name, ept_name); + return NULL; +} + +static int bind_rpmsg_chrdev(const char *rpmsg_dev_name) +{ + char fpath[256]; + char *rpmsg_chdrv = "rpmsg_chrdev"; + int fd; + int ret; + + /* rpmsg dev overrides path */ + sprintf(fpath, "%s/devices/%s/driver_override", + RPMSG_BUS_SYS, rpmsg_dev_name); + PR_DBG("open %s\n", fpath); + fd = open(fpath, O_WRONLY); + if (fd < 0) { + fprintf(stderr, "Failed to open %s, %s\n", + fpath, strerror(errno)); + return -EINVAL; + } + ret = write(fd, rpmsg_chdrv, strlen(rpmsg_chdrv) + 1); + if (ret < 0) { + fprintf(stderr, "Failed to write %s to %s, %s\n", + rpmsg_chdrv, fpath, strerror(errno)); + return -EINVAL; + } + close(fd); + + /* bind the rpmsg device to rpmsg char driver */ + sprintf(fpath, "%s/drivers/%s/bind", RPMSG_BUS_SYS, rpmsg_chdrv); + fd = open(fpath, O_WRONLY); + if (fd < 0) { + fprintf(stderr, "Failed to open %s, %s\n", + fpath, strerror(errno)); + return -EINVAL; + } + PR_DBG("write %s to %s\n", rpmsg_dev_name, fpath); + ret = write(fd, rpmsg_dev_name, strlen(rpmsg_dev_name) + 1); + if (ret < 0) { + fprintf(stderr, "Failed to write %s to %s, %s\n", + rpmsg_dev_name, fpath, strerror(errno)); + return -EINVAL; + } + close(fd); + return 0; +} + +static int get_rpmsg_chrdev_fd(const char *rpmsg_dev_name, + char *rpmsg_ctrl_name) +{ + char dpath[2*NAME_MAX]; + DIR *dir; + struct dirent *ent; + int fd; + + sprintf(dpath, "%s/devices/%s/rpmsg", RPMSG_BUS_SYS, rpmsg_dev_name); + PR_DBG("opendir %s\n", dpath); + dir = opendir(dpath); + if (dir == NULL) { + fprintf(stderr, "opendir %s, %s\n", dpath, strerror(errno)); + return -EINVAL; + } + while ((ent = readdir(dir)) != NULL) { + if (!strncmp(ent->d_name, "rpmsg_ctrl", 10)) { + sprintf(dpath, "/dev/%s", ent->d_name); + closedir(dir); + PR_DBG("open %s\n", dpath); + fd = open(dpath, O_RDWR | O_NONBLOCK); + if (fd < 0) { + fprintf(stderr, "open %s, %s\n", + dpath, strerror(errno)); + return fd; + } + sprintf(rpmsg_ctrl_name, "%s", ent->d_name); + return fd; + } + } + + fprintf(stderr, "No rpmsg_ctrl file found in %s\n", dpath); + closedir(dir); + return -EINVAL; +} + +static void set_src_dst(char *out, struct rpmsg_endpoint_info *pep) +{ + long dst = 0; + char *lastdot = strrchr(out, '.'); + + if (lastdot == NULL) + return; + dst = strtol(lastdot + 1, NULL, 10); + if ((errno == ERANGE && (dst == LONG_MAX || dst == LONG_MIN)) + || (errno != 0 && dst == 0)) { + return; + } + pep->dst = (unsigned int)dst; +} + +/* + * return the first dirent matching rpmsg-openamp-demo-channel + * in /sys/bus/rpmsg/devices/ E.g.: + * virtio0.rpmsg-openamp-demo-channel.-1.1024 + */ +static void lookup_channel(char *out, struct rpmsg_endpoint_info *pep) +{ + char dpath[] = RPMSG_BUS_SYS "/devices"; + struct dirent *ent; + DIR *dir = opendir(dpath); + + if (dir == NULL) { + fprintf(stderr, "opendir %s, %s\n", dpath, strerror(errno)); + return; + } + while ((ent = readdir(dir)) != NULL) { + if (strstr(ent->d_name, pep->name)) { + strncpy(out, ent->d_name, NAME_MAX); + set_src_dst(out, pep); + PR_DBG("using dev file: %s\n", out); + closedir(dir); + return; + } + } + closedir(dir); + fprintf(stderr, "No dev file for %s in %s\n", pep->name, dpath); +} + +/* s is alwasy argv[0] */ +void print_help(const char *s) +{ + const char *default_bin_name = "echo_test"; + if (!s) + s = default_bin_name; + printf("\r\nusage: %s [option: -d, -n, -s, -d]\r\n", s); + printf("-d - rpmsg device name\r\n"); + printf("-n - number of times payload will be sent\r\n"); + printf("-s - source end point address\r\n"); + printf("-d - destination end point address"); +} + +int main(int argc, char *argv[]) +{ + int ret, i, j; + int size, bytes_rcvd, bytes_sent, err_cnt = 0; + int opt, charfd, fd; + int ntimes = 1; + char rpmsg_dev[NAME_MAX] = "virtio0.rpmsg-openamp-demo-channel.-1.0"; + char rpmsg_char_name[16]; + char fpath[2*NAME_MAX]; + struct rpmsg_endpoint_info eptinfo = { + .name = "rpmsg-openamp-demo-channel", .src = 0, .dst = 0 + }; + char ept_dev_name[16]; + char ept_dev_path[32]; + + printf("\r\n Echo test start \r\n"); + + /* Load rpmsg_char driver */ + printf("\r\nMaster>probe rpmsg_char\r\n"); + ret = system("set -x; lsmod; modprobe rpmsg_char"); + if (ret < 0) { + perror("Failed to load rpmsg_char driver.\n"); + return -EINVAL; + } + + lookup_channel(rpmsg_dev, &eptinfo); + + while ((opt = getopt(argc, argv, "d:n:s:e:")) != -1) { + switch (opt) { + case 'd': + strncpy(rpmsg_dev, optarg, sizeof(rpmsg_dev)); + break; + case 'n': + ntimes = atoi(optarg); + break; + case 's': + eptinfo.src = atoi(optarg); + break; + case 'e': + eptinfo.dst = atoi(optarg); + break; + default: + print_help(argv[0]); + break; + } + } + + sprintf(fpath, RPMSG_BUS_SYS "/devices/%s", rpmsg_dev); + if (access(fpath, F_OK)) { + fprintf(stderr, "access(%s): %s\n", fpath, strerror(errno)); + return -EINVAL; + } + ret = bind_rpmsg_chrdev(rpmsg_dev); + if (ret < 0) + return ret; + charfd = get_rpmsg_chrdev_fd(rpmsg_dev, rpmsg_char_name); + if (charfd < 0) + return charfd; + + /* Create endpoint from rpmsg char driver */ + PR_DBG("rpmsg_create_ept: %s[src=%#x,dst=%#x]\n", + eptinfo.name, eptinfo.src, eptinfo.dst); + ret = rpmsg_create_ept(charfd, &eptinfo); + if (ret) { + fprintf(stderr, "rpmsg_create_ept %s\n", strerror(errno)); + return -EINVAL; + } + if (!get_rpmsg_ept_dev_name(rpmsg_char_name, eptinfo.name, + ept_dev_name)) + return -EINVAL; + sprintf(ept_dev_path, "/dev/%s", ept_dev_name); + + printf("open %s\n", ept_dev_path); + fd = open(ept_dev_path, O_RDWR | O_NONBLOCK); + if (fd < 0) { + perror(ept_dev_path); + close(charfd); + return -1; + } + + i_payload = (struct _payload *)malloc(2 * sizeof(unsigned long) + PAYLOAD_MAX_SIZE); + r_payload = (struct _payload *)malloc(2 * sizeof(unsigned long) + PAYLOAD_MAX_SIZE); + + if (i_payload == 0 || r_payload == 0) { + printf("ERROR: Failed to allocate memory for payload.\n"); + return -1; + } + + for (j=0; j < ntimes; j++){ + printf("\r\n **********************************"); + printf("****\r\n"); + printf("\r\n Echo Test Round %d \r\n", j); + printf("\r\n **********************************"); + printf("****\r\n"); + for (i = 0, size = PAYLOAD_MIN_SIZE; i < NUM_PAYLOADS; + i++, size++) { + int k; + + i_payload->num = i; + i_payload->size = size; + + /* Mark the data buffer. */ + memset(&(i_payload->data[0]), 0xA5, size); + + printf("\r\n sending payload number"); + printf(" %ld of size %lu\r\n", i_payload->num, + (2 * sizeof(unsigned long)) + size); + + bytes_sent = write(fd, i_payload, + (2 * sizeof(unsigned long)) + size); + + if (bytes_sent <= 0) { + printf("\r\n Error sending data"); + printf(" .. \r\n"); + break; + } + printf("echo test: sent : %d\n", bytes_sent); + + r_payload->num = 0; + bytes_rcvd = read(fd, r_payload, + (2 * sizeof(unsigned long)) + PAYLOAD_MAX_SIZE); + while (bytes_rcvd <= 0) { + usleep(10000); + bytes_rcvd = read(fd, r_payload, + (2 * sizeof(unsigned long)) + PAYLOAD_MAX_SIZE); + } + printf(" received payload number "); + printf("%ld of size %d\r\n", r_payload->num, bytes_rcvd); + + /* Validate data buffer integrity. */ + for (k = 0; k < r_payload->size; k++) { + + if (r_payload->data[k] != 0xA5) { + printf(" \r\n Data corruption"); + printf(" at index %d \r\n", k); + err_cnt++; + break; + } + } + bytes_rcvd = read(fd, r_payload, + (2 * sizeof(unsigned long)) + PAYLOAD_MAX_SIZE); + + } + printf("\r\n **********************************"); + printf("****\r\n"); + printf("\r\n Echo Test Round %d Test Results: Error count = %d\r\n", + j, err_cnt); + printf("\r\n **********************************"); + printf("****\r\n"); + } + + send_shutdown(fd); + free(i_payload); + free(r_payload); + + close(fd); + if (charfd >= 0) + close(charfd); + return 0; +}