mirror of
https://github.com/AsahiLinux/m1n1
synced 2024-11-10 09:44:13 +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 \
|
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 \
|
||||||
|
|
46
src/fb.c
46
src/fb.c
|
@ -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,
|
||||||
|
};
|
||||||
|
|
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_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
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 <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,
|
||||||
|
};
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue