1// SPDX-License-Identifier: GPL-3.0-or-later
2
3#include "mos/tasks/signal.h"
4#include "mos/tasks/thread.h"
5
6#include <limits.h>
7#include <mos/device/console.h>
8#include <mos/io/io.h>
9#include <mos/lib/structures/list.h>
10#include <mos/lib/structures/ring_buffer.h>
11#include <mos/syslog/printk.h>
12#include <mos/tasks/schedule.h>
13#include <mos/tasks/wait.h>
14#include <mos_string.h>
15
16list_head consoles = LIST_HEAD_INIT(consoles);
17
18static size_t console_io_read(io_t *io, void *data, size_t size)
19{
20 console_t *con = container_of(io, console_t, io);
21
22retry_read:;
23 size_t read = 0;
24
25 spinlock_acquire(&con->read.lock);
26 if (ring_buffer_pos_is_empty(pos: &con->read.pos))
27 {
28 spinlock_release(&con->read.lock);
29 bool ok = reschedule_for_waitlist(waitlist: &con->waitlist);
30 if (!ok)
31 {
32 pr_emerg("console: '%s' closed", con->name);
33 return -EIO;
34 }
35 spinlock_acquire(&con->read.lock);
36
37 if (signal_has_pending())
38 {
39 spinlock_release(&con->read.lock);
40 return -ERESTARTSYS;
41 }
42 }
43 else
44 {
45 read = ring_buffer_pos_pop_front(buffer: con->read.buf, pos: &con->read.pos, buf: data, size);
46 }
47 spinlock_release(&con->read.lock);
48
49 if (read == 0)
50 goto retry_read;
51
52 return read;
53}
54
55static size_t console_io_write(io_t *io, const void *data, size_t size)
56{
57 console_t *con = container_of(io, console_t, io);
58 spinlock_acquire(&con->write.lock);
59 if ((con->caps & CONSOLE_CAP_COLOR))
60 con->ops->set_color(con, con->default_fg, con->default_bg);
61 size_t ret = con->ops->write(con, data, size);
62 spinlock_release(&con->write.lock);
63 return ret;
64}
65
66static const io_op_t console_io_ops = {
67 .read = console_io_read,
68 .write = console_io_write,
69};
70
71void console_register(console_t *con)
72{
73 if (con->caps & CONSOLE_CAP_EXTRA_SETUP)
74 {
75 bool result = con->ops->extra_setup(con);
76 if (!result)
77 {
78 pr_emerg("console: failed to setup '%s'", con->name);
79 return;
80 }
81 }
82
83 MOS_ASSERT_X(con->name != NULL, "console: %p's name is NULL", con);
84
85 con->write.lock = (spinlock_t) SPINLOCK_INIT;
86 io_flags_t flags = IO_WRITABLE;
87
88 if (con->caps & CONSOLE_CAP_READ)
89 {
90 MOS_ASSERT_X(con->read.buf, "console: '%s' has no read buffer", con->name);
91 con->read.lock = (spinlock_t) SPINLOCK_INIT;
92 ring_buffer_pos_init(&con->read.pos, con->read.size);
93 flags |= IO_READABLE;
94 }
95
96 io_init(&con->io, IO_CONSOLE, flags, &console_io_ops);
97 list_node_append(&consoles, list_node(con));
98 waitlist_init(&con->waitlist);
99}
100
101console_t *console_get(const char *name)
102{
103 if (list_is_empty(head: &consoles))
104 return NULL;
105
106 list_foreach(console_t, con, consoles)
107 {
108 if (strcmp(str1: con->name, str2: name) == 0)
109 return con;
110 }
111 return NULL;
112}
113
114console_t *console_get_by_prefix(const char *prefix)
115{
116 list_foreach(console_t, con, consoles)
117 {
118 if (strncmp(str1: con->name, str2: prefix, n: strlen(str: prefix)) == 0)
119 return con;
120 }
121 return NULL;
122}
123
124size_t console_write(console_t *con, const char *data, size_t size)
125{
126 spinlock_acquire(&con->write.lock);
127 size_t ret = con->ops->write(con, data, size);
128 spinlock_release(&con->write.lock);
129 return ret;
130}
131
132size_t console_write_color(console_t *con, const char *data, size_t size, standard_color_t fg, standard_color_t bg)
133{
134 standard_color_t prev_fg, prev_bg;
135 spinlock_acquire(&con->write.lock);
136 if (con->caps & CONSOLE_CAP_COLOR)
137 {
138 con->ops->get_color(con, &prev_fg, &prev_bg);
139 if (prev_fg != fg || prev_bg != bg)
140 con->ops->set_color(con, fg, bg);
141 }
142
143 size_t ret = con->ops->write(con, data, size);
144 spinlock_release(&con->write.lock);
145 return ret;
146}
147
148void console_putc(console_t *con, u8 c)
149{
150 if (c == 0x3)
151 {
152 spinlock_acquire(&con->waitlist.lock);
153 list_foreach(waitable_list_entry_t, entry, con->waitlist.list)
154 {
155 thread_t *thread = thread_get(id: entry->waiter);
156 if (thread)
157 signal_send_to_thread(target: thread, SIGINT);
158 }
159 spinlock_release(&con->waitlist.lock);
160 }
161
162 ring_buffer_pos_push_back_byte(buffer: con->read.buf, pos: &con->read.pos, data: c);
163 waitlist_wake(list: &con->waitlist, INT_MAX);
164}
165