[LTP] [PATCH 2/2] device_drivers/uart01: Add uart01 test

Cixi Geng
2020-03-28T08:27:44Z

Hi Cyril:
Thank you porting the serialcheck.c into LTP
I am sorry to find the serialcheck have not LOOPBACK mode support
the LOOPBACK mode is a better test than HW flow , because most machine's
uart have not connect the Rx & TX
in LOOPBACK mode. we test the uart port directly, So we can test one uart
port Rx and Tx functions at the same time .
here is the diff serialcheck with loopback patch
So I'd prefer use loopback mode test the uart in case.
$ diff serialcheck.c serialcheck-with-loopback.c
14a15,16
> #define TIOCM_LOOP 0x8000
>
42a45
> unsigned char loopback;
53a57
> {"loopback", 'k', NULL, 0, "loopback mode", 0},
69a74
> go->loopback = 0;
115a121,123
> case 'k':
> go->loopback = 1;
> break;
316c324
< ret = poll(&pfd, 1, 10 * 1000);
---
> ret = poll(&pfd, 1, 100 * 1000);
421a430
> unsigned int mcr;
489a499,511
> if (opts.loopback) {
> ret = ioctl(fd, TIOCMGET, &mcr);
> if (ret < 0)
> die("mcr get failed: %m\n");
>
> mcr |= TIOCM_LOOP;
>
> ret = ioctl(fd, TIOCMSET, &mcr);
> if (ret < 0)
> die ("mcr set failed: %m\n");
>
> }
>
514a537,542
> if(opts.loopback){
> mcr &= ~(TIOCM_LOOP);
> ret = ioctl(fd,TIOCMSET,&mcr);
> }
> if(ret)
> printf("disabling loopback failed:%m\n");

Cyril Hrubis <chrubis@suse.cz> ?2020?3?27??? ??9:47???

> Signed-off-by: Cyril Hrubis <chrubis@suse.cz>
> ---
> runtest/kernel_misc | 5 +
> .../kernel/device-drivers/uart/.gitignore | 1 +
> testcases/kernel/device-drivers/uart/Makefile | 4 +
> testcases/kernel/device-drivers/uart/uart01.c | 522 ++++++++++++++++++
> 4 files changed, 532 insertions(+)
> create mode 100644 testcases/kernel/device-drivers/uart/.gitignore
> create mode 100644 testcases/kernel/device-drivers/uart/Makefile
> create mode 100644 testcases/kernel/device-drivers/uart/uart01.c
>
> diff --git a/runtest/kernel_misc b/runtest/kernel_misc
> index 7937c7bbf..a7f1d9b56 100644
> --- a/runtest/kernel_misc
> +++ b/runtest/kernel_misc
> @@ -13,3 +13,8 @@ zram01 zram01.sh
> zram02 zram02.sh
> zram03 zram03
> umip_basic_test umip_basic_test
> +uart01_9600 uart01 -b 9600
> +uart01_19200 uart01 -b 19200
> +uart01_38400 uart01 -b 38400
> +uart01_57600 uart01 -b 57600
> +uart01_115200 uart01 -b 115200
> diff --git a/testcases/kernel/device-drivers/uart/.gitignore
> b/testcases/kernel/device-drivers/uart/.gitignore
> new file mode 100644
> index 000000000..9333e8db9
> --- /dev/null
> +++ b/testcases/kernel/device-drivers/uart/.gitignore
> @@ -0,0 +1 @@
> +uart01
> diff --git a/testcases/kernel/device-drivers/uart/Makefile
> b/testcases/kernel/device-drivers/uart/Makefile
> new file mode 100644
> index 000000000..1c90e5cd6
> --- /dev/null
> +++ b/testcases/kernel/device-drivers/uart/Makefile
> @@ -0,0 +1,4 @@
> +
> +top_srcdir ?= ../../../..
> +include $(top_srcdir)/include/mk/testcases.mk
> +include $(top_srcdir)/include/mk/generic_leaf_target.mk
> diff --git a/testcases/kernel/device-drivers/uart/uart01.c
> b/testcases/kernel/device-drivers/uart/uart01.c
> new file mode 100644
> index 000000000..4647c55e3
> --- /dev/null
> +++ b/testcases/kernel/device-drivers/uart/uart01.c
> @@ -0,0 +1,522 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +
> +/*
> + Copyright (c) 2014 Sebastian Andrzej Siewior <bigeasy@linutronix.de>
> + Copyright (c) 2020 Cyril Hrubis <chrubis@suse.cz>
> +
> + */
> +
> +#include <stdio.h>
> +#include <ctype.h>
> +#include <string.h>
> +#include <stdlib.h>
> +#include <sys/stat.h>
> +#include <fcntl.h>
> +#include <termios.h>
> +#include <stdarg.h>
> +#include <unistd.h>
> +#include <sys/mman.h>
> +#include <stdint.h>
> +#include <poll.h>
> +#include <sys/ioctl.h>
> +#include <linux/serial.h>
> +
> +#include "tst_test.h"
> +
> +static const char hex_asc[] = "0123456789abcdef";
> +#define hex_asc_lo(x) hex_asc[((x) & 0x0f)]
> +#define hex_asc_hi(x) hex_asc[((x) & 0xf0) >> 4]
> +
> +struct g_opt {
> + char *uart_dev;
> + char *file_trans;
> + int baud_rate;
> + unsigned int loops;
> + unsigned char hwflow;
> + unsigned char do_termios;
> +#define MODE_TX_ONLY (1 << 0)
> +#define MODE_RX_ONLY (1 << 1)
> +#define MODE_DUPLEX (MODE_TX_ONLY | MODE_RX_ONLY)
> + unsigned int mode;
> + unsigned char *cmp_buff;
> +};
> +
> +static int vscnprintf(char *buf, size_t size, const char *fmt, va_list
> args)
> +{
> + int i;
> +
> + i = vsnprintf(buf, size, fmt, args);
> +
> + if (i < (int)size)
> + return i;
> + if (size != 0)
> + return size - 1;
> + return 0;
> +}
> +
> +static int scnprintf(char *buf, size_t size, const char *fmt, ...)
> +{
> + va_list args;
> + int i;
> +
> + va_start(args, fmt);
> + i = vscnprintf(buf, size, fmt, args);
> + va_end(args);
> +
> + return i;
> +}
> +
> +
> +static void hex_dump_to_buffer(const void *buf, int len, int rowsize,
> + int groupsize, char *linebuf, int linebuflen, int ascii)
> +{
> + const uint8_t *ptr = buf;
> + uint8_t ch;
> + int j, lx = 0;
> + int ascii_column;
> +
> + if (rowsize != 16 && rowsize != 32)
> + rowsize = 16;
> +
> + if (!len)
> + goto nil;
> + if (len > rowsize) /* limit to one line at a time */
> + len = rowsize;
> + if ((len % groupsize) != 0) /* no mixed size output */
> + groupsize = 1;
> +
> + switch (groupsize) {
> + case 8: {
> + const uint64_t *ptr8 = buf;
> + int ngroups = len / groupsize;
> +
> + for (j = 0; j < ngroups; j++)
> + lx += scnprintf(linebuf + lx, linebuflen - lx,
> + "%s%16.16llx", j ? " " : "",
> + (unsigned long long)*(ptr8 + j));
> + ascii_column = 17 * ngroups + 2;
> + break;
> + }
> +
> + case 4: {
> + const uint32_t *ptr4 = buf;
> + int ngroups = len / groupsize;
> +
> + for (j = 0; j < ngroups; j++)
> + lx += scnprintf(linebuf + lx, linebuflen - lx,
> + "%s%8.8x", j ? " " : "", *(ptr4 +
> j));
> + ascii_column = 9 * ngroups + 2;
> + break;
> + }
> +
> + case 2: {
> + const uint16_t *ptr2 = buf;
> + int ngroups = len / groupsize;
> +
> + for (j = 0; j < ngroups; j++)
> + lx += scnprintf(linebuf + lx, linebuflen - lx,
> + "%s%4.4x", j ? " " : "", *(ptr2 +
> j));
> + ascii_column = 5 * ngroups + 2;
> + break;
> + }
> +
> + default:
> + for (j = 0; (j < len) && (lx + 3) <= linebuflen; j++) {
> + ch = ptr[j];
> + linebuf[lx++] = hex_asc_hi(ch);
> + linebuf[lx++] = hex_asc_lo(ch);
> + linebuf[lx++] = ' ';
> + if (j == 7)
> + linebuf[lx++] = ' ';
> + }
> + if (j)
> + lx--;
> +
> + ascii_column = 3 * rowsize + 2 + 2;
> + break;
> + }
> + if (!ascii)
> + goto nil;
> +
> + while (lx < (linebuflen - 1) && lx < (ascii_column - 1))
> + linebuf[lx++] = ' ';
> + for (j = 0; (j < len) && (lx + 2) < linebuflen; j++) {
> + ch = ptr[j];
> + linebuf[lx++] = (isascii(ch) && isprint(ch)) ? ch : '.';
> + }
> +nil:
> + linebuf[lx++] = '\0';
> +}
> +
> +static void print_hex_dump(const void *buf, int len, int offset)
> +{
> + const uint8_t *ptr = buf;
> + int i, linelen, remaining = len;
> + char linebuf[32 * 3 + 2 + 32 + 2 + 1];
> + int rowsize = 16;
> + int groupsize = 1;
> +
> + if (rowsize != 16 && rowsize != 32)
> + rowsize = 16;
> +
> + for (i = 0; i < len; i += rowsize) {
> + linelen = MIN(remaining, rowsize);
> + remaining -= rowsize;
> +
> + hex_dump_to_buffer(ptr + i, linelen, rowsize, groupsize,
> + linebuf, sizeof(linebuf), 1);
> +
> + printf("%.8x: %s\n", i + offset, linebuf);
> + }
> +}
> +
> +static int stress_test_uart_once(struct g_opt *opts, int fd, unsigned
> char *data,
> + off_t data_len)
> +{
> + unsigned char *cmp_data = opts->cmp_buff;
> + ssize_t size;
> + int wait_rx;
> + int wait_tx;
> + ssize_t progress_rx = 0;
> + ssize_t progress_tx = 0;
> + unsigned int reads = 0;
> + unsigned int writes = 0;
> +
> + do {
> + struct pollfd pfd = {
> + .fd = fd,
> + };
> + int ret;
> +
> + if (opts->mode & MODE_RX_ONLY && progress_rx < data_len) {
> + pfd.events |= POLLIN;
> + wait_rx = 1;
> + } else {
> + wait_rx = 0;
> + }
> +
> + if (opts->mode & MODE_TX_ONLY && progress_tx < data_len) {
> + pfd.events |= POLLOUT;
> + wait_tx = 1;
> + } else {
> + wait_tx = 0;
> + }
> +
> + ret = poll(&pfd, 1, 10 * 1000);
> + if (ret == 0) {
> + tst_res(TFAIL, "timeout, RX/TX: %zd/%zd\n",
> progress_rx, progress_tx);
> + break;
> + }
> + if (ret < 0) {
> + tst_res(TFAIL | TERRNO, "poll() failed");
> + return 1;
> + }
> +
> + if (pfd.revents & POLLIN) {
> + size = read(fd, cmp_data + progress_rx, data_len -
> progress_rx);
> + if (size < 0) {
> + tst_res(TFAIL | TERRNO, "read() failed");
> + return 1;
> + }
> + reads++;
> + progress_rx += size;
> + if (progress_rx >= data_len)
> + wait_rx = 0;
> + }
> +
> + if (pfd.revents & POLLOUT) {
> +
> + size = write(fd, data + progress_tx, data_len -
> progress_tx);
> + if (size < 0) {
> + tst_res(TFAIL | TERRNO, "write() failed");
> + return 1;
> + }
> + writes++;
> + progress_tx += size;
> + if (progress_tx >= data_len)
> + wait_tx = 0;
> + }
> + } while (wait_rx || wait_tx);
> +
> + tst_res(TINFO, "Needed %u reads %u writes ", reads, writes);
> +
> + if (opts->mode & MODE_RX_ONLY) {
> + unsigned int i;
> + int found = 0;
> + unsigned int min_pos;
> + unsigned int max_pos;
> +
> + if (!memcmp(data, cmp_data, data_len)) {
> + tst_res(TPASS, "RX passed");
> + return 0;
> + }
> +
> + for (i = 0; i < data_len && !found; i++) {
> + if (data[i] != cmp_data[i]) {
> + found = 1;
> + break;
> + }
> + }
> +
> + if (!found) {
> + tst_res(TFAIL, "memcmp() didn't match but manual
> cmp did");
> + return 1;
> + }
> +
> + max_pos = (i & ~0xfULL) + 16 * 3;
> + if (max_pos > data_len)
> + max_pos = data_len;
> +
> + min_pos = i & ~0xfULL;
> + if (min_pos > 16 * 3)
> + min_pos -= 16 * 3;
> + else
> + min_pos = 0;
> +
> + tst_res(TFAIL, "Oh oh, inconsistency at pos %d (0x%x)", i,
> i);
> +
> + printf("\nOriginal sample:\n");
> + print_hex_dump(data + min_pos, max_pos - min_pos, min_pos);
> +
> + printf("\nReceived sample:\n");
> + print_hex_dump(cmp_data + min_pos, max_pos - min_pos,
> min_pos);
> + return 1;
> + }
> +
> + if (opts->mode & MODE_TX_ONLY)
> + tst_res(TPASS, "TX passed");
> +
> + return 0;
> +}
> +
> +static int stress_test_uart(struct g_opt *opts, int fd, unsigned char
> *data, off_t data_len)
> +{
> + unsigned int loops = 0;
> + int status;
> +
> + opts->cmp_buff = SAFE_MALLOC(data_len);
> + memset(opts->cmp_buff, 0, data_len);
> +
> + do {
> + status = stress_test_uart_once(opts, fd, data, data_len);
> + memset(opts->cmp_buff, 0, data_len);
> + } while (++loops < opts->loops && !status);
> +
> + free(opts->cmp_buff);
> +
> + return status;
> +}
> +
> +static int setup_uart(struct g_opt *opts, int open_mode, struct termios
> *old_term)
> +{
> + struct termios new_term;
> + int fd;
> + int ret;
> +
> + tst_res(TINFO, "Setting up %s speed %u hwflow=%u",
> + opts->uart_dev, opts->baud_rate, opts->hwflow);
> +
> + fd = SAFE_OPEN(opts->uart_dev, open_mode | O_NONBLOCK);
> +
> + ret = tcgetattr(fd, old_term);
> + if (ret < 0)
> + tst_brk(TBROK, "tcgetattr() failed: %m\n");
> +
> + new_term = *old_term;
> +
> + /* or c_cflag |= BOTHER and c_ospeed for any speed */
> + ret = cfsetspeed(&new_term, opts->baud_rate);
> + if (ret < 0)
> + tst_brk(TBROK, "cfsetspeed(, %u) failed %m\n",
> opts->baud_rate);
> + cfmakeraw(&new_term);
> + new_term.c_cflag |= CREAD;
> + if (opts->hwflow)
> + new_term.c_cflag |= CRTSCTS;
> + else
> + new_term.c_cflag &= ~CRTSCTS;
> + new_term.c_cc[VMIN] = 64;
> + new_term.c_cc[VTIME] = 8;
> +
> + ret = tcsetattr(fd, TCSANOW, &new_term);
> + if (ret < 0)
> + tst_brk(TBROK, "tcsetattr failed: %m\n");
> +
> + if (opts->do_termios) {
> + ret = tcflush(fd, TCIFLUSH);
> + if (ret < 0)
> + tst_brk(TBROK, "tcflush failed: %m\n");
> + }
> +
> + ret = fcntl(fd, F_SETFL, 0);
> + if (ret)
> + printf("Failed to remove nonblock mode\n");
> +
> + return fd;
> +}
> +
> +static void restore_uart(int fd, struct termios *old_term)
> +{
> + int ret = tcsetattr(fd, TCSAFLUSH, old_term);
> + if (ret)
> + printf("tcsetattr() of old ones failed: %m\n");
> +}
> +
> +static void print_counters(const char *prefix,
> + struct serial_icounter_struct *old,
> + struct serial_icounter_struct *new)
> +{
> +#define CNT(x) (new->x - old->x)
> + printf("%scts: %d dsr: %d rng: %d dcd: %d rx: %d tx: %d "
> + "frame %d ovr %d par: %d brk: %d buf_ovrr: %d\n", prefix,
> + CNT(cts), CNT(dsr), CNT(rng), CNT(dcd), CNT(rx),
> + CNT(tx), CNT(frame), CNT(overrun), CNT(parity),
> + CNT(brk), CNT(buf_overrun));
> +#undef CNT
> +}
> +
> +static struct g_opt opts = {
> + .baud_rate = 115200,
> + .loops = 1,
> + .do_termios = 1,
> +};
> +
> +static char *uart_rx;
> +static char *uart_tx;
> +
> +unsigned char *data;
> +static long data_len;
> +
> +void run(void)
> +{
> + struct serial_icounter_struct old_counters;
> + struct serial_icounter_struct new_counters;
> + int ret, fd_rx, fd_tx;
> + struct termios old_term_rx, old_term_tx;
> +
> + struct g_opt opts_in = opts;
> + struct g_opt opts_out = opts;
> +
> + opts_in.uart_dev = uart_rx;
> + opts_out.uart_dev = uart_tx;
> +
> + opts_in.mode = MODE_RX_ONLY;
> + opts_out.mode = MODE_TX_ONLY;
> +
> + fd_rx = setup_uart(&opts_in, O_RDONLY, &old_term_rx);
> +
> + if (!strcmp(uart_rx, uart_tx))
> + fd_tx = SAFE_OPEN(uart_tx, O_WRONLY);
> + else
> + fd_tx = setup_uart(&opts_out, O_WRONLY, &old_term_tx);
> +
> + if (!SAFE_FORK()) {
> + ioctl(fd_rx, TIOCGICOUNT, &old_counters);
> + stress_test_uart(&opts_in, fd_rx, data, data_len);
> + ret = ioctl(fd_rx, TIOCGICOUNT, &new_counters);
> + if (ret > 0)
> + print_counters("RX:", &old_counters,
> &new_counters);
> + exit(0);
> + }
> +
> + if (!SAFE_FORK()) {
> + ioctl(fd_tx, TIOCGICOUNT, &old_counters);
> + stress_test_uart(&opts_out, fd_tx, data, data_len);
> + ret = ioctl(fd_tx, TIOCGICOUNT, &new_counters);
> + if (ret > 0)
> + print_counters("TX:", &old_counters,
> &new_counters);
> + exit(0);
> + }
> +
> + SAFE_WAIT(NULL);
> + SAFE_WAIT(NULL);
> +
> + restore_uart(fd_rx, &old_term_rx);
> +
> + if (strcmp(uart_rx, uart_tx))
> + restore_uart(fd_tx, &old_term_tx);
> +
> + close(fd_rx);
> + close(fd_tx);
> +}
> +
> +static void map_file(const char *fname)
> +{
> + struct stat st;
> + int fd;
> +
> + fd = SAFE_OPEN(fname, O_RDONLY);
> +
> + SAFE_FSTAT(fd, &st);
> +
> + data_len = st.st_size;
> +
> + data = SAFE_MMAP(NULL, data_len, PROT_READ,
> + MAP_SHARED | MAP_LOCKED | MAP_POPULATE, fd, 0);
> +
> + tst_res(TINFO, "Mapped file '%s' size %li bytes", fname, data_len);
> +
> + SAFE_CLOSE(fd);
> +}
> +
> +static void map_buffer(long buf_size)
> +{
> + size_t i;
> +
> + data_len = buf_size;
> +
> + data = SAFE_MMAP(NULL, data_len, PROT_READ | PROT_WRITE,
> + MAP_ANONYMOUS | MAP_SHARED | MAP_LOCKED, -1, 0);
> +
> + long *p = (void*)data;
> +
> + srandom(time(NULL));
> +
> + for (i = 0; i < data_len / sizeof(long); i++)
> + p[i] = random();
> +
> + tst_res(TINFO, "Mapped anynymous memory size %li bytes", data_len);
> +}
> +
> +static char *baud_rate;
> +static char *hwflow;
> +static char *fname;
> +static char *buf_size;
> +
> +static void setup(void)
> +{
> + long size = 1024;
> +
> + if (baud_rate)
> + tst_parse_int(baud_rate, &(opts.baud_rate), 0, INT_MAX);
> +
> + if (hwflow)
> + opts.hwflow = 1;
> +
> + if (fname && buf_size)
> + tst_brk(TBROK, "Only one of -f and -s could be set!");
> +
> + if (buf_size)
> + tst_parse_long(buf_size, &size, 0, LONG_MAX);
> +
> + uart_rx = getenv("UART_RX");
> + uart_tx = getenv("UART_TX");
> +
> + if (fname)
> + map_file(fname);
> + else
> + map_buffer(size);
> +}
> +
> +static struct tst_test test = {
> + .setup = setup,
> + .test_all = run,
> + .options = (struct tst_option[]) {
> + {"b:", &baud_rate, "-b Baud rate (9600, ...)"},
> + {"w", &hwflow , "-w Enable hwflow (RTS/CTS)"},
> + {"f:", &fname, "-f Binary file for transfers"},
> + {"s:", &buf_size, "-s Binary buffer size"},
> + {}
> + },
> + .needs_devices = (const char *const[]) {"UART_RX", "UART_TX",
> NULL},
> + .forks_child = 1,
> +};
> --
> 2.24.1
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.linux.it/pipermail/ltp/attachments/20200328/58f85ae9/attachment-0001.htm>