// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2022 Sean Anderson */ #include #include #include #include #include /** * struct smh_serial_priv - Semihosting serial private data * @infd: stdin file descriptor (or error) * @outfd: stdout file descriptor (or error) * @counter: Counter used to fake pending every other call */ struct smh_serial_priv { int infd; int outfd; unsigned counter; }; #if CONFIG_IS_ENABLED(DM_SERIAL) static int smh_serial_getc(struct udevice *dev) { char ch = 0; struct smh_serial_priv *priv = dev_get_priv(dev); if (priv->infd < 0) return smh_getc(); smh_read(priv->infd, &ch, sizeof(ch)); return ch; } static int smh_serial_putc(struct udevice *dev, const char ch) { smh_putc(ch); return 0; } static ssize_t smh_serial_puts(struct udevice *dev, const char *s, size_t len) { int ret; struct smh_serial_priv *priv = dev_get_priv(dev); unsigned long written; if (priv->outfd < 0) { char *buf; /* Try and avoid a copy if we can */ if (!s[len + 1]) { smh_puts(s); return len; } buf = strndup(s, len); if (!buf) return -ENOMEM; smh_puts(buf); free(buf); return len; } ret = smh_write(priv->outfd, s, len, &written); if (written) return written; return ret; } static int smh_serial_pending(struct udevice *dev, bool input) { struct smh_serial_priv *priv = dev_get_priv(dev); if (input) return priv->counter++ & 1; return false; } static const struct dm_serial_ops smh_serial_ops = { .putc = smh_serial_putc, .puts = smh_serial_puts, .getc = smh_serial_getc, .pending = smh_serial_pending, }; static int smh_serial_bind(struct udevice *dev) { if (semihosting_enabled()) return 0; return -ENOENT; } static int smh_serial_probe(struct udevice *dev) { struct smh_serial_priv *priv = dev_get_priv(dev); priv->infd = smh_open(":tt", MODE_READ); priv->outfd = smh_open(":tt", MODE_WRITE); return 0; } U_BOOT_DRIVER(smh_serial) = { .name = "serial_semihosting", .id = UCLASS_SERIAL, .bind = smh_serial_bind, .probe = smh_serial_probe, .priv_auto = sizeof(struct smh_serial_priv), .ops = &smh_serial_ops, .flags = DM_FLAG_PRE_RELOC, }; U_BOOT_DRVINFO(smh_serial) = { .name = "serial_semihosting", }; #else /* DM_SERIAL */ static int infd = -ENODEV; static int outfd = -ENODEV; static unsigned counter = 1; static int smh_serial_start(void) { infd = smh_open(":tt", MODE_READ); outfd = smh_open(":tt", MODE_WRITE); return 0; } static int smh_serial_stop(void) { if (outfd >= 0) smh_close(outfd); return 0; } static void smh_serial_setbrg(void) { } static int smh_serial_getc(void) { char ch = 0; if (infd < 0) return smh_getc(); smh_read(infd, &ch, sizeof(ch)); return ch; } static int smh_serial_tstc(void) { return counter++ & 1; } static void smh_serial_puts(const char *s) { ulong unused; if (outfd < 0) smh_puts(s); else smh_write(outfd, s, strlen(s), &unused); } struct serial_device serial_smh_device = { .name = "serial_smh", .start = smh_serial_start, .stop = smh_serial_stop, .setbrg = smh_serial_setbrg, .getc = smh_serial_getc, .tstc = smh_serial_tstc, .putc = smh_putc, .puts = smh_serial_puts, }; void smh_serial_initialize(void) { if (semihosting_enabled()) serial_register(&serial_smh_device); } __weak struct serial_device *default_serial_console(void) { return &serial_smh_device; } #endif #ifdef CONFIG_DEBUG_UART_SEMIHOSTING #include static inline void _debug_uart_init(void) { } static inline void _debug_uart_putc(int c) { smh_putc(c); } DEBUG_UART_FUNCS #endif