HelenOS sources
This source file includes following definitions.
- stdin_wire
- stdin_signal
- stdout_wire
- stdout_write
- stdout_redraw
- stdout_scroll_up
- stdout_scroll_down
- kio_init
- grab_console
- release_console
- sys_debug_console
- kio_update
- kio_flush
- kio_push_bytes
- early_putstr
- putstr
- sys_kio_read
- sys_kio
- console_lock
- console_unlock
#include <abi/kio.h>
#include <console/chardev.h>
#include <console/console.h>
#include <errno.h>
#include <ipc/event.h>
#include <log.h>
#include <panic.h>
#include <preemption.h>
#include <proc/task.h>
#include <stdatomic.h>
#include <stdio.h>
#include <stdlib.h>
#include <str.h>
#include <synch/mutex.h>
#include <synch/spinlock.h>
#include <syscall/copy.h>
#include <sysinfo/sysinfo.h>
#define KIO_PAGES 8
#define KIO_LENGTH (KIO_PAGES * PAGE_SIZE)
static char kio[KIO_LENGTH];
static atomic_bool kio_inited = ATOMIC_VAR_INIT(false);
static MUTEX_INITIALIZE(console_mutex, MUTEX_RECURSIVE);
static size_t kio_written = 0;
static size_t kio_processed = 0;
static size_t kio_notified = 0;
IRQ_SPINLOCK_INITIALIZE(kio_lock);
static IRQ_SPINLOCK_INITIALIZE(flush_lock);
static IRQ_SPINLOCK_INITIALIZE(early_mbstate_lock);
static mbstate_t early_mbstate;
static indev_t stdin_sink;
static outdev_t stdout_source;
static void stdin_signal(indev_t *, indev_signal_t);
static indev_operations_t stdin_ops = {
.poll = NULL,
.signal = stdin_signal
};
static void stdout_write(outdev_t *, const char *, size_t);
static void stdout_redraw(outdev_t *);
static void stdout_scroll_up(outdev_t *);
static void stdout_scroll_down(outdev_t *);
static outdev_operations_t stdout_ops = {
.write = stdout_write,
.redraw = stdout_redraw,
.scroll_up = stdout_scroll_up,
.scroll_down = stdout_scroll_down
};
bool console_override = false;
indev_t *stdin = NULL;
outdev_t *stdout = NULL;
indev_t *stdin_wire(void)
{
if (stdin == NULL) {
indev_initialize("stdin", &stdin_sink, &stdin_ops);
stdin = &stdin_sink;
}
return stdin;
}
static void stdin_signal(indev_t *indev, indev_signal_t signal)
{
switch (signal) {
case INDEV_SIGNAL_SCROLL_UP:
if (stdout != NULL)
stdout_scroll_up(stdout);
break;
case INDEV_SIGNAL_SCROLL_DOWN:
if (stdout != NULL)
stdout_scroll_down(stdout);
break;
}
}
void stdout_wire(outdev_t *outdev)
{
if (stdout == NULL) {
outdev_initialize("stdout", &stdout_source, &stdout_ops);
stdout = &stdout_source;
}
list_append(&outdev->link, &stdout->list);
}
static void stdout_write(outdev_t *dev, const char *s, size_t n)
{
list_foreach(dev->list, link, outdev_t, sink) {
if ((sink) && (sink->op->write))
sink->op->write(sink, s, n);
}
}
static void stdout_redraw(outdev_t *dev)
{
list_foreach(dev->list, link, outdev_t, sink) {
if ((sink) && (sink->op->redraw))
sink->op->redraw(sink);
}
}
static void stdout_scroll_up(outdev_t *dev)
{
list_foreach(dev->list, link, outdev_t, sink) {
if ((sink) && (sink->op->scroll_up))
sink->op->scroll_up(sink);
}
}
static void stdout_scroll_down(outdev_t *dev)
{
list_foreach(dev->list, link, outdev_t, sink) {
if ((sink) && (sink->op->scroll_down))
sink->op->scroll_down(sink);
}
}
void kio_init(void)
{
event_set_unmask_callback(EVENT_KIO, kio_update);
atomic_store(&kio_inited, true);
}
void grab_console(void)
{
sysinfo_set_item_val("kconsole", NULL, true);
event_notify_1(EVENT_KCONSOLE, false, true);
bool prev = console_override;
console_override = true;
if ((stdout) && (stdout->op->redraw))
stdout->op->redraw(stdout);
if ((stdin) && (!prev)) {
indev_push_character(stdin, '\n');
}
}
void release_console(void)
{
sysinfo_set_item_val("kconsole", NULL, false);
console_override = false;
event_notify_1(EVENT_KCONSOLE, false, false);
}
sysarg_t sys_debug_console(void)
{
#ifdef CONFIG_KCONSOLE
grab_console();
return true;
#else
return false;
#endif
}
void kio_update(void *event)
{
if (!atomic_load(&kio_inited))
return;
irq_spinlock_lock(&kio_lock, true);
if (kio_notified != kio_written) {
if (event_notify_1(EVENT_KIO, true, kio_written) == EOK)
kio_notified = kio_written;
}
irq_spinlock_unlock(&kio_lock, true);
}
void kio_flush(void)
{
bool ordy = ((stdout) && (stdout->op->write));
if (!ordy)
return;
irq_spinlock_lock(&kio_lock, true);
if (!irq_spinlock_trylock(&flush_lock)) {
irq_spinlock_unlock(&kio_lock, true);
return;
}
char buffer[256];
while (kio_written != kio_processed) {
size_t offset = kio_processed % KIO_LENGTH;
size_t len = min(kio_written - kio_processed, KIO_LENGTH - offset);
len = min(len, sizeof(buffer));
memcpy(buffer, &kio[offset], len);
kio_processed += len;
irq_spinlock_unlock(&kio_lock, true);
stdout->op->write(stdout, buffer, len);
irq_spinlock_lock(&kio_lock, true);
}
irq_spinlock_unlock(&flush_lock, false);
irq_spinlock_unlock(&kio_lock, true);
}
void kio_push_bytes(const char *s, size_t n)
{
if (n > KIO_LENGTH) {
size_t lost = n - KIO_LENGTH;
kio_written += lost;
s += lost;
n -= lost;
}
size_t offset = kio_written % KIO_LENGTH;
if (offset + n > KIO_LENGTH) {
size_t first = KIO_LENGTH - offset;
size_t last = n - first;
memcpy(kio + offset, s, first);
memcpy(kio, s + first, last);
} else {
memcpy(kio + offset, s, n);
}
kio_written += n;
}
static void early_putstr(const char *s, size_t n)
{
irq_spinlock_lock(&early_mbstate_lock, true);
size_t offset = 0;
char32_t c;
while ((c = str_decode_r(s, &offset, n, U_SPECIAL, &early_mbstate)))
early_putuchar(c);
irq_spinlock_unlock(&early_mbstate_lock, true);
}
void putstr(const char *s, size_t n)
{
bool ordy = ((stdout) && (stdout->op->write));
irq_spinlock_lock(&kio_lock, true);
kio_push_bytes(s, n);
irq_spinlock_unlock(&kio_lock, true);
kio_flush();
if (!ordy) {
early_putstr(s, n);
}
if (memchr(s, '\n', n) != NULL)
kio_update(NULL);
}
sysarg_t sys_kio_read(uspace_addr_t buf, size_t size, size_t at)
{
errno_t rc;
size_t missed = 0;
irq_spinlock_lock(&kio_lock, true);
if (at == kio_written) {
irq_spinlock_unlock(&kio_lock, true);
return 0;
}
size_t readable_chars = kio_written - at;
if (readable_chars > KIO_LENGTH) {
missed = readable_chars - KIO_LENGTH;
readable_chars = KIO_LENGTH;
}
size_t actual_read = min(readable_chars, size);
size_t offset = (kio_written - readable_chars) % KIO_LENGTH;
if (offset + actual_read > KIO_LENGTH) {
size_t first = KIO_LENGTH - offset;
size_t last = actual_read - first;
rc = copy_to_uspace(buf, &kio[offset], first);
if (rc == EOK)
rc = copy_to_uspace(buf + first, &kio[0], last);
} else {
rc = copy_to_uspace(buf, &kio[offset], actual_read);
}
irq_spinlock_unlock(&kio_lock, true);
if (rc != EOK) {
log(LF_OTHER, LVL_WARN,
"[%s(%" PRIu64 ")] Terminating due to invalid memory buffer"
" in SYS_KIO_READ.\n", TASK->name, TASK->taskid);
task_kill_self(true);
}
return actual_read + missed;
}
sys_errno_t sys_kio(int cmd, uspace_addr_t buf, size_t size)
{
char *data;
errno_t rc;
switch (cmd) {
case KIO_UPDATE:
kio_update(NULL);
return EOK;
case KIO_WRITE:
case KIO_COMMAND:
break;
default:
return ENOTSUP;
}
if (size > PAGE_SIZE)
return (sys_errno_t) ELIMIT;
if (size > 0) {
data = (char *) malloc(size + 1);
if (!data)
return (sys_errno_t) ENOMEM;
rc = copy_from_uspace(data, buf, size);
if (rc) {
free(data);
return (sys_errno_t) rc;
}
data[size] = 0;
uint8_t substitute = '\x1a';
str_sanitize(data, size, substitute);
switch (cmd) {
case KIO_WRITE:
printf("[%s(%lu)] %s\n", TASK->name,
(unsigned long) TASK->taskid, data);
break;
case KIO_COMMAND:
if (!stdin)
break;
for (unsigned int i = 0; i < size; i++)
indev_push_character(stdin, data[i]);
indev_push_character(stdin, '\n');
break;
}
free(data);
}
return EOK;
}
void console_lock(void)
{
if (!PREEMPTION_DISABLED)
mutex_lock(&console_mutex);
}
void console_unlock(void)
{
if (!PREEMPTION_DISABLED)
mutex_unlock(&console_mutex);
}
HelenOS homepage, sources at GitHub