xref: /openbmc/obmc-console/console-server.c (revision 1a0e03b4385e3d1efa8760c8c69c781cbdcb7ddb)
1d831f960SJeremy Kerr /**
2d831f960SJeremy Kerr  * Console server process for OpenBMC
3d831f960SJeremy Kerr  *
4d831f960SJeremy Kerr  * Copyright © 2016 IBM Corporation <jk@ozlabs.org>
5d831f960SJeremy Kerr  */
6d831f960SJeremy Kerr 
717217845SJeremy Kerr #define _GNU_SOURCE
817217845SJeremy Kerr 
9d831f960SJeremy Kerr #include <stdint.h>
10d831f960SJeremy Kerr #include <stdbool.h>
11d831f960SJeremy Kerr #include <stdlib.h>
12d831f960SJeremy Kerr #include <stdio.h>
13d831f960SJeremy Kerr #include <fcntl.h>
14d831f960SJeremy Kerr #include <unistd.h>
15d831f960SJeremy Kerr #include <err.h>
16d831f960SJeremy Kerr #include <string.h>
17d831f960SJeremy Kerr #include <getopt.h>
1817217845SJeremy Kerr #include <limits.h>
19d831f960SJeremy Kerr 
20d831f960SJeremy Kerr #include <sys/types.h>
21d831f960SJeremy Kerr #include <sys/poll.h>
22d831f960SJeremy Kerr 
23*1a0e03b4SJeremy Kerr #include "console-server.h"
24d831f960SJeremy Kerr 
25*1a0e03b4SJeremy Kerr struct console {
2617217845SJeremy Kerr 	const char	*tty_kname;
2717217845SJeremy Kerr 	char		*tty_sysfs_devnode;
2817217845SJeremy Kerr 	char		*tty_dev;
29957818b4SJeremy Kerr 	int		tty_sirq;
30957818b4SJeremy Kerr 	int		tty_lpc_addr;
31d831f960SJeremy Kerr 	int		tty_fd;
32*1a0e03b4SJeremy Kerr 	struct handler	**handlers;
33*1a0e03b4SJeremy Kerr 	int		n_handlers;
34d831f960SJeremy Kerr };
35d831f960SJeremy Kerr 
36d831f960SJeremy Kerr static void usage(const char *progname)
37d831f960SJeremy Kerr {
38d831f960SJeremy Kerr 	fprintf(stderr,
39d831f960SJeremy Kerr "usage: %s [options]\n"
40d831f960SJeremy Kerr "\n"
41d831f960SJeremy Kerr "Options:\n"
4217217845SJeremy Kerr "  --device <TTY>  Use serial device TTY (eg, ttyS0)\n"
43d831f960SJeremy Kerr "",
44d831f960SJeremy Kerr 		progname);
45d831f960SJeremy Kerr }
46d831f960SJeremy Kerr 
4717217845SJeremy Kerr /* populates tty_dev and tty_sysfs_devnode, using the tty kernel name */
48*1a0e03b4SJeremy Kerr static int tty_find_device(struct console *console)
4917217845SJeremy Kerr {
5017217845SJeremy Kerr 	char *tty_class_device_link;
5117217845SJeremy Kerr 	char *tty_device_tty_dir;
5217217845SJeremy Kerr 	char *tty_device_reldir;
5317217845SJeremy Kerr 	int rc;
5417217845SJeremy Kerr 
5517217845SJeremy Kerr 	rc = -1;
5617217845SJeremy Kerr 	tty_class_device_link = NULL;
5717217845SJeremy Kerr 	tty_device_tty_dir = NULL;
5817217845SJeremy Kerr 	tty_device_reldir = NULL;
5917217845SJeremy Kerr 
6017217845SJeremy Kerr 	rc = asprintf(&tty_class_device_link,
61*1a0e03b4SJeremy Kerr 			"/sys/class/tty/%s", console->tty_kname);
6217217845SJeremy Kerr 	if (rc < 0)
6317217845SJeremy Kerr 		return -1;
6417217845SJeremy Kerr 
6517217845SJeremy Kerr 	tty_device_tty_dir = realpath(tty_class_device_link, NULL);
6617217845SJeremy Kerr 	if (rc < 0) {
67*1a0e03b4SJeremy Kerr 		warn("Can't query sysfs for device %s", console->tty_kname);
6817217845SJeremy Kerr 		goto out_free;
6917217845SJeremy Kerr 	}
7017217845SJeremy Kerr 
7117217845SJeremy Kerr 	rc = asprintf(&tty_device_reldir, "%s/../../", tty_device_tty_dir);
7217217845SJeremy Kerr 	if (rc < 0)
7317217845SJeremy Kerr 		goto out_free;
7417217845SJeremy Kerr 
75*1a0e03b4SJeremy Kerr 	console->tty_sysfs_devnode = realpath(tty_device_reldir, NULL);
76*1a0e03b4SJeremy Kerr 	if (!console->tty_sysfs_devnode)
77*1a0e03b4SJeremy Kerr 		warn("Can't find parent device for %s", console->tty_kname);
7817217845SJeremy Kerr 
7917217845SJeremy Kerr 
8017217845SJeremy Kerr 	/* todo: lookup from major/minor info in sysfs, in case udev has
8117217845SJeremy Kerr 	 * renamed us */
82*1a0e03b4SJeremy Kerr 	rc = asprintf(&console->tty_dev, "/dev/%s", console->tty_kname);
8317217845SJeremy Kerr 	if (rc < 0)
8417217845SJeremy Kerr 		goto out_free;
8517217845SJeremy Kerr 
8617217845SJeremy Kerr 	rc = 0;
8717217845SJeremy Kerr 
8817217845SJeremy Kerr out_free:
8917217845SJeremy Kerr 	free(tty_class_device_link);
9017217845SJeremy Kerr 	free(tty_device_tty_dir);
9117217845SJeremy Kerr 	free(tty_device_reldir);
9217217845SJeremy Kerr 	return rc;
9317217845SJeremy Kerr }
9417217845SJeremy Kerr 
95*1a0e03b4SJeremy Kerr static int tty_set_sysfs_attr(struct console *console, const char *name,
96957818b4SJeremy Kerr 		int value)
97957818b4SJeremy Kerr {
98957818b4SJeremy Kerr 	char *path;
99957818b4SJeremy Kerr 	FILE *fp;
100957818b4SJeremy Kerr 	int rc;
101957818b4SJeremy Kerr 
102*1a0e03b4SJeremy Kerr 	rc = asprintf(&path, "%s/%s", console->tty_sysfs_devnode, name);
103957818b4SJeremy Kerr 	if (rc < 0)
104957818b4SJeremy Kerr 		return -1;
105957818b4SJeremy Kerr 
106957818b4SJeremy Kerr 	fp = fopen(path, "w");
107957818b4SJeremy Kerr 	if (!fp) {
108957818b4SJeremy Kerr 		warn("Can't access attribute %s on device %s",
109*1a0e03b4SJeremy Kerr 				name, console->tty_kname);
110957818b4SJeremy Kerr 		rc = -1;
111957818b4SJeremy Kerr 		goto out_free;
112957818b4SJeremy Kerr 	}
113957818b4SJeremy Kerr 	setvbuf(fp, NULL, _IONBF, 0);
114957818b4SJeremy Kerr 
115957818b4SJeremy Kerr 	rc = fprintf(fp, "0x%x", value);
116957818b4SJeremy Kerr 	if (rc < 0)
117957818b4SJeremy Kerr 		warn("Error writing to %s attribute of device %s",
118*1a0e03b4SJeremy Kerr 				name, console->tty_kname);
119957818b4SJeremy Kerr 	fclose(fp);
120957818b4SJeremy Kerr 
121957818b4SJeremy Kerr 
122957818b4SJeremy Kerr 
123957818b4SJeremy Kerr out_free:
124957818b4SJeremy Kerr 	free(path);
125957818b4SJeremy Kerr 	return rc;
126957818b4SJeremy Kerr }
127957818b4SJeremy Kerr 
128d831f960SJeremy Kerr /**
129d831f960SJeremy Kerr  * Open and initialise the serial device
130d831f960SJeremy Kerr  */
131*1a0e03b4SJeremy Kerr static int tty_init_io(struct console *console)
132d831f960SJeremy Kerr {
133*1a0e03b4SJeremy Kerr 	if (console->tty_sirq)
134*1a0e03b4SJeremy Kerr 		tty_set_sysfs_attr(console, "sirq", console->tty_sirq);
135*1a0e03b4SJeremy Kerr 	if (console->tty_lpc_addr)
136*1a0e03b4SJeremy Kerr 		tty_set_sysfs_attr(console, "lpc_address",
137*1a0e03b4SJeremy Kerr 				console->tty_lpc_addr);
138*1a0e03b4SJeremy Kerr 	tty_set_sysfs_attr(console, "enabled", 1);
139957818b4SJeremy Kerr 
140*1a0e03b4SJeremy Kerr 	console->tty_fd = open(console->tty_dev, O_RDWR);
141*1a0e03b4SJeremy Kerr 	if (console->tty_fd <= 0) {
142*1a0e03b4SJeremy Kerr 		warn("Can't open tty %s", console->tty_dev);
143d831f960SJeremy Kerr 		return -1;
144d831f960SJeremy Kerr 	}
145d831f960SJeremy Kerr 
146d831f960SJeremy Kerr 	/* Disable character delay. We may want to later enable this when
147d831f960SJeremy Kerr 	 * we detect larger amounts of data
148d831f960SJeremy Kerr 	 */
149*1a0e03b4SJeremy Kerr 	fcntl(console->tty_fd, F_SETFL, FNDELAY);
150d831f960SJeremy Kerr 
151d831f960SJeremy Kerr 	return 0;
152d831f960SJeremy Kerr }
153d831f960SJeremy Kerr 
154*1a0e03b4SJeremy Kerr 
155*1a0e03b4SJeremy Kerr int console_data_out(struct console *console, const uint8_t *data, size_t len)
156d831f960SJeremy Kerr {
157*1a0e03b4SJeremy Kerr 	return write_buf_to_fd(console->tty_fd, data, len);
158d831f960SJeremy Kerr }
159d831f960SJeremy Kerr 
160*1a0e03b4SJeremy Kerr static void handlers_init(struct console *console)
161d831f960SJeremy Kerr {
162*1a0e03b4SJeremy Kerr 	extern struct handler *__start_handlers, *__stop_handlers;
163*1a0e03b4SJeremy Kerr 	struct handler *handler;
164*1a0e03b4SJeremy Kerr 	int i;
165d831f960SJeremy Kerr 
166*1a0e03b4SJeremy Kerr 	console->n_handlers = &__stop_handlers - &__start_handlers;
167*1a0e03b4SJeremy Kerr 	console->handlers = &__start_handlers;
168d831f960SJeremy Kerr 
169*1a0e03b4SJeremy Kerr 	printf("%d handler%s\n", console->n_handlers,
170*1a0e03b4SJeremy Kerr 			console->n_handlers == 1 ? "" : "s");
171d831f960SJeremy Kerr 
172*1a0e03b4SJeremy Kerr 	for (i = 0; i < console->n_handlers; i++) {
173*1a0e03b4SJeremy Kerr 		handler = console->handlers[i];
174*1a0e03b4SJeremy Kerr 
175*1a0e03b4SJeremy Kerr 		printf("  %s\n", handler->name);
176*1a0e03b4SJeremy Kerr 
177*1a0e03b4SJeremy Kerr 		if (handler->init)
178*1a0e03b4SJeremy Kerr 			handler->init(handler, console);
179d831f960SJeremy Kerr 	}
180d831f960SJeremy Kerr }
181d831f960SJeremy Kerr 
182*1a0e03b4SJeremy Kerr static void handlers_fini(struct console *console)
183d831f960SJeremy Kerr {
184*1a0e03b4SJeremy Kerr 	struct handler *handler;
185*1a0e03b4SJeremy Kerr 	int i;
186*1a0e03b4SJeremy Kerr 
187*1a0e03b4SJeremy Kerr 	for (i = 0; i < console->n_handlers; i++) {
188*1a0e03b4SJeremy Kerr 		handler = console->handlers[i];
189*1a0e03b4SJeremy Kerr 		if (handler->fini)
190*1a0e03b4SJeremy Kerr 			handler->fini(handler);
191*1a0e03b4SJeremy Kerr 	}
192d831f960SJeremy Kerr }
193d831f960SJeremy Kerr 
194*1a0e03b4SJeremy Kerr static int handlers_data_in(struct console *console, uint8_t *buf, size_t len)
195d831f960SJeremy Kerr {
196*1a0e03b4SJeremy Kerr 	struct handler *handler;
197*1a0e03b4SJeremy Kerr 	int i, rc, tmp;
198d831f960SJeremy Kerr 
199*1a0e03b4SJeremy Kerr 	rc = 0;
200*1a0e03b4SJeremy Kerr 
201*1a0e03b4SJeremy Kerr 	for (i = 0; i < console->n_handlers; i++) {
202*1a0e03b4SJeremy Kerr 		handler = console->handlers[i];
203*1a0e03b4SJeremy Kerr 
204*1a0e03b4SJeremy Kerr 		if (!handler->data_in)
205*1a0e03b4SJeremy Kerr 			continue;
206*1a0e03b4SJeremy Kerr 
207*1a0e03b4SJeremy Kerr 		tmp = handler->data_in(handler, buf, len);
208*1a0e03b4SJeremy Kerr 		if (tmp == HANDLER_EXIT)
209*1a0e03b4SJeremy Kerr 			rc = 1;
210d831f960SJeremy Kerr 	}
211d831f960SJeremy Kerr 
212*1a0e03b4SJeremy Kerr 	return rc;
213d831f960SJeremy Kerr }
214d831f960SJeremy Kerr 
215*1a0e03b4SJeremy Kerr static int handlers_poll_event(struct console *console,
216*1a0e03b4SJeremy Kerr 		struct pollfd *pollfds)
217d831f960SJeremy Kerr {
218*1a0e03b4SJeremy Kerr 	struct handler *handler;
219*1a0e03b4SJeremy Kerr 	int i, rc, tmp;
220d831f960SJeremy Kerr 
221*1a0e03b4SJeremy Kerr 	rc = 0;
222*1a0e03b4SJeremy Kerr 
223*1a0e03b4SJeremy Kerr 	for (i = 0; i < console->n_handlers; i++) {
224*1a0e03b4SJeremy Kerr 		handler = console->handlers[i];
225*1a0e03b4SJeremy Kerr 
226*1a0e03b4SJeremy Kerr 		if (!handler->poll_event)
227*1a0e03b4SJeremy Kerr 			continue;
228*1a0e03b4SJeremy Kerr 
229*1a0e03b4SJeremy Kerr 		tmp = handler->poll_event(handler, pollfds[i].revents);
230*1a0e03b4SJeremy Kerr 		if (tmp == HANDLER_EXIT)
231*1a0e03b4SJeremy Kerr 			rc = 1;
232*1a0e03b4SJeremy Kerr 	}
233*1a0e03b4SJeremy Kerr 
234*1a0e03b4SJeremy Kerr 	return rc;
235*1a0e03b4SJeremy Kerr }
236*1a0e03b4SJeremy Kerr 
237*1a0e03b4SJeremy Kerr int run_console(struct console *console)
238*1a0e03b4SJeremy Kerr {
239*1a0e03b4SJeremy Kerr 	struct handler *handler;
240*1a0e03b4SJeremy Kerr 	struct pollfd *pollfds;
241*1a0e03b4SJeremy Kerr 	int i, rc;
242*1a0e03b4SJeremy Kerr 
243*1a0e03b4SJeremy Kerr 	pollfds = calloc(console->n_handlers + 1, sizeof(*pollfds));
244*1a0e03b4SJeremy Kerr 
245*1a0e03b4SJeremy Kerr 	pollfds[0].fd = console->tty_fd;
246d831f960SJeremy Kerr 	pollfds[0].events = POLLIN;
247d831f960SJeremy Kerr 
248d831f960SJeremy Kerr 	for (;;) {
249d831f960SJeremy Kerr 		uint8_t buf[4096];
250d831f960SJeremy Kerr 
251*1a0e03b4SJeremy Kerr 		/* init pollers */
252*1a0e03b4SJeremy Kerr 		for (i = 0; i < console->n_handlers; i++) {
253*1a0e03b4SJeremy Kerr 			handler = console->handlers[i];
254*1a0e03b4SJeremy Kerr 			handler->init_poll(handler, &pollfds[i+1]);
255*1a0e03b4SJeremy Kerr 		}
256*1a0e03b4SJeremy Kerr 
257*1a0e03b4SJeremy Kerr 		rc = poll(pollfds, console->n_handlers + 1, -1);
258d831f960SJeremy Kerr 		if (rc < 0) {
259d831f960SJeremy Kerr 			warn("poll error");
260d831f960SJeremy Kerr 			return -1;
261d831f960SJeremy Kerr 		}
262d831f960SJeremy Kerr 
263d831f960SJeremy Kerr 		if (pollfds[0].revents) {
264*1a0e03b4SJeremy Kerr 			rc = read(console->tty_fd, buf, sizeof(buf));
265d831f960SJeremy Kerr 			if (rc <= 0) {
266d831f960SJeremy Kerr 				warn("Error reading from tty device");
267d831f960SJeremy Kerr 				return -1;
268d831f960SJeremy Kerr 			}
269*1a0e03b4SJeremy Kerr 			rc = handlers_data_in(console, buf, rc);
270*1a0e03b4SJeremy Kerr 			if (rc)
271d831f960SJeremy Kerr 				return 0;
272d831f960SJeremy Kerr 		}
273d831f960SJeremy Kerr 
274*1a0e03b4SJeremy Kerr 		rc = handlers_poll_event(console, pollfds + 1);
275*1a0e03b4SJeremy Kerr 		if (rc)
276*1a0e03b4SJeremy Kerr 			return 0;
277*1a0e03b4SJeremy Kerr 	}
278*1a0e03b4SJeremy Kerr }
279d831f960SJeremy Kerr static const struct option options[] = {
280d831f960SJeremy Kerr 	{ "device",	required_argument,	0, 'd'},
281957818b4SJeremy Kerr 	{ "sirq",	required_argument,	0, 's'},
282957818b4SJeremy Kerr 	{ "lpc-addr",	required_argument,	0, 'l'},
283d831f960SJeremy Kerr 	{ },
284d831f960SJeremy Kerr };
285d831f960SJeremy Kerr 
286d831f960SJeremy Kerr int main(int argc, char **argv)
287d831f960SJeremy Kerr {
288*1a0e03b4SJeremy Kerr 	struct console *console;
289d831f960SJeremy Kerr 	int rc;
290d831f960SJeremy Kerr 
291*1a0e03b4SJeremy Kerr 	console = malloc(sizeof(struct console));
292*1a0e03b4SJeremy Kerr 	memset(console, 0, sizeof(*console));
293957818b4SJeremy Kerr 	rc = -1;
294d831f960SJeremy Kerr 
295d831f960SJeremy Kerr 	for (;;) {
296957818b4SJeremy Kerr 		char *endp;
297d831f960SJeremy Kerr 		int c, idx;
298d831f960SJeremy Kerr 
299957818b4SJeremy Kerr 		c = getopt_long(argc, argv, "d:s:l:", options, &idx);
300d831f960SJeremy Kerr 		if (c == -1)
301d831f960SJeremy Kerr 			break;
302d831f960SJeremy Kerr 
303d831f960SJeremy Kerr 		switch (c) {
304d831f960SJeremy Kerr 		case 'd':
305*1a0e03b4SJeremy Kerr 			console->tty_kname = optarg;
306d831f960SJeremy Kerr 			break;
307957818b4SJeremy Kerr 		case 'l':
308*1a0e03b4SJeremy Kerr 			console->tty_lpc_addr = strtoul(optarg, &endp, 0);
309957818b4SJeremy Kerr 			if (endp == optarg) {
310957818b4SJeremy Kerr 				warnx("Invalid sirq: '%s'", optarg);
311957818b4SJeremy Kerr 				goto out_free;
312957818b4SJeremy Kerr 			}
313957818b4SJeremy Kerr 			break;
314957818b4SJeremy Kerr 
315957818b4SJeremy Kerr 		case 's':
316*1a0e03b4SJeremy Kerr 			console->tty_sirq = strtoul(optarg, &endp, 0);
317957818b4SJeremy Kerr 			if (endp == optarg) {
318957818b4SJeremy Kerr 				warnx("Invalid sirq: '%s'", optarg);
319957818b4SJeremy Kerr 				goto out_free;
320957818b4SJeremy Kerr 			}
321957818b4SJeremy Kerr 			break;
322d831f960SJeremy Kerr 
323d831f960SJeremy Kerr 		case 'h':
324d831f960SJeremy Kerr 		case '?':
325d831f960SJeremy Kerr 			usage(argv[0]);
326957818b4SJeremy Kerr 			rc = 0;
327957818b4SJeremy Kerr 			goto out_free;
328d831f960SJeremy Kerr 		}
329d831f960SJeremy Kerr 	}
330d831f960SJeremy Kerr 
331*1a0e03b4SJeremy Kerr 	if (!console->tty_kname) {
332d831f960SJeremy Kerr 		fprintf(stderr,
333d831f960SJeremy Kerr 			"Error: No TTY device specified (use --device)\n");
334d831f960SJeremy Kerr 		return EXIT_FAILURE;
335d831f960SJeremy Kerr 	}
336d831f960SJeremy Kerr 
337*1a0e03b4SJeremy Kerr 	rc = tty_find_device(console);
33817217845SJeremy Kerr 	if (rc)
33917217845SJeremy Kerr 		return EXIT_FAILURE;
34017217845SJeremy Kerr 
341*1a0e03b4SJeremy Kerr 	rc = tty_init_io(console);
342d831f960SJeremy Kerr 	if (rc)
343d831f960SJeremy Kerr 		return EXIT_FAILURE;
344d831f960SJeremy Kerr 
345*1a0e03b4SJeremy Kerr 	handlers_init(console);
346d831f960SJeremy Kerr 
347*1a0e03b4SJeremy Kerr 	rc = run_console(console);
348d831f960SJeremy Kerr 
349*1a0e03b4SJeremy Kerr 	handlers_fini(console);
350d831f960SJeremy Kerr 
351957818b4SJeremy Kerr out_free:
352*1a0e03b4SJeremy Kerr 	free(console->tty_sysfs_devnode);
353*1a0e03b4SJeremy Kerr 	free(console->tty_dev);
354*1a0e03b4SJeremy Kerr 	free(console);
355d831f960SJeremy Kerr 
356d831f960SJeremy Kerr 	return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
357d831f960SJeremy Kerr }
358