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 <termios.h> 17d831f960SJeremy Kerr #include <string.h> 18d831f960SJeremy Kerr #include <getopt.h> 1917217845SJeremy Kerr #include <limits.h> 20d831f960SJeremy Kerr 21d831f960SJeremy Kerr #include <sys/types.h> 22d831f960SJeremy Kerr #include <sys/poll.h> 23d831f960SJeremy Kerr 24d831f960SJeremy Kerr #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) 25d831f960SJeremy Kerr 26d831f960SJeremy Kerr static const char esc_str[] = { '\r', '~', '.' }; 27d831f960SJeremy Kerr 28d831f960SJeremy Kerr struct console_ctx { 2917217845SJeremy Kerr const char *tty_kname; 3017217845SJeremy Kerr char *tty_sysfs_devnode; 3117217845SJeremy Kerr char *tty_dev; 32*957818b4SJeremy Kerr int tty_sirq; 33*957818b4SJeremy Kerr int tty_lpc_addr; 34d831f960SJeremy Kerr int tty_fd; 35d831f960SJeremy Kerr int console_fd_in; 36d831f960SJeremy Kerr int console_fd_out; 37d831f960SJeremy Kerr bool console_is_tty; 38d831f960SJeremy Kerr struct termios orig_termios; 39d831f960SJeremy Kerr int esc_str_pos; 40d831f960SJeremy Kerr }; 41d831f960SJeremy Kerr 42d831f960SJeremy Kerr static void usage(const char *progname) 43d831f960SJeremy Kerr { 44d831f960SJeremy Kerr fprintf(stderr, 45d831f960SJeremy Kerr "usage: %s [options]\n" 46d831f960SJeremy Kerr "\n" 47d831f960SJeremy Kerr "Options:\n" 4817217845SJeremy Kerr " --device <TTY> Use serial device TTY (eg, ttyS0)\n" 49d831f960SJeremy Kerr "", 50d831f960SJeremy Kerr progname); 51d831f960SJeremy Kerr } 52d831f960SJeremy Kerr 5317217845SJeremy Kerr /* populates tty_dev and tty_sysfs_devnode, using the tty kernel name */ 5417217845SJeremy Kerr static int tty_find_device(struct console_ctx *ctx) 5517217845SJeremy Kerr { 5617217845SJeremy Kerr char *tty_class_device_link; 5717217845SJeremy Kerr char *tty_device_tty_dir; 5817217845SJeremy Kerr char *tty_device_reldir; 5917217845SJeremy Kerr int rc; 6017217845SJeremy Kerr 6117217845SJeremy Kerr rc = -1; 6217217845SJeremy Kerr tty_class_device_link = NULL; 6317217845SJeremy Kerr tty_device_tty_dir = NULL; 6417217845SJeremy Kerr tty_device_reldir = NULL; 6517217845SJeremy Kerr 6617217845SJeremy Kerr rc = asprintf(&tty_class_device_link, 6717217845SJeremy Kerr "/sys/class/tty/%s", ctx->tty_kname); 6817217845SJeremy Kerr if (rc < 0) 6917217845SJeremy Kerr return -1; 7017217845SJeremy Kerr 7117217845SJeremy Kerr tty_device_tty_dir = realpath(tty_class_device_link, NULL); 7217217845SJeremy Kerr if (rc < 0) { 7317217845SJeremy Kerr warn("Can't query sysfs for device %s", ctx->tty_kname); 7417217845SJeremy Kerr goto out_free; 7517217845SJeremy Kerr } 7617217845SJeremy Kerr 7717217845SJeremy Kerr rc = asprintf(&tty_device_reldir, "%s/../../", tty_device_tty_dir); 7817217845SJeremy Kerr if (rc < 0) 7917217845SJeremy Kerr goto out_free; 8017217845SJeremy Kerr 8117217845SJeremy Kerr ctx->tty_sysfs_devnode = realpath(tty_device_reldir, NULL); 8217217845SJeremy Kerr if (!ctx->tty_sysfs_devnode) 8317217845SJeremy Kerr warn("Can't find parent device for %s", ctx->tty_kname); 8417217845SJeremy Kerr 8517217845SJeremy Kerr 8617217845SJeremy Kerr /* todo: lookup from major/minor info in sysfs, in case udev has 8717217845SJeremy Kerr * renamed us */ 8817217845SJeremy Kerr rc = asprintf(&ctx->tty_dev, "/dev/%s", ctx->tty_kname); 8917217845SJeremy Kerr if (rc < 0) 9017217845SJeremy Kerr goto out_free; 9117217845SJeremy Kerr 9217217845SJeremy Kerr rc = 0; 9317217845SJeremy Kerr 9417217845SJeremy Kerr out_free: 9517217845SJeremy Kerr free(tty_class_device_link); 9617217845SJeremy Kerr free(tty_device_tty_dir); 9717217845SJeremy Kerr free(tty_device_reldir); 9817217845SJeremy Kerr return rc; 9917217845SJeremy Kerr } 10017217845SJeremy Kerr 101*957818b4SJeremy Kerr static int tty_set_sysfs_attr(struct console_ctx *ctx, const char *name, 102*957818b4SJeremy Kerr int value) 103*957818b4SJeremy Kerr { 104*957818b4SJeremy Kerr char *path; 105*957818b4SJeremy Kerr FILE *fp; 106*957818b4SJeremy Kerr int rc; 107*957818b4SJeremy Kerr 108*957818b4SJeremy Kerr rc = asprintf(&path, "%s/%s", ctx->tty_sysfs_devnode, name); 109*957818b4SJeremy Kerr if (rc < 0) 110*957818b4SJeremy Kerr return -1; 111*957818b4SJeremy Kerr 112*957818b4SJeremy Kerr fp = fopen(path, "w"); 113*957818b4SJeremy Kerr if (!fp) { 114*957818b4SJeremy Kerr warn("Can't access attribute %s on device %s", 115*957818b4SJeremy Kerr name, ctx->tty_kname); 116*957818b4SJeremy Kerr rc = -1; 117*957818b4SJeremy Kerr goto out_free; 118*957818b4SJeremy Kerr } 119*957818b4SJeremy Kerr setvbuf(fp, NULL, _IONBF, 0); 120*957818b4SJeremy Kerr 121*957818b4SJeremy Kerr rc = fprintf(fp, "0x%x", value); 122*957818b4SJeremy Kerr if (rc < 0) 123*957818b4SJeremy Kerr warn("Error writing to %s attribute of device %s", 124*957818b4SJeremy Kerr name, ctx->tty_kname); 125*957818b4SJeremy Kerr fclose(fp); 126*957818b4SJeremy Kerr 127*957818b4SJeremy Kerr 128*957818b4SJeremy Kerr 129*957818b4SJeremy Kerr out_free: 130*957818b4SJeremy Kerr free(path); 131*957818b4SJeremy Kerr return rc; 132*957818b4SJeremy Kerr } 133*957818b4SJeremy Kerr 134d831f960SJeremy Kerr /** 135d831f960SJeremy Kerr * Open and initialise the serial device 136d831f960SJeremy Kerr */ 137d831f960SJeremy Kerr static int tty_init_io(struct console_ctx *ctx) 138d831f960SJeremy Kerr { 139*957818b4SJeremy Kerr if (ctx->tty_sirq) 140*957818b4SJeremy Kerr tty_set_sysfs_attr(ctx, "sirq", ctx->tty_sirq); 141*957818b4SJeremy Kerr if (ctx->tty_lpc_addr) 142*957818b4SJeremy Kerr tty_set_sysfs_attr(ctx, "lpc_address", ctx->tty_lpc_addr); 143*957818b4SJeremy Kerr tty_set_sysfs_attr(ctx, "enabled", 1); 144*957818b4SJeremy Kerr 14517217845SJeremy Kerr 146d831f960SJeremy Kerr ctx->tty_fd = open(ctx->tty_dev, O_RDWR); 147d831f960SJeremy Kerr if (ctx->tty_fd <= 0) { 148d831f960SJeremy Kerr warn("Can't open tty %s", ctx->tty_dev); 149d831f960SJeremy Kerr return -1; 150d831f960SJeremy Kerr } 151d831f960SJeremy Kerr 152d831f960SJeremy Kerr /* Disable character delay. We may want to later enable this when 153d831f960SJeremy Kerr * we detect larger amounts of data 154d831f960SJeremy Kerr */ 155d831f960SJeremy Kerr fcntl(ctx->tty_fd, F_SETFL, FNDELAY); 156d831f960SJeremy Kerr 157d831f960SJeremy Kerr return 0; 158d831f960SJeremy Kerr } 159d831f960SJeremy Kerr 160d831f960SJeremy Kerr /* 161d831f960SJeremy Kerr * Setup our console channel for IO: use stdin/stdout, and if we're on a TTY, 162d831f960SJeremy Kerr * put it in canonical mode 163d831f960SJeremy Kerr */ 164d831f960SJeremy Kerr static int console_init_io(struct console_ctx *ctx) 165d831f960SJeremy Kerr { 166d831f960SJeremy Kerr struct termios termios; 167d831f960SJeremy Kerr int rc; 168d831f960SJeremy Kerr 169d831f960SJeremy Kerr ctx->console_fd_in = STDIN_FILENO; 170d831f960SJeremy Kerr ctx->console_fd_out = STDOUT_FILENO; 171d831f960SJeremy Kerr ctx->console_is_tty = isatty(ctx->console_fd_in); 172d831f960SJeremy Kerr 173d831f960SJeremy Kerr if (!ctx->console_is_tty) 174d831f960SJeremy Kerr return 0; 175d831f960SJeremy Kerr 176d831f960SJeremy Kerr rc = tcgetattr(ctx->console_fd_in, &termios); 177d831f960SJeremy Kerr if (rc) { 178d831f960SJeremy Kerr warn("Can't get terminal attributes for console"); 179d831f960SJeremy Kerr return -1; 180d831f960SJeremy Kerr } 181d831f960SJeremy Kerr memcpy(&ctx->orig_termios, &termios, sizeof(ctx->orig_termios)); 182d831f960SJeremy Kerr cfmakeraw(&termios); 183d831f960SJeremy Kerr 184d831f960SJeremy Kerr rc = tcsetattr(ctx->console_fd_in, TCSANOW, &termios); 185d831f960SJeremy Kerr if (rc) { 186d831f960SJeremy Kerr warn("Can't set terminal attributes for console"); 187d831f960SJeremy Kerr return -1; 188d831f960SJeremy Kerr } 189d831f960SJeremy Kerr 190d831f960SJeremy Kerr return 0; 191d831f960SJeremy Kerr } 192d831f960SJeremy Kerr 193d831f960SJeremy Kerr static int console_process_input(struct console_ctx *ctx, 194d831f960SJeremy Kerr uint8_t *buf, size_t len) 195d831f960SJeremy Kerr { 196d831f960SJeremy Kerr unsigned long i; 197d831f960SJeremy Kerr uint8_t e; 198d831f960SJeremy Kerr 199d831f960SJeremy Kerr e = esc_str[ctx->esc_str_pos]; 200d831f960SJeremy Kerr 201d831f960SJeremy Kerr for (i = 0; i < len; i++) { 202d831f960SJeremy Kerr if (buf[i] == e) { 203d831f960SJeremy Kerr ctx->esc_str_pos++; 204d831f960SJeremy Kerr if (ctx->esc_str_pos == ARRAY_SIZE(esc_str)) 205d831f960SJeremy Kerr return 1; 206d831f960SJeremy Kerr e = esc_str[ctx->esc_str_pos]; 207d831f960SJeremy Kerr } else { 208d831f960SJeremy Kerr 209d831f960SJeremy Kerr ctx->esc_str_pos = 0; 210d831f960SJeremy Kerr } 211d831f960SJeremy Kerr } 212d831f960SJeremy Kerr return 0; 213d831f960SJeremy Kerr } 214d831f960SJeremy Kerr 215d831f960SJeremy Kerr static void console_restore_termios(struct console_ctx *ctx) 216d831f960SJeremy Kerr { 217d831f960SJeremy Kerr if (ctx->console_is_tty) 218d831f960SJeremy Kerr tcsetattr(ctx->console_fd_in, TCSANOW, &ctx->orig_termios); 219d831f960SJeremy Kerr } 220d831f960SJeremy Kerr 221d831f960SJeremy Kerr static int write_buf_to_fd(int fd, uint8_t *buf, size_t len) 222d831f960SJeremy Kerr { 223d831f960SJeremy Kerr size_t pos; 224d831f960SJeremy Kerr ssize_t rc; 225d831f960SJeremy Kerr 226d831f960SJeremy Kerr for (pos = 0; pos < len; pos += rc) { 227d831f960SJeremy Kerr rc = write(fd, buf + pos, len - pos); 228d831f960SJeremy Kerr if (rc <= 0) { 229d831f960SJeremy Kerr warn("Write error"); 230d831f960SJeremy Kerr return -1; 231d831f960SJeremy Kerr } 232d831f960SJeremy Kerr } 233d831f960SJeremy Kerr 234d831f960SJeremy Kerr return 0; 235d831f960SJeremy Kerr } 236d831f960SJeremy Kerr 237d831f960SJeremy Kerr int run_console(struct console_ctx *ctx) 238d831f960SJeremy Kerr { 239d831f960SJeremy Kerr struct pollfd pollfds[2]; 240d831f960SJeremy Kerr int rc, len; 241d831f960SJeremy Kerr 242d831f960SJeremy Kerr pollfds[0].fd = ctx->tty_fd; 243d831f960SJeremy Kerr pollfds[0].events = POLLIN; 244d831f960SJeremy Kerr pollfds[1].fd = ctx->console_fd_in; 245d831f960SJeremy Kerr pollfds[1].events = POLLIN; 246d831f960SJeremy Kerr 247d831f960SJeremy Kerr for (;;) { 248d831f960SJeremy Kerr uint8_t buf[4096]; 249d831f960SJeremy Kerr 250d831f960SJeremy Kerr rc = poll(pollfds, 2, -1); 251d831f960SJeremy Kerr if (rc < 0) { 252d831f960SJeremy Kerr warn("poll error"); 253d831f960SJeremy Kerr return -1; 254d831f960SJeremy Kerr } 255d831f960SJeremy Kerr 256d831f960SJeremy Kerr if (pollfds[0].revents) { 257d831f960SJeremy Kerr rc = read(ctx->tty_fd, buf, sizeof(buf)); 258d831f960SJeremy Kerr if (rc <= 0) { 259d831f960SJeremy Kerr warn("Error reading from tty device"); 260d831f960SJeremy Kerr return -1; 261d831f960SJeremy Kerr } 262d831f960SJeremy Kerr rc = write_buf_to_fd(ctx->console_fd_out, buf, rc); 263d831f960SJeremy Kerr if (rc < 0) 264d831f960SJeremy Kerr return -1; 265d831f960SJeremy Kerr } 266d831f960SJeremy Kerr if (pollfds[1].revents) { 267d831f960SJeremy Kerr rc = read(ctx->console_fd_in, buf, sizeof(buf)); 268d831f960SJeremy Kerr if (rc == 0) 269d831f960SJeremy Kerr return 0; 270d831f960SJeremy Kerr 271d831f960SJeremy Kerr if (rc <= 0) { 272d831f960SJeremy Kerr warn("Error reading from console"); 273d831f960SJeremy Kerr return -1; 274d831f960SJeremy Kerr } 275d831f960SJeremy Kerr len = rc; 276d831f960SJeremy Kerr rc = console_process_input(ctx, buf, len); 277d831f960SJeremy Kerr if (rc) { 278d831f960SJeremy Kerr rc = 0; 279d831f960SJeremy Kerr return 0; 280d831f960SJeremy Kerr } 281d831f960SJeremy Kerr rc = write_buf_to_fd(ctx->tty_fd, buf, len); 282d831f960SJeremy Kerr if (rc < 0) 283d831f960SJeremy Kerr return -1; 284d831f960SJeremy Kerr } 285d831f960SJeremy Kerr } 286d831f960SJeremy Kerr } 287d831f960SJeremy Kerr 288d831f960SJeremy Kerr static const struct option options[] = { 289d831f960SJeremy Kerr { "device", required_argument, 0, 'd'}, 290*957818b4SJeremy Kerr { "sirq", required_argument, 0, 's'}, 291*957818b4SJeremy Kerr { "lpc-addr", required_argument, 0, 'l'}, 292d831f960SJeremy Kerr { }, 293d831f960SJeremy Kerr }; 294d831f960SJeremy Kerr 295d831f960SJeremy Kerr int main(int argc, char **argv) 296d831f960SJeremy Kerr { 297d831f960SJeremy Kerr struct console_ctx *ctx; 298d831f960SJeremy Kerr int rc; 299d831f960SJeremy Kerr 300d831f960SJeremy Kerr ctx = malloc(sizeof(struct console_ctx)); 301d831f960SJeremy Kerr memset(ctx, 0, sizeof(*ctx)); 302*957818b4SJeremy Kerr rc = -1; 303d831f960SJeremy Kerr 304d831f960SJeremy Kerr for (;;) { 305*957818b4SJeremy Kerr char *endp; 306d831f960SJeremy Kerr int c, idx; 307d831f960SJeremy Kerr 308*957818b4SJeremy Kerr c = getopt_long(argc, argv, "d:s:l:", options, &idx); 309d831f960SJeremy Kerr if (c == -1) 310d831f960SJeremy Kerr break; 311d831f960SJeremy Kerr 312d831f960SJeremy Kerr switch (c) { 313d831f960SJeremy Kerr case 'd': 31417217845SJeremy Kerr ctx->tty_kname = optarg; 315d831f960SJeremy Kerr break; 316*957818b4SJeremy Kerr case 'l': 317*957818b4SJeremy Kerr ctx->tty_lpc_addr = strtoul(optarg, &endp, 0); 318*957818b4SJeremy Kerr if (endp == optarg) { 319*957818b4SJeremy Kerr warnx("Invalid sirq: '%s'", optarg); 320*957818b4SJeremy Kerr goto out_free; 321*957818b4SJeremy Kerr } 322*957818b4SJeremy Kerr break; 323*957818b4SJeremy Kerr 324*957818b4SJeremy Kerr case 's': 325*957818b4SJeremy Kerr ctx->tty_sirq = strtoul(optarg, &endp, 0); 326*957818b4SJeremy Kerr if (endp == optarg) { 327*957818b4SJeremy Kerr warnx("Invalid sirq: '%s'", optarg); 328*957818b4SJeremy Kerr goto out_free; 329*957818b4SJeremy Kerr } 330*957818b4SJeremy Kerr break; 331d831f960SJeremy Kerr 332d831f960SJeremy Kerr case 'h': 333d831f960SJeremy Kerr case '?': 334d831f960SJeremy Kerr usage(argv[0]); 335*957818b4SJeremy Kerr rc = 0; 336*957818b4SJeremy Kerr goto out_free; 337d831f960SJeremy Kerr } 338d831f960SJeremy Kerr } 339d831f960SJeremy Kerr 34017217845SJeremy Kerr if (!ctx->tty_kname) { 341d831f960SJeremy Kerr fprintf(stderr, 342d831f960SJeremy Kerr "Error: No TTY device specified (use --device)\n"); 343d831f960SJeremy Kerr return EXIT_FAILURE; 344d831f960SJeremy Kerr } 345d831f960SJeremy Kerr 34617217845SJeremy Kerr rc = tty_find_device(ctx); 34717217845SJeremy Kerr if (rc) 34817217845SJeremy Kerr return EXIT_FAILURE; 34917217845SJeremy Kerr 350d831f960SJeremy Kerr rc = tty_init_io(ctx); 351d831f960SJeremy Kerr if (rc) 352d831f960SJeremy Kerr return EXIT_FAILURE; 353d831f960SJeremy Kerr 354d831f960SJeremy Kerr rc = console_init_io(ctx); 355d831f960SJeremy Kerr if (rc) 356d831f960SJeremy Kerr return EXIT_FAILURE; 357d831f960SJeremy Kerr 358d831f960SJeremy Kerr rc = run_console(ctx); 359d831f960SJeremy Kerr 360d831f960SJeremy Kerr console_restore_termios(ctx); 361d831f960SJeremy Kerr 362*957818b4SJeremy Kerr out_free: 36317217845SJeremy Kerr free(ctx->tty_sysfs_devnode); 36417217845SJeremy Kerr free(ctx->tty_dev); 365d831f960SJeremy Kerr free(ctx); 366d831f960SJeremy Kerr 367d831f960SJeremy Kerr return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE; 368d831f960SJeremy Kerr } 369