blob: e68eab1ef5ced4a1e351485a7df4fb2c4b383976 [file] [log] [blame]
Jeremy Kerrd831f962016-01-29 17:18:01 +08001/**
2 * Console server process for OpenBMC
3 *
4 * Copyright © 2016 IBM Corporation <jk@ozlabs.org>
5 */
6
Jeremy Kerr17217842016-01-29 18:44:21 +08007#define _GNU_SOURCE
8
Jeremy Kerrd831f962016-01-29 17:18:01 +08009#include <stdint.h>
10#include <stdbool.h>
11#include <stdlib.h>
12#include <stdio.h>
13#include <fcntl.h>
14#include <unistd.h>
15#include <err.h>
Jeremy Kerrd831f962016-01-29 17:18:01 +080016#include <string.h>
17#include <getopt.h>
Jeremy Kerr17217842016-01-29 18:44:21 +080018#include <limits.h>
Jeremy Kerrd831f962016-01-29 17:18:01 +080019
20#include <sys/types.h>
21#include <sys/poll.h>
22
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +080023#include "console-server.h"
Jeremy Kerrd831f962016-01-29 17:18:01 +080024
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +080025struct console {
Jeremy Kerr17217842016-01-29 18:44:21 +080026 const char *tty_kname;
27 char *tty_sysfs_devnode;
28 char *tty_dev;
Jeremy Kerr957818b2016-03-08 14:35:15 +080029 int tty_sirq;
30 int tty_lpc_addr;
Jeremy Kerrd831f962016-01-29 17:18:01 +080031 int tty_fd;
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +080032 struct handler **handlers;
33 int n_handlers;
Jeremy Kerrd831f962016-01-29 17:18:01 +080034};
35
36static void usage(const char *progname)
37{
38 fprintf(stderr,
39"usage: %s [options]\n"
40"\n"
41"Options:\n"
Jeremy Kerr17217842016-01-29 18:44:21 +080042" --device <TTY> Use serial device TTY (eg, ttyS0)\n"
Jeremy Kerrd831f962016-01-29 17:18:01 +080043"",
44 progname);
45}
46
Jeremy Kerr17217842016-01-29 18:44:21 +080047/* populates tty_dev and tty_sysfs_devnode, using the tty kernel name */
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +080048static int tty_find_device(struct console *console)
Jeremy Kerr17217842016-01-29 18:44:21 +080049{
50 char *tty_class_device_link;
51 char *tty_device_tty_dir;
52 char *tty_device_reldir;
53 int rc;
54
55 rc = -1;
56 tty_class_device_link = NULL;
57 tty_device_tty_dir = NULL;
58 tty_device_reldir = NULL;
59
60 rc = asprintf(&tty_class_device_link,
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +080061 "/sys/class/tty/%s", console->tty_kname);
Jeremy Kerr17217842016-01-29 18:44:21 +080062 if (rc < 0)
63 return -1;
64
65 tty_device_tty_dir = realpath(tty_class_device_link, NULL);
66 if (rc < 0) {
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +080067 warn("Can't query sysfs for device %s", console->tty_kname);
Jeremy Kerr17217842016-01-29 18:44:21 +080068 goto out_free;
69 }
70
71 rc = asprintf(&tty_device_reldir, "%s/../../", tty_device_tty_dir);
72 if (rc < 0)
73 goto out_free;
74
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +080075 console->tty_sysfs_devnode = realpath(tty_device_reldir, NULL);
76 if (!console->tty_sysfs_devnode)
77 warn("Can't find parent device for %s", console->tty_kname);
Jeremy Kerr17217842016-01-29 18:44:21 +080078
79
80 /* todo: lookup from major/minor info in sysfs, in case udev has
81 * renamed us */
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +080082 rc = asprintf(&console->tty_dev, "/dev/%s", console->tty_kname);
Jeremy Kerr17217842016-01-29 18:44:21 +080083 if (rc < 0)
84 goto out_free;
85
86 rc = 0;
87
88out_free:
89 free(tty_class_device_link);
90 free(tty_device_tty_dir);
91 free(tty_device_reldir);
92 return rc;
93}
94
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +080095static int tty_set_sysfs_attr(struct console *console, const char *name,
Jeremy Kerr957818b2016-03-08 14:35:15 +080096 int value)
97{
98 char *path;
99 FILE *fp;
100 int rc;
101
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800102 rc = asprintf(&path, "%s/%s", console->tty_sysfs_devnode, name);
Jeremy Kerr957818b2016-03-08 14:35:15 +0800103 if (rc < 0)
104 return -1;
105
106 fp = fopen(path, "w");
107 if (!fp) {
108 warn("Can't access attribute %s on device %s",
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800109 name, console->tty_kname);
Jeremy Kerr957818b2016-03-08 14:35:15 +0800110 rc = -1;
111 goto out_free;
112 }
113 setvbuf(fp, NULL, _IONBF, 0);
114
115 rc = fprintf(fp, "0x%x", value);
116 if (rc < 0)
117 warn("Error writing to %s attribute of device %s",
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800118 name, console->tty_kname);
Jeremy Kerr957818b2016-03-08 14:35:15 +0800119 fclose(fp);
120
121
122
123out_free:
124 free(path);
125 return rc;
126}
127
Jeremy Kerrd831f962016-01-29 17:18:01 +0800128/**
129 * Open and initialise the serial device
130 */
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800131static int tty_init_io(struct console *console)
Jeremy Kerrd831f962016-01-29 17:18:01 +0800132{
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800133 if (console->tty_sirq)
134 tty_set_sysfs_attr(console, "sirq", console->tty_sirq);
135 if (console->tty_lpc_addr)
136 tty_set_sysfs_attr(console, "lpc_address",
137 console->tty_lpc_addr);
138 tty_set_sysfs_attr(console, "enabled", 1);
Jeremy Kerr957818b2016-03-08 14:35:15 +0800139
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800140 console->tty_fd = open(console->tty_dev, O_RDWR);
141 if (console->tty_fd <= 0) {
142 warn("Can't open tty %s", console->tty_dev);
Jeremy Kerrd831f962016-01-29 17:18:01 +0800143 return -1;
144 }
145
146 /* Disable character delay. We may want to later enable this when
147 * we detect larger amounts of data
148 */
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800149 fcntl(console->tty_fd, F_SETFL, FNDELAY);
Jeremy Kerrd831f962016-01-29 17:18:01 +0800150
151 return 0;
152}
153
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800154
155int console_data_out(struct console *console, const uint8_t *data, size_t len)
Jeremy Kerrd831f962016-01-29 17:18:01 +0800156{
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800157 return write_buf_to_fd(console->tty_fd, data, len);
Jeremy Kerrd831f962016-01-29 17:18:01 +0800158}
159
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800160static void handlers_init(struct console *console)
Jeremy Kerrd831f962016-01-29 17:18:01 +0800161{
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800162 extern struct handler *__start_handlers, *__stop_handlers;
163 struct handler *handler;
164 int i;
Jeremy Kerrd831f962016-01-29 17:18:01 +0800165
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800166 console->n_handlers = &__stop_handlers - &__start_handlers;
167 console->handlers = &__start_handlers;
Jeremy Kerrd831f962016-01-29 17:18:01 +0800168
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800169 printf("%d handler%s\n", console->n_handlers,
170 console->n_handlers == 1 ? "" : "s");
Jeremy Kerrd831f962016-01-29 17:18:01 +0800171
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800172 for (i = 0; i < console->n_handlers; i++) {
173 handler = console->handlers[i];
174
175 printf(" %s\n", handler->name);
176
177 if (handler->init)
178 handler->init(handler, console);
Jeremy Kerrd831f962016-01-29 17:18:01 +0800179 }
Jeremy Kerrd831f962016-01-29 17:18:01 +0800180}
181
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800182static void handlers_fini(struct console *console)
Jeremy Kerrd831f962016-01-29 17:18:01 +0800183{
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800184 struct handler *handler;
185 int i;
186
187 for (i = 0; i < console->n_handlers; i++) {
188 handler = console->handlers[i];
189 if (handler->fini)
190 handler->fini(handler);
191 }
Jeremy Kerrd831f962016-01-29 17:18:01 +0800192}
193
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800194static int handlers_data_in(struct console *console, uint8_t *buf, size_t len)
Jeremy Kerrd831f962016-01-29 17:18:01 +0800195{
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800196 struct handler *handler;
197 int i, rc, tmp;
Jeremy Kerrd831f962016-01-29 17:18:01 +0800198
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800199 rc = 0;
200
201 for (i = 0; i < console->n_handlers; i++) {
202 handler = console->handlers[i];
203
204 if (!handler->data_in)
205 continue;
206
207 tmp = handler->data_in(handler, buf, len);
208 if (tmp == HANDLER_EXIT)
209 rc = 1;
Jeremy Kerrd831f962016-01-29 17:18:01 +0800210 }
211
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800212 return rc;
Jeremy Kerrd831f962016-01-29 17:18:01 +0800213}
214
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800215static int handlers_poll_event(struct console *console,
216 struct pollfd *pollfds)
Jeremy Kerrd831f962016-01-29 17:18:01 +0800217{
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800218 struct handler *handler;
219 int i, rc, tmp;
Jeremy Kerrd831f962016-01-29 17:18:01 +0800220
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800221 rc = 0;
222
223 for (i = 0; i < console->n_handlers; i++) {
224 handler = console->handlers[i];
225
226 if (!handler->poll_event)
227 continue;
228
229 tmp = handler->poll_event(handler, pollfds[i].revents);
230 if (tmp == HANDLER_EXIT)
231 rc = 1;
232 }
233
234 return rc;
235}
236
237int run_console(struct console *console)
238{
239 struct handler *handler;
240 struct pollfd *pollfds;
241 int i, rc;
242
243 pollfds = calloc(console->n_handlers + 1, sizeof(*pollfds));
244
245 pollfds[0].fd = console->tty_fd;
Jeremy Kerrd831f962016-01-29 17:18:01 +0800246 pollfds[0].events = POLLIN;
Jeremy Kerrd831f962016-01-29 17:18:01 +0800247
248 for (;;) {
249 uint8_t buf[4096];
250
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800251 /* init pollers */
252 for (i = 0; i < console->n_handlers; i++) {
253 handler = console->handlers[i];
254 handler->init_poll(handler, &pollfds[i+1]);
255 }
256
257 rc = poll(pollfds, console->n_handlers + 1, -1);
Jeremy Kerrd831f962016-01-29 17:18:01 +0800258 if (rc < 0) {
259 warn("poll error");
260 return -1;
261 }
262
263 if (pollfds[0].revents) {
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800264 rc = read(console->tty_fd, buf, sizeof(buf));
Jeremy Kerrd831f962016-01-29 17:18:01 +0800265 if (rc <= 0) {
266 warn("Error reading from tty device");
267 return -1;
268 }
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800269 rc = handlers_data_in(console, buf, rc);
270 if (rc)
Jeremy Kerrd831f962016-01-29 17:18:01 +0800271 return 0;
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800272 }
Jeremy Kerrd831f962016-01-29 17:18:01 +0800273
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800274 rc = handlers_poll_event(console, pollfds + 1);
275 if (rc)
276 return 0;
Jeremy Kerrd831f962016-01-29 17:18:01 +0800277 }
278}
Jeremy Kerrd831f962016-01-29 17:18:01 +0800279static const struct option options[] = {
280 { "device", required_argument, 0, 'd'},
Jeremy Kerr957818b2016-03-08 14:35:15 +0800281 { "sirq", required_argument, 0, 's'},
282 { "lpc-addr", required_argument, 0, 'l'},
Jeremy Kerrd831f962016-01-29 17:18:01 +0800283 { },
284};
285
286int main(int argc, char **argv)
287{
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800288 struct console *console;
Jeremy Kerrd831f962016-01-29 17:18:01 +0800289 int rc;
290
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800291 console = malloc(sizeof(struct console));
292 memset(console, 0, sizeof(*console));
Jeremy Kerr957818b2016-03-08 14:35:15 +0800293 rc = -1;
Jeremy Kerrd831f962016-01-29 17:18:01 +0800294
295 for (;;) {
Jeremy Kerr957818b2016-03-08 14:35:15 +0800296 char *endp;
Jeremy Kerrd831f962016-01-29 17:18:01 +0800297 int c, idx;
298
Jeremy Kerr957818b2016-03-08 14:35:15 +0800299 c = getopt_long(argc, argv, "d:s:l:", options, &idx);
Jeremy Kerrd831f962016-01-29 17:18:01 +0800300 if (c == -1)
301 break;
302
303 switch (c) {
304 case 'd':
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800305 console->tty_kname = optarg;
Jeremy Kerrd831f962016-01-29 17:18:01 +0800306 break;
Jeremy Kerr957818b2016-03-08 14:35:15 +0800307 case 'l':
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800308 console->tty_lpc_addr = strtoul(optarg, &endp, 0);
Jeremy Kerr957818b2016-03-08 14:35:15 +0800309 if (endp == optarg) {
310 warnx("Invalid sirq: '%s'", optarg);
311 goto out_free;
312 }
313 break;
314
315 case 's':
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800316 console->tty_sirq = strtoul(optarg, &endp, 0);
Jeremy Kerr957818b2016-03-08 14:35:15 +0800317 if (endp == optarg) {
318 warnx("Invalid sirq: '%s'", optarg);
319 goto out_free;
320 }
321 break;
Jeremy Kerrd831f962016-01-29 17:18:01 +0800322
323 case 'h':
324 case '?':
325 usage(argv[0]);
Jeremy Kerr957818b2016-03-08 14:35:15 +0800326 rc = 0;
327 goto out_free;
Jeremy Kerrd831f962016-01-29 17:18:01 +0800328 }
329 }
330
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800331 if (!console->tty_kname) {
Jeremy Kerrd831f962016-01-29 17:18:01 +0800332 fprintf(stderr,
333 "Error: No TTY device specified (use --device)\n");
334 return EXIT_FAILURE;
335 }
336
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800337 rc = tty_find_device(console);
Jeremy Kerr17217842016-01-29 18:44:21 +0800338 if (rc)
339 return EXIT_FAILURE;
340
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800341 rc = tty_init_io(console);
Jeremy Kerrd831f962016-01-29 17:18:01 +0800342 if (rc)
343 return EXIT_FAILURE;
344
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800345 handlers_init(console);
Jeremy Kerrd831f962016-01-29 17:18:01 +0800346
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800347 rc = run_console(console);
Jeremy Kerrd831f962016-01-29 17:18:01 +0800348
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800349 handlers_fini(console);
Jeremy Kerrd831f962016-01-29 17:18:01 +0800350
Jeremy Kerr957818b2016-03-08 14:35:15 +0800351out_free:
Jeremy Kerr1a0e03b2016-03-08 17:57:11 +0800352 free(console->tty_sysfs_devnode);
353 free(console->tty_dev);
354 free(console);
Jeremy Kerrd831f962016-01-29 17:18:01 +0800355
356 return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
357}