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