diff --git a/ioctl/Makefile b/ioctl/Makefile new file mode 100644 index 000000000..27ab0472f --- /dev/null +++ b/ioctl/Makefile @@ -0,0 +1 @@ +$(eval $(call add_unity_test, test_ioctl)) diff --git a/ioctl/test.yaml b/ioctl/test.yaml new file mode 100644 index 000000000..f03ec7ec1 --- /dev/null +++ b/ioctl/test.yaml @@ -0,0 +1,7 @@ +test: + type: unity + tests: + - name: unit + execute: test_ioctl + targets: + exclude: [armv7a9-zynq7000-qemu, armv7a9-zynq7000-zedboard] diff --git a/ioctl/test_ioctl.c b/ioctl/test_ioctl.c new file mode 100644 index 000000000..a75b207cf --- /dev/null +++ b/ioctl/test_ioctl.c @@ -0,0 +1,394 @@ +/* + * Phoenix-RTOS + * + * Phoenix-RTOS standard library functions tests + * HEADER: + * - ioctl.h + * TESTED: + * - ioctl() + * + * Copyright 2023 Phoenix Systems + * Author: Adam Debek + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const int32_t exp_flag_val = 0x12345678; +static uint32_t port; +static int file_desc, dev_desc; +static volatile int thread_running = 1; + +typedef uint8_t test_ioctl_buf[64]; + +#define MAX_FAIL 10 +#define PATH_TF "ioctl_testFile" +#define PATH_REG "ioctl_testRegularFile" +#define DEV_IOCTL_TEST "/dev/ioctlTest" +/* T stands for test, do not confuse with tty */ +#define TEST_GRP ('T') + +#define TEST_IOCTL_SIG _IOC(IOC_VOID, TEST_GRP, 0x01, 0) +#define TEST_IOCTL_IN_VAL _IOC(IOC_VOID, TEST_GRP, 0x02, sizeof(int32_t)) +#define TEST_IOCTL_IN _IOC(IOC_IN, TEST_GRP, 0x03, sizeof(int32_t)) +#define TEST_IOCTL_IN_BIG _IOC(IOC_IN, TEST_GRP, 0x04, sizeof(test_ioctl_buf)) +#define TEST_IOCTL_OUT _IOC(IOC_OUT, TEST_GRP, 0x05, sizeof(int32_t)) +#define TEST_IOCTL_OUT_BIG _IOC(IOC_OUT, TEST_GRP, 0x06, sizeof(test_ioctl_buf)) +#define TEST_IOCTL_INOUT _IOC(IOC_INOUT, TEST_GRP, 0x07, sizeof(int32_t)) +#define TEST_IOCTL_INOUT_BIG _IOC(IOC_INOUT, TEST_GRP, 0x08, sizeof(test_ioctl_buf)) + + +static void *thread(void *arg) +{ + msg_t msg; + unsigned long int rid; + int32_t out; + test_ioctl_buf out2; + + while (thread_running) { + if (msgRecv(port, &msg, &rid) < 0) { + continue; + } + + if (msg.type == mtDevCtl) { + unsigned long request; + int err = 0; + + const void *out_data = NULL; + const void *in_data = ioctl_unpack(&msg, &request, NULL); + + if (lseek(file_desc, 0, SEEK_SET) != 0) { + TEST_MESSAGE("lseek failed"); + } + + switch (request) { + case TEST_IOCTL_IN_VAL: + if (write(file_desc, &in_data, sizeof(int32_t)) != sizeof(int32_t)) { + TEST_MESSAGE("write failed in TEST_IOCTL_IN_VAL request"); + } + break; + case TEST_IOCTL_SIG: + if (write(file_desc, &exp_flag_val, sizeof(int32_t)) != sizeof(int32_t)) { + TEST_MESSAGE("write failed in TEST_IOCTL_SIG request"); + } + break; + case TEST_IOCTL_IN: + if (write(file_desc, in_data, sizeof(int32_t)) != sizeof(int32_t)) { + TEST_MESSAGE("write failed in TEST_IOCTL_IN request"); + } + break; + case TEST_IOCTL_IN_BIG: + if (write(file_desc, in_data, sizeof(test_ioctl_buf)) != sizeof(test_ioctl_buf)) { + TEST_MESSAGE("write failed in TEST_IOCTL_IN_BIG request"); + } + break; + case TEST_IOCTL_OUT: + out = 15; + out_data = (const void *)(&out); + break; + case TEST_IOCTL_OUT_BIG: + memset(out2, 5, sizeof(out2)); + out_data = (const void *)(out2); + break; + case TEST_IOCTL_INOUT: + if (write(file_desc, in_data, sizeof(int32_t)) != sizeof(int32_t)) { + TEST_MESSAGE("write failed in TEST_IOCTL_INOUT request"); + } + out = 18; + out_data = (const void *)(&out); + break; + case TEST_IOCTL_INOUT_BIG: + if (write(file_desc, in_data, sizeof(test_ioctl_buf)) != sizeof(test_ioctl_buf)) { + TEST_MESSAGE("write failed in TEST_IOCTL_INOUT_BIG request"); + } + memset(out2, 8, sizeof(out2)); + out_data = (const void *)(out2); + break; + default: + err = -1; + break; + } + ioctl_setResponse(&msg, request, err, out_data); + } + msgRespond(port, &msg, rid); + } + return NULL; +} + + +TEST_GROUP(ioctl); + + +TEST_SETUP(ioctl) +{ +} + +TEST_TEAR_DOWN(ioctl) +{ +} + + +TEST(ioctl, invalid_req) +{ + int ret = ioctl(dev_desc, 0x1, NULL); + TEST_ASSERT_EQUAL_INT(-1, ret); +} + + +TEST(ioctl, regular_file) +{ + /* try regular file, not special device file */ + int fd_reg = open(PATH_REG, O_RDWR | O_CREAT | O_TRUNC, S_IFREG); + TEST_ASSERT_NOT_EQUAL_INT(-1, fd_reg); + errno = 0; + int ret = ioctl(fd_reg, TEST_IOCTL_SIG, NULL); + TEST_ASSERT_EQUAL_INT(-1, ret); + close(fd_reg); + remove(PATH_REG); +} + + +TEST(ioctl, not_valid_fd) +{ + errno = 0; + int ret = ioctl(1234, 0, NULL); + TEST_ASSERT_EQUAL_INT(-1, ret); + TEST_ASSERT_EQUAL_INT(EBADF, errno); +} + + +TEST(ioctl, no_data) +{ + int32_t flag; + int ret; + + /* Send data to driver by value */ + ret = ioctl(dev_desc, TEST_IOCTL_SIG, NULL); + TEST_ASSERT_EQUAL_INT(0, ret); + TEST_ASSERT_EQUAL_INT(0, lseek(file_desc, 0, SEEK_SET)); + TEST_ASSERT_EQUAL_INT(sizeof(int32_t), read(file_desc, &flag, sizeof(int32_t))); + TEST_ASSERT_EQUAL_INT32(exp_flag_val, flag); +} + + +TEST(ioctl, in_val) +{ + int32_t rdata = 0; + int32_t data_in = 14; + int ret; + + /* Send data to driver by value */ + ret = ioctl(dev_desc, TEST_IOCTL_IN_VAL, data_in); + TEST_ASSERT_EQUAL_INT(0, ret); + TEST_ASSERT_EQUAL_INT(0, lseek(file_desc, 0, SEEK_SET)); + TEST_ASSERT_EQUAL_INT(sizeof(int32_t), read(file_desc, &rdata, sizeof(int32_t))); + TEST_ASSERT_EQUAL_INT32(data_in, rdata); +} + + +TEST(ioctl, data_in) +{ + int32_t rdata = 0; + int32_t data_in = 20; + int ret; + + /* Send data to driver by pointer */ + ret = ioctl(dev_desc, TEST_IOCTL_IN, &data_in); + TEST_ASSERT_EQUAL_INT(0, ret); + TEST_ASSERT_EQUAL_INT(0, lseek(file_desc, 0, SEEK_SET)); + TEST_ASSERT_EQUAL_INT(sizeof(int32_t), read(file_desc, &rdata, sizeof(int32_t))); + TEST_ASSERT_EQUAL_INT32(data_in, rdata); +} + + +TEST(ioctl, data_in_big) +{ + /* Big data so that it is not copied inside message */ + test_ioctl_buf data_in; + test_ioctl_buf rdata; + int ret; + + memset(data_in, 3, sizeof(data_in)); + memset(rdata, 0, sizeof(rdata)); + + /* Send data to driver by pointer, data big enough to not be copied by ioctl_pack directly into message */ + ret = ioctl(dev_desc, TEST_IOCTL_IN_BIG, data_in); + TEST_ASSERT_EQUAL_INT(0, ret); + TEST_ASSERT_EQUAL_INT(0, lseek(file_desc, 0, SEEK_SET)); + TEST_ASSERT_EQUAL_INT(sizeof(rdata), read(file_desc, rdata, sizeof(rdata))); + TEST_ASSERT_EQUAL_MEMORY(data_in, rdata, sizeof(rdata)); +} + + +TEST(ioctl, data_out) +{ + int32_t data_out = 0; + int ret; + + /* Get data from driver */ + ret = ioctl(dev_desc, TEST_IOCTL_OUT, &data_out); + TEST_ASSERT_EQUAL_INT(0, ret); + TEST_ASSERT_EQUAL_INT32(15, data_out); +} + + +TEST(ioctl, data_out_big) +{ + /* Big data so that it is not copied inside message */ + test_ioctl_buf exp_data; + test_ioctl_buf data_out; + int ret; + + memset(exp_data, 5, sizeof(exp_data)); + memset(data_out, 0, sizeof(data_out)); + + /* Get data from driver */ + ret = ioctl(dev_desc, TEST_IOCTL_OUT_BIG, data_out); + TEST_ASSERT_EQUAL_INT(0, ret); + TEST_ASSERT_EQUAL_MEMORY(exp_data, data_out, sizeof(exp_data)); +} + + +TEST(ioctl, data_inout) +{ + const int32_t data_in = 17; + int32_t rdata = 0; + int32_t data_inout = data_in; + int ret; + + ret = ioctl(dev_desc, TEST_IOCTL_INOUT, &data_inout); + + /* Get data sent to driver */ + TEST_ASSERT_EQUAL_INT(0, ret); + TEST_ASSERT_EQUAL_INT(0, lseek(file_desc, 0, SEEK_SET)); + TEST_ASSERT_EQUAL_INT(sizeof(int32_t), read(file_desc, &rdata, sizeof(int32_t))); + TEST_ASSERT_EQUAL_INT32(data_in, rdata); + + /* Check data returned from driver */ + TEST_ASSERT_EQUAL_INT32(18, data_inout); +} + + +TEST(ioctl, data_inout_big) +{ + /* Big data so that it is not copied inside message */ + test_ioctl_buf data_inout; + test_ioctl_buf data_in; + test_ioctl_buf data_out; + test_ioctl_buf rdata; + int ret; + + memset(data_in, 7, sizeof(data_in)); + memset(data_out, 8, sizeof(data_out)); + memset(rdata, 0, sizeof(rdata)); + memcpy(data_inout, data_in, sizeof(data_inout)); + + /* Get data sent to driver */ + ret = ioctl(dev_desc, TEST_IOCTL_INOUT_BIG, data_inout); + TEST_ASSERT_EQUAL_INT(0, ret); + TEST_ASSERT_EQUAL_INT(0, lseek(file_desc, 0, SEEK_SET)); + TEST_ASSERT_EQUAL_INT(sizeof(rdata), read(file_desc, rdata, sizeof(rdata))); + TEST_ASSERT_EQUAL_MEMORY(data_in, rdata, sizeof(rdata)); + + /* Check data returned from driver */ + TEST_ASSERT_EQUAL_MEMORY(data_out, data_inout, sizeof(data_out)); +} + + +TEST_GROUP_RUNNER(ioctl) +{ + /* main thread - run test cases */ + RUN_TEST_CASE(ioctl, invalid_req); + RUN_TEST_CASE(ioctl, regular_file); + RUN_TEST_CASE(ioctl, not_valid_fd); + RUN_TEST_CASE(ioctl, no_data); + RUN_TEST_CASE(ioctl, in_val); + RUN_TEST_CASE(ioctl, data_in); + RUN_TEST_CASE(ioctl, data_in_big); + RUN_TEST_CASE(ioctl, data_out); + RUN_TEST_CASE(ioctl, data_out_big); + RUN_TEST_CASE(ioctl, data_inout); + RUN_TEST_CASE(ioctl, data_inout_big); +} + + +void runner(void) +{ + RUN_TEST_GROUP(ioctl); +} + + +int main(int argc, char *argv[]) +{ + static pthread_t tid; + oid_t dev; + int res, n = 0; + + file_desc = open(PATH_TF, O_RDWR | O_CREAT | O_TRUNC, S_IFREG); + if (file_desc < 0) { + fprintf(stderr, "Couldn't open file\n"); + return 1; + } + + res = portCreate(&port); + if (res != EOK) { + fprintf(stderr, "Couldn't create port\n"); + return 1; + } + + dev.port = port; + dev.id = 0; + + res = create_dev(&dev, DEV_IOCTL_TEST); + if (res != EOK) { + fprintf(stderr, "Couldn't create device\n"); + return 1; + } + + res = pthread_create(&tid, NULL, thread, NULL); + if (res != 0) { + portDestroy(port); + fprintf(stderr, "Couldn't create thread\n"); + return 1; + } + + while ((dev_desc = open(DEV_IOCTL_TEST, O_RDWR)) < 0 && n < MAX_FAIL) { + usleep(10000); + n++; + } + + if (n == MAX_FAIL) { + close(file_desc); + remove(PATH_TF); + fprintf(stderr, "Can't open device file\n"); + return 1; + } + + UnityMain(argc, (const char **)argv, runner); + /* Cause thread to terminate */ + thread_running = 0; + pthread_join(tid, NULL); + + portDestroy(port); + close(dev_desc); + close(file_desc); + remove(PATH_TF); + remove(DEV_IOCTL_TEST); + + return 0; +}