mirror of
https://github.com/AsahiLinux/m1n1
synced 2024-12-12 15:42:27 +00:00
6a92a3e71a
Signed-off-by: Pip Cet <pipcet@gmail.com>
204 lines
4.7 KiB
C
204 lines
4.7 KiB
C
/* 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;
|
|
extern struct iodev iodev_usb[];
|
|
extern struct iodev iodev_usb_sec[];
|
|
|
|
struct iodev *iodevs[IODEV_MAX] = {
|
|
[IODEV_UART] = &iodev_uart, [IODEV_FB] = &iodev_fb,
|
|
[IODEV_USB0] = &iodev_usb[0], [IODEV_USB1] = &iodev_usb[1],
|
|
[IODEV_USB0_SEC] = &iodev_usb_sec[0], [IODEV_USB1_SEC] = &iodev_usb_sec[1],
|
|
};
|
|
|
|
char con_buf[CONSOLE_BUFFER_SIZE];
|
|
size_t con_wp;
|
|
size_t con_rp[IODEV_MAX];
|
|
|
|
ssize_t iodev_can_read(iodev_id_t id)
|
|
{
|
|
if (!iodevs[id]->ops->can_read)
|
|
return 0;
|
|
|
|
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 -1;
|
|
|
|
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 -1;
|
|
|
|
return iodevs[id]->ops->write(iodevs[id]->opaque, buf, length);
|
|
}
|
|
|
|
ssize_t iodev_queue(iodev_id_t id, const void *buf, size_t length)
|
|
{
|
|
if (!iodevs[id]->ops->queue)
|
|
return iodev_write(id, buf, length);
|
|
|
|
return iodevs[id]->ops->queue(iodevs[id]->opaque, buf, length);
|
|
}
|
|
|
|
void iodev_flush(iodev_id_t id)
|
|
{
|
|
if (!iodevs[id]->ops->flush)
|
|
return;
|
|
|
|
iodevs[id]->ops->flush(iodevs[id]->opaque);
|
|
}
|
|
|
|
int in_iodev = 0;
|
|
|
|
void iodev_console_write(const void *buf, size_t length)
|
|
{
|
|
if (in_iodev || !is_primary_core()) {
|
|
if (length) {
|
|
iodev_write(IODEV_UART, "*", 1);
|
|
iodev_write(IODEV_UART, buf, length);
|
|
return;
|
|
}
|
|
}
|
|
in_iodev++;
|
|
|
|
dprintf(" iodev_console_write() wp=%d\n", con_wp);
|
|
for (iodev_id_t id = 0; id < IODEV_MAX; id++) {
|
|
if (!iodevs[id])
|
|
continue;
|
|
|
|
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;
|
|
}
|
|
|
|
in_iodev--;
|
|
}
|
|
|
|
void iodev_handle_events(iodev_id_t id)
|
|
{
|
|
if (in_iodev)
|
|
return;
|
|
|
|
in_iodev++;
|
|
|
|
if (iodevs[id]->ops->handle_events)
|
|
iodevs[id]->ops->handle_events(iodevs[id]->opaque);
|
|
|
|
in_iodev--;
|
|
|
|
if (iodev_can_write(id))
|
|
iodev_console_write(NULL, 0);
|
|
}
|
|
|
|
void iodev_console_kick(void)
|
|
{
|
|
iodev_console_write(NULL, 0);
|
|
|
|
for (iodev_id_t id = 0; id < IODEV_MAX; id++) {
|
|
if (!iodevs[id])
|
|
continue;
|
|
if (!(iodevs[id]->usage & USAGE_CONSOLE))
|
|
continue;
|
|
|
|
iodev_handle_events(id);
|
|
}
|
|
}
|
|
|
|
void iodev_console_flush(void)
|
|
{
|
|
for (iodev_id_t id = 0; id < IODEV_MAX; id++) {
|
|
if (!iodevs[id])
|
|
continue;
|
|
if (!(iodevs[id]->usage & USAGE_CONSOLE))
|
|
continue;
|
|
|
|
iodev_flush(id);
|
|
}
|
|
}
|