1// SPDX-License-Identifier: GPL-3.0-or-later
2
3#include "mos/lib/sync/spinlock.hpp"
4#include "mos/syslog/syslog.hpp"
5#include "mos/tasks/signal.hpp"
6#include "mos/tasks/thread.hpp"
7
8#include <array>
9#include <limits.h>
10#include <mos/device/console.hpp>
11#include <mos/io/io.hpp>
12#include <mos/lib/structures/list.hpp>
13#include <mos/lib/structures/ring_buffer.hpp>
14#include <mos/string.hpp>
15#include <mos/syslog/printk.hpp>
16#include <mos/tasks/schedule.hpp>
17#include <mos/tasks/wait.hpp>
18#include <mos_string.hpp>
19
20std::array<Console *, 128> consoles;
21size_t console_list_size = 0;
22
23std::optional<Console *> console_get(mos::string_view name)
24{
25 for (size_t i = 0; i < console_list_size; i++)
26 {
27 if (consoles[i]->name() == name)
28 return consoles[i];
29 }
30 return std::nullopt;
31}
32std::optional<Console *> console_get_by_prefix(mos::string_view prefix)
33{
34 for (size_t i = 0; i < console_list_size; i++)
35 {
36 if (consoles[i]->name().begins_with(prefix))
37 return consoles[i];
38 }
39 return std::nullopt;
40}
41
42Console::Console(mos::string_view name, ConsoleCapFlags caps, StandardColor default_fg, StandardColor default_bg)
43 : IO(IO_READABLE | ((caps & CONSOLE_CAP_READ) ? IO_WRITABLE : IO_NONE), IO_CONSOLE), //
44 fg(default_fg), bg(default_bg), //
45 caps(caps), //
46 default_fg(default_fg), default_bg(default_bg), //
47 conName(name) //
48{
49}
50
51void Console::Register()
52{
53 if (printk_console == nullptr)
54 printk_console = this;
55 consoles[console_list_size++] = this;
56}
57
58size_t Console::WriteColored(const char *data, size_t size, StandardColor fg, StandardColor bg)
59{
60 spinlock_acquire(&writer.lock);
61 if (caps.test(b: CONSOLE_CAP_COLOR))
62 {
63 if (this->fg != fg || this->bg != bg)
64 {
65 set_color(fg, bg);
66 this->fg = fg;
67 this->bg = bg;
68 }
69 }
70
71 size_t ret = do_write(data, size);
72 spinlock_release(&writer.lock);
73 return ret;
74}
75
76size_t Console::Write(const char *data, size_t size)
77{
78 spinlock_acquire(&writer.lock);
79 size_t ret = do_write(data, size);
80 spinlock_release(&writer.lock);
81 return ret;
82}
83size_t Console::on_read(void *data, size_t size)
84{
85 size_t read = 0;
86 while (read == 0)
87 {
88 SpinLocker locker(&reader.lock);
89
90 const bool buffer_empty = ring_buffer_pos_is_empty(pos: &reader.pos);
91 if (!buffer_empty)
92 {
93 read = ring_buffer_pos_pop_front(buffer: reader.buf, pos: &reader.pos, buf: (u8 *) data, size);
94 continue;
95 }
96
97 {
98 auto unlocker = locker.UnlockTemporarily();
99 bool ok = reschedule_for_waitlist(waitlist: &waitlist);
100 if (!ok)
101 {
102 unlocker.discard(), locker.discard();
103 pr_emerg("console: '%s' closed", conName);
104 return -EIO;
105 }
106 }
107
108 if (signal_has_pending())
109 {
110 return -ERESTARTSYS;
111 }
112 }
113
114 return read;
115}
116
117size_t Console::on_write(const void *data, size_t size)
118{
119 SpinLocker locker(&writer.lock);
120 if (caps.test(b: CONSOLE_CAP_COLOR))
121 set_color(fg: default_fg, bg: default_bg);
122 return do_write(data: (const char *) data, size);
123}
124
125void Console::putc(u8 c)
126{
127 if (c == 0x3)
128 {
129 spinlock_acquire(&waitlist.lock);
130 list_foreach(waitable_list_entry_t, entry, waitlist.list)
131 {
132 Thread *thread = thread_get(id: entry->waiter);
133 if (thread)
134 signal_send_to_thread(target: thread, SIGINT);
135 }
136 spinlock_release(&waitlist.lock);
137 }
138
139 ring_buffer_pos_push_back_byte(buffer: reader.buf, pos: &reader.pos, data: c);
140 waitlist_wake(list: &waitlist, INT_MAX);
141}
142
143mos::string Console::name() const
144{
145 return mos::string(conName);
146}
147
148void Console::on_closed()
149{
150 mInfo << "Closing console " << conName;
151}
152