Xiang Xiao 4e9d907677 testing: Move rpmsgdev to drivers/rpmsgdev
Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
2025-01-26 09:53:02 -03:00

854 lines
18 KiB
C

/****************************************************************************
* 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 <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/time.h>
#include <stdio.h>
#include <errno.h>
#include <syslog.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>
#if defined(__NuttX__) && (defined(CONFIG_DEV_RPMSG) || defined(CONFIG_DEV_RPMSG_SERVER))
#include <nuttx/drivers/rpmsgdev.h>
#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 <regist driver>] [-c <remote cpu>] "
"[-l <localpath>] [-r <remotepath>] [-h <hour>] [-t <test>]\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;
}