Message ID | 564F4A95.3010303@hurleysoftware.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Hi Peter, On Fri, 2015-11-20 at 11:30AM -0500, Peter Hurley wrote: > Hi Sören, > > On 11/20/2015 10:28 AM, Sören Brinkmann wrote: > > On Fri, 2015-11-20 at 07:13AM -0500, Peter Hurley wrote: > >> On 11/19/2015 03:02 PM, Soren Brinkmann wrote: > >>> start_tx must start transmitting characters. Regardless of the state of > >>> the circular buffer, always enable the transmitter hardware. > >> > >> Why? > >> > >> Does cdns_uart_stop_tx() actually stop the transmitter so that > >> data remains in the transmitter? > > > > Well, I saw my system freezing and the cause seemed to be that the UART > > receiver and/or transmitters were disabled while the system was trying > > to print. Hence, I started questioning all locations touching the > > transmitter/receiver enable. I read the docs in > > https://www.kernel.org/doc/Documentation/serial/driver, which simply > > says "Start transmitting characters." for start_tx(). Hence, I thought, > > this function is probably supposed to just do that and start the > > transmitter. I'll test whether this patch can be dropped. > > I don't think that patch would fix any freeze problems, but restarting > the transmitter even if the circ buffer is empty may be necessary to > push out remaining data when the port is restarted after being stopped. > > IOW, something like > > if (uart_tx_stopped(port)) > return; > > .... > > > if (uart_circ_empty(&port->state->xmit) > return; Thanks! I'll change the patch accordingly. > > > Below is a (work-in-progress) serial driver validation test for flow > control handling (it may need some tuning for slow line speeds). > Usual caveats apply. Takes ~40secs @ 115200. I'll try to get that running on my system. Thanks, Sören
On 11/20/2015 11:58 AM, Sören Brinkmann wrote: > On Fri, 2015-11-20 at 11:30AM -0500, Peter Hurley wrote: >> On 11/20/2015 10:28 AM, Sören Brinkmann wrote: >>> On Fri, 2015-11-20 at 07:13AM -0500, Peter Hurley wrote: >>>> On 11/19/2015 03:02 PM, Soren Brinkmann wrote: >>>>> start_tx must start transmitting characters. Regardless of the state of >>>>> the circular buffer, always enable the transmitter hardware. >>>> >>>> Why? >>>> >>>> Does cdns_uart_stop_tx() actually stop the transmitter so that >>>> data remains in the transmitter? >>> >>> Well, I saw my system freezing and the cause seemed to be that the UART >>> receiver and/or transmitters were disabled while the system was trying >>> to print. Hence, I started questioning all locations touching the >>> transmitter/receiver enable. I read the docs in >>> https://www.kernel.org/doc/Documentation/serial/driver, which simply >>> says "Start transmitting characters." for start_tx(). Hence, I thought, >>> this function is probably supposed to just do that and start the >>> transmitter. I'll test whether this patch can be dropped. >> >> I don't think that patch would fix any freeze problems, but restarting >> the transmitter even if the circ buffer is empty may be necessary to >> push out remaining data when the port is restarted after being stopped. >> >> IOW, something like >> >> if (uart_tx_stopped(port)) >> return; >> >> .... >> >> >> if (uart_circ_empty(&port->state->xmit) >> return; > > Thanks! I'll change the patch accordingly. > >> >> >> Below is a (work-in-progress) serial driver validation test for flow >> control handling (it may need some tuning for slow line speeds). >> Usual caveats apply. Takes ~40secs @ 115200. > > I'll try to get that running on my system. The test below should pass too, but I know it won't because this xilinx driver isn't handling x_char at all. Aside: does this h/w have rts driver/cts receiver? --- >% --- --- /dev/null 2015-11-20 07:19:13.265468435 -0500 +++ xchar.c 2015-11-20 11:55:26.210233102 -0500 @@ -0,0 +1,354 @@ +/* + * x_char unit test for tty drivers + * + * Copyright (c) 2014 Peter Hurley + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * Build: gcc -Wall [-DWAKE_TEST] -o xchar xchar.c + * The optional WAKE_TEST define includes tests for spurious write wakeups + * which should not be generated by sending x_char. As of kernel mainline + * v3.15, the write wakeup tests will generate false positive results. + * However, serial_core UART drivers should not generate spurious write + * wakeups when sending x_char, and should be tested on v3.16+ kernels. + * + * Use: + * 1. Connect a null-modem cable between test tty and reflector tty. + * + * 2. Confirm the test tty and reflector tty are using the same line + * settings; eg., 115200n81. + * + * 3. Make sure both test and reflector serial ports are not in use; + * eg., `sudo lsof /dev/ttyS0`. getty will mess up the test :) + * + * 4. Start the reflector. + * stty raw -echo -iexten < /dev/ttyS1 + * cat < /dev/ttyS1 > /dev/ttyS1 + * + * 5. Start the test. For example, + * ./xchar /dev/ttyS0 + * + * 6. Test terminates with EXIT_SUCCESS if tests pass. Otherwise, test + * terminates with EXIT_FAILURE and diagnostic message(s). + * + * Diagnostics: + * No output after 'Test xchar on /dev/ttyXXX' + * Check if tty is already in use + * + * main: opening tty: Permission denied (code: 13) + * Check tty permissions or run as su + * + * Test results: + * PASSED Test(s) passed + * + * test1: broken x_char: not sent No data was received from reflector, + * test2: broken x_char: not sent x_char never sent + * + * test1: broken x_char: n = XX, ch = XX + * test2: broken x_char: n = XX, ch = XX + * If n > 1, too much data was received + * from reflector; only START should + * be received. ch is the first char + * received from reflector. + * + * test1: spurious write wakeup detected + * test2: spurious write wakeup detected + * Sending x_char caused a write wakeup + * (false positive on kernels <= 3.16) + * False positive if the tty driver does + * not declare ops->send_xchar(). + */ + +#include <stdio.h> +#include <stdarg.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <termios.h> +#include <limits.h> +#include <signal.h> + +#ifdef WAKE_TEST +#include <sys/epoll.h> + +int ep; +#endif + +#include "common.h" + +int tty; +struct termios *saved_termios; +struct termios orig_termios, termios; +static char pattern[] = ASCII_PRINTABLE; +static size_t pattern_length = sizeof(pattern) - 1; + +static void restore_termios(void) +{ + if (saved_termios) { + int saved_errno = errno; + if (tcsetattr(tty, TCSANOW, saved_termios) < 0) + error_msg("tcsetattr"); + errno = saved_errno; + saved_termios = NULL; + } +} + +static void sig_handler(int sig) +{ + restore_termios(); + _exit(EXIT_FAILURE); +} + +#ifdef WAKE_TEST +static void setup_wake_test() +{ + struct epoll_event ev = { .data = { .fd = tty, }, + .events = EPOLLOUT | EPOLLET, + }; + int n; + + /* setup epoll to detect write wakeup when sending the x_char */ + ep = epoll_create(1); + if (ep < 0) + error_exit("epoll_create"); + if (epoll_ctl(ep, EPOLL_CTL_ADD, tty, &ev) < 0) + error_exit("epoll_ctl"); + + n = epoll_wait(ep, &ev, 1, 0); + if (n < 0) + error_exit("epoll_wait"); + if (n != 1 || ev.data.fd != tty || (~ev.events & EPOLLOUT)) + msg_exit("tty not ready for write??\n"); +} + +static void wake_test() +{ + struct epoll_event ev; + int n; + + n = epoll_wait(ep, &ev, 1, 0); + if (n < 0) + error_exit("epoll_wait"); + if (n > 0) + error_msg("spurious write wakeup detected\n"); + + close(ep); +} +#else +static void setup_wake_test() {} +static void wake_test() {} +#endif + +static void read_verify(size_t expected) +{ + size_t n = 0; + char buf[8192]; + + do { + ssize_t c; + + c = read(tty, buf, sizeof(buf)); + if (c < 0) + error_exit("read"); + if (c == 0) + msg_exit("FAILED, i/o stalled\n"); + + check_pattern(buf, c, pattern, n, pattern_length); + + n += c; + + } while (n < expected); + + if (n != expected) + msg_exit("FAILED, read %zu (expected %zu)\n", n, expected); +} + +/** + * test1 - send START, idle tty + * + * Send START x_char while tty is idle (ie., not currently outputting). + * Uses edge-triggered epoll check to detect if write wakeup is + * generated (sending x_char should not generate a local write wakeup). + * + * Expected: reflector returns 1 START char. + * no write wakeup detected + */ +static void test1(void) +{ + size_t n; + char buf[MAX_INPUT]; + + setup_wake_test(); + + /* cause START x_char to be sent */ + if (tcflow(tty, TCION) < 0) + error_exit("tcflow(TCION)"); + + /* read the reflected START char */ + n = read(tty, buf, sizeof(buf)); + if (n < 0) + error_exit("waiting for START"); + if (n == 0) + msg_exit("FAILED, broken x_char: not sent\n"); + if (n != 1 || buf[0] != termios.c_cc[VSTART]) + msg_exit("FAILED, broken x_char: n = %zu, ch = %hhx (expected = %hhx)\n", + n, buf[0], termios.c_cc[VSTART]); + + /* check for spurious write wakeup - + * sending x_char should not cause a local write wakeup. + * Check driver start_tx() and tx int handler for bad logic + * which may perform a write wakeup. + */ + wake_test(); +} + +/** + * test2 - send START from write-stalled tty + * + * Test that sending x_char does not cause local output to restart. + * Send data while tty output is disabled; this adds data to the tx ring + * buffer. Send START x_char while tty output is still disabled. + * + * Expected: reflector returns 1 START char + * no write wakeup detected + * no other output is reflected, neither before nor after + */ +static void test2(void) +{ + size_t n, expected; + char buf[8192]; + + /* turn off tty output */ + if (tcflow(tty, TCOOFF) < 0) + error_exit("tcflow(TCOOFF)"); + + expected = pattern_length; + n = write(tty, pattern, expected); + if (n < 0) + error_exit("write error"); + if (n != expected) + msg_exit("bad write: wrote %zu (expected %zu)\n", n, expected); + + n = read(tty, buf, sizeof(buf)); + if (n < 0) + error_exit("read error"); + /* test if the tty wrote even though output is turned off */ + if (n > 0) + msg_exit("FAILED, broken output flow control: received data after TCOOFF\n"); + + setup_wake_test(); + + /* cause START x_char to be sent */ + if (tcflow(tty, TCION) < 0) + error_exit("tcflow(TCION)"); + + /* read the reflected START char */ + n = read(tty, buf, sizeof(buf)); + if (n < 0) + error_exit("waiting for START"); + if (n == 0) + msg_exit("FAILED, broken x_char: not sent\n"); + if (n != 1 || buf[0] != termios.c_cc[VSTART]) + msg_exit("FAILED, broken x_char: n = %zu, ch = %hhx (expected = %hhx)\n", + n, buf[0], termios.c_cc[VSTART]); + + /* check for spurious write wakeup - + * sending x_char should not cause a local write wakeup. + * Check driver start_tx() and tx int handler for bad logic + * which may perform a write wakeup. + */ + wake_test(); + + /* restore tty output */ + if (tcflow(tty, TCOON) < 0) + error_exit("tcflow(TCOON)"); + + /* verify the pattern is now received unmangled */ + read_verify(expected); +} + +static int test(char *fname) +{ + printf("Test xchar on %s\n", fname); + + tty = open(fname, O_RDWR); + if (tty < 0) + error_exit("opening %s", fname); + + if (tcgetattr(tty, &termios) < 0) + error_exit("tcgetattr"); + + orig_termios = termios; + saved_termios = &orig_termios; + + termios.c_lflag &= ~(ICANON | ISIG | IEXTEN | ECHO); + /* turn off IXON so the reflector doesn't trigger our flow control */ + termios.c_iflag &= ~IXON; + termios.c_oflag &= ~OPOST; + termios.c_cc[VMIN] = 0; + termios.c_cc[VTIME] = 1; /* 1/10th sec. */ + + if (tcsetattr(tty, TCSAFLUSH, &termios) < 0) + error_exit("tcsetattr"); + + printf("begin test1\n"); + test1(); + + /* purge i/o buffers so next test starts with empty buffers */ + if (tcflush(tty, TCIOFLUSH) < 0) + error_exit("tcflush() before test2\n"); + + printf("begin test2\n"); + test2(); + + return 0; +} + +static void usage(char *argv[]) { + msg_exit("Usage: %s tty\n" + "\ttty device filename (eg., /dev/ttyS0)\n", + argv[0]); +} + +int main(int argc, char* argv[]) +{ + struct sigaction sa = { .sa_handler = sig_handler, .sa_flags = 0, }; + + setbuf(stdout, NULL); + + if (argc < 2) + usage(argv); + + if (atexit(restore_termios) < 0) + error_exit("atexit"); + + if (sigemptyset(&sa.sa_mask) < 0) + error_exit("sigemptyset"); + if (sigaction(SIGINT, &sa, NULL) < 0) + error_exit("sigaction(SIGINT)"); + if (sigaction(SIGTERM, &sa, NULL) < 0) + error_exit("sigaction(SIGTERM)"); + + test(argv[1]); + + printf("PASSED\n"); + return EXIT_SUCCESS; +}
On Fri, 2015-11-20 at 12:16PM -0500, Peter Hurley wrote: > On 11/20/2015 11:58 AM, Sören Brinkmann wrote: > > On Fri, 2015-11-20 at 11:30AM -0500, Peter Hurley wrote: > >> On 11/20/2015 10:28 AM, Sören Brinkmann wrote: > >>> On Fri, 2015-11-20 at 07:13AM -0500, Peter Hurley wrote: > >>>> On 11/19/2015 03:02 PM, Soren Brinkmann wrote: > >>>>> start_tx must start transmitting characters. Regardless of the state of > >>>>> the circular buffer, always enable the transmitter hardware. > >>>> > >>>> Why? > >>>> > >>>> Does cdns_uart_stop_tx() actually stop the transmitter so that > >>>> data remains in the transmitter? > >>> > >>> Well, I saw my system freezing and the cause seemed to be that the UART > >>> receiver and/or transmitters were disabled while the system was trying > >>> to print. Hence, I started questioning all locations touching the > >>> transmitter/receiver enable. I read the docs in > >>> https://www.kernel.org/doc/Documentation/serial/driver, which simply > >>> says "Start transmitting characters." for start_tx(). Hence, I thought, > >>> this function is probably supposed to just do that and start the > >>> transmitter. I'll test whether this patch can be dropped. > >> > >> I don't think that patch would fix any freeze problems, but restarting > >> the transmitter even if the circ buffer is empty may be necessary to > >> push out remaining data when the port is restarted after being stopped. > >> > >> IOW, something like > >> > >> if (uart_tx_stopped(port)) > >> return; > >> > >> .... > >> > >> > >> if (uart_circ_empty(&port->state->xmit) > >> return; > > > > Thanks! I'll change the patch accordingly. > > > >> > >> > >> Below is a (work-in-progress) serial driver validation test for flow > >> control handling (it may need some tuning for slow line speeds). > >> Usual caveats apply. Takes ~40secs @ 115200. > > > > I'll try to get that running on my system. > > The test below should pass too, but I know it won't because this xilinx > driver isn't handling x_char at all. > > Aside: does this h/w have rts driver/cts receiver? I would have to look up the details. But, IIRC, the HW has all the modem/flow control signals. But on our SoCs those signals are only available when routing the UART through the FPGA part, which makes it a rather unlikely configuration since most platforms want the UART as a low level debug port that should not depend on any FPGA bistreams. Hence, I suspect it might just be a limitation of the driver. Sören
Hi Peter, On 20.11.2015 18:16, Peter Hurley wrote: > On 11/20/2015 11:58 AM, Sören Brinkmann wrote: >> On Fri, 2015-11-20 at 11:30AM -0500, Peter Hurley wrote: >>> On 11/20/2015 10:28 AM, Sören Brinkmann wrote: >>>> On Fri, 2015-11-20 at 07:13AM -0500, Peter Hurley wrote: >>>>> On 11/19/2015 03:02 PM, Soren Brinkmann wrote: >>>>>> start_tx must start transmitting characters. Regardless of the state of >>>>>> the circular buffer, always enable the transmitter hardware. >>>>> >>>>> Why? >>>>> >>>>> Does cdns_uart_stop_tx() actually stop the transmitter so that >>>>> data remains in the transmitter? >>>> >>>> Well, I saw my system freezing and the cause seemed to be that the UART >>>> receiver and/or transmitters were disabled while the system was trying >>>> to print. Hence, I started questioning all locations touching the >>>> transmitter/receiver enable. I read the docs in >>>> https://www.kernel.org/doc/Documentation/serial/driver, which simply >>>> says "Start transmitting characters." for start_tx(). Hence, I thought, >>>> this function is probably supposed to just do that and start the >>>> transmitter. I'll test whether this patch can be dropped. >>> >>> I don't think that patch would fix any freeze problems, but restarting >>> the transmitter even if the circ buffer is empty may be necessary to >>> push out remaining data when the port is restarted after being stopped. >>> >>> IOW, something like >>> >>> if (uart_tx_stopped(port)) >>> return; >>> >>> .... >>> >>> >>> if (uart_circ_empty(&port->state->xmit) >>> return; >> >> Thanks! I'll change the patch accordingly. >> >>> >>> >>> Below is a (work-in-progress) serial driver validation test for flow >>> control handling (it may need some tuning for slow line speeds). >>> Usual caveats apply. Takes ~40secs @ 115200. >> >> I'll try to get that running on my system. > > The test below should pass too, but I know it won't because this xilinx > driver isn't handling x_char at all. > > Aside: does this h/w have rts driver/cts receiver? > > --- >% --- > --- /dev/null 2015-11-20 07:19:13.265468435 -0500 > +++ xchar.c 2015-11-20 11:55:26.210233102 -0500 > @@ -0,0 +1,354 @@ > +/* > + * x_char unit test for tty drivers All these tests looks very interesting. Do you have any any work-in-progress repo with other tests? It will be good to run all of them to validate our drivers. Thanks, Michal
Hi Michal, On 11/23/2015 02:05 AM, Michal Simek wrote: > All these tests looks very interesting. Do you have any any > work-in-progress repo with other tests? It will be good to run all of > them to validate our drivers. I haven't upstreamed these yet, but I plan to do so soon, along with tty core, pty, n_tty i/o, termios, and torture tests and some basic tools. Regards, Peter Hurley
Hi Peter, On 23.11.2015 21:00, Peter Hurley wrote: > Hi Michal, > > On 11/23/2015 02:05 AM, Michal Simek wrote: >> All these tests looks very interesting. Do you have any any >> work-in-progress repo with other tests? It will be good to run all of >> them to validate our drivers. > > I haven't upstreamed these yet, but I plan to do so soon, along with > tty core, pty, n_tty i/o, termios, and torture tests and some basic tools. great. Can you please keep me in the loop? Thanks, Michal
--- /dev/null 2015-11-20 07:19:13.265468435 -0500 +++ common.h 2015-11-20 10:58:05.514073298 -0500 @@ -0,0 +1,67 @@ +#ifndef _COMMON_H_ +#define _COMMON_H_ + +#include <stdio.h> +#include <stdarg.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/wait.h> +#include <termios.h> +#include <unistd.h> +#include <stdarg.h> + +#define ARRAYSIZE(a) (sizeof(a) / sizeof((a)[0])) +#define max(a,b) \ + ({ __typeof__ (a) _a = (a); \ + __typeof__ (b) _b = (b); \ + _a > _b ? _a : _b; }) +#define min(a,b) \ + ({ __typeof__ (a) _a = (a); \ + __typeof__ (b) _b = (b); \ + _a < _b ? _a : _b; }) + +#define ASCII_PRINTABLE \ + /* 1 2 3 4 5 \ + * 12345678901234567890123456789012345678901234567890 */ \ + "+_)(*&^%$#@!~=-0987654321`|}{POIUYTREWQ][poiuytrew" /* 50 */ \ + "q:LKJHGFDSA;lkjhgfdsa?><MNBVCXZ/.,mnbvcxz" /* +41 */ \ + "\"\'\\" /* + 3 */ \ + "+_)(*&^%$#@!~=-0987654321`|}{POIUYTREWQ][poiuytrew" /* 50 */ \ + "q:LKJHGFDSA;lkjhgfdsa?><MNBV\n" /* +29 */ \ + /* 173 */ + +#define error_msg(f, ...) \ + fprintf(stderr, "%s: " f ": %s (code: %d)\n", __func__, ##__VA_ARGS__, \ + strerror(errno), errno) + +#define msg_exit(f, ...) ({ \ + fprintf(stderr, "%s: " f "\n", __func__, ##__VA_ARGS__); \ + exit(EXIT_FAILURE); \ +}) + +#define error_exit(f, ...) ({ \ + error_msg(f, ##__VA_ARGS__); \ + exit(EXIT_FAILURE); \ +}) + +/* validate buffer[0..c-1] == pattern[n..n+c-1] */ +static inline void check_pattern(const void *buf, size_t c, + const void *pattern, size_t n, size_t sz) +{ + size_t i, j, t, len; + + for (i = 0, j = n, len = c; len; i += t, j+= t, len -= t) { + j %= sz; + t = min(sz - j, len); + if (memcmp(buf + i, pattern + j, t) != 0) + msg_exit("\n\tcorrupted data: '%.*s'" + "\n\t expected: '%.*s'\n", + (int)t, (char *)buf + i, (int)t, + (char *)pattern + j); + } +} + +#endif /* _COMMON_H_ */