From cc4a5d312d798cfc52841559e3f2259a829f0c80 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Thu, 15 Apr 2021 22:47:33 +0900 Subject: [PATCH] iodev: Add new framework for I/O and console devices This replaces the earlier earlycon with a single shared console buffer for all output devices. Signed-off-by: Hector Martin --- Makefile | 1 + src/fb.c | 46 ++++++++++++++--- src/fb.h | 2 +- src/iodev.c | 143 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/iodev.h | 39 ++++++++++++++ src/uart.c | 40 ++++++++++++++- src/utils.c | 8 ++- 7 files changed, 264 insertions(+), 15 deletions(-) create mode 100644 src/iodev.c create mode 100644 src/iodev.h diff --git a/Makefile b/Makefile index b1cc62be..77391cb2 100644 --- a/Makefile +++ b/Makefile @@ -43,6 +43,7 @@ OBJECTS := \ exception.o exception_asm.o \ fb.o font.o font_retina.o \ heapblock.o \ + iodev.o \ kboot.o \ main.o \ memory.o memory_asm.o \ diff --git a/src/fb.c b/src/fb.c index ba6aab9e..d339a176 100644 --- a/src/fb.c +++ b/src/fb.c @@ -2,7 +2,9 @@ #include "assert.h" #include "fb.h" +#include "iodev.h" #include "string.h" +#include "types.h" #include "utils.h" #include "xnuboot.h" @@ -206,17 +208,23 @@ void fb_console_reserve_lines(u32 n) fb_console_scroll(1 + n - (console.cursor.max_row - console.cursor.row)); } -void fb_console_write(const char *bfr, size_t len) +ssize_t fb_console_write(const char *bfr, size_t len) { - if (!is_primary_core()) - return; - if (console.disabled) - return; - if (!console.initialized) - return; + ssize_t wrote = 0; - while (len--) + if (!is_primary_core()) + return 0; + if (console.disabled) + return 0; + if (!console.initialized) + return 0; + + while (len--) { fb_putchar(*bfr++); + wrote++; + } + + return wrote; } void fb_console_enable(void) @@ -228,3 +236,25 @@ void fb_console_disable(void) { console.disabled = 1; } + +static bool fb_console_iodev_can_write(void *opaque) +{ + UNUSED(opaque); + return console.initialized; +} + +static ssize_t fb_console_iodev_write(void *opaque, const void *buf, size_t len) +{ + UNUSED(opaque); + return fb_console_write(buf, len); +} + +const struct iodev_ops iodev_fb_ops = { + .can_write = fb_console_iodev_can_write, + .write = fb_console_iodev_write, +}; + +struct iodev iodev_fb = { + .ops = &iodev_fb_ops, + .usage = USAGE_CONSOLE, +}; diff --git a/src/fb.h b/src/fb.h index 5995d345..925c6e14 100644 --- a/src/fb.h +++ b/src/fb.h @@ -29,7 +29,7 @@ void fb_display_logo(void); void fb_console_scroll(u32 n); void fb_console_reserve_lines(u32 n); -void fb_console_write(const char *bfr, size_t len); +ssize_t fb_console_write(const char *bfr, size_t len); void fb_console_enable(void); void fb_console_disable(void); diff --git a/src/iodev.c b/src/iodev.c new file mode 100644 index 00000000..5bde35ad --- /dev/null +++ b/src/iodev.c @@ -0,0 +1,143 @@ +/* SPDX-License-Identifier: MIT */ + +//#define DEBUG_IODEV + +#include "iodev.h" +#include "string.h" + +#ifdef DEBUG_IODEV +#define dprintf printf +#else +#define dprintf(...) \ + do { \ + } while (0) +#endif + +#define CONSOLE_BUFFER_SIZE SZ_2K + +extern struct iodev iodev_uart; +extern struct iodev iodev_fb; + +struct iodev *iodevs[IODEV_MAX] = { + &iodev_uart, + &iodev_fb, +}; + +char con_buf[CONSOLE_BUFFER_SIZE]; +size_t con_wp; +size_t con_rp[IODEV_MAX]; + +bool iodev_can_read(iodev_id_t id) +{ + if (!iodevs[id]->ops->can_read) + return false; + + return iodevs[id]->ops->can_read(iodevs[id]->opaque); +} + +bool iodev_can_write(iodev_id_t id) +{ + if (!iodevs[id]->ops->can_write) + return false; + + return iodevs[id]->ops->can_write(iodevs[id]->opaque); +} + +ssize_t iodev_read(iodev_id_t id, void *buf, size_t length) +{ + if (!iodevs[id]->ops->read) + return false; + + return iodevs[id]->ops->read(iodevs[id]->opaque, buf, length); +} + +ssize_t iodev_write(iodev_id_t id, const void *buf, size_t length) +{ + if (!iodevs[id]->ops->write) + return false; + + return iodevs[id]->ops->write(iodevs[id]->opaque, buf, length); +} + +#ifdef DEBUG_IODEV +bool in_iodev = false; +#endif + +void iodev_console_write(const void *buf, size_t length) +{ +#ifdef DEBUG_IODEV + if (in_iodev) { + iodev_write(IODEV_UART, buf, length); + return; + } + in_iodev = true; +#endif + + dprintf(" iodev_console_write() wp=%d\n", con_wp); + for (iodev_id_t id = 0; id < IODEV_MAX; id++) { + if (!(iodevs[id]->usage & USAGE_CONSOLE)) { + /* Drop buffer */ + con_rp[id] = con_wp + length; + continue; + } + + if (!iodev_can_write(id)) + continue; + + if (con_wp > CONSOLE_BUFFER_SIZE) + con_rp[id] = max(con_wp - CONSOLE_BUFFER_SIZE, con_rp[id]); + + dprintf(" rp=%d\n", con_rp[id]); + // Flush existing buffer to device if possible + while (con_rp[id] < con_wp) { + size_t buf_rp = con_rp[id] % CONSOLE_BUFFER_SIZE; + size_t block = min(con_wp - con_rp[id], CONSOLE_BUFFER_SIZE - buf_rp); + + dprintf(" write buf %d\n", block); + ssize_t ret = iodev_write(id, &con_buf[buf_rp], block); + + if (ret <= 0) + goto next_dev; + + con_rp[id] += ret; + } + + const u8 *p = buf; + size_t wrote = 0; + + // Write the current buffer + while (wrote < length) { + ssize_t ret = iodev_write(id, p, length - wrote); + + if (ret <= 0) + goto next_dev; + + con_rp[id] += ret; + wrote += ret; + p += ret; + } + + next_dev:; + } + + // Update console buffer + + if (length > CONSOLE_BUFFER_SIZE) { + buf += (length - CONSOLE_BUFFER_SIZE); + con_wp += (length - CONSOLE_BUFFER_SIZE); + length = CONSOLE_BUFFER_SIZE; + } + + while (length) { + size_t buf_wp = con_wp % CONSOLE_BUFFER_SIZE; + size_t block = min(length, CONSOLE_BUFFER_SIZE - buf_wp); + memcpy(&con_buf[buf_wp], buf, block); + buf += block; + con_wp += block; + length -= block; + } + +#ifdef DEBUG_IODEV + in_iodev = false; +#endif +} diff --git a/src/iodev.h b/src/iodev.h new file mode 100644 index 00000000..fa76711e --- /dev/null +++ b/src/iodev.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: MIT */ + +#ifndef IODEV_H +#define IODEV_H + +#include "types.h" +#include "utils.h" + +typedef enum _iodev_id_t { IODEV_UART, IODEV_FB, IODEV_MAX } iodev_id_t; + +typedef enum _iodev_usage_t { + USAGE_CONSOLE = BIT(0), + USAGE_UARTPROXY = BIT(1), +} iodev_usage_t; + +struct iodev_ops { + bool (*can_read)(void *opaque); + bool (*can_write)(void *opaque); + ssize_t (*read)(void *opaque, void *buf, size_t length); + ssize_t (*write)(void *opaque, const void *buf, size_t length); +}; + +struct iodev { + const struct iodev_ops *ops; + + iodev_usage_t usage; + void *opaque; +}; + +extern struct iodev *iodevs[IODEV_MAX]; + +bool iodev_can_read(iodev_id_t id); +bool iodev_can_write(iodev_id_t id); +ssize_t iodev_read(iodev_id_t id, void *buf, size_t length); +ssize_t iodev_write(iodev_id_t id, const void *buf, size_t length); + +void iodev_console_write(const void *buf, size_t length); + +#endif diff --git a/src/uart.c b/src/uart.c index 7534af15..104f2151 100644 --- a/src/uart.c +++ b/src/uart.c @@ -2,8 +2,9 @@ #include -#include "types.h" #include "uart.h" +#include "iodev.h" +#include "types.h" #include "utils.h" #include "vsprintf.h" @@ -96,3 +97,40 @@ void uart_flush(void) while (!(read32(UART_BASE + UTRSTAT) & 0x04)) ; } + +static bool uart_iodev_can_write(void *opaque) +{ + UNUSED(opaque); + return true; +} + +static bool uart_iodev_can_read(void *opaque) +{ + UNUSED(opaque); + return read32(UART_BASE + UTRSTAT) & 0x01; +} + +static ssize_t uart_iodev_read(void *opaque, void *buf, size_t len) +{ + UNUSED(opaque); + return uart_read(buf, len); +} + +static ssize_t uart_iodev_write(void *opaque, const void *buf, size_t len) +{ + UNUSED(opaque); + uart_write(buf, len); + return len; +} + +static struct iodev_ops iodev_uart_ops = { + .can_read = uart_iodev_can_read, + .can_write = uart_iodev_can_write, + .read = uart_iodev_read, + .write = uart_iodev_write, +}; + +struct iodev iodev_uart = { + .ops = &iodev_uart_ops, + .usage = USAGE_CONSOLE | USAGE_UARTPROXY, +}; diff --git a/src/utils.c b/src/utils.c index 752fdfde..c8853132 100644 --- a/src/utils.c +++ b/src/utils.c @@ -2,10 +2,9 @@ #include -#include "fb.h" -#include "types.h" -#include "uart.h" #include "utils.h" +#include "iodev.h" +#include "types.h" #include "vsprintf.h" static char ascii(char s) @@ -75,8 +74,7 @@ int debug_printf(const char *fmt, ...) i = vsprintf(buffer, fmt, args); va_end(args); - uart_write(buffer, i); - fb_console_write(buffer, i); + iodev_console_write(buffer, i); return i; }