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