blob: 105d9680c8e4e0b7e793d07d739ebdfe0a42960d [file] [log] [blame]
Alexander Hansena6b29102024-07-10 15:33:37 +02001#include <assert.h>
2#include <err.h>
3#include <errno.h>
4#include <gpiod.h>
5#include <limits.h>
6#include <stddef.h>
7#include <stdlib.h>
8
9#include "console-server.h"
10#include "console-mux.h"
11#include "config.h"
12
13struct console_gpio {
14 char *name;
15 struct gpiod_line *line;
16};
17
18struct console_mux {
19 struct console_gpio *mux_gpios;
20 size_t n_mux_gpios;
21};
22
23static const char *key_mux_gpios = "mux-gpios";
24static const char *key_mux_index = "mux-index";
25
26__attribute__((nonnull)) static size_t strtokcnt(const char *str,
27 const char sep)
28{
29 ssize_t n = 1;
30
31 while (*str) {
32 if (*str == sep) {
33 n++;
34 }
35 str++;
36 }
37
38 return n;
39}
40
41__attribute__((nonnull)) static size_t
42count_mux_gpios(const char *config_mux_gpios)
43{
44 return strtokcnt(config_mux_gpios, ',');
45}
46
47__attribute__((nonnull)) static char *
48extract_mux_gpio_name(const char **config_gpio_names)
49{
50 const char *current;
51 const char *comma;
52 ptrdiff_t length;
53
54 assert(*config_gpio_names);
55 current = *config_gpio_names;
56 comma = strchrnul(current, ',');
57 length = comma - current;
58
59 if (length == 0) {
60 return NULL;
61 }
62
63 char *word = calloc(length + 1, 1);
64 if (!word) {
65 return NULL;
66 }
67
68 strncpy(word, current, length);
69
70 *config_gpio_names = comma + !!(*comma);
71
72 return word;
73}
74
75__attribute__((nonnull)) static struct console_gpio *
76console_mux_find_gpio_by_index(struct console_gpio *gpio,
77 const char **config_gpio_names)
78{
79 assert(*config_gpio_names);
80
81 gpio->name = extract_mux_gpio_name(config_gpio_names);
82 if (gpio->name == NULL) {
83 warnx("could not extract mux gpio name from config '%s'",
84 *config_gpio_names);
85 return NULL;
86 }
87
88 gpio->line = gpiod_line_find(gpio->name);
89 if (gpio->line == NULL) {
90 warnx("libgpiod: could not find line %s", gpio->name);
91 free(gpio->name);
92 return NULL;
93 }
94
95 return gpio;
96}
97
98__attribute__((nonnull)) static void
99console_mux_release_gpio_lines(struct console_server *server)
100{
101 for (unsigned long i = 0; i < server->mux->n_mux_gpios; i++) {
102 struct console_gpio *gpio = &server->mux->mux_gpios[i];
103 gpiod_line_release(gpio->line);
104 gpiod_line_close_chip(gpio->line);
105
106 free(gpio->name);
107 gpio->name = NULL;
108 }
109}
110
111__attribute__((nonnull)) static int
112console_mux_request_gpio_lines(struct console_server *server,
113 const char *config_gpio_names)
114{
115 const char *current = config_gpio_names;
116 struct console_gpio *gpio;
117 int status = 0;
118
119 for (server->mux->n_mux_gpios = 0; *current;
120 server->mux->n_mux_gpios++) {
121 size_t i = server->mux->n_mux_gpios;
122 gpio = console_mux_find_gpio_by_index(
123 &server->mux->mux_gpios[i], &current);
124 if (gpio == NULL) {
125 console_mux_release_gpio_lines(server);
126 return -1;
127 }
128
129 status = gpiod_line_request_output(
130 gpio->line, program_invocation_short_name, 0);
131 if (status != 0) {
132 warnx("could not set line %s as output", gpio->name);
133 warnx("releasing all lines already requested");
134 console_mux_release_gpio_lines(server);
135 return -1;
136 }
137 }
138
139 return 0;
140}
141
142int console_server_mux_init(struct console_server *server)
143{
144 const char *config_gpio_names;
145 size_t max_ngpios;
146 size_t ngpios;
147
148 config_gpio_names = config_get_value(server->config, key_mux_gpios);
149 if (!config_gpio_names) {
150 return 0;
151 }
152
153 ngpios = count_mux_gpios(config_gpio_names);
154 max_ngpios = sizeof(((struct console *)0)->mux_index) * CHAR_BIT;
155 if (ngpios > max_ngpios) {
156 return -1;
157 }
158
159 server->mux = calloc(1, sizeof(struct console_mux));
160 if (!server->mux) {
161 return -1;
162 }
163
164 server->mux->n_mux_gpios = 0;
165 server->mux->mux_gpios = calloc(ngpios, sizeof(struct console_gpio));
166 if (!server->mux->mux_gpios) {
167 return -1;
168 }
169
170 return console_mux_request_gpio_lines(server, config_gpio_names);
171}
172
173void console_server_mux_fini(struct console_server *server)
174{
175 if (!server->mux) {
176 return;
177 }
178
179 console_mux_release_gpio_lines(server);
180
181 free(server->mux->mux_gpios);
182 server->mux->mux_gpios = NULL;
183
184 free(server->mux);
185 server->mux = NULL;
186}
187
188int console_mux_init(struct console *console, struct config *config)
189{
190 if (!console->server->mux) {
191 return 0;
192 }
193
194 if (console->server->mux->n_mux_gpios == 0) {
195 return 0;
196 }
197
198 const char *gpio_value = config_get_section_value(
199 config, console->console_id, key_mux_index);
200
201 if (gpio_value == NULL) {
202 warnx("console %s does not have property %s in config",
203 console->console_id, key_mux_index);
204 return -1;
205 }
206
207 errno = 0;
208 console->mux_index = strtoul(gpio_value, NULL, 0);
209 if (errno == ERANGE) {
210 return -1;
211 }
212
213 return 0;
214}
215
216static int console_timestamp(char *buffer, size_t size)
217{
218 size_t status;
219 time_t rawtime;
220 struct tm *timeinfo;
221
222 time(&rawtime);
223 timeinfo = gmtime(&rawtime);
224
225 status = strftime(buffer, size, "%Y-%m-%d %H:%M:%S UTC", timeinfo);
226 return !status;
227}
228
229static int console_print_timestamped(struct console *console,
230 const char *message)
231{
232#define TIMESTAMP_MAX_SIZE 32
233 char buf_timestamp[TIMESTAMP_MAX_SIZE];
234 int status;
235 char *buf;
236
237 status = console_timestamp(buf_timestamp, sizeof(buf_timestamp));
238 if (status != 0) {
239 warnx("Error: unable to print timestamp");
240 return status;
241 }
242
243 status = asprintf(&buf, "[obmc-console] %s %s\n", buf_timestamp,
244 message);
245 if (status == -1) {
246 return -1;
247 }
248
249 ringbuffer_queue(console->rb, (uint8_t *)buf, strlen(buf));
250
251 free(buf);
252
253 return 0;
254}
255
256static int console_mux_set_lines(struct console *console)
257{
258 int status = 0;
259
260 for (size_t i = 0; i < console->server->mux->n_mux_gpios; i++) {
261 struct console_gpio *gpio = &console->server->mux->mux_gpios[i];
262 const uint8_t value = (console->mux_index >> i) & 0x1;
263
264 status = gpiod_line_set_value(gpio->line, value);
265 if (status != 0) {
266 warnx("could not set line %s", gpio->name);
267 return -1;
268 }
269 }
270
271 return 0;
272}
273
274int console_mux_activate(struct console *console)
275{
276 struct console_server *server = console->server;
277 const bool first_activation = server->active == NULL;
278 const bool is_active = server->active == console;
279 int status = 0;
280
281 if (is_active) {
282 return 0;
283 }
284
285 if (server->mux) {
286 status = console_mux_set_lines(console);
287 }
288
289 if (status != 0) {
290 warnx("Error: unable to set mux gpios");
291 return status;
292 }
293
294 server->active = console;
295
296 /* Don't print disconnect/connect events on startup */
297 if (first_activation) {
298 return 0;
299 }
300
301 for (size_t i = 0; i < server->n_consoles; i++) {
302 struct console *other = server->consoles[i];
303 if (other == console) {
304 continue;
305 }
306 console_print_timestamped(other, "DISCONNECTED");
307
308 for (long j = 0; j < other->n_handlers; j++) {
309 struct handler *h = other->handlers[j];
310
311 if (h->type->deselect) {
312 h->type->deselect(h);
313 }
314 }
315 }
316
317 console_print_timestamped(console, "CONNECTED");
318
319 return 0;
320}