Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 58 additions & 12 deletions drivers/serial/pty.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,20 @@
* Pre-processor Definitions
****************************************************************************/

/* States for the local-echo VT100/ANSI escape sequence detector used by
* pty_read() below (dev->pd_escape). This mirrors the equivalent state
* machine in drivers/serial/serial.c (uart_readv()) -- see the comment
* there for the full rationale. It does not interpret the sequence, it
* only decides how many bytes to swallow from the local echo; the
* sequence bytes themselves are still delivered to the reader
* unmodified either way.
*/

#define UART_ESCAPE_NONE 0 /* Not in an escape sequence */
#define UART_ESCAPE_START 1 /* Saw ESC, waiting for '[', 'O', or other */
#define UART_ESCAPE_CSI 2 /* Saw "ESC [", consuming CSI bytes */
#define UART_ESCAPE_SS3 3 /* Saw "ESC O", waiting for the final byte */

/* Maximum number of threads than can be waiting for POLL events */

#ifndef CONFIG_DEV_PTY_NPOLLWAITERS
Expand All @@ -80,7 +94,10 @@ struct pty_dev_s
struct file pd_src; /* Provides data to read() method (pipe output) */
struct file pd_sink; /* Accepts data from write() method (pipe input) */
bool pd_master; /* True: this is the master */
uint8_t pd_escape; /* Number of the character to be escaped */
uint8_t pd_escape; /* Escape sequence echo-suppression state
* (see the UART_ESCAPE_* values used in
* pty_read())
*/
#if defined(CONFIG_TTY_SIGINT) || defined(CONFIG_TTY_SIGTSTP)
pid_t pd_pid; /* Thread PID to receive signals (-1 if none) */
#endif
Expand Down Expand Up @@ -480,29 +497,58 @@ static ssize_t pty_read(FAR struct file *filep, FAR char *buffer, size_t len)
{
ch = buffer[i];

/* Check for the beginning of a VT100 escape sequence, 3 byte */
/* Detect a VT100/ANSI escape sequence (CSI "ESC [ ... <final>"
* or SS3 "ESC O <final>") so it can be swallowed from the
* local echo below. See the UART_ESCAPE_* comment above.
*/

if (ch == ASCII_ESC)
{
/* Mark that we should skip 2 more bytes */

dev->pd_escape = 2;
dev->pd_escape = UART_ESCAPE_START;
continue;
}
else if (dev->pd_escape == 2 && ch != ASCII_LBRACKET)
else if (dev->pd_escape == UART_ESCAPE_START)
{
/* It's not an <esc>[x 3 byte sequence, show it */
if (ch == ASCII_LBRACKET)
{
dev->pd_escape = UART_ESCAPE_CSI;
continue;
}
else if (ch == ASCII_O)
{
dev->pd_escape = UART_ESCAPE_SS3;
continue;
}

/* Not CSI or SS3 -- an unrecognized two-byte "ESC x"
* sequence. Fall through and consider 'x' for the echo
* batch normally, as before.
*/

dev->pd_escape = 0;
dev->pd_escape = UART_ESCAPE_NONE;
}
else if (dev->pd_escape > 0)
else if (dev->pd_escape == UART_ESCAPE_CSI)
{
/* Skipping character count down */
/* Consuming CSI parameter/intermediate bytes (0x20-0x3f);
* the sequence ends with exactly one final byte in
* 0x40-0x7e.
*/

if (--dev->pd_escape > 0)
if (ch >= 0x40 && ch <= 0x7e)
{
continue;
dev->pd_escape = UART_ESCAPE_NONE;
}

continue;
}
else if (dev->pd_escape == UART_ESCAPE_SS3)
{
/* The byte following "ESC O" is always the final (and
* only) byte.
*/

dev->pd_escape = UART_ESCAPE_NONE;
continue;
}

/* Echo if the character in batch */
Expand Down
78 changes: 68 additions & 10 deletions drivers/serial/serial.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,32 @@

#define POLL_DELAY_USEC 1000

/* States for the local-echo VT100/ANSI escape sequence detector used by
* uart_readv() below (dev->escape). This does not interpret the
* sequence, it only decides how many bytes to swallow from the local
* echo so that raw escape sequences typed by the user are not rendered
* as visible garbage; the sequence bytes themselves are still delivered
* to the reader unmodified either way.
*
* Two forms are recognized:
*
* CSI: ESC '[' <zero or more parameter/intermediate bytes, 0x20-0x3f>
* <one final byte, 0x40-0x7e>
* e.g. ESC[D (left arrow), ESC[3~ (Delete), ESC[1;5D (Ctrl+Left)
*
* SS3: ESC 'O' <one final byte>
* e.g. ESC O F (End, as sent by xterm and many other terminals
* that otherwise use CSI for the plain arrow keys)
*
* Any other byte following ESC is treated as an unrecognized two-byte
* "ESC x" sequence; 'x' itself is echoed normally, as before.
*/

#define UART_ESCAPE_NONE 0 /* Not in an escape sequence */
#define UART_ESCAPE_START 1 /* Saw ESC, waiting for '[', 'O', or other */
#define UART_ESCAPE_CSI 2 /* Saw "ESC [", consuming CSI bytes */
#define UART_ESCAPE_SS3 3 /* Saw "ESC O", waiting for the final byte */

/****************************************************************************
* Private Types
****************************************************************************/
Expand Down Expand Up @@ -1063,26 +1089,58 @@ static ssize_t uart_readv(FAR struct file *filep, FAR struct uio *uio)

if (dev->tc_lflag & ECHO)
{
/* Check for the beginning of a VT100 escape sequence, 3 byte */

if (ch == ASCII_ESC)
{
/* Mark that we should skip 2 more bytes */
/* Start (or restart) tracking a possible escape
* sequence.
*/

dev->escape = 2;
dev->escape = UART_ESCAPE_START;
continue;
}
else if (dev->escape == 2 && ch != ASCII_LBRACKET)
else if (dev->escape == UART_ESCAPE_START)
{
/* First byte after ESC selects the sequence type */

if (ch == ASCII_LBRACKET)
{
dev->escape = UART_ESCAPE_CSI;
continue;
}
else if (ch == ASCII_O)
{
dev->escape = UART_ESCAPE_SS3;
continue;
}

/* Not CSI or SS3 -- an unrecognized two-byte "ESC x"
* sequence. Fall through and echo 'x' normally, as
* before.
*/

dev->escape = UART_ESCAPE_NONE;
}
else if (dev->escape == UART_ESCAPE_CSI)
{
/* It's not an <esc>[x 3 byte sequence, show it */
/* Consuming CSI parameter/intermediate bytes
* (0x20-0x3f); the sequence ends with exactly one
* final byte in 0x40-0x7e.
*/

if (ch >= 0x40 && ch <= 0x7e)
{
dev->escape = UART_ESCAPE_NONE;
}

dev->escape = 0;
continue;
}
else if (dev->escape > 0)
else if (dev->escape == UART_ESCAPE_SS3)
{
/* Skipping character count down */
/* The byte following "ESC O" is always the final
* (and only) byte.
*/

dev->escape--;
dev->escape = UART_ESCAPE_NONE;
continue;
}

Expand Down
6 changes: 5 additions & 1 deletion include/nuttx/serial/serial.h
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,11 @@ struct uart_dev_s
/* State data */

uint8_t open_count; /* Number of times the device has been opened */
uint8_t escape; /* Number of the character to be escaped */
uint8_t escape; /* VT100/ANSI escape sequence echo-
* suppression state (see the
* UART_ESCAPE_* values used in
* uart_readv())
*/
#ifdef CONFIG_SERIAL_REMOVABLE
volatile bool disconnected; /* true: Removable device is not connected */
#endif
Expand Down
Loading