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 | |
16 | list_head consoles = LIST_HEAD_INIT(consoles); |
17 | |
18 | static 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 | |
22 | retry_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 | |
55 | static 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 | |
66 | static const io_op_t console_io_ops = { |
67 | .read = console_io_read, |
68 | .write = console_io_write, |
69 | }; |
70 | |
71 | void 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 | |
101 | console_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 | |
114 | console_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 | |
124 | size_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 | |
132 | size_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 | |
148 | void 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 | |