[Concept,02/13] serial: Support length-based serial output

Message ID 20260204001002.2638622-3-sjg@u-boot.org
State New
Headers
Series Add putsn() for length-based console output |

Commit Message

Simon Glass Feb. 4, 2026, 12:09 a.m. UTC
  From: Simon Glass <simon.glass@canonical.com>

Add serial_putsn() to output a string with a specified length through
the serial port. The serial uclass already supports length-based writes
via __serial_puts(), so this just provides a public wrapper function.

Add a test to check it.

Co-developed-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
---

 drivers/serial/serial-uclass.c | 34 ++++++++++++++++++++++++++++++++++
 include/serial.h               | 12 ++++++++++++
 test/dm/serial.c               | 20 ++++++++++++++++++++
 3 files changed, 66 insertions(+)
  

Patch

diff --git a/drivers/serial/serial-uclass.c b/drivers/serial/serial-uclass.c
index 8d330d687a3..93fb4a0c2e3 100644
--- a/drivers/serial/serial-uclass.c
+++ b/drivers/serial/serial-uclass.c
@@ -309,6 +309,34 @@  static void _serial_puts(struct udevice *dev, const char *str)
 	} while (*str);
 }
 
+static void _serial_putsn(struct udevice *dev, const char *str, size_t len)
+{
+	struct dm_serial_ops *ops = serial_get_ops(dev);
+
+	if (!CONFIG_IS_ENABLED(SERIAL_PUTS) || !ops->puts) {
+		while (len--)
+			_serial_putc(dev, *str++);
+		return;
+	}
+
+	while (len) {
+		const char *newline = memchr(str, '\n', len);
+		size_t seg_len = newline ? newline - str : len;
+
+		if (__serial_puts(dev, str, seg_len))
+			return;
+
+		if (newline && __serial_puts(dev, "\r\n", 2))
+			return;
+
+		if (IS_ENABLED(CONFIG_CONSOLE_FLUSH_ON_NEWLINE) && newline)
+			_serial_flush(dev);
+
+		str += seg_len + !!newline;
+		len -= seg_len + !!newline;
+	}
+}
+
 static int __serial_getc(struct udevice *dev)
 {
 	struct dm_serial_ops *ops = serial_get_ops(dev);
@@ -391,6 +419,12 @@  void serial_puts(const char *str)
 		_serial_puts(gd->cur_serial_dev, str);
 }
 
+void serial_putsn(const char *str, int len)
+{
+	if (gd->cur_serial_dev)
+		_serial_putsn(gd->cur_serial_dev, str, len);
+}
+
 #ifdef CONFIG_CONSOLE_FLUSH_SUPPORT
 void serial_flush(void)
 {
diff --git a/include/serial.h b/include/serial.h
index 51977d8d7b7..66bf17a7cc0 100644
--- a/include/serial.h
+++ b/include/serial.h
@@ -392,6 +392,18 @@  void serial_setbrg(void);
 void serial_putc(const char ch);
 void serial_putc_raw(const char ch);
 void serial_puts(const char *str);
+
+/**
+ * serial_putsn() - Write a string with specified length to the serial console
+ *
+ * This outputs exactly @len characters from @str, regardless of any nul
+ * characters that may be present. This is useful for printing substrings or
+ * binary data that may contain embedded nuls.
+ *
+ * @str: String to output (need not be nul-terminated)
+ * @len: Number of characters to output
+ */
+void serial_putsn(const char *str, int len);
 #if defined(CONFIG_CONSOLE_FLUSH_SUPPORT) && CONFIG_IS_ENABLED(DM_SERIAL)
 void serial_flush(void);
 #else
diff --git a/test/dm/serial.c b/test/dm/serial.c
index 4acb14f41bc..8ed18726dd7 100644
--- a/test/dm/serial.c
+++ b/test/dm/serial.c
@@ -88,3 +88,23 @@  static int dm_test_serial(struct unit_test_state *uts)
 	return 0;
 }
 DM_TEST(dm_test_serial, UTF_SCAN_FDT);
+
+/* Test serial_putsn() function */
+static int dm_test_serial_putsn(struct unit_test_state *uts)
+{
+	const char *test_str = "testing string";
+	size_t test_len = 4;
+	size_t start, written;
+
+	/* Test that serial_putsn() writes the correct number of characters */
+	sandbox_serial_endisable(false);
+	start = sandbox_serial_written();
+	serial_putsn(test_str, test_len);
+	sandbox_serial_endisable(true);
+
+	written = sandbox_serial_written() - start;
+	ut_asserteq(test_len, written);
+
+	return 0;
+}
+DM_TEST(dm_test_serial_putsn, UTF_SCAN_FDT);