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(+) 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); -- 2.43.0