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

Cixi Geng gengcixi@gmail.com
Sat Mar 28 09:27:44 CET 2020


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>


More information about the ltp mailing list