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 <marcan@marcan.st>
This commit is contained in:
Hector Martin 2021-04-15 22:47:33 +09:00
parent 1c518ded09
commit cc4a5d312d
7 changed files with 264 additions and 15 deletions

View file

@ -43,6 +43,7 @@ OBJECTS := \
exception.o exception_asm.o \ exception.o exception_asm.o \
fb.o font.o font_retina.o \ fb.o font.o font_retina.o \
heapblock.o \ heapblock.o \
iodev.o \
kboot.o \ kboot.o \
main.o \ main.o \
memory.o memory_asm.o \ memory.o memory_asm.o \

View file

@ -2,7 +2,9 @@
#include "assert.h" #include "assert.h"
#include "fb.h" #include "fb.h"
#include "iodev.h"
#include "string.h" #include "string.h"
#include "types.h"
#include "utils.h" #include "utils.h"
#include "xnuboot.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)); 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()) ssize_t wrote = 0;
return;
if (console.disabled)
return;
if (!console.initialized)
return;
while (len--) if (!is_primary_core())
return 0;
if (console.disabled)
return 0;
if (!console.initialized)
return 0;
while (len--) {
fb_putchar(*bfr++); fb_putchar(*bfr++);
wrote++;
}
return wrote;
} }
void fb_console_enable(void) void fb_console_enable(void)
@ -228,3 +236,25 @@ void fb_console_disable(void)
{ {
console.disabled = 1; 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,
};

View file

@ -29,7 +29,7 @@ void fb_display_logo(void);
void fb_console_scroll(u32 n); void fb_console_scroll(u32 n);
void fb_console_reserve_lines(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_enable(void);
void fb_console_disable(void); void fb_console_disable(void);

143
src/iodev.c Normal file
View file

@ -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
}

39
src/iodev.h Normal file
View file

@ -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

View file

@ -2,8 +2,9 @@
#include <stdarg.h> #include <stdarg.h>
#include "types.h"
#include "uart.h" #include "uart.h"
#include "iodev.h"
#include "types.h"
#include "utils.h" #include "utils.h"
#include "vsprintf.h" #include "vsprintf.h"
@ -96,3 +97,40 @@ void uart_flush(void)
while (!(read32(UART_BASE + UTRSTAT) & 0x04)) 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,
};

View file

@ -2,10 +2,9 @@
#include <stdarg.h> #include <stdarg.h>
#include "fb.h"
#include "types.h"
#include "uart.h"
#include "utils.h" #include "utils.h"
#include "iodev.h"
#include "types.h"
#include "vsprintf.h" #include "vsprintf.h"
static char ascii(char s) static char ascii(char s)
@ -75,8 +74,7 @@ int debug_printf(const char *fmt, ...)
i = vsprintf(buffer, fmt, args); i = vsprintf(buffer, fmt, args);
va_end(args); va_end(args);
uart_write(buffer, i); iodev_console_write(buffer, i);
fb_console_write(buffer, i);
return i; return i;
} }