Message ID | 20200327134707.4532-3-chrubis@suse.cz |
---|---|
State | Superseded |
Headers | show |
Series | Add needs_devices && basic uart test | expand |
Hi Cyril, > +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 Tested-by: Petr Vorel <pvorel@suse.cz> on all speeds. Reviewed-by: Petr Vorel <pvorel@suse.cz> Thanks for porting serialcheck.c to LTP. Kind regards, Petr
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 > >
Hi! > 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 Thanks for the hint, I had no idea that subset serial port hardware has a loopback test that could be enabled in modem control register which is meant for testing. I will have a closer look tomorrow.
Hi! > > 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 > > Thanks for the hint, I had no idea that subset serial port hardware has > a loopback test that could be enabled in modem control register which is > meant for testing. I will have a closer look tomorrow. If I understand this properly if we set a bit in the modem control register we will test mostly the circuits between CPU and UART controller which is better than nothing, but we are not really testing if the port speed was set correctly since the data are just being copied between registers in the UART controller, so it does not make sense to change the speed in this mode. Or am I mistaken? Also it does not seem to work for me and I've tried with both serial ports on my desktop PC as well as with USB-to-Serial dongle. I can set the bit just fine but loopback does not work.
>If I understand this properly if we set a bit in the modem control >register we will test mostly the circuits between CPU and UART >controller which is better than nothing, but we are not really testing >if the port speed was set correctly since the data are just being copied >between registers in the UART controller, so it does not make sense to >change the speed in this mode. Or am I mistaken? >Also it does not seem to work for me and I've tried with both serial >ports on my desktop PC as well as with USB-to-Serial dongle. I can set >the bit just fine but loopback does not work. In the loopback mode , the data will be transferred in buffer ,and back to CPU by FIFO way. I understand the test flow is CPU->uart Tx-> buffer file->uart Rx->CPU, so it does make sense to the uart driver . BTW, I found the latest seriacheck git is https://github.com/nsekhar/serialcheck.git and I test on my arm64 machine of sprdtream. and it does works. the test log I had send in another patch https://patchwork.ozlabs.org/patch/1264553/ Cyril Hrubis <chrubis@suse.cz> 于2020年4月1日周三 上午2:08写道: > Hi! > > > 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 > > > > Thanks for the hint, I had no idea that subset serial port hardware has > > a loopback test that could be enabled in modem control register which is > > meant for testing. I will have a closer look tomorrow. > > If I understand this properly if we set a bit in the modem control > register we will test mostly the circuits between CPU and UART > controller which is better than nothing, but we are not really testing > if the port speed was set correctly since the data are just being copied > between registers in the UART controller, so it does not make sense to > change the speed in this mode. Or am I mistaken? > > Also it does not seem to work for me and I've tried with both serial > ports on my desktop PC as well as with USB-to-Serial dongle. I can set > the bit just fine but loopback does not work. > > -- > Cyril Hrubis > chrubis@suse.cz >
Hi! > >If I understand this properly if we set a bit in the modem control > >register we will test mostly the circuits between CPU and UART > >controller which is better than nothing, but we are not really testing > >if the port speed was set correctly since the data are just being copied > >between registers in the UART controller, so it does not make sense to > >change the speed in this mode. Or am I mistaken? > > >Also it does not seem to work for me and I've tried with both serial > >ports on my desktop PC as well as with USB-to-Serial dongle. I can set > >the bit just fine but loopback does not work. > > In the loopback mode , the data will be transferred in buffer ,and back to > CPU > by FIFO way. > I understand the test flow is CPU->uart Tx-> buffer file->uart Rx->CPU, > so it does make sense to the uart driver . Indeed but it does not make sense tu run it with a different baud rates, since the data are not transmitted at all. > BTW??? I found the latest seriacheck git is > https://github.com/nsekhar/serialcheck.git > and I test on my arm64 machine of sprdtream. and it does works. > the test log I had send in another patch > https://patchwork.ozlabs.org/patch/1264553/ Unfortunately it does not seem to work on my AMD based desktop at all, my guess is that the loopback bit is silently ignored by the hardware. Which means that we cannot enable the test by default in loopback mode after all.
>Indeed but it does not make sense tu run it with a different baud rates, >since the data are not transmitted at all. The data exchanged between Tx|Rx and buffer have nothing to do with baudrate? I think the baudrate is control Tx|Rx send and receive date rate to|from buffer. >Unfortunately it does not seem to work on my AMD based desktop at all, >my guess is that the loopback bit is silently ignored by the hardware. >Which means that we cannot enable the test by default in loopback mode >after all I will test on my laptop and feedback result today, if it does no work , we should check the uart driver what different between x86 and arm64. Cyril Hrubis <chrubis@suse.cz> 于2020年4月1日周三 下午9:12写道: > Hi! > > >If I understand this properly if we set a bit in the modem control > > >register we will test mostly the circuits between CPU and UART > > >controller which is better than nothing, but we are not really testing > > >if the port speed was set correctly since the data are just being copied > > >between registers in the UART controller, so it does not make sense to > > >change the speed in this mode. Or am I mistaken? > > > > >Also it does not seem to work for me and I've tried with both serial > > >ports on my desktop PC as well as with USB-to-Serial dongle. I can set > > >the bit just fine but loopback does not work. > > > > In the loopback mode , the data will be transferred in buffer ,and back > to > > CPU > > by FIFO way. > > I understand the test flow is CPU->uart Tx-> buffer file->uart Rx->CPU, > > so it does make sense to the uart driver . > > Indeed but it does not make sense tu run it with a different baud rates, > since the data are not transmitted at all. > > > BTW??? I found the latest seriacheck git is > > https://github.com/nsekhar/serialcheck.git > > and I test on my arm64 machine of sprdtream. and it does works. > > the test log I had send in another patch > > https://patchwork.ozlabs.org/patch/1264553/ > > Unfortunately it does not seem to work on my AMD based desktop at all, > my guess is that the loopback bit is silently ignored by the hardware. > > Which means that we cannot enable the test by default in loopback mode > after all. > > -- > Cyril Hrubis > chrubis@suse.cz >
Hi! > >Indeed but it does not make sense tu run it with a different baud rates, > >since the data are not transmitted at all. > The data exchanged between Tx|Rx and buffer have nothing to do with > baudrate? > I think the baudrate is control Tx|Rx send and receive date rate to|from > buffer. That's what I'm not sure about, the documentation says that in loopback mode data written to the port immediatelly appears on the receiving end, which would mean that the uart speed does not matter at all. Can you try a quick test? If you measure the time the test spends writing data in loopback mode for a different uart speeds and they do not differ the uart speed does not matter. > >Unfortunately it does not seem to work on my AMD based desktop at all, > >my guess is that the loopback bit is silently ignored by the hardware. > >Which means that we cannot enable the test by default in loopback mode > >after all > I will test on my laptop and feedback result today, if it does no work , we > should check the uart driver what different between x86 and arm64. I bet that this differs chipset by chipset and I do not think there is anything wrong with the uart driver per se.
Hi I am sorry that when I run the serialcheck on my laptop, there always has some error as follow,which mean I cannot verify the serialcheck on my computer for now. Failed to ioctl(,TIOCGICOUNT,) -- test ttyUSB0 tcgetattr() failed: Input/output error -- test ttyS0 I am trying to find available machine and then run test. Cyril Hrubis <chrubis@suse.cz> 于2020年4月2日周四 下午5:31写道: > Hi! > > >Indeed but it does not make sense tu run it with a different baud rates, > > >since the data are not transmitted at all. > > The data exchanged between Tx|Rx and buffer have nothing to do with > > baudrate? > > I think the baudrate is control Tx|Rx send and receive date rate to|from > > buffer. > > That's what I'm not sure about, the documentation says that in loopback > mode data written to the port immediatelly appears on the receiving end, > which would mean that the uart speed does not matter at all. > > Can you try a quick test? If you measure the time the test spends > writing data in loopback mode for a different uart speeds and they do > not differ the uart speed does not matter. > > > >Unfortunately it does not seem to work on my AMD based desktop at all, > > >my guess is that the loopback bit is silently ignored by the hardware. > > >Which means that we cannot enable the test by default in loopback mode > > >after all > > I will test on my laptop and feedback result today, if it does no work , > we > > should check the uart driver what different between x86 and arm64. > > I bet that this differs chipset by chipset and I do not think there is > anything wrong with the uart driver per se. > > -- > Cyril Hrubis > chrubis@suse.cz >
Hi! > I am sorry that when I run the serialcheck on my laptop, > there always has some error as follow,which mean I cannot > verify the serialcheck on my computer for now. > Failed to ioctl(,TIOCGICOUNT,) -- test ttyUSB0 This one can be ignored, that just means that the counters are not implemented and the statistics are not printed at the test end. > tcgetattr() failed: Input/output error -- test ttyS0 > I am trying to find available machine and then run test. That looks like there is no UART to begin with.
Hi Cyril: Here is my test result, in this test we can make sure that the baudrate is needed in test. the test hope help you 1. test ttyUSB0 in loopback use the same baudrate of Rx and Tx , test pass with 5loop root@Y50:/home/gcx/project/serialcheck# serialcheck -b 115200 -d /dev/ttyUSB0 -f binary -l 5 -m r -k Needed 64 reads 0 writes loops 5 / 5 Failed to ioctl(,TIOCGICOUNT,) gcx@Y50:~/project/serialcheck$ serialcheck -b 115200 -d /dev/ttyUSB0 -f binary -m t -l 5 -k Needed 0 reads 1 writes loops 5 / 5 Failed to ioctl(,TIOCGICOUNT,) 2. test ttyUSB0 in loopback use different baudrate ,Error at the fist loop root@Y50:/home/gcx/project/serialcheck# serialcheck -b 115200 -d /dev/ttyUSB0 -f binary -l 5 -m r -k & [1] 20764 gcx@Y50:~/project/serialcheck$ serialcheck -b 115200 -d /dev/ttyUSB0 -f binary -m t -l 5 -k Needed 0 reads 1 writes loops 5 / 5 Failed to ioctl(,TIOCGICOUNT,) Needed 64 reads 0 writes Oh oh, inconsistency at pos 502 (0x1f6). Original sample: 000001c0: 91 95 eb b6 82 e9 2a e6 16 5a da a3 c2 51 c4 c9 ......*..Z...Q.. 000001d0: c5 51 e1 b7 c9 76 67 d5 09 57 80 77 eb bf 6d d7 .Q...vg..W.w..m. 000001e0: 08 a6 7b fd 52 1b 12 8e f2 50 c1 b7 a7 52 35 39 ..{.R....P...R59 000001f0: 54 d4 50 96 49 55 35 30 33 52 80 89 8e a9 1e a2 T.P.IU503R...... 00000200: 2c a5 0d 1a 26 f6 ea 77 a4 4a b9 69 34 17 cc bc ,...&..w.J.i4... 00000210: e2 6e 0c f9 e1 11 39 9f fd ce 94 9e 19 30 f4 1c .n....9......0.. Received sample: 000001c0: 91 95 eb b6 82 e9 2a e6 16 5a da a3 c2 51 c4 c9 ......*..Z...Q.. 000001d0: c5 51 e1 b7 c9 76 67 d5 09 57 80 77 eb bf 6d d7 .Q...vg..W.w..m. 000001e0: 08 a6 7b fd 52 1b 12 8e f2 50 c1 b7 a7 52 35 39 ..{.R....P...R59 000001f0: 54 d4 50 96 49 55 06 9a 92 01 89 8e a9 1e a2 2c T.P.IU........., 00000200: a5 0d 1a 26 f6 ea 77 a4 4a b9 69 34 17 cc bc e2 ...&..w.J.i4.... 00000210: 6e 0c f9 e1 11 39 9f fd ce 94 9e 19 30 f4 1c 74 n....9......0..t loops 1 / 5 Cyril Hrubis <chrubis@suse.cz> 于2020年4月2日周四 下午7:22写道: > Hi! > > I am sorry that when I run the serialcheck on my laptop, > > there always has some error as follow,which mean I cannot > > verify the serialcheck on my computer for now. > > Failed to ioctl(,TIOCGICOUNT,) -- test ttyUSB0 > > This one can be ignored, that just means that the counters are not > implemented and the statistics are not printed at the test end. > > > tcgetattr() failed: Input/output error -- test ttyS0 > > I am trying to find available machine and then run test. > > That looks like there is no UART to begin with. > > -- > Cyril Hrubis > chrubis@suse.cz >
> Cyril Hrubis <chrubis@suse.cz> 于2020年4月2日周四 下午7:22写道: >> >> Hi! >> > I am sorry that when I run the serialcheck on my laptop, >> > there always has some error as follow,which mean I cannot >> > verify the serialcheck on my computer for now. >> > Failed to ioctl(,TIOCGICOUNT,) -- test ttyUSB0 >> >> This one can be ignored, that just means that the counters are not >> implemented and the statistics are not printed at the test end. >> >> > tcgetattr() failed: Input/output error -- test ttyS0 >> > I am trying to find available machine and then run test. >> >> That looks like there is no UART to begin with. >> >> -- >> Cyril Hrubis >> chrubis@suse.cz Hi Cyril: I would like to know how is it going? lastweek I send the test data . hope it will helpful. In my test result we can found if Rx Tx in different rate, the test will fail. 1. test ttyUSB0 in loopback use the same baudrate of Rx and Tx , test pass with 5loop root@Y50:/home/gcx/project/serialcheck# serialcheck -b 115200 -d /dev/ttyUSB0 -f binary -l 5 -m r -k Needed 64 reads 0 writes loops 5 / 5 Failed to ioctl(,TIOCGICOUNT,) gcx@Y50:~/project/serialcheck$ serialcheck -b 115200 -d /dev/ttyUSB0 -f binary -m t -l 5 -k Needed 0 reads 1 writes loops 5 / 5 Failed to ioctl(,TIOCGICOUNT,) 2. test ttyUSB0 in loopback use different baudrate ,Error at the fist loop root@Y50:/home/gcx/project/serialcheck# serialcheck -b 9600 -d /dev/ttyUSB0 -f binary -l 5 -m r -k & [1] 20764 gcx@Y50:~/project/serialcheck$ serialcheck -b 115200 -d /dev/ttyUSB0 -f binary -m t -l 5 -k Needed 0 reads 1 writes loops 5 / 5 Failed to ioctl(,TIOCGICOUNT,) Needed 64 reads 0 writes Oh oh, inconsistency at pos 502 (0x1f6). Original sample: 000001c0: 91 95 eb b6 82 e9 2a e6 16 5a da a3 c2 51 c4 c9 ......*..Z...Q.. 000001d0: c5 51 e1 b7 c9 76 67 d5 09 57 80 77 eb bf 6d d7 .Q...vg..W.w..m. 000001e0: 08 a6 7b fd 52 1b 12 8e f2 50 c1 b7 a7 52 35 39 ..{.R....P...R59 000001f0: 54 d4 50 96 49 55 35 30 33 52 80 89 8e a9 1e a2 T.P.IU503R...... 00000200: 2c a5 0d 1a 26 f6 ea 77 a4 4a b9 69 34 17 cc bc ,...&..w.J.i4... 00000210: e2 6e 0c f9 e1 11 39 9f fd ce 94 9e 19 30 f4 1c .n....9......0.. Received sample: 000001c0: 91 95 eb b6 82 e9 2a e6 16 5a da a3 c2 51 c4 c9 ......*..Z...Q.. 000001d0: c5 51 e1 b7 c9 76 67 d5 09 57 80 77 eb bf 6d d7 .Q...vg..W.w..m. 000001e0: 08 a6 7b fd 52 1b 12 8e f2 50 c1 b7 a7 52 35 39 ..{.R....P...R59 000001f0: 54 d4 50 96 49 55 06 9a 92 01 89 8e a9 1e a2 2c T.P.IU........., 00000200: a5 0d 1a 26 f6 ea 77 a4 4a b9 69 34 17 cc bc e2 ...&..w.J.i4.... 00000210: 6e 0c f9 e1 11 39 9f fd ce 94 9e 19 30 f4 1c 74 n....9......0..t loops 1 / 5
Hi!
> I would like to know how is it going?
I had a time off and was planting plants in my garden, I will get back
to the uart ASAP, but then we have a public holiday on Friday and Monday
so I may not manage to do much until next week.
> I had a time off and was planting plants in my garden, I will get back > to the uart ASAP, but then we have a public holiday on Friday and Monday > so I may not manage to do much until next week. > > -- > Cyril Hrubis > chrubis@suse.cz Hi Cyril: I had study the ltp execution framework and found that the LTP detection device is not suitable for device driver test at present. Like the UART test, I want have a auto-detect way to find the device needed to test in the current running LtP machines. Now I have test the uart case in several sprdtream Soc these board have different /dev/tty* device。and I nedd run in CI manual export device puzzled me to do the auto-test-job. moreover I wil porting other device driver testcases in the future. So can We expand the LTP detection ?
Hi! > I had study the ltp execution framework and found that the LTP > detection device is > not suitable for device driver test at present. > Like the UART test, I want have a auto-detect way to find the device > needed to test > in the current running LtP machines. > Now I have test the uart case in several sprdtream Soc > these board have different /dev/tty* device???and I nedd run in CI > manual export device puzzled me to do the auto-test-job. > moreover I wil porting other device driver testcases in the future. > So can We expand the LTP detection ? Sorry I haven't managed to work on this before it was time for LTP release. I will resume my work on this once LTP is released. My generall idea is that the LTP framework will get a path to a directory with scripts that would be evaluated at test runtime and will return list of devices to test. These scripts will have to be supplied by the user as managing lab hardware is out of scope of the LTP framework.
Cyril Hrubis <chrubis@suse.cz> 于2020年5月11日周一 下午5:37写道: > > Hi! > > I had study the ltp execution framework and found that the LTP > > detection device is > > not suitable for device driver test at present. > > Like the UART test, I want have a auto-detect way to find the device > > needed to test > > in the current running LtP machines. > > Now I have test the uart case in several sprdtream Soc > > these board have different /dev/tty* device???and I nedd run in CI > > manual export device puzzled me to do the auto-test-job. > > moreover I wil porting other device driver testcases in the future. > > So can We expand the LTP detection ? > > Sorry I haven't managed to work on this before it was time for LTP > release. I will resume my work on this once LTP is released. > > My generall idea is that the LTP framework will get a path to a > directory with scripts that would be evaluated at test runtime and will > return list of devices to test. These scripts will have to be supplied > by the user as managing lab hardware is out of scope of the LTP > framework. > > -- > Cyril Hrubis > chrubis@suse.cz Thank you for your guidance,In the Next time, I will try to fork a process in serialcheck.c So it can be test Tx & Rx at one time.And then to develop the detection scripts if hava a time I'll keep you posted on the status.
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, +};
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