diff --git a/testing/rpmsgdev/CMakeLists.txt b/testing/rpmsgdev/CMakeLists.txt new file mode 100644 index 000000000..5dd7009af --- /dev/null +++ b/testing/rpmsgdev/CMakeLists.txt @@ -0,0 +1,32 @@ +# ############################################################################## +# apps/testing/rpmsgdev/CMakeLists.txt +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for + +# additional information regarding copyright ownership. The ASF licenses this +# file to you under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# ############################################################################## + +if(CONFIG_RPMSGDEV_TEST) + nuttx_add_application( + NAME + rpmsgdev + PRIORITY + ${CONFIG_TESTING_TESTCASES_PRIORITY} + STACKSIZE + ${CONFIG_TESTING_TESTCASES_STACKSIZE} + SRCS + testdev.c) +endif() diff --git a/testing/rpmsgdev/Kconfig b/testing/rpmsgdev/Kconfig new file mode 100644 index 000000000..a519b8455 --- /dev/null +++ b/testing/rpmsgdev/Kconfig @@ -0,0 +1,3 @@ +config RPMSGDEV_TEST + bool "Enable rpmsgdev test" + default n diff --git a/testing/rpmsgdev/Make.defs b/testing/rpmsgdev/Make.defs new file mode 100644 index 000000000..686d1e639 --- /dev/null +++ b/testing/rpmsgdev/Make.defs @@ -0,0 +1,23 @@ +############################################################################ +# apps/testing/rpmsgdev/Make.defs +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_RPMSGDEV_TEST),) +CONFIGURED_APPS += $(APPDIR)/testing/rpmsgdev +endif diff --git a/testing/rpmsgdev/Makefile b/testing/rpmsgdev/Makefile new file mode 100644 index 000000000..771e9ef85 --- /dev/null +++ b/testing/rpmsgdev/Makefile @@ -0,0 +1,30 @@ +############################################################################ +# apps/testing/rpmsgdev/Makefile +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +PROGNAME += testdev +PRIORITY := SCHED_PRIORITY_DEFAULT +STACKSIZE := $(CONFIG_DEFAULT_TASK_STACKSIZE) +MODULE = $(CONFIG_RPMSGDEV_TEST) + +MAINSRC += $(CURDIR)/testdev.c + +include $(APPDIR)/Application.mk diff --git a/testing/rpmsgdev/testdev.c b/testing/rpmsgdev/testdev.c new file mode 100644 index 000000000..cbd79843e --- /dev/null +++ b/testing/rpmsgdev/testdev.c @@ -0,0 +1,816 @@ +/**************************************************************************** + * apps/testing/rpmsgdev/testdev.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_DEV_RPMSG +#include +#endif + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* set a random open flag to check whether the open api behave correctly */ + +#define OPEN_FLAG O_RDWR +#define RET 114514 +#define OFFSET 1234l +#define WHENCE SEEK_SET +#define IOCTL FIONSPACE +#define IOCTL_ARG 0x55AA +static int hour __attribute__((unused)); + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int testdev_open(FAR struct file *filep); +static int testdev_close(FAR struct file *filep); +static ssize_t testdev_read(FAR struct file *filep, FAR char *buffer, + size_t buflen); +static ssize_t testdev_write(FAR struct file *filep, FAR const char *buffer, + size_t buflen); +static off_t testdev_seek(FAR struct file *filep, off_t offset, + int whence); +static int testdev_ioctl(FAR struct file *filep, int cmd, + unsigned long arg); +static int testdev_poll(FAR struct file *filep, FAR struct pollfd *fds, + bool setup); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct file_operations g_testdev_ops = +{ + .open = testdev_open, /* open */ + .close = testdev_close, /* close */ + .read = testdev_read, /* read */ + .write = testdev_write, /* write */ + .seek = testdev_seek, /* seek */ + .ioctl = testdev_ioctl, /* ioctl */ + .poll = testdev_poll /* poll */ +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS + , + .unlink = NULL /* unlink */ +#endif +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int testdev_open(FAR struct file *filep) +{ + if (filep->f_oflags == (OPEN_FLAG | O_NONBLOCK)) + { + filep->f_pos = 0; + syslog(LOG_INFO, "test open success\n"); + return 0; + } + + syslog(LOG_INFO, "test open fail, %d\n", filep->f_oflags); + return -RET; +} + +static int testdev_close(FAR struct file *filep) +{ + if (filep->f_oflags == (OPEN_FLAG | O_NONBLOCK)) + { + syslog(LOG_INFO, "test close success\n"); + return 0; + } + + syslog(LOG_INFO, "test close fail, %d\n", filep->f_oflags); + return -1; +} + +static ssize_t testdev_read(FAR struct file *filep, FAR char *buffer, + size_t buflen) +{ + size_t i; + + for (i = 0; i < buflen; i++) + { + buffer[i] = 'r'; + } + + syslog(LOG_INFO, "test read success\n"); + return buflen; +} + +static ssize_t testdev_write(FAR struct file *filep, FAR const char *buffer, + size_t buflen) +{ + struct timeval tv; + size_t i; + + for (i = 0; i < buflen; i++) + { + if (buffer[i] != 'w') + { + syslog(LOG_INFO, "test write fail\n"); + return -1; + } + } + + tv.tv_usec = buflen; + tv.tv_sec = 0; + select(0, NULL, NULL, NULL, &tv); + syslog(LOG_INFO, "test write success\n"); + return buflen; +} + +static off_t testdev_seek(FAR struct file *filep, off_t offset, int whence) +{ + if (offset != OFFSET || whence != WHENCE) + { + syslog(LOG_ERR, "test seek fail, offset %ld(except %ld), " + "whence %d(except %d)\n", (long)offset, OFFSET, whence, WHENCE); + return -1; + } + + syslog(LOG_INFO, "test seek success, offset %ld\n", (long)offset); + filep->f_pos = offset; + return offset; +} + +static int testdev_ioctl(FAR struct file *filep, int cmd, unsigned long arg) +{ + int ret = -1; + + switch (cmd) + { + case DIOC_SETKEY: + if (arg != 0) + { + syslog(LOG_ERR, "!!!!!!!!!!!!!!!!!! " + "should not print this log " + "!!!!!!!!!!!!!!!!!!!!!\n"); + ret = -1; + } + break; + case IOCTL: + if (arg == 0) + { + syslog(LOG_ERR, "test ioctl fail, cmd %d(except %d), " + "whence 0(expect not 0)\n", cmd, IOCTL); + ret = -1; + } + else + { + if (*(int *)arg == IOCTL_ARG) + { + ret = RET; + } + else + { + syslog(LOG_ERR, "test ioctl fail, cmd %d(except %d), " + "whence %d(expect %d)\n", + cmd, IOCTL, *(int *)arg, IOCTL_ARG); + ret = -1; + } + } + break; + default: + break; + } + + syslog(LOG_INFO, "test ioctl success\n"); + return ret; +} + +static int testdev_poll(FAR struct file *filep, FAR struct pollfd *fds, + bool setup) +{ + if (!setup) + { + syslog(LOG_INFO, "test poll teardown\n"); + return 0; + } + + syslog(LOG_INFO, "test poll, events %lu\n", (unsigned long)fds->events); + poll_notify(&fds, 1, fds->events); + syslog(LOG_INFO, "test poll notify\n"); + return 0; +} + +static void _register_driver(int mode, char *cpu, char *remotepath, + char *localpath) +{ + int ret; + + switch (mode) + { + case 0: + if (remotepath == NULL) + { + syslog(LOG_ERR, "please set -r\n"); + exit(1); + } + + ret = register_driver(remotepath, &g_testdev_ops, 0666, NULL); + break; +#ifdef CONFIG_DEV_RPMSG + case 1: + if (cpu == NULL || remotepath == NULL || localpath == NULL) + { + syslog(LOG_ERR, "please set -c, -r, -l\n"); + exit(1); + } + + ret = rpmsgdev_register(cpu, remotepath, localpath, 0); + break; +#endif + default: + syslog(LOG_ERR, "-r must between 0 and 1\n"); + exit(1); + break; + } + + if (ret < 0) + { + syslog(LOG_ERR, "register driver failed, ret=%d, errno=%d\n", + ret, errno); + exit(1); + } + + syslog(LOG_INFO, "register driver success\n"); +} + +#ifdef CONFIG_DEV_RPMSG +static int test_open(char *path, int flags) +{ + int fd; + + fd = open(path, flags); + if (fd <= 0) + { + syslog(LOG_ERR, "open %s fail, ret %d, errno %d\n", path, fd, errno); + return -1; + } + + return fd; +} + +static int test_close(int fd) +{ + int ret; + + ret = close(fd); + if (ret < 0) + { + syslog(LOG_ERR, "close fail, ret %d, errno %d\n", ret, errno); + } + + syslog(LOG_INFO, "close success, ret %d\n", ret); + return ret; +} + +static int test_read(int fd, size_t len) +{ + ssize_t rd = 0; + ssize_t ret; + char *buf; + + buf = malloc(len); + if (buf == NULL) + { + syslog(LOG_ERR, "read malloc fail\n"); + return -1; + } + + while (rd < len) + { + ret = read(fd, buf + rd, len - rd); + rd += ret; + if (ret <= 0) + { + break; + } + } + + if (rd != len) + { + syslog(LOG_ERR, "read fail, len %zd, except %zu, errno %d\n", + rd, len, errno); + free(buf); + return -1; + } + + for (ret = 0; ret < len; ret++) + { + if (buf[ret] != 'r') + { + syslog(LOG_ERR, "check data error, ret %d, except 'a'", buf[ret]); + free(buf); + return -1; + } + } + + free(buf); + return 0; +} + +static int test_write(int fd, size_t len) +{ + ssize_t wt = 0; + ssize_t ret; + char *buf; + + buf = malloc(len); + if (buf == NULL) + { + syslog(LOG_ERR, "read malloc fail\n"); + return -1; + } + + for (ret = 0; ret < len; ret++) + { + buf[ret] = 'w'; + } + + while (wt < len) + { + ret = write(fd, buf + wt, len - wt); + wt += ret; + if (ret <= 0) + { + break; + } + } + + if (wt != len) + { + free(buf); + syslog(LOG_ERR, "write fail, len %zd, except %zu, errno %d\n", + wt, len, errno); + return -1; + } + + free(buf); + return 0; +} + +static off_t test_seek(int fd, off_t offset, int whence) +{ + off_t ret; + + ret = lseek(fd, offset, whence); + if (ret == -1) + { + syslog(LOG_ERR, "seek fail, errno %d\n", errno); + } + + syslog(LOG_INFO, "test seek return %ld\n", (long)ret); + return ret; +} + +static int test_ioctl(int fd, unsigned long request, unsigned long arg) +{ + int ret; + + ret = ioctl(fd, request, arg); + if (ret == -1) + { + syslog(LOG_ERR, "ioctl fail, errno %d\n", errno); + } + + syslog(LOG_INFO, "test ioctl return %d\n", ret); + return ret; +} + +static int test_poll(int fd, int event, int timeout) +{ + struct pollfd pfd; + int ret; + + memset(&pfd, 0, sizeof(struct pollfd)); + + pfd.fd = fd; + pfd.events = event; + + ret = poll(&pfd, 1, timeout); + if (ret < 0) + { + syslog(LOG_ERR, "fd %d poll failure: %d\n", fd, errno); + return -1; + } + + return ret; +} + +/* open fail -> open success -> read 100 bytes -> close */ + +static int testcase_1(char *path) +{ + size_t len = 100; + int fd; + + fd = open(path, O_RDONLY); + if (fd != -1 || errno != RET) + { + syslog(LOG_ERR, "open test fail, fs %d (expect -1), " + "errno %d (expect %d)\n", fd, errno, RET); + return -1; + } + + if ((fd = test_open(path, OPEN_FLAG)) <= 0) + { + return -1; + } + + if (test_read(fd, len) != 0) + { + return -1; + } + + return test_close(fd); +} + +/* open -> write 100 bytes -> close */ + +static int testcase_2(char *path) +{ + size_t len = 100; + int fd; + + if ((fd = test_open(path, OPEN_FLAG)) <= 0) + { + return -1; + } + + if (test_write(fd, len) != 0) + { + return -1; + } + + return test_close(fd); +} + +/* open -> seek fail -> seek success -> close */ + +static int testcase_3(char *path) +{ + int fd; + + if ((fd = test_open(path, OPEN_FLAG)) <= 0) + { + return -1; + } + + if (test_seek(fd, 123, SEEK_SET) != -1) + { + return -1; + } + + syslog(LOG_INFO, "seek1 success\n"); + + if (test_seek(fd, OFFSET, WHENCE) != OFFSET) + { + return -1; + } + + syslog(LOG_INFO, "seek2 success\n"); + return test_close(fd); +} + +/* open -> ioctl fail -> ioctl success -> close */ + +static int testcase_4(char *path) +{ + int args = 0; + int fd; + + if ((fd = test_open(path, OPEN_FLAG)) <= 0) + { + return -1; + } + + /* cmd not in rpmsgdev_ioctl_arglen */ + + if (test_ioctl(fd, DIOC_SETKEY, IOCTL_ARG) != -1) + { + return -1; + } + + syslog(LOG_INFO, "ioctl0 success\n"); + + if (test_ioctl(fd, IOCTL, (unsigned long)&args) != -1) + { + return -1; + } + + syslog(LOG_INFO, "ioctl1 success\n"); + + args = IOCTL_ARG; + if (test_ioctl(fd, IOCTL, (unsigned long)&args) != RET) + { + return -1; + } + + syslog(LOG_INFO, "ioctl2 success\n"); + return test_close(fd); +} + +/* open -> poll -> close */ + +static int testcase_5(char *path) +{ + int ret; + int fd; + + if ((fd = test_open(path, OPEN_FLAG)) <= 0) + { + return -1; + } + + /* TODO : change poll notify in the threaad */ + + ret = test_poll(fd, POLLIN, 200); + if (ret != 1) + { + syslog(LOG_ERR, "poll test1 fail, ret %d, expect 0\n", ret); + return -1; + } + + syslog(LOG_INFO, "poll1 success\n"); + + ret = test_poll(fd, POLLIN, 2000); + if (ret != 1) + { + syslog(LOG_ERR, "poll test2 fail, ret %d, expect 1\n", ret); + return -1; + } + + syslog(LOG_INFO, "poll2 success\n"); + + ret = test_poll(fd, POLLOUT, 0); + if (ret != 1) + { + syslog(LOG_ERR, "poll test3 fail, ret %d, expect 1\n", ret); + return -1; + } + + syslog(LOG_INFO, "poll3 success\n"); + return test_close(fd); +} + +/* open -> read 20000 bytes -> close */ + +static int testcase_6(char *path) +{ + size_t len = 20000; + int fd; + + if ((fd = test_open(path, OPEN_FLAG | O_NONBLOCK)) <= 0) + { + return -1; + } + + if (test_read(fd, len) != 0) + { + return -1; + } + + return test_close(fd); +} + +/* open -> write 20000 bytes -> close */ + +static int testcase_7(char *path) +{ + size_t len = 20000; + int fd; + + if ((fd = test_open(path, OPEN_FLAG | O_NONBLOCK)) <= 0) + { + return -1; + } + + if (test_write(fd, len) != 0) + { + return -1; + } + + return test_close(fd); +} + +/* open -> write 10000 bytes -> read 10000 bytes -> .... -> close */ + +static int testcase_8(char *path) +{ + size_t len = 10000; + int cnt = 0; + struct timeval t; + time_t start; + time_t finish; + int fd; + + if ((fd = test_open(path, OPEN_FLAG | O_NONBLOCK)) <= 0) + { + return -1; + } + + gettimeofday(&t, NULL); + start = t.tv_sec; + finish = t.tv_sec; + + while ((finish - start) < hour * 3600) + { + cnt++; + gettimeofday(&t, NULL); + finish = t.tv_sec; + + if (test_write(fd, len) != 0) + { + close(fd); + return -1; + } + + if (test_read(fd, len) != 0) + { + close(fd); + return -1; + } + + if (cnt % 50000) + { + cnt = 0; + syslog(LOG_INFO, "[%s] time start: %ld finish: %ld now: %ld\n", + path, (long)start, (long)(start + hour * 3600), + (long)finish); + } + } + + return test_close(fd); +} +#endif + +static void show_usage(void) +{ + syslog(LOG_WARNING, + "Usage: CMD [-d ] [-c ] " + "[-l ] [-r ] [-h ] [-t ]\n" + "\t\t-d: regist driver, 0 for test driver, 1 for rpmsgdev\n" + "\t\t-c: remote cpu which regists test driver\n" + "\t\t-l: localpath, which means rpmsgdev's name\n" + "\t\t-r: remotepath, which means test driver's name\n" + "\t\t-h: set stability test hours\n" + "\t\t-t: set testcases\n" + "\t\t\t 1 for open fail -> open success -> read 100 bytes -> " + "close\n" + "\t\t\t 2 for open -> write 100 bytes -> close\n" + "\t\t\t 3 for open -> seek fail -> seek success -> close\n" + "\t\t\t 4 for open -> ioctl fail -> ioctl success -> close\n" + "\t\t\t 5 for open -> poll -> close\n" + "\t\t\t 6 for open -> write 20000 bytes -> close\n" + "\t\t\t 7 for open -> read 20000 bytes -> close\n" + "\t\t\t 8 read write stability test\n"); + + exit(1); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int main(int argc, char *argv[]) +{ + int o; + int mode = -1; + char *cpu = NULL; + char *remotepath = NULL; + char *localpath = NULL; + int test __attribute__((unused)) = -1; + int ret __attribute__((unused)); + + if (argc <= 1) + { + show_usage(); + } + + hour = 1; + + while ((o = getopt(argc, argv, "d:c:l:r:t:h:")) != EOF) + { + switch (o) + { + case 'd': + mode = atoi(optarg); + break; + case 'c': + cpu = optarg; + break; + case 'l': + localpath = optarg; + break; + case 'r': + remotepath = optarg; + break; + case 't': + test = atoi(optarg); + break; + case 'h': + hour = atoi(optarg); + break; + default: + show_usage(); + break; + } + } + + if (mode >= 0) + { + _register_driver(mode, cpu, remotepath, localpath); + } + +#ifdef CONFIG_DEV_RPMSG + if (test >= 0) + { + if (localpath == NULL) + { + syslog(LOG_ERR, "please set -l\n"); + exit(1); + } + + switch (test) + { + case 1: + ret = testcase_1(localpath); + break; + case 2: + ret = testcase_2(localpath); + break; + case 3: + ret = testcase_3(localpath); + break; + case 4: + ret = testcase_4(localpath); + break; + case 5: + ret = testcase_5(localpath); + break; + case 6: + ret = testcase_6(localpath); + break; + case 7: + ret = testcase_7(localpath); + break; + case 8: + ret = testcase_8(localpath); + break; + default: + syslog(LOG_ERR, "-t out of range\n"); + exit(1); + break; + } + + if (ret == 0) + { + syslog(LOG_INFO, "TEST PASS\n"); + } + else + { + syslog(LOG_INFO, "TEST FAIL, expect 0 (ret %d)\n", ret); + } + } +#endif + + return 0; +}