mirror of
https://github.com/AsahiLinux/m1n1
synced 2024-11-21 22:23:05 +00:00
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:
parent
1c518ded09
commit
cc4a5d312d
7 changed files with 264 additions and 15 deletions
1
Makefile
1
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 \
|
||||
|
|
46
src/fb.c
46
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,
|
||||
};
|
||||
|
|
2
src/fb.h
2
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);
|
||||
|
|
143
src/iodev.c
Normal file
143
src/iodev.c
Normal 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
39
src/iodev.h
Normal 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
|
40
src/uart.c
40
src/uart.c
|
@ -2,8 +2,9 @@
|
|||
|
||||
#include <stdarg.h>
|
||||
|
||||
#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,
|
||||
};
|
||||
|
|
|
@ -2,10 +2,9 @@
|
|||
|
||||
#include <stdarg.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue