[PATCH] console: Implement flush() function

Pali Rohár pali at kernel.org
Wed Feb 23 14:34:01 CET 2022


On certain places it is required to flush output print buffers to ensure
that text strings were sent to console or serial devices. For example when
printing message that U-Boot is going to boot kernel or when U-Boot is
going to change baudrate of terminal device.

Some console devices, like UART, have putc/puts functions which just put
characters into HW transmit queue and do not wait until all data are
transmitted. Doing some sensitive operations (like changing baudrate or
starting kernel which resets UART HW) cause that U-Boot messages are lost.

Therefore introduce a new flush() function, implement it for all serial
devices via pending(false) callback and use this new flush() function on
sensitive places after which output device may go into reset state.

This change fixes printing of U-Boot messages:
"## Starting application at ..."
"## Switch baudrate to ..."

Signed-off-by: Pali Rohár <pali at kernel.org>
---
 arch/sandbox/cpu/os.c          |  5 +++
 boot/bootm_os.c                |  1 +
 cmd/boot.c                     |  1 +
 cmd/elf.c                      |  2 ++
 cmd/load.c                     |  5 +++
 common/console.c               | 56 ++++++++++++++++++++++++++++++++++
 common/stdio.c                 |  6 ++++
 drivers/serial/serial-uclass.c | 25 +++++++++++++++
 drivers/serial/serial.c        |  1 +
 include/_exports.h             |  1 +
 include/os.h                   |  8 +++++
 include/serial.h               |  2 ++
 include/stdio.h                |  7 +++++
 include/stdio_dev.h            |  2 ++
 14 files changed, 122 insertions(+)

diff --git a/arch/sandbox/cpu/os.c b/arch/sandbox/cpu/os.c
index d83c862182dd..72dbf657abc3 100644
--- a/arch/sandbox/cpu/os.c
+++ b/arch/sandbox/cpu/os.c
@@ -653,6 +653,11 @@ void os_puts(const char *str)
 		os_putc(*str++);
 }
 
+void os_flush(void)
+{
+	fflush(stdout);
+}
+
 int os_write_ram_buf(const char *fname)
 {
 	struct sandbox_state *state = state_get_current();
diff --git a/boot/bootm_os.c b/boot/bootm_os.c
index f31820cd07ef..079224ce585b 100644
--- a/boot/bootm_os.c
+++ b/boot/bootm_os.c
@@ -303,6 +303,7 @@ static void do_bootvx_fdt(bootm_headers_t *images)
 #else
 	printf("## Starting vxWorks at 0x%08lx\n", (ulong)images->ep);
 #endif
+	flush();
 
 	boot_jump_vxworks(images);
 
diff --git a/cmd/boot.c b/cmd/boot.c
index be67a5980de3..14839c1cedcc 100644
--- a/cmd/boot.c
+++ b/cmd/boot.c
@@ -32,6 +32,7 @@ static int do_go(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
 	addr = hextoul(argv[1], NULL);
 
 	printf ("## Starting application at 0x%08lX ...\n", addr);
+	flush();
 
 	/*
 	 * pass address parameter as argv[0] (aka command name),
diff --git a/cmd/elf.c b/cmd/elf.c
index 2b33c50bd026..413d551dc05e 100644
--- a/cmd/elf.c
+++ b/cmd/elf.c
@@ -72,6 +72,7 @@ int do_bootelf(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
 		return rcode;
 
 	printf("## Starting application at 0x%08lx ...\n", addr);
+	flush();
 
 	/*
 	 * pass address parameter as argv[0] (aka command name),
@@ -274,6 +275,7 @@ int do_bootvx(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
 		puts("## Not an ELF image, assuming binary\n");
 
 	printf("## Starting vxWorks at 0x%08lx ...\n", addr);
+	flush();
 
 	dcache_disable();
 #if defined(CONFIG_ARM64) && defined(CONFIG_ARMV8_PSCI)
diff --git a/cmd/load.c b/cmd/load.c
index 7e4a552d90ef..7e9d4fb4a91b 100644
--- a/cmd/load.c
+++ b/cmd/load.c
@@ -81,6 +81,7 @@ static int do_load_serial(struct cmd_tbl *cmdtp, int flag, int argc,
 		printf("## Switch baudrate to %d bps and press ENTER ...\n",
 			load_baudrate);
 		udelay(50000);
+		flush();
 		gd->baudrate = load_baudrate;
 		serial_setbrg();
 		udelay(50000);
@@ -124,6 +125,7 @@ static int do_load_serial(struct cmd_tbl *cmdtp, int flag, int argc,
 		printf("## Switch baudrate to %d bps and press ESC ...\n",
 			current_baudrate);
 		udelay(50000);
+		flush();
 		gd->baudrate = current_baudrate;
 		serial_setbrg();
 		udelay(50000);
@@ -315,6 +317,7 @@ int do_save_serial(struct cmd_tbl *cmdtp, int flag, int argc,
 		printf("## Switch baudrate to %d bps and press ESC ...\n",
 			(int)current_baudrate);
 		udelay(50000);
+		flush();
 		gd->baudrate = current_baudrate;
 		serial_setbrg();
 		udelay(50000);
@@ -469,6 +472,7 @@ static int do_load_serial_bin(struct cmd_tbl *cmdtp, int flag, int argc,
 		printf("## Switch baudrate to %d bps and press ENTER ...\n",
 			load_baudrate);
 		udelay(50000);
+		flush();
 		gd->baudrate = load_baudrate;
 		serial_setbrg();
 		udelay(50000);
@@ -531,6 +535,7 @@ static int do_load_serial_bin(struct cmd_tbl *cmdtp, int flag, int argc,
 		printf("## Switch baudrate to %d bps and press ESC ...\n",
 			current_baudrate);
 		udelay(50000);
+		flush();
 		gd->baudrate = current_baudrate;
 		serial_setbrg();
 		udelay(50000);
diff --git a/common/console.c b/common/console.c
index 92b1c93be1ab..d3b93cb20856 100644
--- a/common/console.c
+++ b/common/console.c
@@ -198,6 +198,7 @@ static int console_setfile(int file, struct stdio_dev * dev)
 		case stdout:
 			gd->jt->putc  = putc;
 			gd->jt->puts  = puts;
+			gd->jt->flush = flush;
 			gd->jt->printf = printf;
 			break;
 		}
@@ -363,6 +364,17 @@ static void console_puts(int file, const char *s)
 	}
 }
 
+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);
+	}
+}
+
 #if CONFIG_IS_ENABLED(SYS_CONSOLE_IS_IN_ENV)
 static inline void console_doenv(int file, struct stdio_dev *dev)
 {
@@ -412,6 +424,12 @@ static inline void console_puts(int file, const char *s)
 	stdio_devices[file]->puts(stdio_devices[file], s);
 }
 
+static inline void console_flush(int file)
+{
+	if (stdio_devices[file]->flush)
+		stdio_devices[file]->flush(stdio_devices[file]);
+}
+
 #if CONFIG_IS_ENABLED(SYS_CONSOLE_IS_IN_ENV)
 static inline void console_doenv(int file, struct stdio_dev *dev)
 {
@@ -525,6 +543,12 @@ void fputs(int file, const char *s)
 		console_puts(file, s);
 }
 
+void fflush(int file)
+{
+	if (file < MAX_FILES)
+		console_flush(file);
+}
+
 int fprintf(int file, const char *fmt, ...)
 {
 	va_list args;
@@ -731,6 +755,38 @@ void puts(const char *s)
 	}
 }
 
+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->have_console)
+		return;
+
+	if (gd->flags & GD_FLG_DEVINIT) {
+		/* Send to the standard output */
+		fflush(stdout);
+	} else {
+		/* Send directly to the handler */
+		serial_flush();
+	}
+}
+
 #ifdef CONFIG_CONSOLE_RECORD
 int console_record_init(void)
 {
diff --git a/common/stdio.c b/common/stdio.c
index 063c659bbc37..d128a2cd7548 100644
--- a/common/stdio.c
+++ b/common/stdio.c
@@ -87,6 +87,11 @@ static void stdio_serial_puts(struct stdio_dev *dev, const char *s)
 	serial_puts(s);
 }
 
+static void stdio_serial_flush(struct stdio_dev *dev)
+{
+	serial_flush();
+}
+
 static int stdio_serial_getc(struct stdio_dev *dev)
 {
 	return serial_getc();
@@ -112,6 +117,7 @@ static void drv_system_init (void)
 	dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT;
 	dev.putc = stdio_serial_putc;
 	dev.puts = stdio_serial_puts;
+	dev.flush = stdio_serial_flush;
 	dev.getc = stdio_serial_getc;
 	dev.tstc = stdio_serial_tstc;
 	stdio_register (&dev);
diff --git a/drivers/serial/serial-uclass.c b/drivers/serial/serial-uclass.c
index 362cedd95524..6188087481cd 100644
--- a/drivers/serial/serial-uclass.c
+++ b/drivers/serial/serial-uclass.c
@@ -203,6 +203,16 @@ static void _serial_puts(struct udevice *dev, const char *str)
 		_serial_putc(dev, *str++);
 }
 
+static void _serial_flush(struct udevice *dev)
+{
+	struct dm_serial_ops *ops = serial_get_ops(dev);
+
+	if (!ops->pending)
+		return;
+	while (ops->pending(dev, false) > 0)
+		;
+}
+
 static int __serial_getc(struct udevice *dev)
 {
 	struct dm_serial_ops *ops = serial_get_ops(dev);
@@ -280,6 +290,14 @@ void serial_puts(const char *str)
 		_serial_puts(gd->cur_serial_dev, str);
 }
 
+void serial_flush(void)
+{
+	if (!gd->cur_serial_dev)
+		return;
+
+	_serial_flush(gd->cur_serial_dev);
+}
+
 int serial_getc(void)
 {
 	if (!gd->cur_serial_dev)
@@ -363,6 +381,11 @@ static void serial_stub_puts(struct stdio_dev *sdev, const char *str)
 	_serial_puts(sdev->priv, str);
 }
 
+static void serial_stub_flush(struct stdio_dev *sdev)
+{
+	_serial_flush(sdev->priv);
+}
+
 static int serial_stub_getc(struct stdio_dev *sdev)
 {
 	return _serial_getc(sdev->priv);
@@ -412,6 +435,7 @@ static int on_baudrate(const char *name, const char *value, enum env_op op,
 			printf("## Switch baudrate to %d bps and press ENTER ...\n",
 			       baudrate);
 			udelay(50000);
+			flush();
 		}
 
 		gd->baudrate = baudrate;
@@ -485,6 +509,7 @@ static int serial_post_probe(struct udevice *dev)
 	sdev.priv = dev;
 	sdev.putc = serial_stub_putc;
 	sdev.puts = serial_stub_puts;
+	sdev.flush = serial_stub_flush;
 	sdev.getc = serial_stub_getc;
 	sdev.tstc = serial_stub_tstc;
 
diff --git a/drivers/serial/serial.c b/drivers/serial/serial.c
index ebbd21916d77..7ccc71da2c35 100644
--- a/drivers/serial/serial.c
+++ b/drivers/serial/serial.c
@@ -81,6 +81,7 @@ static int on_baudrate(const char *name, const char *value, enum env_op op,
 			printf("## Switch baudrate to %d"
 				" bps and press ENTER ...\n", baudrate);
 			udelay(50000);
+			flush();
 		}
 
 		gd->baudrate = baudrate;
diff --git a/include/_exports.h b/include/_exports.h
index 8030d70c0bc3..755592edba28 100644
--- a/include/_exports.h
+++ b/include/_exports.h
@@ -12,6 +12,7 @@
 	EXPORT_FUNC(tstc, int, tstc, void)
 	EXPORT_FUNC(putc, void, putc, const char)
 	EXPORT_FUNC(puts, void, puts, const char *)
+	EXPORT_FUNC(flush, void, flush, void)
 	EXPORT_FUNC(printf, int, printf, const char*, ...)
 #if (defined(CONFIG_X86) && !defined(CONFIG_X86_64)) || defined(CONFIG_PPC)
 	EXPORT_FUNC(irq_install_handler, void, install_hdlr,
diff --git a/include/os.h b/include/os.h
index 10e198cf503e..6392cb6303de 100644
--- a/include/os.h
+++ b/include/os.h
@@ -288,6 +288,14 @@ void os_putc(int ch);
  */
 void os_puts(const char *str);
 
+/**
+ * os_flush() - flush controlling OS terminal
+ *
+ * This bypasses the U-Boot console support and flushes directly the OS
+ * stdout file descriptor.
+ */
+void os_flush(void);
+
 /**
  * os_write_ram_buf() - write the sandbox RAM buffer to a existing file
  *
diff --git a/include/serial.h b/include/serial.h
index 19a8c0c67d20..2759e4e9bc56 100644
--- a/include/serial.h
+++ b/include/serial.h
@@ -14,6 +14,7 @@ struct serial_device {
 	int	(*tstc)(void);
 	void	(*putc)(const char c);
 	void	(*puts)(const char *s);
+	void	(*flush)(void);
 #if CONFIG_POST & CONFIG_SYS_POST_UART
 	void	(*loop)(int);
 #endif
@@ -343,6 +344,7 @@ void serial_setbrg(void);
 void serial_putc(const char ch);
 void serial_putc_raw(const char ch);
 void serial_puts(const char *str);
+void serial_flush(void);
 int serial_getc(void);
 int serial_tstc(void);
 
diff --git a/include/stdio.h b/include/stdio.h
index 1939a48f0fb6..d605ff578b6d 100644
--- a/include/stdio.h
+++ b/include/stdio.h
@@ -15,6 +15,7 @@ int tstc(void);
 		defined(CONFIG_SPL_SERIAL))
 void putc(const char c);
 void puts(const char *s);
+void flush(void);
 int __printf(1, 2) printf(const char *fmt, ...);
 int vprintf(const char *fmt, va_list args);
 #else
@@ -26,6 +27,10 @@ static inline void puts(const char *s)
 {
 }
 
+static inline void flush(void)
+{
+}
+
 static inline int __printf(1, 2) printf(const char *fmt, ...)
 {
 	return 0;
@@ -48,11 +53,13 @@ static inline int vprintf(const char *fmt, va_list args)
 /* stderr */
 #define eputc(c)		fputc(stderr, c)
 #define eputs(s)		fputs(stderr, s)
+#define eflush()		fflush(stderr)
 #define eprintf(fmt, args...)	fprintf(stderr, fmt, ##args)
 
 int __printf(2, 3) fprintf(int file, const char *fmt, ...);
 void fputs(int file, const char *s);
 void fputc(int file, const char c);
+void fflush(int file);
 int ftstc(int file);
 int fgetc(int file);
 
diff --git a/include/stdio_dev.h b/include/stdio_dev.h
index 270fa2729fb2..fa650e0b72b9 100644
--- a/include/stdio_dev.h
+++ b/include/stdio_dev.h
@@ -37,6 +37,8 @@ struct stdio_dev {
 	void (*putc)(struct stdio_dev *dev, const char c);
 	/* To put a string (accelerator) */
 	void (*puts)(struct stdio_dev *dev, const char *s);
+	/* To flush output queue */
+	void (*flush)(struct stdio_dev *dev);
 
 /* INPUT functions */
 
-- 
2.20.1



More information about the U-Boot mailing list