Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
39290ca4cd | ||
|
|
cfad2cabd3 | ||
|
|
0ae6a74826 | ||
|
|
c00ce7e4fd | ||
|
|
4dbff9414e | ||
|
|
5a316de7eb | ||
|
|
3df7ef8813 | ||
|
|
89f7699c81 | ||
|
|
13a69d6460 | ||
|
|
7d82e5c79b | ||
|
|
88549138f6 | ||
|
|
831e273881 | ||
|
|
107c045dde | ||
|
|
e1375a5783 | ||
|
|
d84b68603c | ||
|
|
390bf2c3dd | ||
|
|
f75ea56c11 | ||
|
|
06ff6888a1 | ||
|
|
282f24e87d | ||
|
|
41726522d4 | ||
|
|
363aa5cc8d | ||
|
|
56483c8367 | ||
|
|
a845243258 |
@@ -12,6 +12,7 @@
|
|||||||
#include <init.h>
|
#include <init.h>
|
||||||
#include <log.h>
|
#include <log.h>
|
||||||
#include <os.h>
|
#include <os.h>
|
||||||
|
#include <pager.h>
|
||||||
#include <sandbox_host.h>
|
#include <sandbox_host.h>
|
||||||
#include <sort.h>
|
#include <sort.h>
|
||||||
#include <spl.h>
|
#include <spl.h>
|
||||||
@@ -136,6 +137,10 @@ int sandbox_main_loop_init(void)
|
|||||||
{
|
{
|
||||||
struct sandbox_state *state = state_get_current();
|
struct sandbox_state *state = state_get_current();
|
||||||
|
|
||||||
|
/* Apply pager bypass if requested */
|
||||||
|
if (state->pager_bypass)
|
||||||
|
pager_set_test_bypass(gd_pager(), true);
|
||||||
|
|
||||||
/* Execute command if required */
|
/* Execute command if required */
|
||||||
if (state->cmd || state->run_distro_boot) {
|
if (state->cmd || state->run_distro_boot) {
|
||||||
int retval = 0;
|
int retval = 0;
|
||||||
@@ -433,6 +438,15 @@ static int sandbox_cmdline_cb_autoboot_keyed(struct sandbox_state *state,
|
|||||||
}
|
}
|
||||||
SANDBOX_CMDLINE_OPT(autoboot_keyed, 0, "Allow keyed autoboot");
|
SANDBOX_CMDLINE_OPT(autoboot_keyed, 0, "Allow keyed autoboot");
|
||||||
|
|
||||||
|
static int sandbox_cmdline_cb_no_term_present(struct sandbox_state *state,
|
||||||
|
const char *arg)
|
||||||
|
{
|
||||||
|
state->no_term_present = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
SANDBOX_CMDLINE_OPT_SHORT(no_term_present, 'A', 0,
|
||||||
|
"Assume no terminal present (for pager testing)");
|
||||||
|
|
||||||
static int sandbox_cmdline_cb_upl(struct sandbox_state *state, const char *arg)
|
static int sandbox_cmdline_cb_upl(struct sandbox_state *state, const char *arg)
|
||||||
{
|
{
|
||||||
state->upl = true;
|
state->upl = true;
|
||||||
@@ -480,6 +494,16 @@ static int sandbox_cmdline_cb_soft_fail(struct sandbox_state *state,
|
|||||||
SANDBOX_CMDLINE_OPT_SHORT(soft_fail, 'f', 0,
|
SANDBOX_CMDLINE_OPT_SHORT(soft_fail, 'f', 0,
|
||||||
"continue test execution even after it fails");
|
"continue test execution even after it fails");
|
||||||
|
|
||||||
|
static int sandbox_cmdline_cb_pager_bypass(struct sandbox_state *state,
|
||||||
|
const char *arg)
|
||||||
|
{
|
||||||
|
state->pager_bypass = true;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
SANDBOX_CMDLINE_OPT_SHORT(pager_bypass, 'P', 0,
|
||||||
|
"Enable pager bypass mode");
|
||||||
|
|
||||||
static int sandbox_cmdline_cb_bind(struct sandbox_state *state, const char *arg)
|
static int sandbox_cmdline_cb_bind(struct sandbox_state *state, const char *arg)
|
||||||
{
|
{
|
||||||
if (state->num_binds >= SB_MAX_BINDS) {
|
if (state->num_binds >= SB_MAX_BINDS) {
|
||||||
|
|||||||
@@ -175,6 +175,8 @@ struct sandbox_state {
|
|||||||
bool native; /* Adjust to reflect host arch */
|
bool native; /* Adjust to reflect host arch */
|
||||||
bool no_flattree_tests; /* Don't run second set of DM tests */
|
bool no_flattree_tests; /* Don't run second set of DM tests */
|
||||||
bool soft_fail; /* Continue on failure */
|
bool soft_fail; /* Continue on failure */
|
||||||
|
bool pager_bypass; /* Enable pager-bypass mode */
|
||||||
|
bool no_term_present; /* Assume no terminal present */
|
||||||
|
|
||||||
/* Pointer to information for each SPI bus/cs */
|
/* Pointer to information for each SPI bus/cs */
|
||||||
struct sandbox_spi_info spi[CONFIG_SANDBOX_SPI_MAX_BUS]
|
struct sandbox_spi_info spi[CONFIG_SANDBOX_SPI_MAX_BUS]
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
#include <lmb.h>
|
#include <lmb.h>
|
||||||
#include <mapmem.h>
|
#include <mapmem.h>
|
||||||
#include <net.h>
|
#include <net.h>
|
||||||
|
#include <pager.h>
|
||||||
#include <serial.h>
|
#include <serial.h>
|
||||||
#include <video.h>
|
#include <video.h>
|
||||||
#include <vsprintf.h>
|
#include <vsprintf.h>
|
||||||
@@ -132,6 +133,8 @@ static int bdinfo_print_all(struct bd_info *bd)
|
|||||||
lprint_num_l("fdt_blob", (ulong)map_to_sysmem(gd->fdt_blob));
|
lprint_num_l("fdt_blob", (ulong)map_to_sysmem(gd->fdt_blob));
|
||||||
if (IS_ENABLED(CONFIG_VIDEO))
|
if (IS_ENABLED(CONFIG_VIDEO))
|
||||||
show_video_info();
|
show_video_info();
|
||||||
|
if (IS_ENABLED(CONFIG_CONSOLE_PAGER))
|
||||||
|
printf("pager = %d\n", gd_pager_page_len());
|
||||||
#if CONFIG_IS_ENABLED(MULTI_DTB_FIT)
|
#if CONFIG_IS_ENABLED(MULTI_DTB_FIT)
|
||||||
lprint_num_l("multi_dtb_fit", (ulong)gd->multi_dtb_fit);
|
lprint_num_l("multi_dtb_fit", (ulong)gd->multi_dtb_fit);
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -332,6 +332,28 @@ config SYS_DEVICE_NULLDEV
|
|||||||
operation of the console by setting stdout to "nulldev". Enable
|
operation of the console by setting stdout to "nulldev". Enable
|
||||||
this to use a serial console under board control.
|
this to use a serial console under board control.
|
||||||
|
|
||||||
|
config CONSOLE_PAGER
|
||||||
|
bool "Enable console output paging"
|
||||||
|
depends on CONSOLE_MUX
|
||||||
|
imply CMD_CONSOLE_EXTRA
|
||||||
|
imply SERIAL_TERM_PRESENT
|
||||||
|
default y if SANDBOX
|
||||||
|
help
|
||||||
|
Enable pager functionality for console output. When enabled, long
|
||||||
|
output will be paused after a configurable number of lines, waiting
|
||||||
|
for user input (SPACE) to continue. The number of lines per page is
|
||||||
|
controlled by the 'pager' environment variable. If the variable is
|
||||||
|
not set or is empty, paging is disabled.
|
||||||
|
|
||||||
|
config CONSOLE_PAGER_LINES
|
||||||
|
int "Number of lines per page"
|
||||||
|
depends on CONSOLE_PAGER
|
||||||
|
default 25
|
||||||
|
help
|
||||||
|
Sets the default number of lines that the pager assumes is visible on
|
||||||
|
the display. This is used as a default if the "pager" environment
|
||||||
|
variable is unset.
|
||||||
|
|
||||||
endmenu
|
endmenu
|
||||||
|
|
||||||
menu "Logging"
|
menu "Logging"
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ obj-$(CONFIG_MII) += miiphyutil.o
|
|||||||
obj-$(CONFIG_CMD_MII) += miiphyutil.o
|
obj-$(CONFIG_CMD_MII) += miiphyutil.o
|
||||||
obj-$(CONFIG_PHYLIB) += miiphyutil.o
|
obj-$(CONFIG_PHYLIB) += miiphyutil.o
|
||||||
|
|
||||||
|
obj-$(CONFIG_CONSOLE_PAGER) += pager.o
|
||||||
|
|
||||||
obj-$(CONFIG_USB_HOST) += usb.o usb_hub.o
|
obj-$(CONFIG_USB_HOST) += usb.o usb_hub.o
|
||||||
obj-$(CONFIG_USB_GADGET) += usb.o
|
obj-$(CONFIG_USB_GADGET) += usb.o
|
||||||
obj-$(CONFIG_USB_STORAGE) += usb_storage.o
|
obj-$(CONFIG_USB_STORAGE) += usb_storage.o
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
#include <command.h>
|
#include <command.h>
|
||||||
#include <hang.h>
|
#include <hang.h>
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
|
#include <pager.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <watchdog.h>
|
#include <watchdog.h>
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
@@ -650,6 +651,9 @@ int cli_readline_into_buffer(const char *const prompt, char *buffer,
|
|||||||
uint len = CONFIG_SYS_CBSIZE;
|
uint len = CONFIG_SYS_CBSIZE;
|
||||||
int rc;
|
int rc;
|
||||||
static int initted;
|
static int initted;
|
||||||
|
bool old_bypass;
|
||||||
|
|
||||||
|
old_bypass = pager_set_bypass(gd_pager(), true);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Say N to CMD_HISTORY_USE_CALLOC will skip runtime
|
* Say N to CMD_HISTORY_USE_CALLOC will skip runtime
|
||||||
@@ -673,9 +677,14 @@ int cli_readline_into_buffer(const char *const prompt, char *buffer,
|
|||||||
puts(prompt);
|
puts(prompt);
|
||||||
|
|
||||||
rc = cread_line(prompt, p, &len, timeout);
|
rc = cread_line(prompt, p, &len, timeout);
|
||||||
return rc < 0 ? rc : len;
|
rc = rc < 0 ? rc : len;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
return cread_line_simple(prompt, p);
|
rc = cread_line_simple(prompt, p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pager_set_bypass(gd_pager(), old_bypass);
|
||||||
|
pager_reset(gd_pager());
|
||||||
|
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|||||||
170
common/console.c
170
common/console.c
@@ -16,6 +16,7 @@
|
|||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
#include <mapmem.h>
|
#include <mapmem.h>
|
||||||
#include <os.h>
|
#include <os.h>
|
||||||
|
#include <pager.h>
|
||||||
#include <serial.h>
|
#include <serial.h>
|
||||||
#include <stdio_dev.h>
|
#include <stdio_dev.h>
|
||||||
#include <exports.h>
|
#include <exports.h>
|
||||||
@@ -256,14 +257,22 @@ static int console_tstc(int file)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void console_putc(int file, const char c)
|
static void console_puts_pager(int file, const char *s);
|
||||||
{
|
|
||||||
int i;
|
|
||||||
struct stdio_dev *dev;
|
|
||||||
|
|
||||||
for_each_console_dev(i, file, dev) {
|
static void console_putc_pager(int file, const char c)
|
||||||
if (dev->putc != NULL)
|
{
|
||||||
dev->putc(dev, 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -312,14 +321,39 @@ int console_printf_select_stderr(bool serial_only, const char *fmt, ...)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void console_puts(int file, const char *s)
|
static void console_puts(int file, bool use_pager, const char *s)
|
||||||
{
|
{
|
||||||
int i;
|
int key = 0;
|
||||||
struct stdio_dev *dev;
|
|
||||||
|
|
||||||
for_each_console_dev(i, file, dev) {
|
for (s = pager_post(gd_pager(), use_pager, s); s;
|
||||||
if (dev->puts != NULL)
|
s = pager_next(gd_pager(), use_pager, key)) {
|
||||||
dev->puts(dev, s);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -342,7 +376,39 @@ static inline void console_doenv(int file, struct stdio_dev *dev)
|
|||||||
iomux_doenv(file, dev->name);
|
iomux_doenv(file, dev->name);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#else
|
|
||||||
|
/**
|
||||||
|
* 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 void console_devices_set(int file, struct stdio_dev *dev)
|
||||||
{
|
{
|
||||||
@@ -368,7 +434,7 @@ static inline int console_tstc(int file)
|
|||||||
return stdio_devices[file]->tstc(stdio_devices[file]);
|
return stdio_devices[file]->tstc(stdio_devices[file]);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void console_putc(int file, const char c)
|
static inline void console_putc_pager(int file, const char c)
|
||||||
{
|
{
|
||||||
stdio_devices[file]->putc(stdio_devices[file], c);
|
stdio_devices[file]->putc(stdio_devices[file], c);
|
||||||
}
|
}
|
||||||
@@ -380,7 +446,7 @@ void console_puts_select(int file, bool serial_only, const char *s)
|
|||||||
stdio_devices[file]->puts(stdio_devices[file], s);
|
stdio_devices[file]->puts(stdio_devices[file], s);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void console_puts(int file, const char *s)
|
static inline void console_puts_pager(int file, const char *s)
|
||||||
{
|
{
|
||||||
stdio_devices[file]->puts(stdio_devices[file], s);
|
stdio_devices[file]->puts(stdio_devices[file], s);
|
||||||
}
|
}
|
||||||
@@ -399,8 +465,52 @@ static inline void console_doenv(int file, struct stdio_dev *dev)
|
|||||||
console_setfile(file, dev);
|
console_setfile(file, dev);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static inline struct udevice *sdev_file_has_uclass(int file, enum uclass_id id)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* CONIFIG_IS_ENABLED(CONSOLE_MUX) */
|
#endif /* CONIFIG_IS_ENABLED(CONSOLE_MUX) */
|
||||||
|
|
||||||
|
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)
|
||||||
|
serial_query_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)
|
static void __maybe_unused console_setfile_and_devices(int file, struct stdio_dev *dev)
|
||||||
{
|
{
|
||||||
console_setfile(file, dev);
|
console_setfile(file, dev);
|
||||||
@@ -497,13 +607,13 @@ int ftstc(int file)
|
|||||||
void fputc(int file, const char c)
|
void fputc(int file, const char c)
|
||||||
{
|
{
|
||||||
if ((unsigned int)file < MAX_FILES)
|
if ((unsigned int)file < MAX_FILES)
|
||||||
console_putc(file, c);
|
console_putc_pager(file, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
void fputs(int file, const char *s)
|
void fputs(int file, const char *s)
|
||||||
{
|
{
|
||||||
if ((unsigned int)file < MAX_FILES)
|
if ((unsigned int)file < MAX_FILES)
|
||||||
console_puts(file, s);
|
console_puts_pager(file, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_CONSOLE_FLUSH_SUPPORT
|
#ifdef CONFIG_CONSOLE_FLUSH_SUPPORT
|
||||||
@@ -1075,6 +1185,15 @@ static int on_console(const char *name, const char *value, enum env_op op,
|
|||||||
break;
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
U_BOOT_ENV_CALLBACK(console, on_console);
|
U_BOOT_ENV_CALLBACK(console, on_console);
|
||||||
@@ -1101,6 +1220,19 @@ static int on_silent(const char *name, const char *value, enum env_op op,
|
|||||||
U_BOOT_ENV_CALLBACK(silent, on_silent);
|
U_BOOT_ENV_CALLBACK(silent, on_silent);
|
||||||
#endif
|
#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)
|
#if CONFIG_IS_ENABLED(SYS_CONSOLE_IS_IN_ENV)
|
||||||
/* Called after the relocation - use desired console functions */
|
/* Called after the relocation - use desired console functions */
|
||||||
int console_init_r(void)
|
int console_init_r(void)
|
||||||
@@ -1185,6 +1317,7 @@ done:
|
|||||||
}
|
}
|
||||||
|
|
||||||
gd->flags |= GD_FLG_DEVINIT; /* device initialization completed */
|
gd->flags |= GD_FLG_DEVINIT; /* device initialization completed */
|
||||||
|
setup_pager();
|
||||||
|
|
||||||
print_pre_console_buffer(flushpoint);
|
print_pre_console_buffer(flushpoint);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -1252,6 +1385,7 @@ int console_init_r(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
gd->flags |= GD_FLG_DEVINIT; /* device initialization completed */
|
gd->flags |= GD_FLG_DEVINIT; /* device initialization completed */
|
||||||
|
setup_pager();
|
||||||
|
|
||||||
print_pre_console_buffer(flushpoint);
|
print_pre_console_buffer(flushpoint);
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
219
common/pager.c
Normal file
219
common/pager.c
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0+
|
||||||
|
/*
|
||||||
|
* Deals with splitting up text output into separate screenfuls
|
||||||
|
*
|
||||||
|
* Copyright 2025 Simon Glass <sjg@chromium.org>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define LOG_CATEGORY LOGC_CONSOLE
|
||||||
|
|
||||||
|
#include <env.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
#include <pager.h>
|
||||||
|
#include <asm/global_data.h>
|
||||||
|
|
||||||
|
DECLARE_GLOBAL_DATA_PTR;
|
||||||
|
|
||||||
|
const char *pager_post(struct pager *pag, bool use_pager, const char *s)
|
||||||
|
{
|
||||||
|
struct membuf old;
|
||||||
|
int ret, len;
|
||||||
|
|
||||||
|
if (!pag || !use_pager || pag->test_bypass || pag->state == PAGERST_BYPASS)
|
||||||
|
return s;
|
||||||
|
|
||||||
|
len = strlen(s);
|
||||||
|
if (!len)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
old = pag->mb;
|
||||||
|
ret = membuf_put(&pag->mb, s, len);
|
||||||
|
if (ret == len) {
|
||||||
|
/* all is well */
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* We couldn't store any of the text, so we'll store none of
|
||||||
|
* it. The pager is now in an non-functional state until it
|
||||||
|
* can eject the overflow text.
|
||||||
|
*
|
||||||
|
* The buffer is presumably empty, since callers are not allowed
|
||||||
|
* to call pager_post() unless all the output from the previous
|
||||||
|
* call was provided via pager_next().
|
||||||
|
*/
|
||||||
|
pag->overflow = s;
|
||||||
|
pag->mb = old;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pager_next(pag, true, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *pager_next(struct pager *pag, bool use_pager, int key)
|
||||||
|
{
|
||||||
|
char *str, *p, *end;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!use_pager || (pag && pag->test_bypass))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* replace the real character we overwrite with nul, if needed */
|
||||||
|
if (pag->nulch) {
|
||||||
|
*pag->nulch = pag->oldch;
|
||||||
|
pag->nulch = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if we're at the limit, wait */
|
||||||
|
switch (pag->state) {
|
||||||
|
case PAGERST_OK:
|
||||||
|
break;
|
||||||
|
case PAGERST_AT_LIMIT:
|
||||||
|
pag->state = PAGERST_WAIT_USER;
|
||||||
|
return PAGER_PROMPT;
|
||||||
|
case PAGERST_WAIT_USER:
|
||||||
|
if (key != ' ')
|
||||||
|
return PAGER_WAITING;
|
||||||
|
pag->state = PAGERST_CLEAR_PROMPT;
|
||||||
|
return PAGER_BLANK;
|
||||||
|
case PAGERST_CLEAR_PROMPT:
|
||||||
|
pag->state = PAGERST_OK;
|
||||||
|
break;
|
||||||
|
case PAGERST_BYPASS:
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = membuf_getraw(&pag->mb, pag->buf.size - 1, false, &str);
|
||||||
|
if (!ret) {
|
||||||
|
if (pag->overflow) {
|
||||||
|
const char *oflow = pag->overflow;
|
||||||
|
|
||||||
|
pag->overflow = NULL;
|
||||||
|
return oflow;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* return lines until we reach the limit */
|
||||||
|
for (p = str, end = str + ret; p < end; p++) {
|
||||||
|
if (*p == '\n' && ++pag->line_count == pag->page_len - 1) {
|
||||||
|
/* remember to display the pager message next time */
|
||||||
|
pag->state = PAGERST_AT_LIMIT;
|
||||||
|
pag->line_count = 0;
|
||||||
|
|
||||||
|
/* skip the newline, since our prompt has one */
|
||||||
|
p++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* remove the used bytes from the membuf */
|
||||||
|
ret = membuf_getraw(&pag->mb, p - str, true, &str);
|
||||||
|
|
||||||
|
/* don't output the newline, since our prompt has one */
|
||||||
|
if (pag->state == PAGERST_AT_LIMIT)
|
||||||
|
p--;
|
||||||
|
|
||||||
|
/* terminate the string */
|
||||||
|
pag->nulch = p;
|
||||||
|
pag->oldch = *pag->nulch;
|
||||||
|
*pag->nulch = '\0';
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pager_set_bypass(struct pager *pag, bool bypass)
|
||||||
|
{
|
||||||
|
bool was_bypassed = false;
|
||||||
|
|
||||||
|
if (!pag)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
was_bypassed = pag->state == PAGERST_BYPASS;
|
||||||
|
if (bypass)
|
||||||
|
pag->state = PAGERST_BYPASS;
|
||||||
|
else
|
||||||
|
pag->state = PAGERST_OK;
|
||||||
|
|
||||||
|
return was_bypassed;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pager_set_test_bypass(struct pager *pag, bool bypass)
|
||||||
|
{
|
||||||
|
bool was_bypassed = false;
|
||||||
|
|
||||||
|
if (!pag)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
was_bypassed = pag->test_bypass;
|
||||||
|
pag->test_bypass = bypass;
|
||||||
|
|
||||||
|
return was_bypassed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pager_set_page_len(struct pager *pag, int page_len)
|
||||||
|
{
|
||||||
|
if (page_len < 2)
|
||||||
|
return;
|
||||||
|
pag->page_len = page_len;
|
||||||
|
pag->line_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pager_reset(struct pager *pag)
|
||||||
|
{
|
||||||
|
pag->line_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int on_pager(const char *name, const char *value, enum env_op op,
|
||||||
|
int flags)
|
||||||
|
{
|
||||||
|
struct pager *pag = gd_pager();
|
||||||
|
int new_page_len;
|
||||||
|
|
||||||
|
if (!IS_ENABLED(CONFIG_CONSOLE_PAGER) || !pag)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
switch (op) {
|
||||||
|
case env_op_create:
|
||||||
|
case env_op_overwrite:
|
||||||
|
if (value) {
|
||||||
|
new_page_len = simple_strtoul(value, NULL, 16);
|
||||||
|
pager_set_page_len(pag, new_page_len);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case env_op_delete:
|
||||||
|
/* Reset to default when deleted */
|
||||||
|
pager_set_page_len(pag, CONFIG_CONSOLE_PAGER_LINES);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
U_BOOT_ENV_CALLBACK(pager, on_pager);
|
||||||
|
|
||||||
|
void pager_uninit(struct pager *pag)
|
||||||
|
{
|
||||||
|
abuf_uninit(&pag->buf);
|
||||||
|
free(pag);
|
||||||
|
}
|
||||||
|
|
||||||
|
int pager_init(struct pager **pagp, int page_len, int buf_size)
|
||||||
|
{
|
||||||
|
struct pager *pag;
|
||||||
|
|
||||||
|
pag = malloc(sizeof(struct pager));
|
||||||
|
if (!pag)
|
||||||
|
return log_msg_ret("pag", -ENOMEM);
|
||||||
|
memset(pag, '\0', sizeof(struct pager));
|
||||||
|
pag->page_len = page_len;
|
||||||
|
if (!abuf_init_size(&pag->buf, buf_size))
|
||||||
|
return log_msg_ret("pah", -ENOMEM);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* nul-terminate the buffer, which will come in handy if we need to
|
||||||
|
* return up to the last byte
|
||||||
|
*/
|
||||||
|
((char *)pag->buf.data)[buf_size - 1] = '\0';
|
||||||
|
membuf_init(&pag->mb, pag->buf.data, buf_size);
|
||||||
|
*pagp = pag;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -20,6 +20,7 @@ U-Boot API documentation
|
|||||||
lmb
|
lmb
|
||||||
logging
|
logging
|
||||||
nvmem
|
nvmem
|
||||||
|
pager
|
||||||
part
|
part
|
||||||
pinctrl
|
pinctrl
|
||||||
rng
|
rng
|
||||||
|
|||||||
10
doc/api/pager.rst
Normal file
10
doc/api/pager.rst
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
.. SPDX-License-Identifier: GPL-2.0+
|
||||||
|
.. Copyright Simon Glass <sjg@chromium.org>
|
||||||
|
|
||||||
|
Console Pager
|
||||||
|
=============
|
||||||
|
|
||||||
|
See :doc:`/develop/console`.
|
||||||
|
|
||||||
|
.. kernel-doc:: include/pager.h
|
||||||
|
:internal:
|
||||||
41
doc/develop/console.rst
Normal file
41
doc/develop/console.rst
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
.. SPDX-License-Identifier: GPL-2.0+
|
||||||
|
.. sectionauthor:: Simon Glass <sjg@chromium.org>
|
||||||
|
|
||||||
|
=======
|
||||||
|
Console
|
||||||
|
=======
|
||||||
|
|
||||||
|
|
||||||
|
Paging
|
||||||
|
------
|
||||||
|
|
||||||
|
See the user documentation at :doc:`/usage/console`. For the API, see
|
||||||
|
:doc:`/api/pager`.
|
||||||
|
|
||||||
|
The pager is implemented in `common/pager.c` and integrated into the console
|
||||||
|
system via `console_puts()` in `common/console.c`. When output is sent to
|
||||||
|
stdout:
|
||||||
|
|
||||||
|
1. Text is passed to `pager_post()` which buffers it
|
||||||
|
2. `pager_next()` returns portions of text up to the page limit
|
||||||
|
3. When the page limit is reached, a prompt is displayed
|
||||||
|
4. The system waits for user input (SPACE key) via `getchar()`
|
||||||
|
5. After SPACE is pressed, the prompt is cleared and output continues
|
||||||
|
|
||||||
|
The pager maintains state through the `struct pager` which tracks:
|
||||||
|
|
||||||
|
- Current line count within the page
|
||||||
|
- Page length setting
|
||||||
|
- Text buffer and overflow handling
|
||||||
|
- Current pager state (normal, waiting for user, clearing prompt)
|
||||||
|
|
||||||
|
Bypass Mode
|
||||||
|
~~~~~~~~~~~
|
||||||
|
|
||||||
|
The pager can be put into bypass mode using `pager_set_bypass()`, which is
|
||||||
|
useful for automated testing. In bypass mode, all text flows through without
|
||||||
|
any paging interruptions.
|
||||||
|
|
||||||
|
The pager automatically enters bypass mode when output is not connected to a
|
||||||
|
terminal, preventing paging prompts from appearing in non-interactive
|
||||||
|
environments like scripts or automated builds.
|
||||||
@@ -35,6 +35,7 @@ Implementation
|
|||||||
ci_testing
|
ci_testing
|
||||||
commands
|
commands
|
||||||
config_binding
|
config_binding
|
||||||
|
console
|
||||||
cyclic
|
cyclic
|
||||||
devicetree/index
|
devicetree/index
|
||||||
distro
|
distro
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
.. SPDX-License-Identifier: GPL-2.0+
|
.. SPDX-License-Identifier: GPL-2.0+
|
||||||
.. sectionauthor:: Paolo Scaffardi, AIRVENT SAM s.p.a - RIMINI(ITALY), arsenio@tin.it
|
.. sectionauthor:: Paolo Scaffardi, AIRVENT SAM s.p.a - RIMINI(ITALY), arsenio@tin.it
|
||||||
.. (C) Copyright 2000
|
.. (C) Copyright 2000
|
||||||
|
.. sectionauthor:: Simon Glass <sjg@chromium.org>
|
||||||
|
|
||||||
=======================
|
=======================
|
||||||
U-Boot console handling
|
U-Boot console handling
|
||||||
=======================
|
=======================
|
||||||
|
|
||||||
HOW THE CONSOLE WORKS?
|
Introduction
|
||||||
----------------------
|
------------
|
||||||
|
|
||||||
At system startup U-Boot initializes a serial console. When U-Boot
|
At system-startup U-Boot initializes a serial console. When U-Boot
|
||||||
relocates itself to RAM, all console drivers are initialized (they
|
relocates itself to RAM, all console drivers are initialized (they
|
||||||
will register all detected console devices to the system for further
|
will register all detected console devices to the system for further
|
||||||
use).
|
use).
|
||||||
@@ -17,43 +18,90 @@ use).
|
|||||||
If not defined in the environment, the first input device is assigned
|
If not defined in the environment, the first input device is assigned
|
||||||
to the 'stdin' file, the first output one to 'stdout' and 'stderr'.
|
to the 'stdin' file, the first output one to 'stdout' and 'stderr'.
|
||||||
|
|
||||||
You can use the command "coninfo" to see all registered console
|
You can use the command `coninfo` to see all registered console
|
||||||
devices and their flags. You can assign a standard file (stdin,
|
devices and their flags. You can assign a standard file (stdin,
|
||||||
stdout or stderr) to any device you see in that list simply by
|
stdout or stderr) to any device you see in that list simply by
|
||||||
assigning its name to the corresponding environment variable. For
|
assigning its name to the corresponding environment variable. For
|
||||||
example:
|
example::
|
||||||
|
|
||||||
setenv stdin serial <- To use the serial input
|
# Use the serial input
|
||||||
setenv stdout video <- To use the video console
|
setenv stdin serial
|
||||||
|
|
||||||
Do a simple "saveenv" to save the console settings in the environment
|
# Use the video console
|
||||||
|
setenv stdout vidconsole
|
||||||
|
|
||||||
|
Do a simple `saveenv` to save the console settings in the environment
|
||||||
and get them working on the next startup, too.
|
and get them working on the next startup, too.
|
||||||
|
|
||||||
HOW CAN I USE STANDARD FILE INTO THE SOURCES?
|
How to output text to the console
|
||||||
---------------------------------------------
|
---------------------------------
|
||||||
|
|
||||||
You can use the following functions to access the console:
|
You can use the following functions to access the console:
|
||||||
|
|
||||||
* STDOUT:
|
stdout
|
||||||
putc (to put a char to stdout)
|
- putc() - write a char to stdout
|
||||||
puts (to put a string to stdout)
|
- puts() - write a string to stdout
|
||||||
printf (to format and put a string to stdout)
|
- printf() - format and write a string to stdout
|
||||||
|
|
||||||
* STDIN:
|
stdin
|
||||||
tstc (to test for the presence of a char in stdin)
|
- tstc() - test for the presence of a char in stdin
|
||||||
getc (to get a char from stdin)
|
- getchar() - get a char from stdin
|
||||||
|
|
||||||
* STDERR:
|
stderr
|
||||||
eputc (to put a char to stderr)
|
- eputc() - write a char to stderr
|
||||||
eputs (to put a string to stderr)
|
- eputs() - write a string to stderr
|
||||||
eprintf (to format and put a string to stderr)
|
- eprintf() - format and write a string to stderr
|
||||||
|
|
||||||
* FILE (can be 'stdin', 'stdout', 'stderr'):
|
file ('stdin', 'stdout' or 'stderr')
|
||||||
fputc (like putc but redirected to a file)
|
- fputc() - write a char to a file
|
||||||
fputs (like puts but redirected to a file)
|
- fputs() - write a string to a file
|
||||||
fprintf (like printf but redirected to a file)
|
- fprintf() - format and write a string to a file
|
||||||
ftstc (like tstc but redirected to a file)
|
- ftstc() - test for the presence of a char in file
|
||||||
fgetc (like getc but redirected to a file)
|
- fgetc() - get a char from a file
|
||||||
|
|
||||||
Remember that all FILE-related functions CANNOT be used before
|
Remember that FILE-related functions CANNOT be used before U-Boot relocation,
|
||||||
U-Boot relocation (done in 'board_init_r' in `arch/*/lib/board.c`).
|
which is done in `board_init_r()`.
|
||||||
|
|
||||||
|
Pager
|
||||||
|
-----
|
||||||
|
|
||||||
|
U-Boot has a simple pager feature, enabled with `CONFIG_CONSOLE_PAGER`. It is
|
||||||
|
only available if `CONFIG_CONSOLE_MUX` is also enabled.
|
||||||
|
|
||||||
|
When activated, the pager pauses at the end of each 'page' (screenful) of
|
||||||
|
output, shows a prompt ": Press SPACE to continue" and lets the user read the
|
||||||
|
output. To continue to the next page, press the SPACE key.
|
||||||
|
|
||||||
|
Page Size Configuration
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The number of lines per page is determined in the following order of priority:
|
||||||
|
|
||||||
|
1. **Environment variable**: The `pager` environment variable (hex value)
|
||||||
|
takes highest priority. Set to 0 to disable paging.
|
||||||
|
|
||||||
|
2. **Video console detection**: If no environment variable is set and a video
|
||||||
|
console is active, the pager uses the number of rows from the video console.
|
||||||
|
|
||||||
|
3. **Serial TTY detection**: For serial consoles, the pager checks if the
|
||||||
|
output is connected to a terminal (TTY). If not connected to a TTY, paging
|
||||||
|
is disabled. This check works by sending a few special characters to the
|
||||||
|
terminal and (hopefully) receiving a reply. If you are logging the output of
|
||||||
|
U-Boot, you may see these characters in the log. Disable
|
||||||
|
`CONFIG_SERIAL_TERM_PRESENT` is this is unwanted.
|
||||||
|
|
||||||
|
4. **Configuration default**: If none of the above apply, falls back to
|
||||||
|
`CONFIG_CONSOLE_PAGER_LINES`.
|
||||||
|
|
||||||
|
Examples::
|
||||||
|
|
||||||
|
# Set page size to 30 lines (hex value 1e)
|
||||||
|
setenv pager 1e
|
||||||
|
|
||||||
|
# Set page size to 24 lines (hex value 18)
|
||||||
|
setenv pager 18
|
||||||
|
|
||||||
|
# Disable paging
|
||||||
|
setenv pager 0
|
||||||
|
|
||||||
|
For developer documentation, please see :doc:`/develop/console`.
|
||||||
|
|||||||
@@ -335,6 +335,10 @@ netretry
|
|||||||
Useful on scripts which control the retry operation
|
Useful on scripts which control the retry operation
|
||||||
themselves.
|
themselves.
|
||||||
|
|
||||||
|
pager
|
||||||
|
Decimal number of visible lines on the display, or serial console.
|
||||||
|
:doc:`/usage/console`.
|
||||||
|
|
||||||
rng_seed_size
|
rng_seed_size
|
||||||
Size of random value added to device-tree node /chosen/rng-seed.
|
Size of random value added to device-tree node /chosen/rng-seed.
|
||||||
This variable is given as a decimal number.
|
This variable is given as a decimal number.
|
||||||
|
|||||||
@@ -18,6 +18,9 @@
|
|||||||
#include <asm/global_data.h>
|
#include <asm/global_data.h>
|
||||||
#include <dm/lists.h>
|
#include <dm/lists.h>
|
||||||
#include <dm/device-internal.h>
|
#include <dm/device-internal.h>
|
||||||
|
#ifdef CONFIG_SANDBOX
|
||||||
|
#include <asm/state.h>
|
||||||
|
#endif
|
||||||
#include <dm/of_access.h>
|
#include <dm/of_access.h>
|
||||||
#include <linux/build_bug.h>
|
#include <linux/build_bug.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
@@ -647,6 +650,14 @@ int serial_query_size(int *rowsp, int *colsp)
|
|||||||
if (!CONFIG_IS_ENABLED(SERIAL_TERM_PRESENT))
|
if (!CONFIG_IS_ENABLED(SERIAL_TERM_PRESENT))
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
|
|
||||||
|
#ifdef CONFIG_SANDBOX
|
||||||
|
if (IS_ENABLED(CONFIG_SANDBOX)) {
|
||||||
|
struct sandbox_state *state = state_get_current();
|
||||||
|
if (state->no_term_present)
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Empty input buffer */
|
/* Empty input buffer */
|
||||||
while (tstc())
|
while (tstc())
|
||||||
getchar();
|
getchar();
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
#include <event_internal.h>
|
#include <event_internal.h>
|
||||||
#include <fdtdec.h>
|
#include <fdtdec.h>
|
||||||
#include <membuf.h>
|
#include <membuf.h>
|
||||||
|
#include <pager.h>
|
||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
#include <linux/build_bug.h>
|
#include <linux/build_bug.h>
|
||||||
#include <asm-offsets.h>
|
#include <asm-offsets.h>
|
||||||
@@ -477,6 +478,14 @@ struct global_data {
|
|||||||
*/
|
*/
|
||||||
struct upl *upl;
|
struct upl *upl;
|
||||||
#endif
|
#endif
|
||||||
|
#if CONFIG_IS_ENABLED(CONSOLE_PAGER)
|
||||||
|
/**
|
||||||
|
* @pager: Pointer to the pager settings, or NULL if none
|
||||||
|
*
|
||||||
|
* This is set up in console_init_r()
|
||||||
|
*/
|
||||||
|
struct pager *pager;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
#ifndef DO_DEPS_ONLY
|
#ifndef DO_DEPS_ONLY
|
||||||
static_assert(sizeof(struct global_data) == GD_SIZE);
|
static_assert(sizeof(struct global_data) == GD_SIZE);
|
||||||
@@ -618,6 +627,16 @@ static_assert(sizeof(struct global_data) == GD_SIZE);
|
|||||||
#define gd_passage_dtb() 0
|
#define gd_passage_dtb() 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if CONFIG_IS_ENABLED(CONSOLE_PAGER)
|
||||||
|
#define gd_pager() gd->pager
|
||||||
|
#define gd_pagerp() &gd->pager
|
||||||
|
#define gd_pager_page_len() gd->pager->page_len
|
||||||
|
#else
|
||||||
|
#define gd_pager() NULL
|
||||||
|
#define gd_pagerp() NULL
|
||||||
|
#define gd_pager_page_len() 0
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* enum gd_flags - global data flags
|
* enum gd_flags - global data flags
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -202,6 +202,16 @@ int console_clear(void);
|
|||||||
*/
|
*/
|
||||||
int console_remove_by_name(const char *name);
|
int console_remove_by_name(const char *name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* calc_check_console_lines() - Calculate console page length
|
||||||
|
*
|
||||||
|
* This calculates the appropriate number of lines to use for console paging,
|
||||||
|
* considering environment variables, config defaults, and device capabilities.
|
||||||
|
*
|
||||||
|
* Return: number of lines for paging, or 0 to disable paging
|
||||||
|
*/
|
||||||
|
int calc_check_console_lines(void);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* CONSOLE multiplexing.
|
* CONSOLE multiplexing.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -75,6 +75,12 @@
|
|||||||
#define DFU_CALLBACK
|
#define DFU_CALLBACK
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_CONSOLE_PAGER
|
||||||
|
#define PAGER_CALLBACK "pager:pager,"
|
||||||
|
#else
|
||||||
|
#define PAGER_CALLBACK
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This list of callback bindings is static, but may be overridden by defining
|
* This list of callback bindings is static, but may be overridden by defining
|
||||||
* a new association in the ".callbacks" environment variable.
|
* a new association in the ".callbacks" environment variable.
|
||||||
@@ -88,6 +94,7 @@
|
|||||||
DFU_CALLBACK \
|
DFU_CALLBACK \
|
||||||
"loadaddr:loadaddr," \
|
"loadaddr:loadaddr," \
|
||||||
SILENT_CALLBACK \
|
SILENT_CALLBACK \
|
||||||
|
PAGER_CALLBACK \
|
||||||
"stdin:console,stdout:console,stderr:console," \
|
"stdin:console,stdout:console,stderr:console," \
|
||||||
"serial#:serialno," \
|
"serial#:serialno," \
|
||||||
CFG_ENV_CALLBACK_LIST_STATIC
|
CFG_ENV_CALLBACK_LIST_STATIC
|
||||||
|
|||||||
212
include/pager.h
Normal file
212
include/pager.h
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||||
|
/*
|
||||||
|
* Deals with splitting up text output into separate screenfuls
|
||||||
|
*
|
||||||
|
* Copyright 2025 Simon Glass <sjg@chromium.org>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __PAGER_H
|
||||||
|
#define __PAGER_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <abuf.h>
|
||||||
|
#include <membuf.h>
|
||||||
|
#include <linux/sizes.h>
|
||||||
|
|
||||||
|
#define PAGER_BUF_SIZE SZ_4K
|
||||||
|
|
||||||
|
/* Special return value from pager_next() indicating waiting for user input */
|
||||||
|
#define PAGER_WAITING ((const char *)1)
|
||||||
|
|
||||||
|
/* Prompt shown to user when pager reaches page limit */
|
||||||
|
#define PAGER_PROMPT "\n: Press SPACE to continue"
|
||||||
|
|
||||||
|
/* String used to blank/clear the pager prompt */
|
||||||
|
#define PAGER_BLANK "\r \r"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* enum pager_state: Tracks the state of the pager
|
||||||
|
*
|
||||||
|
* @PAGERST_OK: Normal output is happening
|
||||||
|
* @PAGERST_AT_LIMIT: No more output can be provided; the next call to
|
||||||
|
* pager_next() will return a user prompt
|
||||||
|
* @PAGERST_WAIT_USER: Waiting for the user to press a key
|
||||||
|
* @PAGERST_CLEAR_PROMPT: Clearing the prompt ready for more output
|
||||||
|
* @PAGERST_BYPASS: Pager is being bypassed
|
||||||
|
*/
|
||||||
|
enum pager_state {
|
||||||
|
PAGERST_OK,
|
||||||
|
PAGERST_AT_LIMIT,
|
||||||
|
PAGERST_WAIT_USER,
|
||||||
|
PAGERST_CLEAR_PROMPT,
|
||||||
|
PAGERST_BYPASS,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct pager - pager state
|
||||||
|
*
|
||||||
|
* The pager uses a buffer @buf to hold text that it is in the process of
|
||||||
|
* sending out. This helps deal with the stdio puts() interface, which does not
|
||||||
|
* permit passing a string length, only a string, which means that strings must
|
||||||
|
* be nul-terminated. The termination is handled automatically by the pager.
|
||||||
|
*
|
||||||
|
* If the text passed to pager_post() is too large for @buf then all the next
|
||||||
|
* will be written at once, without any paging, in the next call to
|
||||||
|
* pager_next().
|
||||||
|
*
|
||||||
|
* The membuf @mb is only used to feed out text in chunks, with a pager message
|
||||||
|
* (and a keypress wait) inserted between each chunk.
|
||||||
|
*
|
||||||
|
* @line_count: Number of lines output since last pause
|
||||||
|
* @page_len: Sets the height of the page in lines. The maximum lines to display
|
||||||
|
* before pausing is one less than this. Set from 'pager' env variable
|
||||||
|
* @buf: Buffer containing text to eventually be returned
|
||||||
|
* @mb: Circular buffer to manage @buf
|
||||||
|
* @overflow: pointer to overflow text to send nexts
|
||||||
|
* @nulch: pointer to where a nul character was written, NULL if none
|
||||||
|
* @oldch: old character that was at @nulch
|
||||||
|
* @state: current state of the pager state-machine
|
||||||
|
* @test_bypass: true if pager should behave as if in test mode (bypass all)
|
||||||
|
*/
|
||||||
|
struct pager {
|
||||||
|
int line_count;
|
||||||
|
int page_len;
|
||||||
|
struct abuf buf;
|
||||||
|
struct membuf mb;
|
||||||
|
const char *overflow;
|
||||||
|
char *nulch;
|
||||||
|
int oldch;
|
||||||
|
enum pager_state state;
|
||||||
|
bool test_bypass;
|
||||||
|
};
|
||||||
|
|
||||||
|
#if CONFIG_IS_ENABLED(CONSOLE_PAGER)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pager_post() - Add text to the input buffer for later handling
|
||||||
|
*
|
||||||
|
* If @use_pager the text is added to the pager buffer and fed out a screenful
|
||||||
|
* at a time. This function calls pager_post() after storing the text.
|
||||||
|
*
|
||||||
|
* After calling pager_post(), if it returns anything other than NULL, you must
|
||||||
|
* repeatedly call pager_next() until it returns NULL, otherwise text may be
|
||||||
|
* lost
|
||||||
|
*
|
||||||
|
* If @pag is NULL, this does nothing but return @s
|
||||||
|
*
|
||||||
|
* @pag: Pager to use, may be NULL
|
||||||
|
* @use_pager: Whether or not to use the pager functionality
|
||||||
|
* @s: Text to add
|
||||||
|
* Return: text which should be sent to output, or NULL if there is no more.
|
||||||
|
* If !@use_pager this just returns @s and does not affect the pager state
|
||||||
|
*/
|
||||||
|
const char *pager_post(struct pager *pag, bool use_pager, const char *s);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pager_next() - Returns the next screenful of text to show
|
||||||
|
*
|
||||||
|
* If this function returns PAGER_WAITING then the caller must check for user
|
||||||
|
* input and pass in the keypress in the next call to pager_next(). It can
|
||||||
|
* busy-wait for a keypress, if desired, since pager_next() will only ever
|
||||||
|
* return PAGER_WAITING until @ch is non-zero.
|
||||||
|
*
|
||||||
|
* @pag: Pager to use
|
||||||
|
* @use_pager: Whether or not to use the pager functionality
|
||||||
|
* @ch: Key that the user has pressed, or 0 if none
|
||||||
|
*
|
||||||
|
* Return: text which should be sent to output, or PAGER_WAITING if waiting for
|
||||||
|
* the user to press a key, or NULL if there is no more text.
|
||||||
|
* If !@use_pager this just returns NULL and does not affect the pager state
|
||||||
|
*/
|
||||||
|
const char *pager_next(struct pager *pag, bool use_pager, int ch);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pager_set_bypass() - put the pager into bypass mode
|
||||||
|
*
|
||||||
|
* This is used when output is not connected to a terminal. Bypass mode stops
|
||||||
|
* the pager from doing anything to interrupt output.
|
||||||
|
*
|
||||||
|
* @pag: Pager to use, may be NULL in which case this function does nothing
|
||||||
|
* @bypass: true to put the pager in bypass mode, false for normal mode
|
||||||
|
* Return: old value of the bypass flag
|
||||||
|
*/
|
||||||
|
bool pager_set_bypass(struct pager *pag, bool bypass);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pager_set_test_bypass() - put the pager into test bypass mode
|
||||||
|
*
|
||||||
|
* This is used for tests. Test bypass mode stops the pager from doing
|
||||||
|
* anything to interrupt output, regardless of the current pager state.
|
||||||
|
*
|
||||||
|
* @pag: Pager to use, may be NULL in which case this function does nothing
|
||||||
|
* @bypass: true to put the pager in test bypass mode, false for normal
|
||||||
|
* Return: old value of the test bypass flag
|
||||||
|
*/
|
||||||
|
bool pager_set_test_bypass(struct pager *pag, bool bypass);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pager_reset() - reset the line count in the pager
|
||||||
|
*
|
||||||
|
* Sets line_count to zero so that the pager starts afresh with its counting.
|
||||||
|
*
|
||||||
|
* @pag: Pager to update
|
||||||
|
*/
|
||||||
|
void pager_reset(struct pager *pag);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pager_uninit() - Uninit the pager
|
||||||
|
*
|
||||||
|
* Frees all memory and also @pag
|
||||||
|
*
|
||||||
|
* @pag: Pager to uninit
|
||||||
|
*/
|
||||||
|
void pager_uninit(struct pager *pag);
|
||||||
|
|
||||||
|
#else
|
||||||
|
static inline const char *pager_post(struct pager *pag, bool use_pager,
|
||||||
|
const char *s)
|
||||||
|
{
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline const char *pager_next(struct pager *pag, bool use_pager, int ch)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool pager_set_bypass(struct pager *pag, bool bypass)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool pager_set_test_bypass(struct pager *pag, bool bypass)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void pager_reset(struct pager *pag)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pager_set_page_len() - Set the page length of a pager
|
||||||
|
*
|
||||||
|
* @pag: Pager to use
|
||||||
|
* @page_len: Page length to set
|
||||||
|
*/
|
||||||
|
void pager_set_page_len(struct pager *pag, int page_len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pager_init() - Set up a new pager
|
||||||
|
*
|
||||||
|
* @pagp: Returns allocaed pager, on success
|
||||||
|
* @page_len: Number of lines per page
|
||||||
|
* @buf_size: Buffer size to use in bytes, this is the maximum amount of output
|
||||||
|
* that can be paged
|
||||||
|
* Return: 0 if OK, -ENOMEM if out of memory
|
||||||
|
*/
|
||||||
|
int pager_init(struct pager **pagp, int page_len, int buf_size);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -406,8 +406,8 @@ int serial_tstc(void);
|
|||||||
* When using a serial console or the net console we can only devise the
|
* 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.
|
* terminal size by querying the terminal using ECMA-48 control sequences.
|
||||||
*
|
*
|
||||||
* @rowsp: returns number of rows
|
* @rowsp: returns number of rows on success
|
||||||
* @colsp: returns number of columns
|
* @colsp: returns number of columns on success
|
||||||
* Returns: 0 on success, -NOENT if no terminal is present, -ETIMEDOUT if we
|
* 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
|
* checked for a terminal but didn't get a response in time, -EPROTO if the
|
||||||
* terminal did not respond as expected
|
* terminal did not respond as expected
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ config EFI_APP
|
|||||||
depends on X86 || ARM
|
depends on X86 || ARM
|
||||||
select CHARSET
|
select CHARSET
|
||||||
select EVENT
|
select EVENT
|
||||||
|
imply CONSOLE_PAGER
|
||||||
help
|
help
|
||||||
Build U-Boot as an application which can be started from EFI. This
|
Build U-Boot as an application which can be started from EFI. This
|
||||||
is useful for examining a platform in the early stages of porting
|
is useful for examining a platform in the early stages of porting
|
||||||
|
|||||||
@@ -184,6 +184,9 @@ static int bdinfo_test_all(struct unit_test_state *uts)
|
|||||||
if (IS_ENABLED(CONFIG_VIDEO))
|
if (IS_ENABLED(CONFIG_VIDEO))
|
||||||
ut_assertok(test_video_info(uts));
|
ut_assertok(test_video_info(uts));
|
||||||
|
|
||||||
|
if (IS_ENABLED(CONFIG_CONSOLE_PAGER))
|
||||||
|
ut_assert_nextline("pager = %d", gd_pager_page_len());
|
||||||
|
|
||||||
/* The gd->multi_dtb_fit may not be available, hence, #if below. */
|
/* The gd->multi_dtb_fit may not be available, hence, #if below. */
|
||||||
#if CONFIG_IS_ENABLED(MULTI_DTB_FIT)
|
#if CONFIG_IS_ENABLED(MULTI_DTB_FIT)
|
||||||
ut_assertok(test_num_l(uts, "multi_dtb_fit", (ulong)gd->multi_dtb_fit));
|
ut_assertok(test_num_l(uts, "multi_dtb_fit", (ulong)gd->multi_dtb_fit));
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ obj-$(CONFIG_$(PHASE_)CMDLINE) += bloblist.o
|
|||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
obj-$(CONFIG_CONSOLE_PAGER) += console.o
|
||||||
obj-$(CONFIG_CYCLIC) += cyclic.o
|
obj-$(CONFIG_CYCLIC) += cyclic.o
|
||||||
obj-$(CONFIG_EVENT_DYNAMIC) += event.o
|
obj-$(CONFIG_EVENT_DYNAMIC) += event.o
|
||||||
obj-y += cread.o
|
obj-y += cread.o
|
||||||
|
obj-$(CONFIG_CONSOLE_PAGER) += pager.o
|
||||||
obj-$(CONFIG_$(PHASE_)CMDLINE) += print.o
|
obj-$(CONFIG_$(PHASE_)CMDLINE) += print.o
|
||||||
|
|||||||
183
test/common/console.c
Normal file
183
test/common/console.c
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0+
|
||||||
|
/*
|
||||||
|
* Copyright 2025 Simon Glass <sjg@chromium.org>
|
||||||
|
*
|
||||||
|
* Test for console functionality
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <console.h>
|
||||||
|
#include <env.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
#include <serial.h>
|
||||||
|
#include <video.h>
|
||||||
|
#include <test/common.h>
|
||||||
|
#include <test/test.h>
|
||||||
|
#include <test/ut.h>
|
||||||
|
#include <asm/global_data.h>
|
||||||
|
#include <asm/state.h>
|
||||||
|
|
||||||
|
DECLARE_GLOBAL_DATA_PTR;
|
||||||
|
|
||||||
|
/* Test calc_check_console_lines() with environment variable override */
|
||||||
|
static int console_test_calc_lines_env_override(struct unit_test_state *uts)
|
||||||
|
{
|
||||||
|
int original_lines, lines;
|
||||||
|
char *orig_env;
|
||||||
|
|
||||||
|
/* Save original environment value */
|
||||||
|
orig_env = env_get("pager");
|
||||||
|
original_lines = calc_check_console_lines();
|
||||||
|
|
||||||
|
/* Test with hex environment variable */
|
||||||
|
ut_assertok(env_set("pager", "1a")); /* 26 in hex */
|
||||||
|
lines = calc_check_console_lines();
|
||||||
|
ut_asserteq(26, lines);
|
||||||
|
|
||||||
|
/* Test with decimal-looking hex value */
|
||||||
|
ut_assertok(env_set("pager", "20")); /* 32 in hex */
|
||||||
|
lines = calc_check_console_lines();
|
||||||
|
ut_asserteq(32, lines);
|
||||||
|
|
||||||
|
/* Test with zero (should disable pager) */
|
||||||
|
ut_assertok(env_set("pager", "0"));
|
||||||
|
lines = calc_check_console_lines();
|
||||||
|
ut_asserteq(0, lines);
|
||||||
|
|
||||||
|
/* Restore original environment */
|
||||||
|
ut_assertok(env_set("pager", orig_env));
|
||||||
|
|
||||||
|
/* Verify restoration */
|
||||||
|
lines = calc_check_console_lines();
|
||||||
|
ut_asserteq(original_lines, lines);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
COMMON_TEST(console_test_calc_lines_env_override, 0);
|
||||||
|
|
||||||
|
/* Test calc_check_console_lines() with invalid environment values */
|
||||||
|
static int console_test_calc_lines_env_invalid(struct unit_test_state *uts)
|
||||||
|
{
|
||||||
|
int original_lines, lines;
|
||||||
|
char *orig_env;
|
||||||
|
|
||||||
|
/* Save original environment value */
|
||||||
|
orig_env = env_get("pager");
|
||||||
|
original_lines = calc_check_console_lines();
|
||||||
|
|
||||||
|
/* Test with invalid hex value - should fall back to default */
|
||||||
|
ut_assertok(env_set("pager", "xyz"));
|
||||||
|
lines = calc_check_console_lines();
|
||||||
|
/* Should return default since 'pager' reads as 0 */
|
||||||
|
ut_asserteq(CONFIG_CONSOLE_PAGER_LINES, lines);
|
||||||
|
|
||||||
|
/* Test with empty string */
|
||||||
|
ut_assertok(env_set("pager", ""));
|
||||||
|
lines = calc_check_console_lines();
|
||||||
|
ut_asserteq(CONFIG_CONSOLE_PAGER_LINES, lines);
|
||||||
|
|
||||||
|
/* Restore original environment */
|
||||||
|
ut_assertok(env_set("pager", orig_env));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
COMMON_TEST(console_test_calc_lines_env_invalid, 0);
|
||||||
|
|
||||||
|
/* Test calc_check_console_lines() default behavior without environment */
|
||||||
|
static int console_test_calc_lines_default(struct unit_test_state *uts)
|
||||||
|
{
|
||||||
|
int lines;
|
||||||
|
char *orig_env;
|
||||||
|
|
||||||
|
/* Save and clear environment variable */
|
||||||
|
orig_env = env_get("pager");
|
||||||
|
ut_assertok(env_set("pager", NULL));
|
||||||
|
|
||||||
|
/* Get default behavior */
|
||||||
|
lines = calc_check_console_lines();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The function considers device detection (video/serial) which may
|
||||||
|
* override the CONFIG default. In sandbox, serial_is_tty() may return
|
||||||
|
* false during tests, causing the function to return 0. Just verify it
|
||||||
|
* returns a reasonable value.
|
||||||
|
*/
|
||||||
|
ut_assert(lines >= 0);
|
||||||
|
|
||||||
|
/* If CONFIG_CONSOLE_PAGER is not enabled, should definitely be 0 */
|
||||||
|
if (!IS_ENABLED(CONFIG_CONSOLE_PAGER)) {
|
||||||
|
ut_asserteq(0, lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Restore original environment */
|
||||||
|
ut_assertok(env_set("pager", orig_env));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
COMMON_TEST(console_test_calc_lines_default, 0);
|
||||||
|
|
||||||
|
/* Test calc_check_console_lines() precedence: env overrides everything */
|
||||||
|
static int console_test_calc_lines_precedence(struct unit_test_state *uts)
|
||||||
|
{
|
||||||
|
int lines;
|
||||||
|
char *orig_env;
|
||||||
|
|
||||||
|
/* Save original environment value */
|
||||||
|
orig_env = env_get("pager");
|
||||||
|
|
||||||
|
/* Set environment to a specific value */
|
||||||
|
ut_assertok(env_set("pager", "2a")); /* 42 in hex */
|
||||||
|
lines = calc_check_console_lines();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Environment should always take precedence regardless of video/serial
|
||||||
|
* state
|
||||||
|
*/
|
||||||
|
ut_asserteq(42, lines);
|
||||||
|
|
||||||
|
/* Test with zero environment value */
|
||||||
|
ut_assertok(env_set("pager", "0"));
|
||||||
|
lines = calc_check_console_lines();
|
||||||
|
ut_asserteq(0, lines);
|
||||||
|
|
||||||
|
/* Restore original environment */
|
||||||
|
ut_assertok(env_set("pager", orig_env));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
COMMON_TEST(console_test_calc_lines_precedence, 0);
|
||||||
|
|
||||||
|
/* Test calc_check_console_lines() with serial TTY detection */
|
||||||
|
static int console_test_calc_lines_serial_tty(struct unit_test_state *uts)
|
||||||
|
{
|
||||||
|
int lines;
|
||||||
|
char *orig_env;
|
||||||
|
bool orig_tty;
|
||||||
|
struct sandbox_state *state;
|
||||||
|
|
||||||
|
/* Save original state */
|
||||||
|
orig_env = env_get("pager");
|
||||||
|
state = state_get_current();
|
||||||
|
orig_tty = state->serial_is_tty;
|
||||||
|
|
||||||
|
/* Clear environment to test device detection */
|
||||||
|
ut_assertok(env_set("pager", NULL));
|
||||||
|
|
||||||
|
/* Test with serial TTY enabled */
|
||||||
|
state->serial_is_tty = true;
|
||||||
|
lines = calc_check_console_lines();
|
||||||
|
/* Should attempt to query serial size or use default */
|
||||||
|
ut_assert(lines >= 0);
|
||||||
|
|
||||||
|
/* Test with serial TTY disabled (not a terminal) */
|
||||||
|
state->serial_is_tty = false;
|
||||||
|
lines = calc_check_console_lines();
|
||||||
|
/* Should return 0 when not connected to TTY */
|
||||||
|
ut_asserteq(0, lines);
|
||||||
|
|
||||||
|
/* Restore original state */
|
||||||
|
state->serial_is_tty = orig_tty;
|
||||||
|
ut_assertok(env_set("pager", orig_env));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
COMMON_TEST(console_test_calc_lines_serial_tty, 0);
|
||||||
580
test/common/pager.c
Normal file
580
test/common/pager.c
Normal file
@@ -0,0 +1,580 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0+
|
||||||
|
/*
|
||||||
|
* Copyright 2025 Simon Glass <sjg@chromium.org>
|
||||||
|
*
|
||||||
|
* Test for pager functionality
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
#include <pager.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <test/common.h>
|
||||||
|
#include <test/test.h>
|
||||||
|
#include <test/ut.h>
|
||||||
|
#include <asm/global_data.h>
|
||||||
|
|
||||||
|
DECLARE_GLOBAL_DATA_PTR;
|
||||||
|
|
||||||
|
/* Test basic pager init and cleanup */
|
||||||
|
static int pager_test_basic_init(struct unit_test_state *uts)
|
||||||
|
{
|
||||||
|
struct pager *pag;
|
||||||
|
|
||||||
|
/* Test successful init */
|
||||||
|
ut_assertok(pager_init(&pag, 20, 1024));
|
||||||
|
ut_assertnonnull(pag);
|
||||||
|
ut_asserteq(20, pag->page_len);
|
||||||
|
ut_asserteq(0, pag->line_count);
|
||||||
|
ut_assertnull(pag->overflow);
|
||||||
|
ut_assertnull(pag->nulch);
|
||||||
|
|
||||||
|
/* Clean up */
|
||||||
|
pager_uninit(pag);
|
||||||
|
|
||||||
|
/* Test init with different parameters */
|
||||||
|
ut_assertok(pager_init(&pag, 10, 2048));
|
||||||
|
ut_assertnonnull(pag);
|
||||||
|
ut_asserteq(10, pag->page_len);
|
||||||
|
|
||||||
|
pager_uninit(pag);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
COMMON_TEST(pager_test_basic_init, 0);
|
||||||
|
|
||||||
|
/* Test pager with simple text */
|
||||||
|
static int pager_test_simple_text(struct unit_test_state *uts)
|
||||||
|
{
|
||||||
|
struct pager *pag;
|
||||||
|
const char *text = "Hello, World!";
|
||||||
|
const char *result;
|
||||||
|
|
||||||
|
ut_assertok(pager_init(&pag, 20, 1024));
|
||||||
|
|
||||||
|
/* Post some text and get it back */
|
||||||
|
result = pager_post(pag, true, text);
|
||||||
|
ut_assertnonnull(result);
|
||||||
|
ut_asserteq_str(text, result);
|
||||||
|
|
||||||
|
/* Should be no more text */
|
||||||
|
result = pager_next(pag, true, 0);
|
||||||
|
ut_assertnull(result);
|
||||||
|
|
||||||
|
pager_uninit(pag);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
COMMON_TEST(pager_test_simple_text, 0);
|
||||||
|
|
||||||
|
/* Test pager with multiple lines */
|
||||||
|
static int pager_test_multiline(struct unit_test_state *uts)
|
||||||
|
{
|
||||||
|
struct pager *pag;
|
||||||
|
const char *text1 = "Line 1\n";
|
||||||
|
const char *text2 = "Line 2\n";
|
||||||
|
const char *text3 = "Line 3\n";
|
||||||
|
const char *result;
|
||||||
|
ulong start;
|
||||||
|
|
||||||
|
start = ut_check_free();
|
||||||
|
|
||||||
|
ut_assertok(pager_init(&pag, 20, 1024));
|
||||||
|
|
||||||
|
/* Post multiple pieces of text */
|
||||||
|
result = pager_post(pag, true, text1);
|
||||||
|
ut_assertnonnull(result);
|
||||||
|
ut_asserteq_str(text1, result);
|
||||||
|
|
||||||
|
/* Should be no more text after first post */
|
||||||
|
result = pager_next(pag, true, 0);
|
||||||
|
ut_assertnull(result);
|
||||||
|
|
||||||
|
result = pager_post(pag, true, text2);
|
||||||
|
ut_assertnonnull(result);
|
||||||
|
ut_asserteq_str(text2, result);
|
||||||
|
|
||||||
|
/* Should be no more text after second post */
|
||||||
|
result = pager_next(pag, true, 0);
|
||||||
|
ut_assertnull(result);
|
||||||
|
|
||||||
|
result = pager_post(pag, true, text3);
|
||||||
|
ut_assertnonnull(result);
|
||||||
|
ut_asserteq_str(text3, result);
|
||||||
|
|
||||||
|
/* Should be no more text after third post */
|
||||||
|
result = pager_next(pag, true, 0);
|
||||||
|
ut_assertnull(result);
|
||||||
|
|
||||||
|
pager_uninit(pag);
|
||||||
|
|
||||||
|
/* Check for memory leaks */
|
||||||
|
ut_assertok(ut_check_delta(start));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
COMMON_TEST(pager_test_multiline, 0);
|
||||||
|
|
||||||
|
/* Test pager with large text that fills the buffer */
|
||||||
|
static int pager_test_large_text(struct unit_test_state *uts)
|
||||||
|
{
|
||||||
|
struct pager *pag;
|
||||||
|
const char *result;
|
||||||
|
|
||||||
|
ut_assertok(pager_init(&pag, 20, 16)); /* Small buffer */
|
||||||
|
|
||||||
|
/* Post large text - should fit in buffer */
|
||||||
|
result = pager_post(pag, true, "this is 16 chars");
|
||||||
|
ut_assertnonnull(result);
|
||||||
|
ut_asserteq_str("this is 16 chars", result);
|
||||||
|
ut_assertnull(pager_next(pag, true, 0));
|
||||||
|
|
||||||
|
pager_uninit(pag);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
COMMON_TEST(pager_test_large_text, 0);
|
||||||
|
|
||||||
|
/* Test pager overflow handling */
|
||||||
|
static int pager_test_overflow(struct unit_test_state *uts)
|
||||||
|
{
|
||||||
|
struct pager *pag;
|
||||||
|
const char *result;
|
||||||
|
|
||||||
|
ut_assertok(pager_init(&pag, 20, 4)); /* Small buffer */
|
||||||
|
|
||||||
|
/* send some text which is too long for the buffer */
|
||||||
|
result = pager_post(pag, true, "test1");
|
||||||
|
ut_assertnonnull(result);
|
||||||
|
|
||||||
|
/* overflow handling should return the text */
|
||||||
|
ut_asserteq_str("test1", result);
|
||||||
|
ut_assertnull(pager_next(pag, true, 0));
|
||||||
|
|
||||||
|
pager_uninit(pag);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
COMMON_TEST(pager_test_overflow, 0);
|
||||||
|
|
||||||
|
/* Test pager with NULL input */
|
||||||
|
static int pager_test_null_input(struct unit_test_state *uts)
|
||||||
|
{
|
||||||
|
const char *result;
|
||||||
|
|
||||||
|
/* Test pager_post with NULL pager */
|
||||||
|
result = pager_post(NULL, true, "test");
|
||||||
|
ut_asserteq_str("test", result);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
COMMON_TEST(pager_test_null_input, 0);
|
||||||
|
|
||||||
|
/* Test pager with empty strings */
|
||||||
|
static int pager_test_empty_strings(struct unit_test_state *uts)
|
||||||
|
{
|
||||||
|
struct pager *pag;
|
||||||
|
const char *result;
|
||||||
|
|
||||||
|
ut_assertok(pager_init(&pag, 20, 1024));
|
||||||
|
|
||||||
|
/* Post empty string */
|
||||||
|
result = pager_post(pag, true, "");
|
||||||
|
ut_assertnull(result);
|
||||||
|
|
||||||
|
/* Should be no more text */
|
||||||
|
result = pager_next(pag, true, 0);
|
||||||
|
ut_assertnull(result);
|
||||||
|
|
||||||
|
pager_uninit(pag);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
COMMON_TEST(pager_test_empty_strings, 0);
|
||||||
|
|
||||||
|
/* Test pager buffer management */
|
||||||
|
static int pager_test_buffer_management(struct unit_test_state *uts)
|
||||||
|
{
|
||||||
|
struct pager *pag;
|
||||||
|
const char *text = "Test buffer management";
|
||||||
|
const char *result;
|
||||||
|
|
||||||
|
ut_assertok(pager_init(&pag, 20, 1024));
|
||||||
|
|
||||||
|
/* Verify buffer is properly inited */
|
||||||
|
ut_assertnonnull(pag->buf.data);
|
||||||
|
ut_asserteq(1024, pag->buf.size);
|
||||||
|
|
||||||
|
/* Post text and verify buffer state */
|
||||||
|
result = pager_post(pag, true, text);
|
||||||
|
ut_assertnonnull(result);
|
||||||
|
|
||||||
|
/* Verify the buffer contains our text */
|
||||||
|
ut_asserteq_str(text, result);
|
||||||
|
|
||||||
|
pager_uninit(pag);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
COMMON_TEST(pager_test_buffer_management, 0);
|
||||||
|
|
||||||
|
/* Test pager with very long single line */
|
||||||
|
static int pager_test_long_single_line(struct unit_test_state *uts)
|
||||||
|
{
|
||||||
|
struct pager *pag;
|
||||||
|
char long_line[1000];
|
||||||
|
const char *result;
|
||||||
|
|
||||||
|
ut_assertok(pager_init(&pag, 20, 1024));
|
||||||
|
|
||||||
|
/* Create a very long line without newlines */
|
||||||
|
memset(long_line, 'X', sizeof(long_line) - 1);
|
||||||
|
long_line[sizeof(long_line) - 1] = '\0';
|
||||||
|
|
||||||
|
/* Post the long line */
|
||||||
|
result = pager_post(pag, true, long_line);
|
||||||
|
ut_assertnonnull(result);
|
||||||
|
|
||||||
|
/* Should get our text back */
|
||||||
|
ut_asserteq_str(long_line, result);
|
||||||
|
|
||||||
|
pager_uninit(pag);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
COMMON_TEST(pager_test_long_single_line, 0);
|
||||||
|
|
||||||
|
/* Test pager line counting and page breaks */
|
||||||
|
static int pager_test_line_counting(struct unit_test_state *uts)
|
||||||
|
{
|
||||||
|
struct pager *pag;
|
||||||
|
const char *multiline_text = "Line 1\nLine 2\nLine 3\nLine 4\nLine 5\n";
|
||||||
|
const char *result;
|
||||||
|
|
||||||
|
/* Init with page length of 4 lines */
|
||||||
|
ut_assertok(pager_init(&pag, 4, 1024));
|
||||||
|
|
||||||
|
/* Post multiline text */
|
||||||
|
result = pager_post(pag, true, multiline_text);
|
||||||
|
ut_assertnonnull(result);
|
||||||
|
|
||||||
|
/* Should get first 3 lines (excluding the 3rd newline) */
|
||||||
|
ut_asserteq_str("Line 1\nLine 2\nLine 3", result);
|
||||||
|
/* line_count is reset to 0 when page limit is reached */
|
||||||
|
ut_asserteq(0, pag->line_count);
|
||||||
|
|
||||||
|
/* Next call should return pager prompt */
|
||||||
|
result = pager_next(pag, true, 0);
|
||||||
|
ut_assertnonnull(result);
|
||||||
|
ut_asserteq_str(PAGER_PROMPT, result);
|
||||||
|
|
||||||
|
/* Press space to continue */
|
||||||
|
result = pager_next(pag, true, ' ');
|
||||||
|
ut_assertnonnull(result);
|
||||||
|
ut_asserteq_str(PAGER_BLANK, result);
|
||||||
|
|
||||||
|
/* Get remaining lines */
|
||||||
|
result = pager_next(pag, true, 0);
|
||||||
|
ut_assertnonnull(result);
|
||||||
|
ut_asserteq_str("Line 4\nLine 5\n", result);
|
||||||
|
|
||||||
|
/* Should be no more text */
|
||||||
|
result = pager_next(pag, true, 0);
|
||||||
|
ut_assertnull(result);
|
||||||
|
|
||||||
|
pager_uninit(pag);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
COMMON_TEST(pager_test_line_counting, 0);
|
||||||
|
|
||||||
|
/* Test that PAGER_WAITING is returned when pager waits for user input */
|
||||||
|
static int pager_test_pager_waiting(struct unit_test_state *uts)
|
||||||
|
{
|
||||||
|
struct pager *pag;
|
||||||
|
const char *result;
|
||||||
|
|
||||||
|
/* Create pager with small page size to trigger waiting quickly */
|
||||||
|
ut_assertok(pager_init(&pag, 3, 1024));
|
||||||
|
|
||||||
|
/* Post text that fills exactly the page limit */
|
||||||
|
result = pager_post(pag, true, "Line 1\nLine 2\n");
|
||||||
|
ut_assertnonnull(result);
|
||||||
|
ut_asserteq_str("Line 1\nLine 2", result);
|
||||||
|
|
||||||
|
/* Next call should return the prompt */
|
||||||
|
result = pager_next(pag, true, 0);
|
||||||
|
ut_assertnonnull(result);
|
||||||
|
ut_asserteq_str(PAGER_PROMPT, result);
|
||||||
|
|
||||||
|
/* Next call without space key should return PAGER_WAITING */
|
||||||
|
result = pager_next(pag, true, 0);
|
||||||
|
ut_asserteq_ptr(PAGER_WAITING, result);
|
||||||
|
|
||||||
|
/* Another call without space should still return PAGER_WAITING */
|
||||||
|
result = pager_next(pag, true, 'x'); /* Wrong key */
|
||||||
|
ut_asserteq_ptr(PAGER_WAITING, result);
|
||||||
|
|
||||||
|
/* Pressing space should clear the prompt */
|
||||||
|
result = pager_next(pag, true, ' ');
|
||||||
|
ut_assertnonnull(result);
|
||||||
|
ut_asserteq_str(PAGER_BLANK, result);
|
||||||
|
|
||||||
|
/* Now should return NULL (no more content) */
|
||||||
|
result = pager_next(pag, true, 0);
|
||||||
|
ut_assertnull(result);
|
||||||
|
|
||||||
|
pager_uninit(pag);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
COMMON_TEST(pager_test_pager_waiting, 0);
|
||||||
|
|
||||||
|
/* Test use_pager parameter - output text directly, while buffer is non-empty */
|
||||||
|
static int pager_test_use_pager_param(struct unit_test_state *uts)
|
||||||
|
{
|
||||||
|
struct pager *pag;
|
||||||
|
const char *buffered_text = "Line 1\nLine 2\nLine 3\nLine 4\nLine 5\n";
|
||||||
|
const char *direct_text = "This should be written immediately";
|
||||||
|
const char *result;
|
||||||
|
|
||||||
|
/* Init with small page length to ensure paging occurs */
|
||||||
|
ut_assertok(pager_init(&pag, 3, 1024));
|
||||||
|
|
||||||
|
/* Post text with use_pager=true - should trigger paging */
|
||||||
|
result = pager_post(pag, true, buffered_text);
|
||||||
|
ut_assertnonnull(result);
|
||||||
|
/* Should get first 2 lines */
|
||||||
|
ut_asserteq_str("Line 1\nLine 2", result);
|
||||||
|
|
||||||
|
/* Now call pager_post with use_pager=false while text is still buffered */
|
||||||
|
result = pager_post(pag, false, direct_text);
|
||||||
|
/* Should get the text immediately, not from buffer */
|
||||||
|
ut_asserteq_ptr(direct_text, result);
|
||||||
|
|
||||||
|
/* Call pager_next with use_pager=false - should return NULL */
|
||||||
|
result = pager_next(pag, false, 0);
|
||||||
|
ut_assertnull(result);
|
||||||
|
|
||||||
|
/* Now continue with use_pager=true to get buffered text */
|
||||||
|
result = pager_next(pag, true, 0);
|
||||||
|
ut_assertnonnull(result);
|
||||||
|
/* Should get the pager prompt */
|
||||||
|
ut_asserteq_str(PAGER_PROMPT, result);
|
||||||
|
|
||||||
|
/* Press space to continue */
|
||||||
|
result = pager_next(pag, true, ' ');
|
||||||
|
ut_assertnonnull(result);
|
||||||
|
ut_asserteq_str(PAGER_BLANK, result);
|
||||||
|
|
||||||
|
/* Get remaining buffered lines - should be next 2 lines due to page limit */
|
||||||
|
result = pager_next(pag, true, 0);
|
||||||
|
ut_assertnonnull(result);
|
||||||
|
ut_asserteq_str("Line 3\nLine 4", result);
|
||||||
|
|
||||||
|
/* Should get pager prompt again */
|
||||||
|
result = pager_next(pag, true, 0);
|
||||||
|
ut_assertnonnull(result);
|
||||||
|
ut_asserteq_str(PAGER_PROMPT, result);
|
||||||
|
|
||||||
|
/* Press space to continue */
|
||||||
|
result = pager_next(pag, true, ' ');
|
||||||
|
ut_assertnonnull(result);
|
||||||
|
ut_asserteq_str(PAGER_BLANK, result);
|
||||||
|
|
||||||
|
/* Get final line */
|
||||||
|
result = pager_next(pag, true, 0);
|
||||||
|
ut_assertnonnull(result);
|
||||||
|
ut_asserteq_str("Line 5\n", result);
|
||||||
|
|
||||||
|
/* Should be no more text */
|
||||||
|
result = pager_next(pag, true, 0);
|
||||||
|
ut_assertnull(result);
|
||||||
|
|
||||||
|
pager_uninit(pag);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
COMMON_TEST(pager_test_use_pager_param, 0);
|
||||||
|
|
||||||
|
/* Test pager bypass mode */
|
||||||
|
static int pager_test_bypass_mode(struct unit_test_state *uts)
|
||||||
|
{
|
||||||
|
struct pager *pag;
|
||||||
|
const char *text = "This text should be returned directly";
|
||||||
|
const char *result;
|
||||||
|
|
||||||
|
/* Init with small page length to ensure paging would normally occur */
|
||||||
|
ut_assertok(pager_init(&pag, 2, 1024));
|
||||||
|
|
||||||
|
/* Enable bypass mode */
|
||||||
|
pager_set_test_bypass(pag, true);
|
||||||
|
|
||||||
|
/* Post text - should get original string back directly */
|
||||||
|
result = pager_post(pag, true, text);
|
||||||
|
ut_asserteq_ptr(text, result); /* Should be same pointer */
|
||||||
|
|
||||||
|
/* pager_next should return NULL in bypass mode */
|
||||||
|
result = pager_next(pag, true, 0);
|
||||||
|
ut_assertnull(result);
|
||||||
|
|
||||||
|
/* Disable bypass mode */
|
||||||
|
pager_set_test_bypass(pag, false);
|
||||||
|
|
||||||
|
/* Now pager should work normally */
|
||||||
|
result = pager_post(pag, true, text);
|
||||||
|
ut_assertnonnull(result);
|
||||||
|
/* In normal mode, result should be different from original text */
|
||||||
|
ut_assert(result != text);
|
||||||
|
|
||||||
|
pager_uninit(pag);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
COMMON_TEST(pager_test_bypass_mode, 0);
|
||||||
|
|
||||||
|
/* Test that single character output via putc goes through pager */
|
||||||
|
static int pager_test_putc(struct unit_test_state *uts)
|
||||||
|
{
|
||||||
|
struct pager *pag;
|
||||||
|
const char *result;
|
||||||
|
|
||||||
|
/* Init pager */
|
||||||
|
ut_assertok(pager_init(&pag, 20, 1024));
|
||||||
|
pager_set_bypass(pag, true);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test that individual characters can be posted via pager API
|
||||||
|
* This verifies that console_putc_pager() routes through the pager
|
||||||
|
* system
|
||||||
|
*/
|
||||||
|
result = pager_post(pag, true, "A");
|
||||||
|
ut_asserteq_ptr("A", result); /* Bypass mode returns original pointer */
|
||||||
|
|
||||||
|
result = pager_post(pag, true, "\n");
|
||||||
|
ut_asserteq_ptr("\n", result);
|
||||||
|
|
||||||
|
result = pager_post(pag, true, "B");
|
||||||
|
ut_asserteq_ptr("B", result);
|
||||||
|
|
||||||
|
/* Disable bypass to test normal functionality with single chars */
|
||||||
|
pager_set_bypass(pag, false);
|
||||||
|
|
||||||
|
result = pager_post(pag, true, "X");
|
||||||
|
ut_assertnonnull(result);
|
||||||
|
ut_asserteq_str("X", result);
|
||||||
|
|
||||||
|
result = pager_next(pag, true, 0);
|
||||||
|
ut_assertnull(result);
|
||||||
|
|
||||||
|
pager_uninit(pag);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
COMMON_TEST(pager_test_putc, 0);
|
||||||
|
|
||||||
|
/* Test writing up to page limit then adding final newline */
|
||||||
|
static int pager_test_limit_plus_newline(struct unit_test_state *uts)
|
||||||
|
{
|
||||||
|
struct pager *pag;
|
||||||
|
const char *result;
|
||||||
|
|
||||||
|
/* Init with page length of 3 lines */
|
||||||
|
ut_assertok(pager_init(&pag, 3, 1024));
|
||||||
|
|
||||||
|
/* Write text that reaches exactly the page limit (2 newlines) */
|
||||||
|
result = pager_post(pag, true, "Line 1\nLine 2");
|
||||||
|
ut_assertnonnull(result);
|
||||||
|
ut_asserteq_str("Line 1\nLine 2", result);
|
||||||
|
ut_asserteq(1, pag->line_count); /* Should have 1 line counted */
|
||||||
|
|
||||||
|
/* Should be no more text yet - haven't hit limit */
|
||||||
|
result = pager_next(pag, true, 0);
|
||||||
|
ut_assertnull(result);
|
||||||
|
|
||||||
|
/* Now post a single newline - this should trigger the page limit */
|
||||||
|
result = pager_post(pag, true, "\n");
|
||||||
|
ut_assertnonnull(result);
|
||||||
|
/*
|
||||||
|
* Should get empty string since we hit the limit and the newline is
|
||||||
|
* consumed
|
||||||
|
*/
|
||||||
|
ut_asserteq_str("", result);
|
||||||
|
|
||||||
|
/* Next call should return the pager prompt since we hit the limit */
|
||||||
|
result = pager_next(pag, true, 0);
|
||||||
|
ut_assertnonnull(result);
|
||||||
|
ut_asserteq_str(PAGER_PROMPT, result);
|
||||||
|
|
||||||
|
/* Press space to continue */
|
||||||
|
result = pager_next(pag, true, ' ');
|
||||||
|
ut_assertnonnull(result);
|
||||||
|
ut_asserteq_str(PAGER_BLANK, result);
|
||||||
|
|
||||||
|
/* Should be no more text */
|
||||||
|
result = pager_next(pag, true, 0);
|
||||||
|
ut_assertnull(result);
|
||||||
|
|
||||||
|
pager_uninit(pag);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
COMMON_TEST(pager_test_limit_plus_newline, 0);
|
||||||
|
|
||||||
|
/* Test console integration - pager prompt appears in console output */
|
||||||
|
static int pager_test_console(struct unit_test_state *uts)
|
||||||
|
{
|
||||||
|
struct pager *pag, *orig_pag;
|
||||||
|
char line[100];
|
||||||
|
int avail, ret;
|
||||||
|
|
||||||
|
/* Save original pager */
|
||||||
|
orig_pag = gd_pager();
|
||||||
|
|
||||||
|
/* Create our own pager for testing */
|
||||||
|
ret = pager_init(&pag, 2, 1024);
|
||||||
|
if (ret) {
|
||||||
|
gd->pager = orig_pag;
|
||||||
|
return CMD_RET_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set up pager to be one away from limit (1 line already counted) */
|
||||||
|
pag->line_count = 1;
|
||||||
|
|
||||||
|
/* Assign our pager to the global data */
|
||||||
|
gd->pager = pag;
|
||||||
|
|
||||||
|
/* Trigger paging with a second newline */
|
||||||
|
putc('\n');
|
||||||
|
|
||||||
|
/* Check if there's any console output available at all */
|
||||||
|
avail = console_record_avail();
|
||||||
|
|
||||||
|
/* Restore original pager first */
|
||||||
|
gd->pager = orig_pag;
|
||||||
|
pager_uninit(pag);
|
||||||
|
|
||||||
|
/* Now check what we got */
|
||||||
|
if (!avail) {
|
||||||
|
ut_reportf("No console output was recorded at all");
|
||||||
|
return CMD_RET_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Try to read the actual output */
|
||||||
|
ret = console_record_readline(line, sizeof(line));
|
||||||
|
if (ret < 0) {
|
||||||
|
ut_reportf("Failed to read first line, avail was %d", avail);
|
||||||
|
return CMD_RET_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* console recording does not see the pager prompt, so we should have
|
||||||
|
* just got a newline
|
||||||
|
*/
|
||||||
|
ut_asserteq_str("", line);
|
||||||
|
|
||||||
|
ut_assert_console_end();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
COMMON_TEST(pager_test_console, UTF_CONSOLE);
|
||||||
@@ -52,6 +52,13 @@ class ConsoleSandbox(ConsoleBase):
|
|||||||
if self.use_dtb:
|
if self.use_dtb:
|
||||||
cmd += ['-d', self.config.dtb]
|
cmd += ['-d', self.config.dtb]
|
||||||
cmd += self.sandbox_flags
|
cmd += self.sandbox_flags
|
||||||
|
|
||||||
|
# Always disable the pager
|
||||||
|
cmd.append('-P')
|
||||||
|
|
||||||
|
# Always disable detected the terminal size
|
||||||
|
cmd.append('-A')
|
||||||
|
|
||||||
return Spawn(cmd, cwd=self.config.source_dir, decode_signal=True)
|
return Spawn(cmd, cwd=self.config.source_dir, decode_signal=True)
|
||||||
|
|
||||||
def restart_uboot_with_flags(self, flags, use_dtb=True):
|
def restart_uboot_with_flags(self, flags, use_dtb=True):
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
#include <net.h>
|
#include <net.h>
|
||||||
#include <of_live.h>
|
#include <of_live.h>
|
||||||
#include <os.h>
|
#include <os.h>
|
||||||
|
#include <pager.h>
|
||||||
#include <spl.h>
|
#include <spl.h>
|
||||||
#include <usb.h>
|
#include <usb.h>
|
||||||
#include <dm/ofnode.h>
|
#include <dm/ofnode.h>
|
||||||
@@ -747,8 +748,10 @@ int ut_run_list(struct unit_test_state *uts, const char *category,
|
|||||||
memcpy(uts->fdt_copy, gd->fdt_blob, uts->fdt_size);
|
memcpy(uts->fdt_copy, gd->fdt_blob, uts->fdt_size);
|
||||||
}
|
}
|
||||||
uts->force_run = force_run;
|
uts->force_run = force_run;
|
||||||
|
pager_set_test_bypass(gd_pager(), true);
|
||||||
ret = ut_run_tests(uts, prefix, tests, count, select_name,
|
ret = ut_run_tests(uts, prefix, tests, count, select_name,
|
||||||
test_insert);
|
test_insert);
|
||||||
|
pager_set_test_bypass(gd_pager(), false);
|
||||||
|
|
||||||
/* Best efforts only...ignore errors */
|
/* Best efforts only...ignore errors */
|
||||||
if (has_dm_tests)
|
if (has_dm_tests)
|
||||||
|
|||||||
Reference in New Issue
Block a user