From: Peter Hurley <peter@hurleysoftware.com>
To: "Sören Brinkmann" <soren.brinkmann@xilinx.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
Jiri Slaby <jslaby@suse.com>,
linux-arm-kernel@lists.infradead.org,
Michal Simek <michal.simek@xilinx.com>,
linux-serial@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATH RESEND v2 03/10] tty: xuartps: Always enable transmitter in start_tx
Date: Fri, 20 Nov 2015 12:16:36 -0500 [thread overview]
Message-ID: <564F5574.4070407@hurleysoftware.com> (raw)
In-Reply-To: <20151120165804.GV32017@xsjsorenbubuntu>
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;
+}
WARNING: multiple messages have this Message-ID (diff)
From: peter@hurleysoftware.com (Peter Hurley)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATH RESEND v2 03/10] tty: xuartps: Always enable transmitter in start_tx
Date: Fri, 20 Nov 2015 12:16:36 -0500 [thread overview]
Message-ID: <564F5574.4070407@hurleysoftware.com> (raw)
In-Reply-To: <20151120165804.GV32017@xsjsorenbubuntu>
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;
+}
next prev parent reply other threads:[~2015-11-20 17:16 UTC|newest]
Thread overview: 62+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-11-19 20:02 [PATH RESEND v2 00/10] tty: xuartps: Fix lock ups Soren Brinkmann
2015-11-19 20:02 ` Soren Brinkmann
2015-11-19 20:02 ` Soren Brinkmann
2015-11-19 20:02 ` [PATH RESEND v2 01/10] tty: xuartps: Beautify read-modify writes Soren Brinkmann
2015-11-19 20:02 ` Soren Brinkmann
2015-11-19 20:02 ` Soren Brinkmann
2015-11-19 20:02 ` [PATH RESEND v2 02/10] tty: xuartps: Use spinlock to serialize HW access Soren Brinkmann
2015-11-19 20:02 ` Soren Brinkmann
2015-11-19 20:02 ` Soren Brinkmann
2015-11-19 20:02 ` [PATH RESEND v2 03/10] tty: xuartps: Always enable transmitter in start_tx Soren Brinkmann
2015-11-19 20:02 ` Soren Brinkmann
2015-11-19 20:02 ` Soren Brinkmann
2015-11-19 23:15 ` Peter Hurley
2015-11-20 12:13 ` Peter Hurley
2015-11-20 12:13 ` Peter Hurley
2015-11-20 15:28 ` Sören Brinkmann
2015-11-20 15:28 ` Sören Brinkmann
2015-11-20 15:28 ` Sören Brinkmann
2015-11-20 16:30 ` Peter Hurley
2015-11-20 16:30 ` Peter Hurley
2015-11-20 16:58 ` Sören Brinkmann
2015-11-20 16:58 ` Sören Brinkmann
2015-11-20 16:58 ` Sören Brinkmann
2015-11-20 17:16 ` Peter Hurley [this message]
2015-11-20 17:16 ` Peter Hurley
2015-11-20 17:29 ` Sören Brinkmann
2015-11-20 17:29 ` Sören Brinkmann
2015-11-20 17:29 ` Sören Brinkmann
2015-11-23 7:05 ` Michal Simek
2015-11-23 7:05 ` Michal Simek
2015-11-23 7:05 ` Michal Simek
2015-11-23 20:00 ` Peter Hurley
2015-11-23 20:00 ` Peter Hurley
2015-11-24 7:26 ` Michal Simek
2015-11-24 7:26 ` Michal Simek
2015-11-24 7:26 ` Michal Simek
2015-11-20 17:05 ` Sören Brinkmann
2015-11-20 17:05 ` Sören Brinkmann
2015-11-20 17:05 ` Sören Brinkmann
2015-11-20 17:12 ` Peter Hurley
2015-11-20 17:12 ` Peter Hurley
2015-11-19 20:02 ` [PATH RESEND v2 04/10] tty: xuartps: Clear interrupt status register in shutdown Soren Brinkmann
2015-11-19 20:02 ` Soren Brinkmann
2015-11-19 20:02 ` Soren Brinkmann
2015-11-19 20:02 ` [PATH RESEND v2 05/10] tty: xuartps: Improve startup function Soren Brinkmann
2015-11-19 20:02 ` Soren Brinkmann
2015-11-19 20:02 ` Soren Brinkmann
2015-11-19 20:02 ` [PATH RESEND v2 06/10] tty: xuartps: Keep lock for whole ISR Soren Brinkmann
2015-11-19 20:02 ` Soren Brinkmann
2015-11-19 20:02 ` Soren Brinkmann
2015-11-19 20:02 ` [PATH RESEND v2 07/10] tty: xuartps: Acquire port lock for shutdown Soren Brinkmann
2015-11-19 20:02 ` Soren Brinkmann
2015-11-19 20:02 ` Soren Brinkmann
2015-11-19 20:02 ` [PATH RESEND v2 08/10] tty: xuartps: Move RX path into helper function Soren Brinkmann
2015-11-19 20:02 ` Soren Brinkmann
2015-11-19 20:02 ` Soren Brinkmann
2015-11-19 20:02 ` [PATH RESEND v2 09/10] tty: xuartps: Only handle RX IRQs when RX is enabled Soren Brinkmann
2015-11-19 20:02 ` Soren Brinkmann
2015-11-19 20:02 ` Soren Brinkmann
2015-11-19 20:02 ` [PATH RESEND v2 10/10] tty: xuartps: Cleanup: Reformat if-else Soren Brinkmann
2015-11-19 20:02 ` Soren Brinkmann
2015-11-19 20:02 ` Soren Brinkmann
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=564F5574.4070407@hurleysoftware.com \
--to=peter@hurleysoftware.com \
--cc=gregkh@linuxfoundation.org \
--cc=jslaby@suse.com \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-serial@vger.kernel.org \
--cc=michal.simek@xilinx.com \
--cc=soren.brinkmann@xilinx.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.