/**************************************************************************** * apps/testing/drivers/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 #if defined(__NuttX__) && (defined(CONFIG_DEV_RPMSG) || defined(CONFIG_DEV_RPMSG_SERVER)) #include #endif /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ #ifdef __ANDROID__ #define syslog(l,...) printf(__VA_ARGS__) #endif /* set a random open flag to check whether the open api behave correctly */ #define OPEN_FLAG O_RDWR #define RET ENOENT #define OFFSET 1234l #define WHENCE SEEK_SET #ifdef __NuttX__ #define IOCTL FIONSPACE #define IOCTL_ARG 0x55AA #endif static int hour __attribute__((unused)); /**************************************************************************** * Private Function Prototypes ****************************************************************************/ #ifdef __NuttX__ 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)) == (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)) == (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 = -EINVAL; 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 case 2: #ifdef CONFIG_DEV_RPMSG_SERVER if (cpu == NULL || localpath == NULL) { syslog(LOG_ERR, "please set -c, -l\n"); exit(1); } ret = rpmsgdev_export(cpu, localpath); #else syslog(LOG_WARNING, "feature of case %d not enabled\n", mode); #endif break; default: syslog(LOG_ERR, "-d must between 0 and 2\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"); } #endif #if defined(CONFIG_DEV_RPMSG) || defined(TEST_RPMSGDEV) 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] %d, except 'r'\n", ret, 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; } #ifdef __NuttX__ 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; } #endif 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, fd %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); } #ifdef __NuttX__ /* 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); } #endif /* 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 1\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"); #if 0 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"); #endif 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\n" "\t\t\t 0 for test driver\n" "\t\t\t 1 for rpmsgdev(client register)\n" "\t\t\t 2 for rpmsgdev(server export)\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) { #ifdef __NuttX__ _register_driver(mode, cpu, remotepath, localpath); #endif } #if defined(CONFIG_DEV_RPMSG) || defined(TEST_RPMSGDEV) 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; #ifdef __NuttX__ case 4: ret = testcase_4(localpath); break; #endif 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; }