diff options
author | Daniel Thompson <daniel.thompson@linaro.org> | 2014-03-28 09:28:11 +0000 |
---|---|---|
committer | Daniel Thompson <daniel.thompson@linaro.org> | 2015-07-06 13:57:46 +0100 |
commit | 29cf8a9b1453bc0037e38b511407f8fe1bd8373f (patch) | |
tree | d5b698e56678941196b934941ac4ad6dd8b30438 | |
parent | d770e558e21961ad6cfdf0ff7df0eb5d7d4f0754 (diff) |
serial: Emulate break using control characters
Currently the magic SysRq functions are accessed by sending a break.
Unfortunately some networked serial proxies makes is difficult to send
a break meaning SysRq functions cannot be reached. We avoid this problem
by allowing the (fairly unlikely) sequence of ^B^R^K characters to
emulate a real break.
This approach is very nearly as robust as normal sysrq/break handling
because all trigger recognition happens during interrupt handling however
to emulate a break we must enter the ISR four times (instead of twice) and
manage an extra byte of state.
Signed-off-by: Daniel Thompson <daniel.thompson@linaro.org>
-rw-r--r-- | include/linux/serial_core.h | 83 | ||||
-rw-r--r-- | lib/Kconfig.debug | 15 |
2 files changed, 80 insertions, 18 deletions
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 297d4fa1cfe5..ffd1719b9969 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -159,6 +159,9 @@ struct uart_port { struct console *cons; /* struct console, if any */ #if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(SUPPORT_SYSRQ) unsigned long sysrq; /* sysrq timeout */ +#ifdef CONFIG_MAGIC_SYSRQ_BREAK_EMULATION + char sysrq_emul; /* sysrq break emulation */ +#endif #endif /* flags must be updated while holding port mutex */ @@ -426,24 +429,6 @@ extern void uart_handle_cts_change(struct uart_port *uport, extern void uart_insert_char(struct uart_port *port, unsigned int status, unsigned int overrun, unsigned int ch, unsigned int flag); -#ifdef SUPPORT_SYSRQ -static inline int -uart_handle_sysrq_char(struct uart_port *port, unsigned int ch) -{ - if (port->sysrq) { - if (ch && time_before(jiffies, port->sysrq)) { - handle_sysrq(ch); - port->sysrq = 0; - return 1; - } - port->sysrq = 0; - } - return 0; -} -#else -#define uart_handle_sysrq_char(port,ch) ({ (void)port; 0; }) -#endif - /* * We do the SysRQ and SAK checking like this... */ @@ -468,6 +453,68 @@ static inline int uart_handle_break(struct uart_port *port) return 0; } +#if defined(SUPPORT_SYSRQ) && defined(CONFIG_MAGIC_SYSRQ_BREAK_EMULATION) +/* + * Emulate a break if we are the serial console and receive ^B, ^R, ^K. + */ +static inline int +uart_handle_sysrq_break_emulation(struct uart_port *port, unsigned int ch) +{ + const unsigned int ctrlb = 'B' & 31; + const unsigned int ctrlr = 'R' & 31; + const unsigned int ctrlk = 'K' & 31; + + if (uart_console(port)) { + if ((port->sysrq_emul == 0 && ch == ctrlb) || + (port->sysrq_emul == ctrlb && ch == ctrlr)) { + /* for either of the first two trigger characters + * update the state variable and move on. + */ + port->sysrq_emul = ch; + return 1; + } else if (port->sysrq_emul == ctrlr && + ch == ctrlk && uart_handle_break(port)) { + /* the break has already been emulated whilst + * evaluating the condition, tidy up and move on + */ + port->sysrq_emul = 0; + return 1; + } + } + + if (port->sysrq_emul) { + /* received a partial (false) trigger, tidy up and move on */ + uart_insert_char(port, 0, 0, ctrlb, TTY_NORMAL); + if (port->sysrq_emul == ctrlr) + uart_insert_char(port, 0, 0, ctrlr, TTY_NORMAL); + port->sysrq_emul = 0; + } + + return 0; +} +#else +#define uart_handle_sysrq_break_emulation(port,ch) ({ (void)port; 0; }) +#endif + +#ifdef SUPPORT_SYSRQ +static inline int +uart_handle_sysrq_char(struct uart_port *port, unsigned int ch) +{ + if (port->sysrq) { + if (ch && time_before(jiffies, port->sysrq)) { + handle_sysrq(ch); + port->sysrq = 0; + return 1; + } + port->sysrq = 0; + } + + return uart_handle_sysrq_break_emulation(port, ch); +} +#else +#define uart_handle_sysrq_char(port,ch) ({ (void)port; 0; }) +#endif + /* * UART_ENABLE_MS - determine if port should enable modem status irqs */ diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index e2894b23efb6..9563d5315cf1 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -372,6 +372,21 @@ config MAGIC_SYSRQ_DEFAULT_ENABLE This may be set to 1 or 0 to enable or disable them all, or to a bitmask as described in Documentation/sysrq.txt. +config MAGIC_SYSRQ_BREAK_EMULATION + bool "Enable magic SysRq serial break emulation" + depends on MAGIC_SYSRQ && SERIAL_CORE_CONSOLE + default n + help + If you say Y here, then you can use the character sequence ^B^R^K + to simulate a BREAK on the serial console. This is useful if for + some reason you cannot send a BREAK to your console's serial port. + For example, if you have a serial device server that cannot + send a BREAK. Enabling this feature can delay the delivery of + characters to the TTY because the ^B and a subsequent ^R will be + delayed until we know what the next character is. + + If unsure, say N. + config DEBUG_KERNEL bool "Kernel debugging" help |