1 #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
13 struct console_gpio {
14 char *name;
15 struct gpiod_line *line;
16 };
17
18 struct console_mux {
19 struct console_gpio *mux_gpios;
20 size_t n_mux_gpios;
21 };
22
23 static const char *key_mux_gpios = "mux-gpios";
24 static const char *key_mux_index = "mux-index";
25
strtokcnt(const char * str,const char sep)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
count_mux_gpios(const char * config_mux_gpios)42 count_mux_gpios(const char *config_mux_gpios)
43 {
44 return strtokcnt(config_mux_gpios, ',');
45 }
46
47 __attribute__((nonnull)) static char *
extract_mux_gpio_name(const char ** config_gpio_names)48 extract_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 *
console_mux_find_gpio_by_index(struct console_gpio * gpio,const char ** config_gpio_names)76 console_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
console_mux_release_gpio_lines(struct console_server * server)99 console_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
console_mux_request_gpio_lines(struct console_server * server,const char * config_gpio_names)112 console_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], ¤t);
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
console_server_mux_init(struct console_server * server)142 int 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
console_server_mux_fini(struct console_server * server)173 void 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
console_mux_init(struct console * console,struct config * config)188 int 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
console_timestamp(char * buffer,size_t size)216 static 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
console_print_timestamped(struct console * console,const char * message)229 static 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
console_mux_set_lines(struct console * console)256 static 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
console_mux_activate(struct console * console)274 int 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 }
321