Files
u-boot/common/console.c
Simon Glass 4aefafb381 console: Recheck the page length when stdout device changes
It is annoying to have the pager use the line count from the display
when console output is actually using serial. Check this when stdio
changes and recaculate as needed.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-08-26 14:36:07 -06:00

1429 lines
31 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2000
* Paolo Scaffardi, AIRVENT SAM s.p.a - RIMINI(ITALY), arsenio@tin.it
*/
#define LOG_CATEGORY LOGC_CONSOLE
#include <console.h>
#include <debug_uart.h>
#include <display_options.h>
#include <dm.h>
#include <env.h>
#include <stdarg.h>
#include <iomux.h>
#include <malloc.h>
#include <mapmem.h>
#include <os.h>
#include <pager.h>
#include <serial.h>
#include <stdio_dev.h>
#include <exports.h>
#include <env_internal.h>
#include <video_console.h>
#include <watchdog.h>
#include <asm/global_data.h>
#include <linux/delay.h>
DECLARE_GLOBAL_DATA_PTR;
#define CSI "\x1b["
#ifdef CONFIG_CONSOLE_RECORD
/* helper function: access to gd->console_out and gd->console_in */
static void console_record_putc(const char c)
{
if (!(gd->flags & GD_FLG_RECORD))
return;
if (gd->console_out.start &&
!membuf_putbyte((struct membuf *)&gd->console_out, c))
gd->flags |= GD_FLG_RECORD_OVF;
}
static void console_record_puts(const char *s)
{
if (!(gd->flags & GD_FLG_RECORD))
return;
if (gd->console_out.start) {
int len = strlen(s);
if (membuf_put((struct membuf *)&gd->console_out, s, len) !=
len)
gd->flags |= GD_FLG_RECORD_OVF;
}
}
static int console_record_getc(void)
{
if (!(gd->flags & GD_FLG_RECORD))
return -1;
if (!gd->console_in.start)
return -1;
return membuf_getbyte((struct membuf *)&gd->console_in);
}
static int console_record_tstc(void)
{
if (!(gd->flags & GD_FLG_RECORD))
return 0;
if (gd->console_in.start) {
if (membuf_peekbyte((struct membuf *)&gd->console_in) != -1)
return 1;
}
return 0;
}
#else
static void console_record_putc(char c)
{
}
static void console_record_puts(const char *s)
{
}
static int console_record_getc(void)
{
return -1;
}
static int console_record_tstc(void)
{
return 0;
}
#endif
#if CONFIG_IS_ENABLED(SYS_CONSOLE_IS_IN_ENV)
/*
* if overwrite_console returns 1, the stdin, stderr and stdout
* are switched to the serial port, else the settings in the
* environment are used
*/
#ifdef CONFIG_SYS_CONSOLE_OVERWRITE_ROUTINE
extern int overwrite_console(void);
#define OVERWRITE_CONSOLE overwrite_console()
#else
#define OVERWRITE_CONSOLE 0
#endif /* CONFIG_SYS_CONSOLE_OVERWRITE_ROUTINE */
#endif /* CONFIG_IS_ENABLED(SYS_CONSOLE_IS_IN_ENV) */
static int console_setfile(int file, struct stdio_dev * dev)
{
int error = 0;
if (dev == NULL)
return -1;
switch (file) {
case stdin:
case stdout:
case stderr:
error = console_start(file, dev);
if (error)
break;
/* Assign the new device (leaving the existing one started) */
stdio_devices[file] = dev;
#ifndef CONFIG_XPL_BUILD
/*
* Update monitor functions
* (to use the console stuff by other applications)
*/
switch (file) {
case stdin:
gd->jt->getc = getchar;
gd->jt->tstc = tstc;
break;
case stdout:
gd->jt->putc = putc;
gd->jt->puts = puts;
STDIO_DEV_ASSIGN_FLUSH(gd->jt, flush);
gd->jt->printf = printf;
break;
}
break;
#endif
default: /* Invalid file ID */
error = -1;
}
return error;
}
/**
* console_dev_is_serial() - Check if a stdio device is a serial device
*
* @sdev: Device to check
* Return: true if this device is in the serial uclass (or for pre-driver-model,
* whether it is called "serial".
*/
static bool console_dev_is_serial(struct stdio_dev *sdev)
{
bool is_serial;
if (IS_ENABLED(CONFIG_DM_SERIAL) && (sdev->flags & DEV_FLAGS_DM)) {
struct udevice *dev = sdev->priv;
is_serial = device_get_uclass_id(dev) == UCLASS_SERIAL;
} else {
is_serial = !strcmp(sdev->name, "serial");
}
return is_serial;
}
#if CONFIG_IS_ENABLED(CONSOLE_MUX)
/** Console I/O multiplexing *******************************************/
/* tstcdev: save the last stdio device with pending characters, with tstc != 0 */
static struct stdio_dev *tstcdev;
struct stdio_dev **console_devices[MAX_FILES];
int cd_count[MAX_FILES];
static void console_devices_set(int file, struct stdio_dev *dev)
{
console_devices[file][0] = dev;
cd_count[file] = 1;
}
/**
* console_needs_start_stop() - check if we need to start or stop the STDIO device
* @file: STDIO file
* @sdev: STDIO device in question
*
* This function checks if we need to start or stop the stdio device used for
* a console. For IOMUX case it simply enforces one time start and one time
* stop of the device independently of how many STDIO files are using it. In
* other words, we start console once before first STDIO device wants it and
* stop after the last is gone.
*/
static bool console_needs_start_stop(int file, struct stdio_dev *sdev)
{
int i;
for (i = 0; i < ARRAY_SIZE(cd_count); i++) {
if (i == file)
continue;
if (iomux_match_device(console_devices[i], cd_count[i], sdev) >= 0)
return false;
}
return true;
}
/*
* This depends on tstc() always being called before getchar().
* This is guaranteed to be true because this routine is called
* only from fgetc() which assures it.
* No attempt is made to demultiplex multiple input sources.
*/
static int console_getc(int file)
{
unsigned char ret;
/* This is never called with testcdev == NULL */
ret = tstcdev->getc(tstcdev);
tstcdev = NULL;
return ret;
}
/* Upper layer may have already called tstc(): check the saved result */
static bool console_has_tstc(void)
{
return !!tstcdev;
}
static int console_tstc(int file)
{
int i, ret;
struct stdio_dev *dev;
int prev;
prev = disable_ctrlc(1);
for_each_console_dev(i, file, dev) {
if (dev->tstc != NULL) {
ret = dev->tstc(dev);
if (ret > 0) {
tstcdev = dev;
disable_ctrlc(prev);
return ret;
}
}
}
disable_ctrlc(prev);
return 0;
}
static void console_puts_pager(int file, const char *s);
static void console_putc_pager(int file, const char c)
{
if (IS_ENABLED(CONFIG_CONSOLE_PAGER) && gd_pager()) {
char str[2] = {c, '\0'};
console_puts_pager(file, str);
} else {
int i;
struct stdio_dev *dev;
for_each_console_dev(i, file, dev) {
if (dev->putc)
dev->putc(dev, c);
}
}
}
/**
* console_puts_select() - Output a string to all console devices
*
* @file: File number to output to (e,g, stdout, see stdio.h)
* @serial_only: true to output only to serial, false to output to everything
* else
* @s: String to output
*/
static void console_puts_select(int file, bool serial_only, const char *s)
{
int i;
struct stdio_dev *dev;
for_each_console_dev(i, file, dev) {
bool is_serial = console_dev_is_serial(dev);
if (dev->puts && serial_only == is_serial)
dev->puts(dev, s);
}
}
void console_puts_select_stderr(bool serial_only, const char *s)
{
if (gd->flags & GD_FLG_DEVINIT)
console_puts_select(stderr, serial_only, s);
}
int console_printf_select_stderr(bool serial_only, const char *fmt, ...)
{
char buf[CONFIG_SYS_PBSIZE];
va_list args;
int ret;
va_start(args, fmt);
/* For this to work, buf must be larger than anything we ever want to
* print.
*/
ret = vscnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
console_puts_select_stderr(serial_only, buf);
return ret;
}
static void console_puts(int file, bool use_pager, const char *s)
{
int key = 0;
for (s = pager_post(gd_pager(), use_pager, s); s;
s = pager_next(gd_pager(), use_pager, key)) {
struct stdio_dev *dev;
int i;
key = 0;
if (IS_ENABLED(CONFIG_CONSOLE_PAGER) && s == PAGER_WAITING) {
key = getchar();
} else if (*s) {
for_each_console_dev(i, file, dev) {
if (dev->puts != NULL)
dev->puts(dev, s);
}
}
}
}
static void console_puts_pager(int file, const char *s)
{
if (IS_ENABLED(CONFIG_CONSOLE_PAGER) && gd_pager()) {
console_puts(file, true, s);
} else {
struct stdio_dev *dev;
int i;
for_each_console_dev(i, file, dev) {
if (dev->puts != NULL)
dev->puts(dev, s);
}
}
}
#ifdef CONFIG_CONSOLE_FLUSH_SUPPORT
static void console_flush(int file)
{
int i;
struct stdio_dev *dev;
for_each_console_dev(i, file, dev) {
if (dev->flush != NULL)
dev->flush(dev);
}
}
#endif
#if CONFIG_IS_ENABLED(SYS_CONSOLE_IS_IN_ENV)
static inline void console_doenv(int file, struct stdio_dev *dev)
{
iomux_doenv(file, dev->name);
}
#endif
/**
* sdev_file_has_uclass() - Find a file has a device of a particular uclass
*
* Given a file, this returns the first device of the given uclass that is found
* within that file.
*
* This only works with driver model
*
* @file: File to check (e.g. stdout)
* @id: uclass ID to look for
* Return: device, if found, NULL if not
*/
static struct udevice *sdev_file_has_uclass(int file, enum uclass_id id)
{
struct stdio_dev *sdev;
int i;
for_each_console_dev(i, file, sdev) {
if (sdev->flags & DEV_FLAGS_DM) {
struct udevice *dev = sdev->priv;
if (device_get_uclass_id(dev) == id)
return dev;
} else if (id == UCLASS_SERIAL && console_dev_is_serial(sdev)) {
return gd->cur_serial_dev;
}
}
return NULL;
}
#else /* !CONSOLE_MUX */
static void console_devices_set(int file, struct stdio_dev *dev)
{
}
static inline bool console_needs_start_stop(int file, struct stdio_dev *sdev)
{
return true;
}
static inline int console_getc(int file)
{
return stdio_devices[file]->getc(stdio_devices[file]);
}
static bool console_has_tstc(void)
{
return false;
}
static inline int console_tstc(int file)
{
return stdio_devices[file]->tstc(stdio_devices[file]);
}
static inline void console_putc_pager(int file, const char c)
{
stdio_devices[file]->putc(stdio_devices[file], c);
}
void console_puts_select(int file, bool serial_only, const char *s)
{
if ((gd->flags & GD_FLG_DEVINIT) &&
serial_only == console_dev_is_serial(stdio_devices[file]))
stdio_devices[file]->puts(stdio_devices[file], s);
}
static inline void console_puts_pager(int file, const char *s)
{
stdio_devices[file]->puts(stdio_devices[file], s);
}
#ifdef CONFIG_CONSOLE_FLUSH_SUPPORT
static inline void console_flush(int file)
{
if (stdio_devices[file]->flush)
stdio_devices[file]->flush(stdio_devices[file]);
}
#endif
#if CONFIG_IS_ENABLED(SYS_CONSOLE_IS_IN_ENV)
static inline void console_doenv(int file, struct stdio_dev *dev)
{
console_setfile(file, dev);
}
#endif
static inline struct udevice *sdev_file_has_uclass(int file, enum uclass_id id)
{
return NULL;
}
#endif /* CONIFIG_IS_ENABLED(CONSOLE_MUX) */
static int calc_check_console_lines(void)
{
int lines, dev_lines = -1;
struct udevice *dev;
lines = env_get_hex("pager", -1);
if (lines != -1)
return lines;
lines = IF_ENABLED_INT(CONFIG_CONSOLE_PAGER,
CONFIG_CONSOLE_PAGER_LINES);
/* get number of lines from the video console, if available */
if (IS_ENABLED(CONFIG_VIDEO) && video_is_visible()) {
dev = sdev_file_has_uclass(stdout, UCLASS_VIDEO_CONSOLE);
if (dev) {
struct vidconsole_priv *priv;
priv = dev_get_uclass_priv(dev);
dev_lines = priv->rows;
}
}
/* get number of lines from the serial console, if available */
if (IS_ENABLED(CONFIG_DM_SERIAL) &&
sdev_file_has_uclass(stdout, UCLASS_SERIAL)) {
int cols;
if (!serial_is_tty())
dev_lines = 0;
else if (dev_lines == -1) /* Keep as -1 if query fails */
serial_get_size(&dev_lines, &cols);
}
if (dev_lines != -1)
lines = dev_lines;
return lines;
}
static void __maybe_unused console_setfile_and_devices(int file, struct stdio_dev *dev)
{
console_setfile(file, dev);
console_devices_set(file, dev);
}
int console_start(int file, struct stdio_dev *sdev)
{
int error;
if (!console_needs_start_stop(file, sdev))
return 0;
/* Start new device */
if (sdev->start) {
error = sdev->start(sdev);
/* If it's not started don't use it */
if (error < 0)
return error;
}
return 0;
}
void console_stop(int file, struct stdio_dev *sdev)
{
if (!console_needs_start_stop(file, sdev))
return;
if (sdev->stop)
sdev->stop(sdev);
}
/** U-Boot INITIAL CONSOLE-NOT COMPATIBLE FUNCTIONS *************************/
int serial_printf(const char *fmt, ...)
{
va_list args;
uint i;
char printbuffer[CONFIG_SYS_PBSIZE];
va_start(args, fmt);
/* For this to work, printbuffer must be larger than
* anything we ever want to print.
*/
i = vscnprintf(printbuffer, sizeof(printbuffer), fmt, args);
va_end(args);
serial_puts(printbuffer);
return i;
}
int fgetc(int file)
{
if ((unsigned int)file < MAX_FILES) {
/*
* Effectively poll for input wherever it may be available.
*/
for (;;) {
schedule();
if (CONFIG_IS_ENABLED(CONSOLE_MUX)) {
/*
* Upper layer may have already called tstc() so
* check for that first.
*/
if (console_has_tstc())
return console_getc(file);
console_tstc(file);
} else {
if (console_tstc(file))
return console_getc(file);
}
/*
* If the watchdog must be rate-limited then it should
* already be handled in board-specific code.
*/
if (IS_ENABLED(CONFIG_WATCHDOG))
udelay(1);
}
}
return -1;
}
int ftstc(int file)
{
if ((unsigned int)file < MAX_FILES)
return console_tstc(file);
return -1;
}
void fputc(int file, const char c)
{
if ((unsigned int)file < MAX_FILES)
console_putc_pager(file, c);
}
void fputs(int file, const char *s)
{
if ((unsigned int)file < MAX_FILES)
console_puts_pager(file, s);
}
#ifdef CONFIG_CONSOLE_FLUSH_SUPPORT
void fflush(int file)
{
if ((unsigned int)file < MAX_FILES)
console_flush(file);
}
#endif
int fprintf(int file, const char *fmt, ...)
{
va_list args;
uint i;
char printbuffer[CONFIG_SYS_PBSIZE];
va_start(args, fmt);
/* For this to work, printbuffer must be larger than
* anything we ever want to print.
*/
i = vscnprintf(printbuffer, sizeof(printbuffer), fmt, args);
va_end(args);
/* Send to desired file */
fputs(file, printbuffer);
return i;
}
/** U-Boot INITIAL CONSOLE-COMPATIBLE FUNCTION *****************************/
int getchar(void)
{
int ch;
if (IS_ENABLED(CONFIG_DISABLE_CONSOLE) && (gd->flags & GD_FLG_DISABLE_CONSOLE))
return 0;
if (!(gd->flags & GD_FLG_HAVE_CONSOLE))
return 0;
ch = console_record_getc();
if (ch != -1)
return ch;
if (gd->flags & GD_FLG_DEVINIT) {
/* Get from the standard input */
return fgetc(stdin);
}
/* Send directly to the handler */
return serial_getc();
}
int tstc(void)
{
if (IS_ENABLED(CONFIG_DISABLE_CONSOLE) && (gd->flags & GD_FLG_DISABLE_CONSOLE))
return 0;
if (!(gd->flags & GD_FLG_HAVE_CONSOLE))
return 0;
if (console_record_tstc())
return 1;
if (gd->flags & GD_FLG_DEVINIT) {
/* Test the standard input */
return ftstc(stdin);
}
/* Send directly to the handler */
return serial_tstc();
}
#define PRE_CONSOLE_FLUSHPOINT1_SERIAL 0
#define PRE_CONSOLE_FLUSHPOINT2_EVERYTHING_BUT_SERIAL 1
#if CONFIG_IS_ENABLED(PRE_CONSOLE_BUFFER)
#define CIRC_BUF_IDX(idx) ((idx) % (unsigned long)CONFIG_VAL(PRE_CON_BUF_SZ))
static void pre_console_putc(const char c)
{
char *buffer;
if (gd->precon_buf_idx < 0)
return;
buffer = map_sysmem(CONFIG_VAL(PRE_CON_BUF_ADDR), CONFIG_VAL(PRE_CON_BUF_SZ));
buffer[CIRC_BUF_IDX(gd->precon_buf_idx++)] = c;
unmap_sysmem(buffer);
}
static void pre_console_puts(const char *s)
{
if (gd->precon_buf_idx < 0)
return;
while (*s)
pre_console_putc(*s++);
}
static void print_pre_console_buffer(int flushpoint)
{
long in = 0, out = 0;
char buf_out[CONFIG_VAL(PRE_CON_BUF_SZ) + 1];
char *buf_in;
if (IS_ENABLED(CONFIG_SILENT_CONSOLE) && (gd->flags & GD_FLG_SILENT))
return;
buf_in = map_sysmem(CONFIG_VAL(PRE_CON_BUF_ADDR), CONFIG_VAL(PRE_CON_BUF_SZ));
if (gd->precon_buf_idx > CONFIG_VAL(PRE_CON_BUF_SZ))
in = gd->precon_buf_idx - CONFIG_VAL(PRE_CON_BUF_SZ);
while (in < gd->precon_buf_idx)
buf_out[out++] = buf_in[CIRC_BUF_IDX(in++)];
unmap_sysmem(buf_in);
buf_out[out] = 0;
gd->precon_buf_idx = -1;
switch (flushpoint) {
case PRE_CONSOLE_FLUSHPOINT1_SERIAL:
puts(buf_out);
break;
case PRE_CONSOLE_FLUSHPOINT2_EVERYTHING_BUT_SERIAL:
console_puts_select(stdout, false, buf_out);
break;
}
gd->precon_buf_idx = in;
}
#else
static inline void pre_console_putc(const char c) {}
static inline void pre_console_puts(const char *s) {}
static inline void print_pre_console_buffer(int flushpoint) {}
#endif
void putc(const char c)
{
if (!gd)
return;
console_record_putc(c);
/* sandbox can send characters to stdout before it has a console */
if (IS_ENABLED(CONFIG_SANDBOX) && !(gd->flags & GD_FLG_SERIAL_READY)) {
os_putc(c);
return;
}
/* if we don't have a console yet, use the debug UART */
if (IS_ENABLED(CONFIG_DEBUG_UART) && !(gd->flags & GD_FLG_SERIAL_READY)) {
printch(c);
return;
}
if (IS_ENABLED(CONFIG_SILENT_CONSOLE) && (gd->flags & GD_FLG_SILENT)) {
if (!(gd->flags & GD_FLG_DEVINIT))
pre_console_putc(c);
return;
}
if (IS_ENABLED(CONFIG_DISABLE_CONSOLE) && (gd->flags & GD_FLG_DISABLE_CONSOLE))
return;
if (!(gd->flags & GD_FLG_HAVE_CONSOLE))
return pre_console_putc(c);
if (gd->flags & GD_FLG_DEVINIT) {
/* Send to the standard output */
fputc(stdout, c);
} else {
/* Send directly to the handler */
pre_console_putc(c);
serial_putc(c);
}
}
void puts(const char *s)
{
if (!gd)
return;
console_record_puts(s);
/* sandbox can send characters to stdout before it has a console */
if (IS_ENABLED(CONFIG_SANDBOX) && !(gd->flags & GD_FLG_SERIAL_READY)) {
os_puts(s);
return;
}
if (IS_ENABLED(CONFIG_DEBUG_UART) && !(gd->flags & GD_FLG_SERIAL_READY)) {
printascii(s);
return;
}
if (IS_ENABLED(CONFIG_SILENT_CONSOLE) && (gd->flags & GD_FLG_SILENT)) {
if (!(gd->flags & GD_FLG_DEVINIT))
pre_console_puts(s);
return;
}
if (IS_ENABLED(CONFIG_DISABLE_CONSOLE) && (gd->flags & GD_FLG_DISABLE_CONSOLE))
return;
if (!(gd->flags & GD_FLG_HAVE_CONSOLE))
return pre_console_puts(s);
if (gd->flags & GD_FLG_DEVINIT) {
/* Send to the standard output */
fputs(stdout, s);
} else {
/* Send directly to the handler */
pre_console_puts(s);
serial_puts(s);
}
}
#ifdef CONFIG_CONSOLE_FLUSH_SUPPORT
void flush(void)
{
if (!gd)
return;
/* sandbox can send characters to stdout before it has a console */
if (IS_ENABLED(CONFIG_SANDBOX) && !(gd->flags & GD_FLG_SERIAL_READY)) {
os_flush();
return;
}
if (IS_ENABLED(CONFIG_DEBUG_UART) && !(gd->flags & GD_FLG_SERIAL_READY))
return;
if (IS_ENABLED(CONFIG_SILENT_CONSOLE) && (gd->flags & GD_FLG_SILENT))
return;
if (IS_ENABLED(CONFIG_DISABLE_CONSOLE) && (gd->flags & GD_FLG_DISABLE_CONSOLE))
return;
if (!(gd->flags & GD_FLG_HAVE_CONSOLE))
return;
if (gd->flags & GD_FLG_DEVINIT) {
/* Send to the standard output */
fflush(stdout);
} else {
/* Send directly to the handler */
serial_flush();
}
}
#endif
#ifdef CONFIG_CONSOLE_RECORD
int console_record_init(void)
{
int ret;
ret = membuf_new((struct membuf *)&gd->console_out,
gd->flags & GD_FLG_RELOC ?
CONFIG_CONSOLE_RECORD_OUT_SIZE :
CONFIG_CONSOLE_RECORD_OUT_SIZE_F);
if (ret)
return ret;
ret = membuf_new((struct membuf *)&gd->console_in,
CONFIG_CONSOLE_RECORD_IN_SIZE);
/* Start recording from the beginning */
gd->flags |= GD_FLG_RECORD;
return ret;
}
void console_record_reset(void)
{
membuf_purge((struct membuf *)&gd->console_out);
membuf_purge((struct membuf *)&gd->console_in);
gd->flags &= ~GD_FLG_RECORD_OVF;
}
int console_record_reset_enable(void)
{
console_record_reset();
gd->flags |= GD_FLG_RECORD;
return 0;
}
int console_record_readline(char *str, int maxlen)
{
if (gd->flags & GD_FLG_RECORD_OVF)
return -ENOSPC;
if (console_record_isempty())
return -ENOENT;
return membuf_readline((struct membuf *)&gd->console_out, str,
maxlen, '\0', false);
}
int console_record_avail(void)
{
return membuf_avail((struct membuf *)&gd->console_out);
}
bool console_record_isempty(void)
{
return membuf_isempty((struct membuf *)&gd->console_out);
}
int console_in_puts(const char *str)
{
return membuf_put((struct membuf *)&gd->console_in, str, strlen(str));
}
#endif
/* test if ctrl-c was pressed */
static int ctrlc_disabled = 0; /* see disable_ctrl() */
static int ctrlc_was_pressed = 0;
int ctrlc(void)
{
if (!ctrlc_disabled && (gd->flags & GD_FLG_HAVE_CONSOLE)) {
if (tstc()) {
switch (getchar()) {
case 0x03: /* ^C - Control C */
ctrlc_was_pressed = 1;
return 1;
default:
break;
}
}
}
return 0;
}
/* Reads user's confirmation.
Returns 1 if user's input is "y", "Y", "yes" or "YES"
*/
int confirm_yesno(void)
{
int i;
char str_input[5];
/* Flush input */
while (tstc())
getchar();
i = 0;
while (i < sizeof(str_input)) {
str_input[i] = getchar();
putc(str_input[i]);
if (str_input[i] == '\r')
break;
i++;
}
putc('\n');
if (strncmp(str_input, "y\r", 2) == 0 ||
strncmp(str_input, "Y\r", 2) == 0 ||
strncmp(str_input, "yes\r", 4) == 0 ||
strncmp(str_input, "YES\r", 4) == 0)
return 1;
return 0;
}
/* pass 1 to disable ctrlc() checking, 0 to enable.
* returns previous state
*/
int disable_ctrlc(int disable)
{
int prev = ctrlc_disabled; /* save previous state */
ctrlc_disabled = disable;
return prev;
}
int had_ctrlc (void)
{
return ctrlc_was_pressed;
}
void clear_ctrlc(void)
{
ctrlc_was_pressed = 0;
}
/** U-Boot INIT FUNCTIONS *************************************************/
struct stdio_dev *console_search_dev(int flags, const char *name)
{
struct stdio_dev *dev;
dev = stdio_get_by_name(name);
#ifdef CONFIG_VIDCONSOLE_AS_LCD
if (!dev && !strcmp(name, CONFIG_VIDCONSOLE_AS_NAME))
dev = stdio_get_by_name("vidconsole");
#endif
if (dev && (dev->flags & flags))
return dev;
return NULL;
}
int console_assign(int file, const char *devname)
{
int flag;
struct stdio_dev *dev;
/* Check for valid file */
flag = stdio_file_to_flags(file);
if (flag < 0)
return flag;
/* Check for valid device name */
dev = console_search_dev(flag, devname);
if (dev)
return console_setfile(file, dev);
return -1;
}
/* return true if the 'silent' flag is removed */
static bool console_update_silent(void)
{
unsigned long flags = gd->flags;
if (!IS_ENABLED(CONFIG_SILENT_CONSOLE))
return false;
if (IS_ENABLED(CONFIG_SILENT_CONSOLE_UNTIL_ENV) && !(gd->flags & GD_FLG_ENV_READY)) {
gd->flags |= GD_FLG_SILENT;
return false;
}
if (env_get("silent")) {
gd->flags |= GD_FLG_SILENT;
return false;
}
gd->flags &= ~GD_FLG_SILENT;
return !!(flags & GD_FLG_SILENT);
}
int console_announce_r(void)
{
#if !CONFIG_IS_ENABLED(PRE_CONSOLE_BUFFER)
char buf[DISPLAY_OPTIONS_BANNER_LENGTH];
display_options_get_banner(false, buf, sizeof(buf));
console_puts_select(stdout, false, buf);
#endif
return 0;
}
/* Called before relocation - use serial functions */
int console_init_f(void)
{
gd->flags |= GD_FLG_HAVE_CONSOLE;
console_update_silent();
print_pre_console_buffer(PRE_CONSOLE_FLUSHPOINT1_SERIAL);
return 0;
}
int console_clear(void)
{
/*
* Send clear screen and home
*
* FIXME(Heinrich Schuchardt <xypron.glpk@gmx.de>): This should go
* through an API and only be written to serial terminals, not video
* displays
*/
printf(CSI "2J" CSI "1;1H");
if (IS_ENABLED(CONFIG_VIDEO_ANSI))
return 0;
if (IS_ENABLED(CONFIG_VIDEO)) {
struct udevice *dev;
int ret;
ret = uclass_first_device_err(UCLASS_VIDEO_CONSOLE, &dev);
if (ret)
return ret;
ret = vidconsole_clear_and_reset(dev);
if (ret)
return ret;
}
return 0;
}
static char *get_stdio(const u8 std)
{
return stdio_devices[std] ? stdio_devices[std]->name : "No devices available!";
}
static void stdio_print_current_devices(void)
{
char *stdinname = NULL;
char *stdoutname = NULL;
char *stderrname = NULL;
if (CONFIG_IS_ENABLED(CONSOLE_MUX) &&
CONFIG_IS_ENABLED(SYS_CONSOLE_IS_IN_ENV)) {
/* stdin stdout and stderr are in environment */
stdinname = env_get("stdin");
stdoutname = env_get("stdout");
stderrname = env_get("stderr");
}
stdinname = stdinname ? : get_stdio(stdin);
stdoutname = stdoutname ? : get_stdio(stdout);
stderrname = stderrname ? : get_stdio(stderr);
/* Print information */
puts("In: ");
printf("%s\n", stdinname);
puts("Out: ");
printf("%s\n", stdoutname);
puts("Err: ");
printf("%s\n", stderrname);
}
static int on_console(const char *name, const char *value, enum env_op op,
int flags)
{
int console = -1;
int result = 0;
/* Check for console redirection */
if (strcmp(name, "stdin") == 0)
console = stdin;
else if (strcmp(name, "stdout") == 0)
console = stdout;
else if (strcmp(name, "stderr") == 0)
console = stderr;
/* if not actually setting a console variable, we don't care */
if (console == -1 || (gd->flags & GD_FLG_DEVINIT) == 0)
return 0;
switch (op) {
case env_op_create:
case env_op_overwrite:
if (CONFIG_IS_ENABLED(CONSOLE_MUX)) {
if (iomux_doenv(console, value))
result = 1;
} else {
/* Try assigning specified device */
if (console_assign(console, value) < 0)
result = 1;
}
break;
case env_op_delete:
if ((flags & H_FORCE) == 0)
printf("Can't delete \"%s\"\n", name);
result = 1;
break;
default:
break;
}
if (IS_ENABLED(CONFIG_CONSOLE_PAGER) && console == stdout) {
int lines = calc_check_console_lines();
/* Set bypass mode if not connected to a terminal */
pager_set_bypass(gd_pager(), lines != 0);
if (lines)
pager_set_page_len(gd_pager(), lines);
}
return result;
}
U_BOOT_ENV_CALLBACK(console, on_console);
#ifdef CONFIG_SILENT_CONSOLE
static int on_silent(const char *name, const char *value, enum env_op op,
int flags)
{
if (!CONFIG_IS_ENABLED(SILENT_CONSOLE_UPDATE_ON_SET))
if (flags & H_INTERACTIVE)
return 0;
if (!CONFIG_IS_ENABLED(SILENT_CONSOLE_UPDATE_ON_RELOC))
if ((flags & H_INTERACTIVE) == 0)
return 0;
if (value)
gd->flags |= GD_FLG_SILENT;
else
gd->flags &= ~GD_FLG_SILENT;
return 0;
}
U_BOOT_ENV_CALLBACK(silent, on_silent);
#endif
static void setup_pager(void)
{
/* Init pager now that console is ready */
if (IS_ENABLED(CONFIG_CONSOLE_PAGER)) {
int ret;
ret = pager_init(gd_pagerp(), calc_check_console_lines(),
PAGER_BUF_SIZE);
if (ret)
printf("Failed to init pager\n");
}
}
#if CONFIG_IS_ENABLED(SYS_CONSOLE_IS_IN_ENV)
/* Called after the relocation - use desired console functions */
int console_init_r(void)
{
char *stdinname, *stdoutname, *stderrname;
struct stdio_dev *inputdev = NULL, *outputdev = NULL, *errdev = NULL;
int i;
int iomux_err = 0;
int flushpoint;
/* update silent for env loaded from flash (initr_env) */
if (console_update_silent())
flushpoint = PRE_CONSOLE_FLUSHPOINT1_SERIAL;
else
flushpoint = PRE_CONSOLE_FLUSHPOINT2_EVERYTHING_BUT_SERIAL;
/* set default handlers at first */
gd->jt->getc = serial_getc;
gd->jt->tstc = serial_tstc;
gd->jt->putc = serial_putc;
gd->jt->puts = serial_puts;
gd->jt->printf = serial_printf;
/* stdin stdout and stderr are in environment */
/* scan for it */
stdinname = env_get("stdin");
stdoutname = env_get("stdout");
stderrname = env_get("stderr");
if (OVERWRITE_CONSOLE == 0) { /* if not overwritten by config switch */
inputdev = console_search_dev(DEV_FLAGS_INPUT, stdinname);
outputdev = console_search_dev(DEV_FLAGS_OUTPUT, stdoutname);
errdev = console_search_dev(DEV_FLAGS_OUTPUT, stderrname);
if (CONFIG_IS_ENABLED(CONSOLE_MUX)) {
iomux_err = iomux_doenv(stdin, stdinname);
iomux_err += iomux_doenv(stdout, stdoutname);
iomux_err += iomux_doenv(stderr, stderrname);
if (!iomux_err)
/* Successful, so skip all the code below. */
goto done;
}
}
/* if the devices are overwritten or not found, use default device */
if (inputdev == NULL) {
inputdev = console_search_dev(DEV_FLAGS_INPUT, "serial");
}
if (outputdev == NULL) {
outputdev = console_search_dev(DEV_FLAGS_OUTPUT, "serial");
}
if (errdev == NULL) {
errdev = console_search_dev(DEV_FLAGS_OUTPUT, "serial");
}
/* Initializes output console first */
if (outputdev != NULL) {
/* need to set a console if not done above. */
console_doenv(stdout, outputdev);
}
if (errdev != NULL) {
/* need to set a console if not done above. */
console_doenv(stderr, errdev);
}
if (inputdev != NULL) {
/* need to set a console if not done above. */
console_doenv(stdin, inputdev);
}
done:
if (!IS_ENABLED(CONFIG_SYS_CONSOLE_INFO_QUIET))
stdio_print_current_devices();
#ifdef CONFIG_VIDCONSOLE_AS_LCD
if (strstr(stdoutname, CONFIG_VIDCONSOLE_AS_NAME))
printf("Warning: Please change '%s' to 'vidconsole' in stdout/stderr environment vars\n",
CONFIG_VIDCONSOLE_AS_NAME);
#endif
if (IS_ENABLED(CONFIG_SYS_CONSOLE_ENV_OVERWRITE)) {
/* set the environment variables (will overwrite previous env settings) */
for (i = 0; i < MAX_FILES; i++)
env_set(stdio_names[i], stdio_devices[i]->name);
}
gd->flags |= GD_FLG_DEVINIT; /* device initialization completed */
setup_pager();
print_pre_console_buffer(flushpoint);
return 0;
}
#else /* !CONFIG_IS_ENABLED(SYS_CONSOLE_IS_IN_ENV) */
/* Called after the relocation - use desired console functions */
int console_init_r(void)
{
struct stdio_dev *inputdev = NULL, *outputdev = NULL;
int i;
struct list_head *list = stdio_get_list();
struct list_head *pos;
struct stdio_dev *dev;
int flushpoint;
/* update silent for env loaded from flash (initr_env) */
if (console_update_silent())
flushpoint = PRE_CONSOLE_FLUSHPOINT1_SERIAL;
else
flushpoint = PRE_CONSOLE_FLUSHPOINT2_EVERYTHING_BUT_SERIAL;
/*
* suppress all output if splash screen is enabled and we have
* a bmp to display. We redirect the output from frame buffer
* console to serial console in this case or suppress it if
* "silent" mode was requested.
*/
if (IS_ENABLED(CONFIG_SPLASH_SCREEN) && env_get("splashimage")) {
if (!(gd->flags & GD_FLG_SILENT))
outputdev = console_search_dev (DEV_FLAGS_OUTPUT, "serial");
}
/* Scan devices looking for input and output devices */
list_for_each(pos, list) {
dev = list_entry(pos, struct stdio_dev, list);
if ((dev->flags & DEV_FLAGS_INPUT) && (inputdev == NULL)) {
inputdev = dev;
}
if ((dev->flags & DEV_FLAGS_OUTPUT) && (outputdev == NULL)) {
outputdev = dev;
}
if(inputdev && outputdev)
break;
}
/* Initializes output console first */
if (outputdev != NULL) {
console_setfile_and_devices(stdout, outputdev);
console_setfile_and_devices(stderr, outputdev);
}
/* Initializes input console */
if (inputdev != NULL)
console_setfile_and_devices(stdin, inputdev);
if (!IS_ENABLED(CONFIG_SYS_CONSOLE_INFO_QUIET))
stdio_print_current_devices();
/* Setting environment variables */
for (i = 0; i < MAX_FILES; i++) {
env_set(stdio_names[i], stdio_devices[i]->name);
}
gd->flags |= GD_FLG_DEVINIT; /* device initialization completed */
setup_pager();
print_pre_console_buffer(flushpoint);
return 0;
}
#endif /* CONFIG_IS_ENABLED(SYS_CONSOLE_IS_IN_ENV) */
int console_remove_by_name(const char *name)
{
int err = 0;
#if CONFIG_IS_ENABLED(CONSOLE_MUX)
int fnum;
log_debug("removing console device %s\n", name);
for (fnum = 0; fnum < MAX_FILES; fnum++) {
struct stdio_dev **src, **dest;
int i;
log_debug("file %d: %d devices: ", fnum, cd_count[fnum]);
src = console_devices[fnum];
dest = src;
for (i = 0; i < cd_count[fnum]; i++, src++) {
struct stdio_dev *sdev = *src;
int ret = 0;
if (!strcmp(sdev->name, name))
ret = stdio_deregister_dev(sdev, true);
else
*dest++ = *src;
if (ret && !err)
err = ret;
}
cd_count[fnum] = dest - console_devices[fnum];
log_debug("now %d\n", cd_count[fnum]);
}
#endif /* CONSOLE_MUX */
return err;
}