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