<div dir="ltr">Hi Cyril:<div>Thank you porting the serialcheck.c into LTP</div><div>I am sorry to find the serialcheck have not LOOPBACK mode support</div><div>the LOOPBACK mode is a better test than HW flow , because most machine's uart have not connect the Rx & TX</div><div>in LOOPBACK mode. we test the uart port directly, So we can test one uart port Rx and Tx functions at the same time .</div><div>here is the diff serialcheck with loopback patch</div><div>So I'd prefer use loopback mode test the uart in case.</div><div><h3 class="gmail-iw" style="overflow:hidden;white-space:nowrap;font-size:0.75rem;font-weight:inherit;margin:inherit;text-overflow:ellipsis;font-family:Roboto,RobotoDraft,Helvetica,Arial,sans-serif;letter-spacing:0.3px;color:rgb(95,99,104);line-height:20px">$ diff serialcheck.c serialcheck-with-loopback.c<br>14a15,16<br>> #define TIOCM_LOOP 0x8000<br>><br>42a45<br>> unsigned char loopback;<br>53a57<br>> {"loopback", 'k', NULL, 0, "loopback mode", 0},<br>69a74<br>> go->loopback = 0;<br>115a121,123<br>> case 'k':<br>> go->loopback = 1;<br>> break;<br>316c324<br>< ret = poll(&pfd, 1, 10 * 1000);<br>---<br>> ret = poll(&pfd, 1, 100 * 1000);<br>421a430<br>> unsigned int mcr;<br>489a499,511<br>> if (opts.loopback) {<br>> ret = ioctl(fd, TIOCMGET, &mcr);<br>> if (ret < 0)<br>> die("mcr get failed: %m\n");<br>><br>> mcr |= TIOCM_LOOP;<br>><br>> ret = ioctl(fd, TIOCMSET, &mcr);<br>> if (ret < 0)<br>> die ("mcr set failed: %m\n");<br>><br>> }<br>><br>514a537,542<br>> if(opts.loopback){<br>> mcr &= ~(TIOCM_LOOP);<br>> ret = ioctl(fd,TIOCMSET,&mcr);<br>> }<br>> if(ret)<br>> printf("disabling loopback failed:%m\n");<br></h3></div><div> </div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">Cyril Hrubis <<a href="mailto:chrubis@suse.cz">chrubis@suse.cz</a>> 于2020年3月27日周五 下午9:47写道:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Signed-off-by: Cyril Hrubis <<a href="mailto:chrubis@suse.cz" target="_blank">chrubis@suse.cz</a>><br>
---<br>
runtest/kernel_misc | 5 +<br>
.../kernel/device-drivers/uart/.gitignore | 1 +<br>
testcases/kernel/device-drivers/uart/Makefile | 4 +<br>
testcases/kernel/device-drivers/uart/uart01.c | 522 ++++++++++++++++++<br>
4 files changed, 532 insertions(+)<br>
create mode 100644 testcases/kernel/device-drivers/uart/.gitignore<br>
create mode 100644 testcases/kernel/device-drivers/uart/Makefile<br>
create mode 100644 testcases/kernel/device-drivers/uart/uart01.c<br>
<br>
diff --git a/runtest/kernel_misc b/runtest/kernel_misc<br>
index 7937c7bbf..a7f1d9b56 100644<br>
--- a/runtest/kernel_misc<br>
+++ b/runtest/kernel_misc<br>
@@ -13,3 +13,8 @@ zram01 zram01.sh<br>
zram02 zram02.sh<br>
zram03 zram03<br>
umip_basic_test umip_basic_test<br>
+uart01_9600 uart01 -b 9600<br>
+uart01_19200 uart01 -b 19200<br>
+uart01_38400 uart01 -b 38400<br>
+uart01_57600 uart01 -b 57600<br>
+uart01_115200 uart01 -b 115200<br>
diff --git a/testcases/kernel/device-drivers/uart/.gitignore b/testcases/kernel/device-drivers/uart/.gitignore<br>
new file mode 100644<br>
index 000000000..9333e8db9<br>
--- /dev/null<br>
+++ b/testcases/kernel/device-drivers/uart/.gitignore<br>
@@ -0,0 +1 @@<br>
+uart01<br>
diff --git a/testcases/kernel/device-drivers/uart/Makefile b/testcases/kernel/device-drivers/uart/Makefile<br>
new file mode 100644<br>
index 000000000..1c90e5cd6<br>
--- /dev/null<br>
+++ b/testcases/kernel/device-drivers/uart/Makefile<br>
@@ -0,0 +1,4 @@<br>
+<br>
+top_srcdir ?= ../../../..<br>
+include $(top_srcdir)/include/mk/<a href="http://testcases.mk" rel="noreferrer" target="_blank">testcases.mk</a><br>
+include $(top_srcdir)/include/mk/<a href="http://generic_leaf_target.mk" rel="noreferrer" target="_blank">generic_leaf_target.mk</a><br>
diff --git a/testcases/kernel/device-drivers/uart/uart01.c b/testcases/kernel/device-drivers/uart/uart01.c<br>
new file mode 100644<br>
index 000000000..4647c55e3<br>
--- /dev/null<br>
+++ b/testcases/kernel/device-drivers/uart/uart01.c<br>
@@ -0,0 +1,522 @@<br>
+// SPDX-License-Identifier: GPL-2.0-only<br>
+<br>
+/*<br>
+ Copyright (c) 2014 Sebastian Andrzej Siewior <<a href="mailto:bigeasy@linutronix.de" target="_blank">bigeasy@linutronix.de</a>><br>
+ Copyright (c) 2020 Cyril Hrubis <<a href="mailto:chrubis@suse.cz" target="_blank">chrubis@suse.cz</a>><br>
+<br>
+ */<br>
+<br>
+#include <stdio.h><br>
+#include <ctype.h><br>
+#include <string.h><br>
+#include <stdlib.h><br>
+#include <sys/stat.h><br>
+#include <fcntl.h><br>
+#include <termios.h><br>
+#include <stdarg.h><br>
+#include <unistd.h><br>
+#include <sys/mman.h><br>
+#include <stdint.h><br>
+#include <poll.h><br>
+#include <sys/ioctl.h><br>
+#include <linux/serial.h><br>
+<br>
+#include "tst_test.h"<br>
+<br>
+static const char hex_asc[] = "0123456789abcdef";<br>
+#define hex_asc_lo(x) hex_asc[((x) & 0x0f)]<br>
+#define hex_asc_hi(x) hex_asc[((x) & 0xf0) >> 4]<br>
+<br>
+struct g_opt {<br>
+ char *uart_dev;<br>
+ char *file_trans;<br>
+ int baud_rate;<br>
+ unsigned int loops;<br>
+ unsigned char hwflow;<br>
+ unsigned char do_termios;<br>
+#define MODE_TX_ONLY (1 << 0)<br>
+#define MODE_RX_ONLY (1 << 1)<br>
+#define MODE_DUPLEX (MODE_TX_ONLY | MODE_RX_ONLY)<br>
+ unsigned int mode;<br>
+ unsigned char *cmp_buff;<br>
+};<br>
+<br>
+static int vscnprintf(char *buf, size_t size, const char *fmt, va_list args)<br>
+{<br>
+ int i;<br>
+<br>
+ i = vsnprintf(buf, size, fmt, args);<br>
+<br>
+ if (i < (int)size)<br>
+ return i;<br>
+ if (size != 0)<br>
+ return size - 1;<br>
+ return 0;<br>
+}<br>
+<br>
+static int scnprintf(char *buf, size_t size, const char *fmt, ...)<br>
+{<br>
+ va_list args;<br>
+ int i;<br>
+<br>
+ va_start(args, fmt);<br>
+ i = vscnprintf(buf, size, fmt, args);<br>
+ va_end(args);<br>
+<br>
+ return i;<br>
+}<br>
+<br>
+<br>
+static void hex_dump_to_buffer(const void *buf, int len, int rowsize,<br>
+ int groupsize, char *linebuf, int linebuflen, int ascii)<br>
+{<br>
+ const uint8_t *ptr = buf;<br>
+ uint8_t ch;<br>
+ int j, lx = 0;<br>
+ int ascii_column;<br>
+<br>
+ if (rowsize != 16 && rowsize != 32)<br>
+ rowsize = 16;<br>
+<br>
+ if (!len)<br>
+ goto nil;<br>
+ if (len > rowsize) /* limit to one line at a time */<br>
+ len = rowsize;<br>
+ if ((len % groupsize) != 0) /* no mixed size output */<br>
+ groupsize = 1;<br>
+<br>
+ switch (groupsize) {<br>
+ case 8: {<br>
+ const uint64_t *ptr8 = buf;<br>
+ int ngroups = len / groupsize;<br>
+<br>
+ for (j = 0; j < ngroups; j++)<br>
+ lx += scnprintf(linebuf + lx, linebuflen - lx,<br>
+ "%s%16.16llx", j ? " " : "",<br>
+ (unsigned long long)*(ptr8 + j));<br>
+ ascii_column = 17 * ngroups + 2;<br>
+ break;<br>
+ }<br>
+<br>
+ case 4: {<br>
+ const uint32_t *ptr4 = buf;<br>
+ int ngroups = len / groupsize;<br>
+<br>
+ for (j = 0; j < ngroups; j++)<br>
+ lx += scnprintf(linebuf + lx, linebuflen - lx,<br>
+ "%s%8.8x", j ? " " : "", *(ptr4 + j));<br>
+ ascii_column = 9 * ngroups + 2;<br>
+ break;<br>
+ }<br>
+<br>
+ case 2: {<br>
+ const uint16_t *ptr2 = buf;<br>
+ int ngroups = len / groupsize;<br>
+<br>
+ for (j = 0; j < ngroups; j++)<br>
+ lx += scnprintf(linebuf + lx, linebuflen - lx,<br>
+ "%s%4.4x", j ? " " : "", *(ptr2 + j));<br>
+ ascii_column = 5 * ngroups + 2;<br>
+ break;<br>
+ }<br>
+<br>
+ default:<br>
+ for (j = 0; (j < len) && (lx + 3) <= linebuflen; j++) {<br>
+ ch = ptr[j];<br>
+ linebuf[lx++] = hex_asc_hi(ch);<br>
+ linebuf[lx++] = hex_asc_lo(ch);<br>
+ linebuf[lx++] = ' ';<br>
+ if (j == 7)<br>
+ linebuf[lx++] = ' ';<br>
+ }<br>
+ if (j)<br>
+ lx--;<br>
+<br>
+ ascii_column = 3 * rowsize + 2 + 2;<br>
+ break;<br>
+ }<br>
+ if (!ascii)<br>
+ goto nil;<br>
+<br>
+ while (lx < (linebuflen - 1) && lx < (ascii_column - 1))<br>
+ linebuf[lx++] = ' ';<br>
+ for (j = 0; (j < len) && (lx + 2) < linebuflen; j++) {<br>
+ ch = ptr[j];<br>
+ linebuf[lx++] = (isascii(ch) && isprint(ch)) ? ch : '.';<br>
+ }<br>
+nil:<br>
+ linebuf[lx++] = '\0';<br>
+}<br>
+<br>
+static void print_hex_dump(const void *buf, int len, int offset)<br>
+{<br>
+ const uint8_t *ptr = buf;<br>
+ int i, linelen, remaining = len;<br>
+ char linebuf[32 * 3 + 2 + 32 + 2 + 1];<br>
+ int rowsize = 16;<br>
+ int groupsize = 1;<br>
+<br>
+ if (rowsize != 16 && rowsize != 32)<br>
+ rowsize = 16;<br>
+<br>
+ for (i = 0; i < len; i += rowsize) {<br>
+ linelen = MIN(remaining, rowsize);<br>
+ remaining -= rowsize;<br>
+<br>
+ hex_dump_to_buffer(ptr + i, linelen, rowsize, groupsize,<br>
+ linebuf, sizeof(linebuf), 1);<br>
+<br>
+ printf("%.8x: %s\n", i + offset, linebuf);<br>
+ }<br>
+}<br>
+<br>
+static int stress_test_uart_once(struct g_opt *opts, int fd, unsigned char *data,<br>
+ off_t data_len)<br>
+{<br>
+ unsigned char *cmp_data = opts->cmp_buff;<br>
+ ssize_t size;<br>
+ int wait_rx;<br>
+ int wait_tx;<br>
+ ssize_t progress_rx = 0;<br>
+ ssize_t progress_tx = 0;<br>
+ unsigned int reads = 0;<br>
+ unsigned int writes = 0;<br>
+<br>
+ do {<br>
+ struct pollfd pfd = {<br>
+ .fd = fd,<br>
+ };<br>
+ int ret;<br>
+<br>
+ if (opts->mode & MODE_RX_ONLY && progress_rx < data_len) {<br>
+ pfd.events |= POLLIN;<br>
+ wait_rx = 1;<br>
+ } else {<br>
+ wait_rx = 0;<br>
+ }<br>
+<br>
+ if (opts->mode & MODE_TX_ONLY && progress_tx < data_len) {<br>
+ pfd.events |= POLLOUT;<br>
+ wait_tx = 1;<br>
+ } else {<br>
+ wait_tx = 0;<br>
+ }<br>
+<br>
+ ret = poll(&pfd, 1, 10 * 1000);<br>
+ if (ret == 0) {<br>
+ tst_res(TFAIL, "timeout, RX/TX: %zd/%zd\n", progress_rx, progress_tx);<br>
+ break;<br>
+ }<br>
+ if (ret < 0) {<br>
+ tst_res(TFAIL | TERRNO, "poll() failed");<br>
+ return 1;<br>
+ }<br>
+<br>
+ if (pfd.revents & POLLIN) {<br>
+ size = read(fd, cmp_data + progress_rx, data_len - progress_rx);<br>
+ if (size < 0) {<br>
+ tst_res(TFAIL | TERRNO, "read() failed");<br>
+ return 1;<br>
+ }<br>
+ reads++;<br>
+ progress_rx += size;<br>
+ if (progress_rx >= data_len)<br>
+ wait_rx = 0;<br>
+ }<br>
+<br>
+ if (pfd.revents & POLLOUT) {<br>
+<br>
+ size = write(fd, data + progress_tx, data_len - progress_tx);<br>
+ if (size < 0) {<br>
+ tst_res(TFAIL | TERRNO, "write() failed");<br>
+ return 1;<br>
+ }<br>
+ writes++;<br>
+ progress_tx += size;<br>
+ if (progress_tx >= data_len)<br>
+ wait_tx = 0;<br>
+ }<br>
+ } while (wait_rx || wait_tx);<br>
+<br>
+ tst_res(TINFO, "Needed %u reads %u writes ", reads, writes);<br>
+<br>
+ if (opts->mode & MODE_RX_ONLY) {<br>
+ unsigned int i;<br>
+ int found = 0;<br>
+ unsigned int min_pos;<br>
+ unsigned int max_pos;<br>
+<br>
+ if (!memcmp(data, cmp_data, data_len)) {<br>
+ tst_res(TPASS, "RX passed");<br>
+ return 0;<br>
+ }<br>
+<br>
+ for (i = 0; i < data_len && !found; i++) {<br>
+ if (data[i] != cmp_data[i]) {<br>
+ found = 1;<br>
+ break;<br>
+ }<br>
+ }<br>
+<br>
+ if (!found) {<br>
+ tst_res(TFAIL, "memcmp() didn't match but manual cmp did");<br>
+ return 1;<br>
+ }<br>
+<br>
+ max_pos = (i & ~0xfULL) + 16 * 3;<br>
+ if (max_pos > data_len)<br>
+ max_pos = data_len;<br>
+<br>
+ min_pos = i & ~0xfULL;<br>
+ if (min_pos > 16 * 3)<br>
+ min_pos -= 16 * 3;<br>
+ else<br>
+ min_pos = 0;<br>
+<br>
+ tst_res(TFAIL, "Oh oh, inconsistency at pos %d (0x%x)", i, i);<br>
+<br>
+ printf("\nOriginal sample:\n");<br>
+ print_hex_dump(data + min_pos, max_pos - min_pos, min_pos);<br>
+<br>
+ printf("\nReceived sample:\n");<br>
+ print_hex_dump(cmp_data + min_pos, max_pos - min_pos, min_pos);<br>
+ return 1;<br>
+ }<br>
+<br>
+ if (opts->mode & MODE_TX_ONLY)<br>
+ tst_res(TPASS, "TX passed");<br>
+<br>
+ return 0;<br>
+}<br>
+<br>
+static int stress_test_uart(struct g_opt *opts, int fd, unsigned char *data, off_t data_len)<br>
+{<br>
+ unsigned int loops = 0;<br>
+ int status;<br>
+<br>
+ opts->cmp_buff = SAFE_MALLOC(data_len);<br>
+ memset(opts->cmp_buff, 0, data_len);<br>
+<br>
+ do {<br>
+ status = stress_test_uart_once(opts, fd, data, data_len);<br>
+ memset(opts->cmp_buff, 0, data_len);<br>
+ } while (++loops < opts->loops && !status);<br>
+<br>
+ free(opts->cmp_buff);<br>
+<br>
+ return status;<br>
+}<br>
+<br>
+static int setup_uart(struct g_opt *opts, int open_mode, struct termios *old_term)<br>
+{<br>
+ struct termios new_term;<br>
+ int fd;<br>
+ int ret;<br>
+<br>
+ tst_res(TINFO, "Setting up %s speed %u hwflow=%u",<br>
+ opts->uart_dev, opts->baud_rate, opts->hwflow);<br>
+<br>
+ fd = SAFE_OPEN(opts->uart_dev, open_mode | O_NONBLOCK);<br>
+<br>
+ ret = tcgetattr(fd, old_term);<br>
+ if (ret < 0)<br>
+ tst_brk(TBROK, "tcgetattr() failed: %m\n");<br>
+<br>
+ new_term = *old_term;<br>
+<br>
+ /* or c_cflag |= BOTHER and c_ospeed for any speed */<br>
+ ret = cfsetspeed(&new_term, opts->baud_rate);<br>
+ if (ret < 0)<br>
+ tst_brk(TBROK, "cfsetspeed(, %u) failed %m\n", opts->baud_rate);<br>
+ cfmakeraw(&new_term);<br>
+ new_term.c_cflag |= CREAD;<br>
+ if (opts->hwflow)<br>
+ new_term.c_cflag |= CRTSCTS;<br>
+ else<br>
+ new_term.c_cflag &= ~CRTSCTS;<br>
+ new_term.c_cc[VMIN] = 64;<br>
+ new_term.c_cc[VTIME] = 8;<br>
+<br>
+ ret = tcsetattr(fd, TCSANOW, &new_term);<br>
+ if (ret < 0)<br>
+ tst_brk(TBROK, "tcsetattr failed: %m\n");<br>
+<br>
+ if (opts->do_termios) {<br>
+ ret = tcflush(fd, TCIFLUSH);<br>
+ if (ret < 0)<br>
+ tst_brk(TBROK, "tcflush failed: %m\n");<br>
+ }<br>
+<br>
+ ret = fcntl(fd, F_SETFL, 0);<br>
+ if (ret)<br>
+ printf("Failed to remove nonblock mode\n");<br>
+<br>
+ return fd;<br>
+}<br>
+<br>
+static void restore_uart(int fd, struct termios *old_term)<br>
+{<br>
+ int ret = tcsetattr(fd, TCSAFLUSH, old_term);<br>
+ if (ret)<br>
+ printf("tcsetattr() of old ones failed: %m\n");<br>
+}<br>
+<br>
+static void print_counters(const char *prefix,<br>
+ struct serial_icounter_struct *old,<br>
+ struct serial_icounter_struct *new)<br>
+{<br>
+#define CNT(x) (new->x - old->x)<br>
+ printf("%scts: %d dsr: %d rng: %d dcd: %d rx: %d tx: %d "<br>
+ "frame %d ovr %d par: %d brk: %d buf_ovrr: %d\n", prefix,<br>
+ CNT(cts), CNT(dsr), CNT(rng), CNT(dcd), CNT(rx),<br>
+ CNT(tx), CNT(frame), CNT(overrun), CNT(parity),<br>
+ CNT(brk), CNT(buf_overrun));<br>
+#undef CNT<br>
+}<br>
+<br>
+static struct g_opt opts = {<br>
+ .baud_rate = 115200,<br>
+ .loops = 1,<br>
+ .do_termios = 1,<br>
+};<br>
+<br>
+static char *uart_rx;<br>
+static char *uart_tx;<br>
+<br>
+unsigned char *data;<br>
+static long data_len;<br>
+<br>
+void run(void)<br>
+{<br>
+ struct serial_icounter_struct old_counters;<br>
+ struct serial_icounter_struct new_counters;<br>
+ int ret, fd_rx, fd_tx;<br>
+ struct termios old_term_rx, old_term_tx;<br>
+<br>
+ struct g_opt opts_in = opts;<br>
+ struct g_opt opts_out = opts;<br>
+<br>
+ opts_in.uart_dev = uart_rx;<br>
+ opts_out.uart_dev = uart_tx;<br>
+<br>
+ opts_in.mode = MODE_RX_ONLY;<br>
+ opts_out.mode = MODE_TX_ONLY;<br>
+<br>
+ fd_rx = setup_uart(&opts_in, O_RDONLY, &old_term_rx);<br>
+<br>
+ if (!strcmp(uart_rx, uart_tx))<br>
+ fd_tx = SAFE_OPEN(uart_tx, O_WRONLY);<br>
+ else<br>
+ fd_tx = setup_uart(&opts_out, O_WRONLY, &old_term_tx);<br>
+<br>
+ if (!SAFE_FORK()) {<br>
+ ioctl(fd_rx, TIOCGICOUNT, &old_counters);<br>
+ stress_test_uart(&opts_in, fd_rx, data, data_len);<br>
+ ret = ioctl(fd_rx, TIOCGICOUNT, &new_counters);<br>
+ if (ret > 0)<br>
+ print_counters("RX:", &old_counters, &new_counters);<br>
+ exit(0);<br>
+ }<br>
+<br>
+ if (!SAFE_FORK()) {<br>
+ ioctl(fd_tx, TIOCGICOUNT, &old_counters);<br>
+ stress_test_uart(&opts_out, fd_tx, data, data_len);<br>
+ ret = ioctl(fd_tx, TIOCGICOUNT, &new_counters);<br>
+ if (ret > 0)<br>
+ print_counters("TX:", &old_counters, &new_counters);<br>
+ exit(0);<br>
+ }<br>
+<br>
+ SAFE_WAIT(NULL);<br>
+ SAFE_WAIT(NULL);<br>
+<br>
+ restore_uart(fd_rx, &old_term_rx);<br>
+<br>
+ if (strcmp(uart_rx, uart_tx))<br>
+ restore_uart(fd_tx, &old_term_tx);<br>
+<br>
+ close(fd_rx);<br>
+ close(fd_tx);<br>
+}<br>
+<br>
+static void map_file(const char *fname)<br>
+{<br>
+ struct stat st;<br>
+ int fd;<br>
+<br>
+ fd = SAFE_OPEN(fname, O_RDONLY);<br>
+<br>
+ SAFE_FSTAT(fd, &st);<br>
+<br>
+ data_len = st.st_size;<br>
+<br>
+ data = SAFE_MMAP(NULL, data_len, PROT_READ,<br>
+ MAP_SHARED | MAP_LOCKED | MAP_POPULATE, fd, 0);<br>
+<br>
+ tst_res(TINFO, "Mapped file '%s' size %li bytes", fname, data_len);<br>
+<br>
+ SAFE_CLOSE(fd);<br>
+}<br>
+<br>
+static void map_buffer(long buf_size)<br>
+{<br>
+ size_t i;<br>
+<br>
+ data_len = buf_size;<br>
+<br>
+ data = SAFE_MMAP(NULL, data_len, PROT_READ | PROT_WRITE,<br>
+ MAP_ANONYMOUS | MAP_SHARED | MAP_LOCKED, -1, 0);<br>
+<br>
+ long *p = (void*)data;<br>
+<br>
+ srandom(time(NULL));<br>
+<br>
+ for (i = 0; i < data_len / sizeof(long); i++)<br>
+ p[i] = random();<br>
+<br>
+ tst_res(TINFO, "Mapped anynymous memory size %li bytes", data_len);<br>
+}<br>
+<br>
+static char *baud_rate;<br>
+static char *hwflow;<br>
+static char *fname;<br>
+static char *buf_size;<br>
+<br>
+static void setup(void)<br>
+{<br>
+ long size = 1024;<br>
+<br>
+ if (baud_rate)<br>
+ tst_parse_int(baud_rate, &(opts.baud_rate), 0, INT_MAX);<br>
+<br>
+ if (hwflow)<br>
+ opts.hwflow = 1;<br>
+<br>
+ if (fname && buf_size)<br>
+ tst_brk(TBROK, "Only one of -f and -s could be set!");<br>
+<br>
+ if (buf_size)<br>
+ tst_parse_long(buf_size, &size, 0, LONG_MAX);<br>
+<br>
+ uart_rx = getenv("UART_RX");<br>
+ uart_tx = getenv("UART_TX");<br>
+<br>
+ if (fname)<br>
+ map_file(fname);<br>
+ else<br>
+ map_buffer(size);<br>
+}<br>
+<br>
+static struct tst_test test = {<br>
+ .setup = setup,<br>
+ .test_all = run,<br>
+ .options = (struct tst_option[]) {<br>
+ {"b:", &baud_rate, "-b Baud rate (9600, ...)"},<br>
+ {"w", &hwflow , "-w Enable hwflow (RTS/CTS)"},<br>
+ {"f:", &fname, "-f Binary file for transfers"},<br>
+ {"s:", &buf_size, "-s Binary buffer size"},<br>
+ {}<br>
+ },<br>
+ .needs_devices = (const char *const[]) {"UART_RX", "UART_TX", NULL},<br>
+ .forks_child = 1,<br>
+};<br>
-- <br>
2.24.1<br>
<br>
</blockquote></div>