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