
From: Simon Glass <sjg@chromium.org> The EFI loader has some code to check the size (rows and columns) of an attached terminal. Move this over to the serial uclass so that it can be used elsewhere. Create a new CONFIG_SERIAL_TERM_PRESENT to control whether it should be used. Enable that for the EFI loader to preserve existing behaviour. Adjust the implementation so that it returns a useful error code on failure. Put the ESC and cESC values in the serial.h header. Signed-off-by: Simon Glass <sjg@chromium.org> --- (no changes since v1) drivers/serial/Kconfig | 15 ++++ drivers/serial/serial-uclass.c | 118 +++++++++++++++++++++++++++++++ include/serial.h | 18 +++++ lib/efi_loader/efi_console.c | 124 +-------------------------------- 4 files changed, 154 insertions(+), 121 deletions(-) diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index d58152abc7f..7d27dad327d 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -202,6 +202,21 @@ config VPL_DM_SERIAL implements serial_putc() etc. The uclass interface is defined in include/serial.h. +config SERIAL_TERM_PRESENT + bool "Assume there is a serial terminal present on the serial line" + depends on DM_SERIAL + default y if EFI_LOADER + help + In some cases boards are connected to a serial terminal and it is + useful to emit ANSI codes or other characters to determine the + properties of the terminal. + + Enable this if you wish to have these features. + + Note that enabling this for a board which is sending its output to + a log will result in junk in the log. It also introduces a delay of + up to 100ms on startup. + config DEBUG_UART bool "Enable an early debug UART for debugging" help diff --git a/drivers/serial/serial-uclass.c b/drivers/serial/serial-uclass.c index a08678dde4e..1204fb5d4c4 100644 --- a/drivers/serial/serial-uclass.c +++ b/drivers/serial/serial-uclass.c @@ -13,6 +13,7 @@ #include <os.h> #include <serial.h> #include <stdio_dev.h> +#include <time.h> #include <watchdog.h> #include <asm/global_data.h> #include <dm/lists.h> @@ -557,6 +558,123 @@ static int on_baudrate(const char *name, const char *value, enum env_op op, } U_BOOT_ENV_CALLBACK(baudrate, on_baudrate); +/** + * term_get_char() - read a character from the console + * + * Wait for up to 100 ms to read a character from the console. + * + * @c: pointer to the buffer to receive the character + * Return: 0 on success, -ETIMEDOUT if timed out + */ +static int term_get_char(s32 *c) +{ + u64 timeout; + + /* Wait up to 100 ms for a character */ + timeout = timer_get_us() + 100000; + + while (!tstc()) + if (timer_get_us() > timeout) + return -ETIMEDOUT; + + *c = getchar(); + + return 0; +} + +/** + * term_read_reply() - receive and parse a reply from the terminal + * + * @n: array of return values + * @num: number of return values expected + * @end_char: character indicating end of terminal message + * Return: non-zero indicates error + */ +static int term_read_reply(int *n, int num, char end_char) +{ + int ret, i = 0; + s32 c; + + ret = term_get_char(&c); + if (ret) + return ret; + if (c != cESC) + return -EPROTO; + + ret = term_get_char(&c); + if (ret) + return ret; + if (c != '[') + return -EPROTO; + + n[0] = 0; + while (1) { + int ret; + + ret = term_get_char(&c); + if (ret) + return ret; + + if (c == ';') { + i++; + if (i >= num) + return -EPROTO; + n[i] = 0; + continue; + } else if (c == end_char) { + break; + } else if (c > '9' || c < '0') { + return -EPROTO; + } + + /* Read one more decimal position */ + n[i] *= 10; + n[i] += c - '0'; + } + if (i != num - 1) + return -EPROTO; + + return 0; +} + +int serial_query_size(int *rowsp, int *colsp) +{ + int ret = 0; + int n[2]; + + if (!CONFIG_IS_ENABLED(SERIAL_TERM_PRESENT)) + return -ENOENT; + + /* Empty input buffer */ + while (tstc()) + getchar(); + + /* + * Not all terminals understand CSI [18t for querying the console size. + * We should adhere to escape sequences documented in the console_codes + * man page and the ECMA-48 standard. + * + * So here we follow a different approach. We position the cursor to the + * bottom right and query its position. Before leaving the function we + * restore the original cursor position. + */ + puts(ESC "7" /* Save cursor position */ + ESC "[r" /* Set scrolling region to full window */ + ESC "[999;999H" /* Move to bottom right corner */ + ESC "[6n"); /* Query cursor position */ + + /* Read {rows,cols} */ + ret = term_read_reply(n, 2, 'R'); + if (!ret) { + *colsp = n[1]; + *rowsp = n[0]; + } + + printf(ESC "8"); /* Restore cursor position */ + + return ret; +} + #if CONFIG_IS_ENABLED(SERIAL_PRESENT) static int serial_post_probe(struct udevice *dev) { diff --git a/include/serial.h b/include/serial.h index e5f6d984d28..2aba4c313c2 100644 --- a/include/serial.h +++ b/include/serial.h @@ -3,6 +3,10 @@ #include <post.h> +/* Escape value */ +#define cESC '\x1b' +#define ESC "\x1b" + struct serial_device { /* enough bytes to match alignment of following func pointer */ char name[16]; @@ -382,4 +386,18 @@ static inline void serial_flush(void) {} int serial_getc(void); int serial_tstc(void); +/** + * serial_query_size() - query serial console size + * + * When using a serial console or the net console we can only devise the + * terminal size by querying the terminal using ECMA-48 control sequences. + * + * @rowsp: returns number of rows + * @colsp: returns number of columns + * Returns: 0 on success, -NOENT if no terminal is present, -ETIMEDOUT if we + * checked for a terminal but didn't get a response in time, -EPROTO if the + * terminal did not respond as expected + */ +int serial_query_size(int *rowsp, int *colsp); + #endif diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c index ade44cb6e36..d2eabfdb07e 100644 --- a/lib/efi_loader/efi_console.c +++ b/lib/efi_loader/efi_console.c @@ -16,6 +16,7 @@ #include <efi_loader.h> #include <env.h> #include <log.h> +#include <serial.h> #include <stdio_dev.h> #include <video_console.h> #include <linux/delay.h> @@ -59,9 +60,6 @@ const efi_guid_t efi_guid_text_input_protocol = const efi_guid_t efi_guid_text_output_protocol = EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_GUID; -#define cESC '\x1b' -#define ESC "\x1b" - /* * efi_con_mode - mode information of the Simple Text Output Protocol * @@ -77,76 +75,6 @@ static struct simple_text_output_mode efi_con_mode = { .cursor_visible = 1, }; -/** - * term_get_char() - read a character from the console - * - * Wait for up to 100 ms to read a character from the console. - * - * @c: pointer to the buffer to receive the character - * Return: 0 on success, 1 otherwise - */ -static int term_get_char(s32 *c) -{ - u64 timeout; - - /* Wait up to 100 ms for a character */ - timeout = timer_get_us() + 100000; - - while (!tstc()) - if (timer_get_us() > timeout) - return 1; - - *c = getchar(); - return 0; -} - -/** - * term_read_reply() - receive and parse a reply from the terminal - * - * @n: array of return values - * @num: number of return values expected - * @end_char: character indicating end of terminal message - * Return: non-zero indicates error - */ -static int term_read_reply(int *n, int num, char end_char) -{ - s32 c; - int i = 0; - - if (term_get_char(&c) || c != cESC) - return -1; - - if (term_get_char(&c) || c != '[') - return -1; - - n[0] = 0; - while (1) { - if (!term_get_char(&c)) { - if (c == ';') { - i++; - if (i >= num) - return -1; - n[i] = 0; - continue; - } else if (c == end_char) { - break; - } else if (c > '9' || c < '0') { - return -1; - } - - /* Read one more decimal position */ - n[i] *= 10; - n[i] += c - '0'; - } else { - return -1; - } - } - if (i != num - 1) - return -1; - - return 0; -} - /** * efi_cout_output_string() - write Unicode string to console * @@ -272,52 +200,6 @@ static bool cout_mode_matches(struct cout_mode *mode, int rows, int cols) return (mode->rows == rows) && (mode->columns == cols); } -/** - * query_console_serial() - query serial console size - * - * When using a serial console or the net console we can only devise the - * terminal size by querying the terminal using ECMA-48 control sequences. - * - * @rows: pointer to return number of rows - * @cols: pointer to return number of columns - * Returns: 0 on success - */ -static int query_console_serial(int *rows, int *cols) -{ - int ret = 0; - int n[2]; - - /* Empty input buffer */ - while (tstc()) - getchar(); - - /* - * Not all terminals understand CSI [18t for querying the console size. - * We should adhere to escape sequences documented in the console_codes - * man page and the ECMA-48 standard. - * - * So here we follow a different approach. We position the cursor to the - * bottom right and query its position. Before leaving the function we - * restore the original cursor position. - */ - printf(ESC "7" /* Save cursor position */ - ESC "[r" /* Set scrolling region to full window */ - ESC "[999;999H" /* Move to bottom right corner */ - ESC "[6n"); /* Query cursor position */ - - /* Read {rows,cols} */ - if (term_read_reply(n, 2, 'R')) { - ret = 1; - goto out; - } - - *cols = n[1]; - *rows = n[0]; -out: - printf(ESC "8"); /* Restore cursor position */ - return ret; -} - /** * query_vidconsole() - query video console size * @@ -363,8 +245,8 @@ void efi_setup_console_size(void) if (IS_ENABLED(CONFIG_VIDEO)) ret = query_vidconsole(&rows, &cols); - if (ret) - ret = query_console_serial(&rows, &cols); + if (ret && IS_ENABLED(CONFIG_DM_SERIAL)) + ret = serial_query_size(&rows, &cols); if (ret) return; -- 2.43.0