1d831f960SJeremy Kerr /** 2d831f960SJeremy Kerr * Console server process for OpenBMC 3d831f960SJeremy Kerr * 49326d779SJeremy Kerr * Copyright © 2016 IBM Corporation 59326d779SJeremy Kerr * 69326d779SJeremy Kerr * Licensed under the Apache License, Version 2.0 (the "License"); 79326d779SJeremy Kerr * you may not use this file except in compliance with the License. 89326d779SJeremy Kerr * You may obtain a copy of the License at 99326d779SJeremy Kerr * 109326d779SJeremy Kerr * http://www.apache.org/licenses/LICENSE-2.0 119326d779SJeremy Kerr * 129326d779SJeremy Kerr * Unless required by applicable law or agreed to in writing, software 139326d779SJeremy Kerr * distributed under the License is distributed on an "AS IS" BASIS, 149326d779SJeremy Kerr * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 159326d779SJeremy Kerr * See the License for the specific language governing permissions and 169326d779SJeremy Kerr * limitations under the License. 17d831f960SJeremy Kerr */ 18d831f960SJeremy Kerr 19329a35f5SJeremy Kerr #include <assert.h> 20769cee1aSJeremy Kerr #include <errno.h> 21769cee1aSJeremy Kerr #include <signal.h> 22d831f960SJeremy Kerr #include <stdint.h> 23d831f960SJeremy Kerr #include <stdbool.h> 24d831f960SJeremy Kerr #include <stdlib.h> 25d831f960SJeremy Kerr #include <stdio.h> 26d831f960SJeremy Kerr #include <fcntl.h> 27d831f960SJeremy Kerr #include <unistd.h> 28d831f960SJeremy Kerr #include <err.h> 29d831f960SJeremy Kerr #include <string.h> 30d831f960SJeremy Kerr #include <getopt.h> 317dc08baaSZev Weiss #include <glob.h> 3217217845SJeremy Kerr #include <limits.h> 331cecc5deSJohnathan Mantey #include <time.h> 3454e9569dSJeremy Kerr #include <termios.h> 35d831f960SJeremy Kerr 36d831f960SJeremy Kerr #include <sys/types.h> 371cecc5deSJohnathan Mantey #include <sys/time.h> 38b14ca19cSNinad Palsule #include <sys/socket.h> 3987e344cdSJoel Stanley #include <poll.h> 40d831f960SJeremy Kerr 411a0e03b4SJeremy Kerr #include "console-server.h" 421e04f449SAlexander Hansen #include "config.h" 43d831f960SJeremy Kerr 4430ea6385SAndrew Jeffery #define DEV_PTS_PATH "/dev/pts" 4530ea6385SAndrew Jeffery 467f2bfb9bSMedicine Yeh /* default size of the shared backlog ringbuffer */ 477f2bfb9bSMedicine Yeh const size_t default_buffer_size = 128ul * 1024ul; 48f733c85aSJeremy Kerr 49769cee1aSJeremy Kerr /* state shared with the signal handler */ 50553cb663SAndrew Jeffery static volatile sig_atomic_t sigint; 51329a35f5SJeremy Kerr 52d831f960SJeremy Kerr static void usage(const char *progname) 53d831f960SJeremy Kerr { 54d831f960SJeremy Kerr fprintf(stderr, 556221ce94SVishwanatha Subbanna "usage: %s [options] <DEVICE>\n" 56d831f960SJeremy Kerr "\n" 57d831f960SJeremy Kerr "Options:\n" 58954be0fbSAndrew Jeffery " --config <FILE>\tUse FILE for configuration\n" 59954be0fbSAndrew Jeffery " --console-id <NAME>\tUse NAME in the UNIX domain socket address\n" 60d831f960SJeremy Kerr "", 61d831f960SJeremy Kerr progname); 62d831f960SJeremy Kerr } 63d831f960SJeremy Kerr 6430ea6385SAndrew Jeffery /* populates console->tty.dev and console->tty.sysfs_devnode, using the tty kernel name */ 651a0e03b4SJeremy Kerr static int tty_find_device(struct console *console) 6617217845SJeremy Kerr { 67d3cb9c22SAndrew Jeffery char *tty_class_device_link = NULL; 68d3cb9c22SAndrew Jeffery char *tty_path_input_real = NULL; 69d3cb9c22SAndrew Jeffery char *tty_device_tty_dir = NULL; 7030ea6385SAndrew Jeffery char *tty_vuart_lpc_addr = NULL; 71d3cb9c22SAndrew Jeffery char *tty_device_reldir = NULL; 7230ea6385SAndrew Jeffery char *tty_sysfs_devnode = NULL; 73d3cb9c22SAndrew Jeffery char *tty_kname_real = NULL; 7430ea6385SAndrew Jeffery char *tty_path_input = NULL; 7517217845SJeremy Kerr int rc; 7617217845SJeremy Kerr 7730ea6385SAndrew Jeffery console->tty.type = TTY_DEVICE_UNDEFINED; 7830ea6385SAndrew Jeffery 7930ea6385SAndrew Jeffery assert(console->tty.kname); 8030ea6385SAndrew Jeffery if (!strlen(console->tty.kname)) { 8130ea6385SAndrew Jeffery warnx("TTY kname must not be empty"); 8230ea6385SAndrew Jeffery rc = -1; 8330ea6385SAndrew Jeffery goto out_free; 842834c5b1SAndrew Jeffery } 8517217845SJeremy Kerr 8630ea6385SAndrew Jeffery if (console->tty.kname[0] == '/') { 8730ea6385SAndrew Jeffery tty_path_input = strdup(console->tty.kname); 8830ea6385SAndrew Jeffery if (!tty_path_input) { 8930ea6385SAndrew Jeffery rc = -1; 9030ea6385SAndrew Jeffery goto out_free; 9130ea6385SAndrew Jeffery } 9230ea6385SAndrew Jeffery } else { 9330ea6385SAndrew Jeffery rc = asprintf(&tty_path_input, "/dev/%s", console->tty.kname); 9430ea6385SAndrew Jeffery if (rc < 0) { 9530ea6385SAndrew Jeffery goto out_free; 9630ea6385SAndrew Jeffery } 9730ea6385SAndrew Jeffery } 9830ea6385SAndrew Jeffery 9930ea6385SAndrew Jeffery /* udev may rename the tty name with a symbol link, try to resolve */ 10045ad7676SYi Li tty_path_input_real = realpath(tty_path_input, NULL); 10145ad7676SYi Li if (!tty_path_input_real) { 10230ea6385SAndrew Jeffery warn("Can't find realpath for %s", tty_path_input); 10315792aa7SAndrew Jeffery rc = -1; 10445ad7676SYi Li goto out_free; 10545ad7676SYi Li } 10645ad7676SYi Li 10730ea6385SAndrew Jeffery /* 10830ea6385SAndrew Jeffery * Allow hooking obmc-console-server up to PTYs for testing 10930ea6385SAndrew Jeffery * 11030ea6385SAndrew Jeffery * https://amboar.github.io/notes/2023/05/02/testing-obmc-console-with-socat.html 11130ea6385SAndrew Jeffery */ 11230ea6385SAndrew Jeffery if (!strncmp(DEV_PTS_PATH, tty_path_input_real, strlen(DEV_PTS_PATH))) { 11330ea6385SAndrew Jeffery console->tty.type = TTY_DEVICE_PTY; 11430ea6385SAndrew Jeffery console->tty.dev = strdup(console->tty.kname); 11530ea6385SAndrew Jeffery rc = console->tty.dev ? 0 : -1; 11630ea6385SAndrew Jeffery goto out_free; 11730ea6385SAndrew Jeffery } 11830ea6385SAndrew Jeffery 11945ad7676SYi Li tty_kname_real = basename(tty_path_input_real); 12045ad7676SYi Li if (!tty_kname_real) { 12130ea6385SAndrew Jeffery warn("Can't find real name for %s", console->tty.kname); 12215792aa7SAndrew Jeffery rc = -1; 12345ad7676SYi Li goto out_free; 12445ad7676SYi Li } 12545ad7676SYi Li 126a72711afSAndrew Jeffery rc = asprintf(&tty_class_device_link, "/sys/class/tty/%s", 127a72711afSAndrew Jeffery tty_kname_real); 1282834c5b1SAndrew Jeffery if (rc < 0) { 12945ad7676SYi Li goto out_free; 1302834c5b1SAndrew Jeffery } 13145ad7676SYi Li 13217217845SJeremy Kerr tty_device_tty_dir = realpath(tty_class_device_link, NULL); 13345ad7676SYi Li if (!tty_device_tty_dir) { 13445ad7676SYi Li warn("Can't query sysfs for device %s", tty_kname_real); 13515792aa7SAndrew Jeffery rc = -1; 13617217845SJeremy Kerr goto out_free; 13717217845SJeremy Kerr } 13817217845SJeremy Kerr 13917217845SJeremy Kerr rc = asprintf(&tty_device_reldir, "%s/../../", tty_device_tty_dir); 1402834c5b1SAndrew Jeffery if (rc < 0) { 14117217845SJeremy Kerr goto out_free; 1422834c5b1SAndrew Jeffery } 14317217845SJeremy Kerr 14430ea6385SAndrew Jeffery tty_sysfs_devnode = realpath(tty_device_reldir, NULL); 14530ea6385SAndrew Jeffery if (!tty_sysfs_devnode) { 14645ad7676SYi Li warn("Can't find parent device for %s", tty_kname_real); 1472834c5b1SAndrew Jeffery } 14817217845SJeremy Kerr 14930ea6385SAndrew Jeffery rc = asprintf(&console->tty.dev, "/dev/%s", tty_kname_real); 1502834c5b1SAndrew Jeffery if (rc < 0) { 15117217845SJeremy Kerr goto out_free; 1522834c5b1SAndrew Jeffery } 15317217845SJeremy Kerr 154955d140eSOskar Senft // Default to non-VUART 155955d140eSOskar Senft console->tty.type = TTY_DEVICE_UART; 156955d140eSOskar Senft 15730ea6385SAndrew Jeffery /* Arbitrarily pick an attribute to differentiate UART vs VUART */ 158955d140eSOskar Senft if (tty_sysfs_devnode) { 159955d140eSOskar Senft rc = asprintf(&tty_vuart_lpc_addr, "%s/lpc_address", 160955d140eSOskar Senft tty_sysfs_devnode); 16130ea6385SAndrew Jeffery if (rc < 0) { 16230ea6385SAndrew Jeffery goto out_free; 16330ea6385SAndrew Jeffery } 16430ea6385SAndrew Jeffery 16530ea6385SAndrew Jeffery rc = access(tty_vuart_lpc_addr, F_OK); 166955d140eSOskar Senft if (!rc) { 167955d140eSOskar Senft console->tty.type = TTY_DEVICE_VUART; 168955d140eSOskar Senft console->tty.vuart.sysfs_devnode = 169955d140eSOskar Senft strdup(tty_sysfs_devnode); 170955d140eSOskar Senft } 171955d140eSOskar Senft } 17230ea6385SAndrew Jeffery 17317217845SJeremy Kerr rc = 0; 17417217845SJeremy Kerr 17517217845SJeremy Kerr out_free: 17630ea6385SAndrew Jeffery free(tty_vuart_lpc_addr); 17717217845SJeremy Kerr free(tty_class_device_link); 178982090d9SAndrew Jeffery free(tty_sysfs_devnode); 17917217845SJeremy Kerr free(tty_device_tty_dir); 18017217845SJeremy Kerr free(tty_device_reldir); 18145ad7676SYi Li free(tty_path_input); 18245ad7676SYi Li free(tty_path_input_real); 18317217845SJeremy Kerr return rc; 18417217845SJeremy Kerr } 18517217845SJeremy Kerr 1861a0e03b4SJeremy Kerr static int tty_set_sysfs_attr(struct console *console, const char *name, 187957818b4SJeremy Kerr int value) 188957818b4SJeremy Kerr { 189957818b4SJeremy Kerr char *path; 190957818b4SJeremy Kerr FILE *fp; 191957818b4SJeremy Kerr int rc; 192957818b4SJeremy Kerr 19330ea6385SAndrew Jeffery assert(console->tty.type == TTY_DEVICE_VUART); 19430ea6385SAndrew Jeffery 19530ea6385SAndrew Jeffery if (!console->tty.vuart.sysfs_devnode) { 19630ea6385SAndrew Jeffery return -1; 19730ea6385SAndrew Jeffery } 19830ea6385SAndrew Jeffery 19930ea6385SAndrew Jeffery rc = asprintf(&path, "%s/%s", console->tty.vuart.sysfs_devnode, name); 2002834c5b1SAndrew Jeffery if (rc < 0) { 201957818b4SJeremy Kerr return -1; 2022834c5b1SAndrew Jeffery } 203957818b4SJeremy Kerr 204957818b4SJeremy Kerr fp = fopen(path, "w"); 205957818b4SJeremy Kerr if (!fp) { 206a72711afSAndrew Jeffery warn("Can't access attribute %s on device %s", name, 20730ea6385SAndrew Jeffery console->tty.kname); 208957818b4SJeremy Kerr rc = -1; 209957818b4SJeremy Kerr goto out_free; 210957818b4SJeremy Kerr } 211957818b4SJeremy Kerr setvbuf(fp, NULL, _IONBF, 0); 212957818b4SJeremy Kerr 213957818b4SJeremy Kerr rc = fprintf(fp, "0x%x", value); 2142834c5b1SAndrew Jeffery if (rc < 0) { 215a72711afSAndrew Jeffery warn("Error writing to %s attribute of device %s", name, 21630ea6385SAndrew Jeffery console->tty.kname); 2172834c5b1SAndrew Jeffery } 218957818b4SJeremy Kerr fclose(fp); 219957818b4SJeremy Kerr 220957818b4SJeremy Kerr out_free: 221957818b4SJeremy Kerr free(path); 222957818b4SJeremy Kerr return rc; 223957818b4SJeremy Kerr } 224957818b4SJeremy Kerr 225d831f960SJeremy Kerr /** 226c7fbcd48SBenjamin Fair * Set termios attributes on the console tty. 22754e9569dSJeremy Kerr */ 228e258e51fSNinad Palsule void tty_init_termios(struct console *console) 22954e9569dSJeremy Kerr { 23054e9569dSJeremy Kerr struct termios termios; 23154e9569dSJeremy Kerr int rc; 23254e9569dSJeremy Kerr 23330ea6385SAndrew Jeffery rc = tcgetattr(console->tty.fd, &termios); 23454e9569dSJeremy Kerr if (rc) { 23554e9569dSJeremy Kerr warn("Can't read tty termios"); 23654e9569dSJeremy Kerr return; 23754e9569dSJeremy Kerr } 23854e9569dSJeremy Kerr 23930ea6385SAndrew Jeffery if (console->tty.type == TTY_DEVICE_UART && console->tty.uart.baud) { 24030ea6385SAndrew Jeffery if (cfsetspeed(&termios, console->tty.uart.baud) < 0) { 24130ea6385SAndrew Jeffery warn("Couldn't set speeds for %s", console->tty.kname); 242c7fbcd48SBenjamin Fair } 2432834c5b1SAndrew Jeffery } 244c7fbcd48SBenjamin Fair 245c7fbcd48SBenjamin Fair /* Set console to raw mode: we don't want any processing to occur on 246c7fbcd48SBenjamin Fair * the underlying terminal input/output. 247c7fbcd48SBenjamin Fair */ 24854e9569dSJeremy Kerr cfmakeraw(&termios); 249c7fbcd48SBenjamin Fair 25030ea6385SAndrew Jeffery rc = tcsetattr(console->tty.fd, TCSANOW, &termios); 2512834c5b1SAndrew Jeffery if (rc) { 25230ea6385SAndrew Jeffery warn("Can't set terminal options for %s", console->tty.kname); 25354e9569dSJeremy Kerr } 2542834c5b1SAndrew Jeffery } 25554e9569dSJeremy Kerr 25654e9569dSJeremy Kerr /** 257d831f960SJeremy Kerr * Open and initialise the serial device 258d831f960SJeremy Kerr */ 25930ea6385SAndrew Jeffery static void tty_init_vuart_io(struct console *console) 260d831f960SJeremy Kerr { 26130ea6385SAndrew Jeffery assert(console->tty.type == TTY_DEVICE_VUART); 26230ea6385SAndrew Jeffery 26330ea6385SAndrew Jeffery if (console->tty.vuart.sirq) { 26430ea6385SAndrew Jeffery tty_set_sysfs_attr(console, "sirq", console->tty.vuart.sirq); 2652834c5b1SAndrew Jeffery } 266957818b4SJeremy Kerr 26730ea6385SAndrew Jeffery if (console->tty.vuart.lpc_addr) { 26830ea6385SAndrew Jeffery tty_set_sysfs_attr(console, "lpc_address", 26930ea6385SAndrew Jeffery console->tty.vuart.lpc_addr); 27030ea6385SAndrew Jeffery } 27130ea6385SAndrew Jeffery } 27230ea6385SAndrew Jeffery 27330ea6385SAndrew Jeffery static int tty_init_io(struct console *console) 27430ea6385SAndrew Jeffery { 27530ea6385SAndrew Jeffery console->tty.fd = open(console->tty.dev, O_RDWR); 27630ea6385SAndrew Jeffery if (console->tty.fd <= 0) { 27730ea6385SAndrew Jeffery warn("Can't open tty %s", console->tty.dev); 278d831f960SJeremy Kerr return -1; 279d831f960SJeremy Kerr } 280d831f960SJeremy Kerr 281d831f960SJeremy Kerr /* Disable character delay. We may want to later enable this when 282d831f960SJeremy Kerr * we detect larger amounts of data 283d831f960SJeremy Kerr */ 28430ea6385SAndrew Jeffery fcntl(console->tty.fd, F_SETFL, FNDELAY); 285d831f960SJeremy Kerr 28654e9569dSJeremy Kerr tty_init_termios(console); 28754e9569dSJeremy Kerr 28830ea6385SAndrew Jeffery console->pollfds[console->n_pollers].fd = console->tty.fd; 289329a35f5SJeremy Kerr console->pollfds[console->n_pollers].events = POLLIN; 290329a35f5SJeremy Kerr 291d831f960SJeremy Kerr return 0; 292d831f960SJeremy Kerr } 293d831f960SJeremy Kerr 29430ea6385SAndrew Jeffery static int tty_init_vuart(struct console *console, struct config *config) 295d66195c1SJeremy Kerr { 296fd883a88SAndrew Jeffery unsigned long parsed; 297d66195c1SJeremy Kerr const char *val; 298d66195c1SJeremy Kerr char *endp; 29930ea6385SAndrew Jeffery 30030ea6385SAndrew Jeffery assert(console->tty.type == TTY_DEVICE_VUART); 301d66195c1SJeremy Kerr 302d66195c1SJeremy Kerr val = config_get_value(config, "lpc-address"); 303d66195c1SJeremy Kerr if (val) { 304fd883a88SAndrew Jeffery errno = 0; 305fd883a88SAndrew Jeffery parsed = strtoul(val, &endp, 0); 306fd883a88SAndrew Jeffery if (parsed == ULONG_MAX && errno == ERANGE) { 307fd883a88SAndrew Jeffery warn("Cannot interpret 'lpc-address' value as an unsigned long: '%s'", 308fd883a88SAndrew Jeffery val); 309fd883a88SAndrew Jeffery return -1; 310fd883a88SAndrew Jeffery } 311fd883a88SAndrew Jeffery 312fd883a88SAndrew Jeffery if (parsed > UINT16_MAX) { 313fd883a88SAndrew Jeffery warn("Invalid LPC address '%s'", val); 314fd883a88SAndrew Jeffery return -1; 315fd883a88SAndrew Jeffery } 316fd883a88SAndrew Jeffery 31730ea6385SAndrew Jeffery console->tty.vuart.lpc_addr = (uint16_t)parsed; 318d66195c1SJeremy Kerr if (endp == optarg) { 319d66195c1SJeremy Kerr warn("Invalid LPC address: '%s'", val); 320d66195c1SJeremy Kerr return -1; 321d66195c1SJeremy Kerr } 322d66195c1SJeremy Kerr } 323d66195c1SJeremy Kerr 324d66195c1SJeremy Kerr val = config_get_value(config, "sirq"); 325d66195c1SJeremy Kerr if (val) { 326fd883a88SAndrew Jeffery errno = 0; 327fd883a88SAndrew Jeffery parsed = strtoul(val, &endp, 0); 328fd883a88SAndrew Jeffery if (parsed == ULONG_MAX && errno == ERANGE) { 329fd883a88SAndrew Jeffery warn("Cannot interpret 'sirq' value as an unsigned long: '%s'", 330fd883a88SAndrew Jeffery val); 331fd883a88SAndrew Jeffery } 332fd883a88SAndrew Jeffery 3332834c5b1SAndrew Jeffery if (parsed > 16) { 334fd883a88SAndrew Jeffery warn("Invalid LPC SERIRQ: '%s'", val); 3352834c5b1SAndrew Jeffery } 336fd883a88SAndrew Jeffery 33730ea6385SAndrew Jeffery console->tty.vuart.sirq = (int)parsed; 3382834c5b1SAndrew Jeffery if (endp == optarg) { 339d66195c1SJeremy Kerr warn("Invalid sirq: '%s'", val); 340d66195c1SJeremy Kerr } 3412834c5b1SAndrew Jeffery } 342d66195c1SJeremy Kerr 34330ea6385SAndrew Jeffery return 0; 3442834c5b1SAndrew Jeffery } 345c7fbcd48SBenjamin Fair 34630ea6385SAndrew Jeffery static int tty_init(struct console *console, struct config *config, 34730ea6385SAndrew Jeffery const char *tty_arg) 34830ea6385SAndrew Jeffery { 34930ea6385SAndrew Jeffery const char *val; 35030ea6385SAndrew Jeffery int rc; 35130ea6385SAndrew Jeffery 352d769eecfSAndrew Jeffery if (tty_arg) { 35330ea6385SAndrew Jeffery console->tty.kname = tty_arg; 354d769eecfSAndrew Jeffery } else if ((val = config_get_value(config, "upstream-tty"))) { 35530ea6385SAndrew Jeffery console->tty.kname = val; 356d769eecfSAndrew Jeffery } else { 357d66195c1SJeremy Kerr warnx("Error: No TTY device specified"); 358d66195c1SJeremy Kerr return -1; 359d66195c1SJeremy Kerr } 360d66195c1SJeremy Kerr 361d66195c1SJeremy Kerr rc = tty_find_device(console); 3622834c5b1SAndrew Jeffery if (rc) { 363d66195c1SJeremy Kerr return rc; 3642834c5b1SAndrew Jeffery } 365d66195c1SJeremy Kerr 36630ea6385SAndrew Jeffery switch (console->tty.type) { 36730ea6385SAndrew Jeffery case TTY_DEVICE_VUART: 36830ea6385SAndrew Jeffery rc = tty_init_vuart(console, config); 36930ea6385SAndrew Jeffery if (rc) { 370d66195c1SJeremy Kerr return rc; 371d66195c1SJeremy Kerr } 372d66195c1SJeremy Kerr 37330ea6385SAndrew Jeffery tty_init_vuart_io(console); 37430ea6385SAndrew Jeffery break; 37530ea6385SAndrew Jeffery case TTY_DEVICE_UART: 37630ea6385SAndrew Jeffery val = config_get_value(config, "baud"); 37730ea6385SAndrew Jeffery if (val) { 37830ea6385SAndrew Jeffery if (config_parse_baud(&console->tty.uart.baud, val)) { 37930ea6385SAndrew Jeffery warnx("Invalid baud rate: '%s'", val); 38030ea6385SAndrew Jeffery } 38130ea6385SAndrew Jeffery } 38230ea6385SAndrew Jeffery break; 38330ea6385SAndrew Jeffery case TTY_DEVICE_PTY: 38430ea6385SAndrew Jeffery break; 38530ea6385SAndrew Jeffery case TTY_DEVICE_UNDEFINED: 38630ea6385SAndrew Jeffery default: 38730ea6385SAndrew Jeffery warnx("Cannot configure unrecognised TTY device"); 38830ea6385SAndrew Jeffery return -1; 38930ea6385SAndrew Jeffery } 39030ea6385SAndrew Jeffery 39130ea6385SAndrew Jeffery return tty_init_io(console); 39230ea6385SAndrew Jeffery } 39330ea6385SAndrew Jeffery 39430ea6385SAndrew Jeffery static void tty_fini(struct console *console) 39530ea6385SAndrew Jeffery { 39630ea6385SAndrew Jeffery if (console->tty.type == TTY_DEVICE_VUART) { 39730ea6385SAndrew Jeffery free(console->tty.vuart.sysfs_devnode); 39830ea6385SAndrew Jeffery } 39930ea6385SAndrew Jeffery free(console->tty.dev); 40030ea6385SAndrew Jeffery } 40130ea6385SAndrew Jeffery 4027dc08baaSZev Weiss static int write_to_path(const char *path, const char *data) 4037dc08baaSZev Weiss { 4047dc08baaSZev Weiss int rc = 0; 4057dc08baaSZev Weiss FILE *f = fopen(path, "w"); 4067dc08baaSZev Weiss if (!f) { 4077dc08baaSZev Weiss return -1; 4087dc08baaSZev Weiss } 4097dc08baaSZev Weiss 4107dc08baaSZev Weiss if (fprintf(f, "%s", data) < 0) { 4117dc08baaSZev Weiss rc = -1; 4127dc08baaSZev Weiss } 4137dc08baaSZev Weiss 4147dc08baaSZev Weiss if (fclose(f)) { 4157dc08baaSZev Weiss rc = -1; 4167dc08baaSZev Weiss } 4177dc08baaSZev Weiss 4187dc08baaSZev Weiss return rc; 4197dc08baaSZev Weiss } 4207dc08baaSZev Weiss 4217dc08baaSZev Weiss #define ASPEED_UART_ROUTING_PATTERN \ 4227dc08baaSZev Weiss "/sys/bus/platform/drivers/aspeed-uart-routing/*.uart-routing" 4237dc08baaSZev Weiss 4247dc08baaSZev Weiss static void uart_routing_init(struct config *config) 4257dc08baaSZev Weiss { 4267dc08baaSZev Weiss const char *muxcfg; 4277dc08baaSZev Weiss const char *p; 4287dc08baaSZev Weiss size_t buflen; 4297dc08baaSZev Weiss char *sink; 4307dc08baaSZev Weiss char *source; 4317dc08baaSZev Weiss char *muxdir; 4327dc08baaSZev Weiss char *path; 4337dc08baaSZev Weiss glob_t globbuf; 4347dc08baaSZev Weiss 4357dc08baaSZev Weiss muxcfg = config_get_value(config, "aspeed-uart-routing"); 4367dc08baaSZev Weiss if (!muxcfg) { 4377dc08baaSZev Weiss return; 4387dc08baaSZev Weiss } 4397dc08baaSZev Weiss 4407dc08baaSZev Weiss /* Find the driver's sysfs directory */ 4417dc08baaSZev Weiss if (glob(ASPEED_UART_ROUTING_PATTERN, GLOB_ERR | GLOB_NOSORT, NULL, 4427dc08baaSZev Weiss &globbuf) != 0) { 4437dc08baaSZev Weiss warn("Couldn't find uart-routing driver directory, cannot apply config"); 4447dc08baaSZev Weiss return; 4457dc08baaSZev Weiss } 4467dc08baaSZev Weiss if (globbuf.gl_pathc != 1) { 4477dc08baaSZev Weiss warnx("Found %zd uart-routing driver directories, cannot apply config", 4487dc08baaSZev Weiss globbuf.gl_pathc); 4497dc08baaSZev Weiss goto out_free_glob; 4507dc08baaSZev Weiss } 4517dc08baaSZev Weiss muxdir = globbuf.gl_pathv[0]; 4527dc08baaSZev Weiss 4537dc08baaSZev Weiss /* 4547dc08baaSZev Weiss * Rather than faff about tracking a bunch of separate buffer sizes, 4557dc08baaSZev Weiss * just use one (worst-case) size for all of them -- +2 for a trailing 4567dc08baaSZev Weiss * NUL and a '/' separator to construct the sysfs file path. 4577dc08baaSZev Weiss */ 4587dc08baaSZev Weiss buflen = strlen(muxdir) + strlen(muxcfg) + 2; 4597dc08baaSZev Weiss 4607dc08baaSZev Weiss sink = malloc(buflen); 4617dc08baaSZev Weiss source = malloc(buflen); 4627dc08baaSZev Weiss path = malloc(buflen); 4637dc08baaSZev Weiss if (!path || !sink || !source) { 4647dc08baaSZev Weiss warnx("Out of memory applying uart routing config"); 4657dc08baaSZev Weiss goto out_free_bufs; 4667dc08baaSZev Weiss } 4677dc08baaSZev Weiss 4687dc08baaSZev Weiss p = muxcfg; 4697dc08baaSZev Weiss while (*p) { 4707dc08baaSZev Weiss ssize_t bytes_scanned; 4717dc08baaSZev Weiss 4727dc08baaSZev Weiss if (sscanf(p, " %[^:/ \t]:%[^: \t] %zn", sink, source, 4737dc08baaSZev Weiss &bytes_scanned) != 2) { 4747dc08baaSZev Weiss warnx("Invalid syntax in aspeed uart config: '%s' not applied", 4757dc08baaSZev Weiss p); 4767dc08baaSZev Weiss break; 4777dc08baaSZev Weiss } 4787dc08baaSZev Weiss p += bytes_scanned; 4797dc08baaSZev Weiss 4807dc08baaSZev Weiss /* 4817dc08baaSZev Weiss * Check that the sink name looks reasonable before proceeding 4827dc08baaSZev Weiss * (there are other writable files in the same directory that 4837dc08baaSZev Weiss * we shouldn't be touching, such as 'driver_override' and 4847dc08baaSZev Weiss * 'uevent'). 4857dc08baaSZev Weiss */ 4867dc08baaSZev Weiss if (strncmp(sink, "io", strlen("io")) != 0 && 4877dc08baaSZev Weiss strncmp(sink, "uart", strlen("uart")) != 0) { 4887dc08baaSZev Weiss warnx("Skipping invalid uart routing name '%s' (must be ioN or uartN)", 4897dc08baaSZev Weiss sink); 4907dc08baaSZev Weiss continue; 4917dc08baaSZev Weiss } 4927dc08baaSZev Weiss 4937dc08baaSZev Weiss snprintf(path, buflen, "%s/%s", muxdir, sink); 4947dc08baaSZev Weiss if (write_to_path(path, source)) { 4957dc08baaSZev Weiss warn("Failed to apply uart-routing config '%s:%s'", 4967dc08baaSZev Weiss sink, source); 4977dc08baaSZev Weiss } 4987dc08baaSZev Weiss } 4997dc08baaSZev Weiss 5007dc08baaSZev Weiss out_free_bufs: 5017dc08baaSZev Weiss free(path); 5027dc08baaSZev Weiss free(source); 5037dc08baaSZev Weiss free(sink); 5047dc08baaSZev Weiss out_free_glob: 5057dc08baaSZev Weiss globfree(&globbuf); 5067dc08baaSZev Weiss } 5077dc08baaSZev Weiss 5081a0e03b4SJeremy Kerr int console_data_out(struct console *console, const uint8_t *data, size_t len) 509d831f960SJeremy Kerr { 51030ea6385SAndrew Jeffery return write_buf_to_fd(console->tty.fd, data, len); 511d831f960SJeremy Kerr } 512d831f960SJeremy Kerr 5135ba20b5bSNinad Palsule /* Prepare a socket name */ 514954be0fbSAndrew Jeffery static int set_socket_info(struct console *console, struct config *config, 515954be0fbSAndrew Jeffery const char *console_id) 516b14ca19cSNinad Palsule { 517b14ca19cSNinad Palsule ssize_t len; 518b14ca19cSNinad Palsule 5195ba20b5bSNinad Palsule /* Get console id */ 5205ba20b5bSNinad Palsule console->console_id = config_resolve_console_id(config, console_id); 521954be0fbSAndrew Jeffery 522b14ca19cSNinad Palsule /* Get the socket name/path */ 5235ba20b5bSNinad Palsule len = console_socket_path(console->socket_name, console->console_id); 524b14ca19cSNinad Palsule if (len < 0) { 525b14ca19cSNinad Palsule warn("Failed to set socket path: %s", strerror(errno)); 526b14ca19cSNinad Palsule return EXIT_FAILURE; 527b14ca19cSNinad Palsule } 528b14ca19cSNinad Palsule 529b14ca19cSNinad Palsule /* Socket name is not a null terminated string hence save the length */ 530b14ca19cSNinad Palsule console->socket_name_len = len; 531b14ca19cSNinad Palsule 532b14ca19cSNinad Palsule return 0; 533b14ca19cSNinad Palsule } 534b14ca19cSNinad Palsule 535d47963e5SJeremy Kerr static void handlers_init(struct console *console, struct config *config) 536d831f960SJeremy Kerr { 537b70f8713SAndrew Jeffery /* NOLINTBEGIN(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) */ 538*adedc333SAndrew Jeffery extern const struct handler_type *const __start_handlers[]; 539*adedc333SAndrew Jeffery extern const struct handler_type *const __stop_handlers[]; 540b70f8713SAndrew Jeffery /* NOLINTEND(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) */ 541e2826c7dSJeremy Kerr size_t n_types; 542e2826c7dSJeremy Kerr int j = 0; 543e2826c7dSJeremy Kerr size_t i; 544e2826c7dSJeremy Kerr 545*adedc333SAndrew Jeffery n_types = __stop_handlers - __start_handlers; 546e2826c7dSJeremy Kerr console->handlers = calloc(n_types, sizeof(struct handler *)); 547e2826c7dSJeremy Kerr if (!console->handlers) { 548e2826c7dSJeremy Kerr err(EXIT_FAILURE, "malloc(handlers)"); 549e2826c7dSJeremy Kerr } 550e2826c7dSJeremy Kerr 551079fc516SAndrew Jeffery printf("%zu handler type%s\n", n_types, n_types == 1 ? "" : "s"); 552e2826c7dSJeremy Kerr 553e2826c7dSJeremy Kerr for (i = 0; i < n_types; i++) { 554*adedc333SAndrew Jeffery const struct handler_type *type = __start_handlers[i]; 5551a0e03b4SJeremy Kerr struct handler *handler; 556d831f960SJeremy Kerr 557e2826c7dSJeremy Kerr /* Should be picked up at build time by 558e2826c7dSJeremy Kerr * console_handler_register, but check anyway 559e2826c7dSJeremy Kerr */ 560e2826c7dSJeremy Kerr if (!type->init || !type->fini) { 561e2826c7dSJeremy Kerr errx(EXIT_FAILURE, 562e2826c7dSJeremy Kerr "invalid handler type %s: no init() / fini()", 563e2826c7dSJeremy Kerr type->name); 5642834c5b1SAndrew Jeffery } 565021b91f0SJeremy Kerr 566e2826c7dSJeremy Kerr handler = type->init(type, console, config); 567021b91f0SJeremy Kerr 568e2826c7dSJeremy Kerr printf(" console '%s': handler %s [%sactive]\n", 569e2826c7dSJeremy Kerr console->console_id, type->name, handler ? "" : "in"); 570e2826c7dSJeremy Kerr 571e2826c7dSJeremy Kerr if (handler) { 572e2826c7dSJeremy Kerr handler->type = type; 573e2826c7dSJeremy Kerr console->handlers[j++] = handler; 574d831f960SJeremy Kerr } 575d831f960SJeremy Kerr } 576d831f960SJeremy Kerr 577e2826c7dSJeremy Kerr console->n_handlers = j; 578e2826c7dSJeremy Kerr } 579e2826c7dSJeremy Kerr 5801a0e03b4SJeremy Kerr static void handlers_fini(struct console *console) 581d831f960SJeremy Kerr { 5821a0e03b4SJeremy Kerr struct handler *handler; 5831a0e03b4SJeremy Kerr int i; 5841a0e03b4SJeremy Kerr 5851a0e03b4SJeremy Kerr for (i = 0; i < console->n_handlers; i++) { 5861a0e03b4SJeremy Kerr handler = console->handlers[i]; 587e2826c7dSJeremy Kerr handler->type->fini(handler); 5881a0e03b4SJeremy Kerr } 589e2826c7dSJeremy Kerr 590e2826c7dSJeremy Kerr free(console->handlers); 591e2826c7dSJeremy Kerr console->handlers = NULL; 592e2826c7dSJeremy Kerr console->n_handlers = 0; 5932834c5b1SAndrew Jeffery } 594d831f960SJeremy Kerr 5951cecc5deSJohnathan Mantey static int get_current_time(struct timeval *tv) 5961cecc5deSJohnathan Mantey { 5971cecc5deSJohnathan Mantey struct timespec t; 5981cecc5deSJohnathan Mantey int rc; 5991cecc5deSJohnathan Mantey 6001cecc5deSJohnathan Mantey /* 6011cecc5deSJohnathan Mantey * We use clock_gettime(CLOCK_MONOTONIC) so we're immune to 6021cecc5deSJohnathan Mantey * local time changes. However, a struct timeval is more 6031cecc5deSJohnathan Mantey * convenient for calculations, so convert to that. 6041cecc5deSJohnathan Mantey */ 6051cecc5deSJohnathan Mantey rc = clock_gettime(CLOCK_MONOTONIC, &t); 6062834c5b1SAndrew Jeffery if (rc) { 6071cecc5deSJohnathan Mantey return rc; 6082834c5b1SAndrew Jeffery } 6091cecc5deSJohnathan Mantey 6101cecc5deSJohnathan Mantey tv->tv_sec = t.tv_sec; 6111cecc5deSJohnathan Mantey tv->tv_usec = t.tv_nsec / 1000; 6121cecc5deSJohnathan Mantey 6131cecc5deSJohnathan Mantey return 0; 6141cecc5deSJohnathan Mantey } 6151cecc5deSJohnathan Mantey 616a72711afSAndrew Jeffery struct ringbuffer_consumer * 617a72711afSAndrew Jeffery console_ringbuffer_consumer_register(struct console *console, 618f733c85aSJeremy Kerr ringbuffer_poll_fn_t poll_fn, void *data) 619d831f960SJeremy Kerr { 620f733c85aSJeremy Kerr return ringbuffer_consumer_register(console->rb, poll_fn, data); 621d831f960SJeremy Kerr } 622d831f960SJeremy Kerr 62355c9712dSJeremy Kerr struct poller *console_poller_register(struct console *console, 624a72711afSAndrew Jeffery struct handler *handler, 625a72711afSAndrew Jeffery poller_event_fn_t poller_fn, 6261cecc5deSJohnathan Mantey poller_timeout_fn_t timeout_fn, int fd, 6271cecc5deSJohnathan Mantey int events, void *data) 628d831f960SJeremy Kerr { 629329a35f5SJeremy Kerr struct poller *poller; 6305c359cc6SAndrew Jeffery long n; 631329a35f5SJeremy Kerr 632329a35f5SJeremy Kerr poller = malloc(sizeof(*poller)); 633329a35f5SJeremy Kerr poller->remove = false; 634329a35f5SJeremy Kerr poller->handler = handler; 6351cecc5deSJohnathan Mantey poller->event_fn = poller_fn; 6361cecc5deSJohnathan Mantey poller->timeout_fn = timeout_fn; 6379cc8459aSAndrew Jeffery timerclear(&poller->timeout); 638329a35f5SJeremy Kerr poller->data = data; 639329a35f5SJeremy Kerr 640329a35f5SJeremy Kerr /* add one to our pollers array */ 641329a35f5SJeremy Kerr n = console->n_pollers++; 64291b52175SAndrew Jeffery /* 64391b52175SAndrew Jeffery * We're managing an array of pointers to aggregates, so don't warn about sizeof() on a 64491b52175SAndrew Jeffery * pointer type. 64591b52175SAndrew Jeffery */ 64691b52175SAndrew Jeffery /* NOLINTBEGIN(bugprone-sizeof-expression) */ 64791b52175SAndrew Jeffery console->pollers = reallocarray(console->pollers, console->n_pollers, 64891b52175SAndrew Jeffery sizeof(*console->pollers)); 64991b52175SAndrew Jeffery /* NOLINTEND(bugprone-sizeof-expression) */ 650329a35f5SJeremy Kerr 651329a35f5SJeremy Kerr console->pollers[n] = poller; 652329a35f5SJeremy Kerr 653329a35f5SJeremy Kerr /* increase pollfds array too */ 6544e44c790SAndrew Jeffery console->pollfds = reallocarray( 6554e44c790SAndrew Jeffery console->pollfds, (MAX_INTERNAL_POLLFD + console->n_pollers), 65691b52175SAndrew Jeffery sizeof(*console->pollfds)); 657329a35f5SJeremy Kerr 658329a35f5SJeremy Kerr /* shift the end pollfds up by one */ 659a72711afSAndrew Jeffery memcpy(&console->pollfds[n + 1], &console->pollfds[n], 660f9c8f6caSCheng C Yang sizeof(*console->pollfds) * MAX_INTERNAL_POLLFD); 661329a35f5SJeremy Kerr 662329a35f5SJeremy Kerr console->pollfds[n].fd = fd; 6635c359cc6SAndrew Jeffery console->pollfds[n].events = (short)(events & 0x7fff); 664329a35f5SJeremy Kerr 665329a35f5SJeremy Kerr return poller; 666329a35f5SJeremy Kerr } 667329a35f5SJeremy Kerr 668a72711afSAndrew Jeffery void console_poller_unregister(struct console *console, struct poller *poller) 669329a35f5SJeremy Kerr { 670329a35f5SJeremy Kerr int i; 671329a35f5SJeremy Kerr 672329a35f5SJeremy Kerr /* find the entry in our pollers array */ 6732834c5b1SAndrew Jeffery for (i = 0; i < console->n_pollers; i++) { 6742834c5b1SAndrew Jeffery if (console->pollers[i] == poller) { 675329a35f5SJeremy Kerr break; 6762834c5b1SAndrew Jeffery } 6772834c5b1SAndrew Jeffery } 678329a35f5SJeremy Kerr 679329a35f5SJeremy Kerr assert(i < console->n_pollers); 680329a35f5SJeremy Kerr 681329a35f5SJeremy Kerr console->n_pollers--; 682329a35f5SJeremy Kerr 68391b52175SAndrew Jeffery /* 68491b52175SAndrew Jeffery * Remove the item from the pollers array... 68591b52175SAndrew Jeffery * 68691b52175SAndrew Jeffery * We're managing an array of pointers to aggregates, so don't warn about sizeof() on a 68791b52175SAndrew Jeffery * pointer type. 68891b52175SAndrew Jeffery */ 68991b52175SAndrew Jeffery /* NOLINTBEGIN(bugprone-sizeof-expression) */ 690329a35f5SJeremy Kerr memmove(&console->pollers[i], &console->pollers[i + 1], 691a72711afSAndrew Jeffery sizeof(*console->pollers) * (console->n_pollers - i)); 692329a35f5SJeremy Kerr 6937851a396SAndrew Jeffery if (console->n_pollers == 0) { 6947851a396SAndrew Jeffery free(console->pollers); 6957851a396SAndrew Jeffery console->pollers = NULL; 6967851a396SAndrew Jeffery } else { 6977851a396SAndrew Jeffery console->pollers = reallocarray(console->pollers, 6987851a396SAndrew Jeffery console->n_pollers, 69991b52175SAndrew Jeffery sizeof(*console->pollers)); 7007851a396SAndrew Jeffery } 70191b52175SAndrew Jeffery /* NOLINTEND(bugprone-sizeof-expression) */ 702329a35f5SJeremy Kerr 703329a35f5SJeremy Kerr /* ... and the pollfds array */ 704329a35f5SJeremy Kerr memmove(&console->pollfds[i], &console->pollfds[i + 1], 705329a35f5SJeremy Kerr sizeof(*console->pollfds) * 706f9c8f6caSCheng C Yang (MAX_INTERNAL_POLLFD + console->n_pollers - i)); 707329a35f5SJeremy Kerr 7084e44c790SAndrew Jeffery console->pollfds = reallocarray( 7094e44c790SAndrew Jeffery console->pollfds, (MAX_INTERNAL_POLLFD + console->n_pollers), 71091b52175SAndrew Jeffery sizeof(*console->pollfds)); 711329a35f5SJeremy Kerr 712329a35f5SJeremy Kerr free(poller); 713329a35f5SJeremy Kerr } 714329a35f5SJeremy Kerr 7156b1fed27SJeremy Kerr void console_poller_set_events(struct console *console, struct poller *poller, 7166b1fed27SJeremy Kerr int events) 7176b1fed27SJeremy Kerr { 7186b1fed27SJeremy Kerr int i; 7196b1fed27SJeremy Kerr 7206b1fed27SJeremy Kerr /* find the entry in our pollers array */ 7212834c5b1SAndrew Jeffery for (i = 0; i < console->n_pollers; i++) { 7222834c5b1SAndrew Jeffery if (console->pollers[i] == poller) { 7236b1fed27SJeremy Kerr break; 7242834c5b1SAndrew Jeffery } 7252834c5b1SAndrew Jeffery } 7266b1fed27SJeremy Kerr 7275c359cc6SAndrew Jeffery console->pollfds[i].events = (short)(events & 0x7fff); 7286b1fed27SJeremy Kerr } 7296b1fed27SJeremy Kerr 730fd048328SAndrew Jeffery void console_poller_set_timeout(struct console *console __attribute__((unused)), 731fd048328SAndrew Jeffery struct poller *poller, const struct timeval *tv) 7321cecc5deSJohnathan Mantey { 7331cecc5deSJohnathan Mantey struct timeval now; 7341cecc5deSJohnathan Mantey int rc; 7351cecc5deSJohnathan Mantey 7361cecc5deSJohnathan Mantey rc = get_current_time(&now); 7372834c5b1SAndrew Jeffery if (rc) { 7381cecc5deSJohnathan Mantey return; 7392834c5b1SAndrew Jeffery } 7401cecc5deSJohnathan Mantey 7411cecc5deSJohnathan Mantey timeradd(&now, tv, &poller->timeout); 7421cecc5deSJohnathan Mantey } 7431cecc5deSJohnathan Mantey 7445c359cc6SAndrew Jeffery static long get_poll_timeout(struct console *console, struct timeval *cur_time) 7451cecc5deSJohnathan Mantey { 746b70f8713SAndrew Jeffery struct timeval *earliest; 747b70f8713SAndrew Jeffery struct timeval interval; 7481cecc5deSJohnathan Mantey struct poller *poller; 7491cecc5deSJohnathan Mantey int i; 7501cecc5deSJohnathan Mantey 7511cecc5deSJohnathan Mantey earliest = NULL; 7521cecc5deSJohnathan Mantey 7531cecc5deSJohnathan Mantey for (i = 0; i < console->n_pollers; i++) { 7541cecc5deSJohnathan Mantey poller = console->pollers[i]; 7551cecc5deSJohnathan Mantey 7561cecc5deSJohnathan Mantey if (poller->timeout_fn && timerisset(&poller->timeout) && 7571cecc5deSJohnathan Mantey (!earliest || 7581cecc5deSJohnathan Mantey (earliest && timercmp(&poller->timeout, earliest, <)))) { 7591cecc5deSJohnathan Mantey // poller is buffering data and needs the poll 7601cecc5deSJohnathan Mantey // function to timeout. 7611cecc5deSJohnathan Mantey earliest = &poller->timeout; 7621cecc5deSJohnathan Mantey } 7631cecc5deSJohnathan Mantey } 7641cecc5deSJohnathan Mantey 7651cecc5deSJohnathan Mantey if (earliest) { 7661cecc5deSJohnathan Mantey if (timercmp(earliest, cur_time, >)) { 7671cecc5deSJohnathan Mantey /* recalculate the timeout period, time period has 7681cecc5deSJohnathan Mantey * not elapsed */ 7691cecc5deSJohnathan Mantey timersub(earliest, cur_time, &interval); 7701cecc5deSJohnathan Mantey return ((interval.tv_sec * 1000) + 7711cecc5deSJohnathan Mantey (interval.tv_usec / 1000)); 7720b7b0477SAndrew Jeffery } /* return from poll immediately */ 7731cecc5deSJohnathan Mantey return 0; 7740b7b0477SAndrew Jeffery 7750b7b0477SAndrew Jeffery } /* poll indefinitely */ 7761cecc5deSJohnathan Mantey return -1; 7771cecc5deSJohnathan Mantey } 7781cecc5deSJohnathan Mantey 7791cecc5deSJohnathan Mantey static int call_pollers(struct console *console, struct timeval *cur_time) 780329a35f5SJeremy Kerr { 781329a35f5SJeremy Kerr struct poller *poller; 782329a35f5SJeremy Kerr struct pollfd *pollfd; 783329a35f5SJeremy Kerr enum poller_ret prc; 784b70f8713SAndrew Jeffery int i; 785b70f8713SAndrew Jeffery int rc; 786d831f960SJeremy Kerr 7871a0e03b4SJeremy Kerr rc = 0; 7881a0e03b4SJeremy Kerr 789329a35f5SJeremy Kerr /* 790329a35f5SJeremy Kerr * Process poll events by iterating through the pollers and pollfds 791329a35f5SJeremy Kerr * in-step, calling any pollers that we've found revents for. 792329a35f5SJeremy Kerr */ 793329a35f5SJeremy Kerr for (i = 0; i < console->n_pollers; i++) { 794329a35f5SJeremy Kerr poller = console->pollers[i]; 795329a35f5SJeremy Kerr pollfd = &console->pollfds[i]; 7961cecc5deSJohnathan Mantey prc = POLLER_OK; 7971a0e03b4SJeremy Kerr 7981cecc5deSJohnathan Mantey /* process pending events... */ 7991cecc5deSJohnathan Mantey if (pollfd->revents) { 8001cecc5deSJohnathan Mantey prc = poller->event_fn(poller->handler, pollfd->revents, 801329a35f5SJeremy Kerr poller->data); 8022834c5b1SAndrew Jeffery if (prc == POLLER_EXIT) { 803329a35f5SJeremy Kerr rc = -1; 8042834c5b1SAndrew Jeffery } else if (prc == POLLER_REMOVE) { 805329a35f5SJeremy Kerr poller->remove = true; 806329a35f5SJeremy Kerr } 8072834c5b1SAndrew Jeffery } 808329a35f5SJeremy Kerr 8091cecc5deSJohnathan Mantey if ((prc == POLLER_OK) && poller->timeout_fn && 8101cecc5deSJohnathan Mantey timerisset(&poller->timeout) && 8111cecc5deSJohnathan Mantey timercmp(&poller->timeout, cur_time, <=)) { 8121cecc5deSJohnathan Mantey /* One of the ringbuffer consumers is buffering the 8131cecc5deSJohnathan Mantey data stream. The amount of idle time the consumer 8141cecc5deSJohnathan Mantey desired has expired. Process the buffered data for 8151cecc5deSJohnathan Mantey transmission. */ 8161cecc5deSJohnathan Mantey timerclear(&poller->timeout); 8171cecc5deSJohnathan Mantey prc = poller->timeout_fn(poller->handler, poller->data); 8181cecc5deSJohnathan Mantey if (prc == POLLER_EXIT) { 8191cecc5deSJohnathan Mantey rc = -1; 8201cecc5deSJohnathan Mantey } else if (prc == POLLER_REMOVE) { 8211cecc5deSJohnathan Mantey poller->remove = true; 8221cecc5deSJohnathan Mantey } 8231cecc5deSJohnathan Mantey } 8241cecc5deSJohnathan Mantey } 8251cecc5deSJohnathan Mantey 826329a35f5SJeremy Kerr /** 827329a35f5SJeremy Kerr * Process deferred removals; restarting each time we unregister, as 828329a35f5SJeremy Kerr * the array will have changed 829329a35f5SJeremy Kerr */ 830329a35f5SJeremy Kerr for (;;) { 831329a35f5SJeremy Kerr bool removed = false; 832329a35f5SJeremy Kerr 833329a35f5SJeremy Kerr for (i = 0; i < console->n_pollers; i++) { 834329a35f5SJeremy Kerr poller = console->pollers[i]; 835329a35f5SJeremy Kerr if (poller->remove) { 83655c9712dSJeremy Kerr console_poller_unregister(console, poller); 837329a35f5SJeremy Kerr removed = true; 838329a35f5SJeremy Kerr break; 839329a35f5SJeremy Kerr } 840329a35f5SJeremy Kerr } 8412834c5b1SAndrew Jeffery if (!removed) { 842329a35f5SJeremy Kerr break; 8431a0e03b4SJeremy Kerr } 8442834c5b1SAndrew Jeffery } 8451a0e03b4SJeremy Kerr 8461a0e03b4SJeremy Kerr return rc; 8471a0e03b4SJeremy Kerr } 8481a0e03b4SJeremy Kerr 849769cee1aSJeremy Kerr static void sighandler(int signal) 850769cee1aSJeremy Kerr { 8512834c5b1SAndrew Jeffery if (signal == SIGINT) { 852553cb663SAndrew Jeffery sigint = 1; 853769cee1aSJeremy Kerr } 8542834c5b1SAndrew Jeffery } 855769cee1aSJeremy Kerr 8566925740dSAlexander Hansen static int run_console_iteration(struct console *console) 8571a0e03b4SJeremy Kerr { 8586925740dSAlexander Hansen uint8_t buf[4096]; 8591cecc5deSJohnathan Mantey struct timeval tv; 8605c359cc6SAndrew Jeffery long timeout; 8615c359cc6SAndrew Jeffery ssize_t rc; 862769cee1aSJeremy Kerr 8637f2bfb9bSMedicine Yeh if (console->rb->size < sizeof(buf)) { 8646925740dSAlexander Hansen fprintf(stderr, "Ringbuffer size should be greater than %zuB\n", 8657f2bfb9bSMedicine Yeh sizeof(buf)); 8666925740dSAlexander Hansen return -1; 8677f2bfb9bSMedicine Yeh } 8681764145dSJeremy Kerr 869769cee1aSJeremy Kerr if (sigint) { 870769cee1aSJeremy Kerr fprintf(stderr, "Received interrupt, exiting\n"); 8716925740dSAlexander Hansen return -1; 872769cee1aSJeremy Kerr } 873769cee1aSJeremy Kerr 8741cecc5deSJohnathan Mantey rc = get_current_time(&tv); 8751cecc5deSJohnathan Mantey if (rc) { 8761cecc5deSJohnathan Mantey warn("Failed to read current time"); 8776925740dSAlexander Hansen return -1; 8781cecc5deSJohnathan Mantey } 8791cecc5deSJohnathan Mantey 8801cecc5deSJohnathan Mantey timeout = get_poll_timeout(console, &tv); 8811cecc5deSJohnathan Mantey 8826925740dSAlexander Hansen rc = poll(console->pollfds, console->n_pollers + MAX_INTERNAL_POLLFD, 8835c359cc6SAndrew Jeffery (int)timeout); 8841cecc5deSJohnathan Mantey 885d831f960SJeremy Kerr if (rc < 0) { 886769cee1aSJeremy Kerr if (errno == EINTR) { 8876925740dSAlexander Hansen return 0; 8880b7b0477SAndrew Jeffery } 889d831f960SJeremy Kerr warn("poll error"); 8906925740dSAlexander Hansen return -1; 891769cee1aSJeremy Kerr } 892d831f960SJeremy Kerr 893329a35f5SJeremy Kerr /* process internal fd first */ 894329a35f5SJeremy Kerr if (console->pollfds[console->n_pollers].revents) { 89530ea6385SAndrew Jeffery rc = read(console->tty.fd, buf, sizeof(buf)); 896d831f960SJeremy Kerr if (rc <= 0) { 897d831f960SJeremy Kerr warn("Error reading from tty device"); 8986925740dSAlexander Hansen return -1; 899d831f960SJeremy Kerr } 900f733c85aSJeremy Kerr rc = ringbuffer_queue(console->rb, buf, rc); 9012834c5b1SAndrew Jeffery if (rc) { 9026925740dSAlexander Hansen return -1; 903d831f960SJeremy Kerr } 9042834c5b1SAndrew Jeffery } 905d831f960SJeremy Kerr 906f9c8f6caSCheng C Yang if (console->pollfds[console->n_pollers + 1].revents) { 907f9c8f6caSCheng C Yang sd_bus_process(console->bus, NULL); 908f9c8f6caSCheng C Yang } 909f9c8f6caSCheng C Yang 910329a35f5SJeremy Kerr /* ... and then the pollers */ 9111cecc5deSJohnathan Mantey rc = call_pollers(console, &tv); 9122834c5b1SAndrew Jeffery if (rc) { 9136925740dSAlexander Hansen return -1; 9146925740dSAlexander Hansen } 9156925740dSAlexander Hansen return 0; 9166925740dSAlexander Hansen } 9176925740dSAlexander Hansen 9186925740dSAlexander Hansen int run_console(struct console *console) 9196925740dSAlexander Hansen { 9206925740dSAlexander Hansen sighandler_t sighandler_save = signal(SIGINT, sighandler); 9216925740dSAlexander Hansen ssize_t rc = 0; 9226925740dSAlexander Hansen 9236925740dSAlexander Hansen for (;;) { 9246925740dSAlexander Hansen rc = run_console_iteration(console); 9256925740dSAlexander Hansen if (rc) { 926769cee1aSJeremy Kerr break; 9271a0e03b4SJeremy Kerr } 9282834c5b1SAndrew Jeffery } 929769cee1aSJeremy Kerr 930769cee1aSJeremy Kerr signal(SIGINT, sighandler_save); 931f9c8f6caSCheng C Yang sd_bus_unref(console->bus); 932769cee1aSJeremy Kerr 933769cee1aSJeremy Kerr return rc ? -1 : 0; 9341a0e03b4SJeremy Kerr } 935d831f960SJeremy Kerr static const struct option options[] = { 936d66195c1SJeremy Kerr { "config", required_argument, 0, 'c' }, 937954be0fbSAndrew Jeffery { "console-id", required_argument, 0, 'i' }, 938f5858b5bSJoel Stanley { 0, 0, 0, 0 }, 939d831f960SJeremy Kerr }; 940d831f960SJeremy Kerr 941d831f960SJeremy Kerr int main(int argc, char **argv) 942d831f960SJeremy Kerr { 9437f2bfb9bSMedicine Yeh size_t buffer_size = default_buffer_size; 944d66195c1SJeremy Kerr const char *config_filename = NULL; 9456221ce94SVishwanatha Subbanna const char *config_tty_kname = NULL; 9467f2bfb9bSMedicine Yeh const char *buffer_size_str = NULL; 947954be0fbSAndrew Jeffery const char *console_id = NULL; 9481a0e03b4SJeremy Kerr struct console *console; 949d66195c1SJeremy Kerr struct config *config; 950d66195c1SJeremy Kerr int rc; 951d831f960SJeremy Kerr 952d831f960SJeremy Kerr for (;;) { 953b70f8713SAndrew Jeffery int c; 954b70f8713SAndrew Jeffery int idx; 955d831f960SJeremy Kerr 956954be0fbSAndrew Jeffery c = getopt_long(argc, argv, "c:i:", options, &idx); 9572834c5b1SAndrew Jeffery if (c == -1) { 958d831f960SJeremy Kerr break; 9592834c5b1SAndrew Jeffery } 960d831f960SJeremy Kerr 961d831f960SJeremy Kerr switch (c) { 962d66195c1SJeremy Kerr case 'c': 963d66195c1SJeremy Kerr config_filename = optarg; 964d831f960SJeremy Kerr break; 965954be0fbSAndrew Jeffery case 'i': 966954be0fbSAndrew Jeffery console_id = optarg; 967954be0fbSAndrew Jeffery break; 968d831f960SJeremy Kerr case 'h': 969d831f960SJeremy Kerr case '?': 970d831f960SJeremy Kerr usage(argv[0]); 971d66195c1SJeremy Kerr return EXIT_SUCCESS; 972d831f960SJeremy Kerr } 973d831f960SJeremy Kerr } 974d831f960SJeremy Kerr 9752834c5b1SAndrew Jeffery if (optind < argc) { 9766221ce94SVishwanatha Subbanna config_tty_kname = argv[optind]; 9772834c5b1SAndrew Jeffery } 9786221ce94SVishwanatha Subbanna 9797f2bfb9bSMedicine Yeh config = config_init(config_filename); 98028a1761aSAndrew Jeffery if (!config) { 98128a1761aSAndrew Jeffery return EXIT_FAILURE; 98228a1761aSAndrew Jeffery } 9837f2bfb9bSMedicine Yeh 984d66195c1SJeremy Kerr console = malloc(sizeof(struct console)); 98528a1761aSAndrew Jeffery if (!console) { 98628a1761aSAndrew Jeffery rc = -1; 98728a1761aSAndrew Jeffery goto out_config_fini; 98828a1761aSAndrew Jeffery } 98928a1761aSAndrew Jeffery 990d66195c1SJeremy Kerr memset(console, 0, sizeof(*console)); 991a72711afSAndrew Jeffery console->pollfds = 992a72711afSAndrew Jeffery calloc(MAX_INTERNAL_POLLFD, sizeof(*console->pollfds)); 99328a1761aSAndrew Jeffery 9947f2bfb9bSMedicine Yeh buffer_size_str = config_get_value(config, "ringbuffer-size"); 9957f2bfb9bSMedicine Yeh if (buffer_size_str) { 9967f2bfb9bSMedicine Yeh rc = config_parse_bytesize(buffer_size_str, &buffer_size); 9977f2bfb9bSMedicine Yeh if (rc) { 9987f2bfb9bSMedicine Yeh warn("Invalid ringbuffer-size. Default to %zukB", 9997f2bfb9bSMedicine Yeh buffer_size >> 10); 10007f2bfb9bSMedicine Yeh } 10017f2bfb9bSMedicine Yeh } 1002f733c85aSJeremy Kerr console->rb = ringbuffer_init(buffer_size); 10033f8d5bebSAndrew Jeffery if (!console->rb) { 10043f8d5bebSAndrew Jeffery rc = -1; 100528a1761aSAndrew Jeffery goto out_console_fini; 10063f8d5bebSAndrew Jeffery } 1007329a35f5SJeremy Kerr 1008954be0fbSAndrew Jeffery if (set_socket_info(console, config, console_id)) { 100929c59c44SAndrew Jeffery rc = -1; 10103f8d5bebSAndrew Jeffery goto out_ringbuffer_fini; 1011b14ca19cSNinad Palsule } 1012b14ca19cSNinad Palsule 10137dc08baaSZev Weiss uart_routing_init(config); 10147dc08baaSZev Weiss 1015d769eecfSAndrew Jeffery rc = tty_init(console, config, config_tty_kname); 10162834c5b1SAndrew Jeffery if (rc) { 10173f8d5bebSAndrew Jeffery goto out_ringbuffer_fini; 10182834c5b1SAndrew Jeffery } 1019d831f960SJeremy Kerr 1020498a4a81SAndrew Jeffery rc = dbus_init(console, config); 1021498a4a81SAndrew Jeffery if (rc) { 1022498a4a81SAndrew Jeffery goto out_tty_fini; 1023498a4a81SAndrew Jeffery } 1024f9c8f6caSCheng C Yang 1025d47963e5SJeremy Kerr handlers_init(console, config); 1026d831f960SJeremy Kerr 10271a0e03b4SJeremy Kerr rc = run_console(console); 1028d831f960SJeremy Kerr 10291a0e03b4SJeremy Kerr handlers_fini(console); 1030d831f960SJeremy Kerr 1031498a4a81SAndrew Jeffery out_tty_fini: 103230ea6385SAndrew Jeffery tty_fini(console); 103330ea6385SAndrew Jeffery 10343f8d5bebSAndrew Jeffery out_ringbuffer_fini: 10353f8d5bebSAndrew Jeffery ringbuffer_fini(console->rb); 10363f8d5bebSAndrew Jeffery 103728a1761aSAndrew Jeffery out_console_fini: 103889ea8198SJeremy Kerr free(console->pollers); 103989ea8198SJeremy Kerr free(console->pollfds); 10401a0e03b4SJeremy Kerr free(console); 1041d831f960SJeremy Kerr 104228a1761aSAndrew Jeffery out_config_fini: 104328a1761aSAndrew Jeffery config_fini(config); 104428a1761aSAndrew Jeffery 1045d831f960SJeremy Kerr return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE; 1046d831f960SJeremy Kerr } 1047