xref: /openbmc/obmc-console/console-mux.c (revision a6b29104)
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], &current);
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