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" 42d831f960SJeremy Kerr 4330ea6385SAndrew Jeffery #define DEV_PTS_PATH "/dev/pts" 4430ea6385SAndrew Jeffery 457f2bfb9bSMedicine Yeh /* default size of the shared backlog ringbuffer */ 467f2bfb9bSMedicine Yeh const size_t default_buffer_size = 128ul * 1024ul; 47f733c85aSJeremy Kerr 48769cee1aSJeremy Kerr /* state shared with the signal handler */ 49769cee1aSJeremy Kerr static bool sigint; 50329a35f5SJeremy Kerr 51d831f960SJeremy Kerr static void usage(const char *progname) 52d831f960SJeremy Kerr { 53d831f960SJeremy Kerr fprintf(stderr, 546221ce94SVishwanatha Subbanna "usage: %s [options] <DEVICE>\n" 55d831f960SJeremy Kerr "\n" 56d831f960SJeremy Kerr "Options:\n" 57954be0fbSAndrew Jeffery " --config <FILE>\tUse FILE for configuration\n" 58954be0fbSAndrew Jeffery " --console-id <NAME>\tUse NAME in the UNIX domain socket address\n" 59d831f960SJeremy Kerr "", 60d831f960SJeremy Kerr progname); 61d831f960SJeremy Kerr } 62d831f960SJeremy Kerr 6330ea6385SAndrew Jeffery /* populates console->tty.dev and console->tty.sysfs_devnode, using the tty kernel name */ 641a0e03b4SJeremy Kerr static int tty_find_device(struct console *console) 6517217845SJeremy Kerr { 66d3cb9c22SAndrew Jeffery char *tty_class_device_link = NULL; 67d3cb9c22SAndrew Jeffery char *tty_path_input_real = NULL; 68d3cb9c22SAndrew Jeffery char *tty_device_tty_dir = NULL; 6930ea6385SAndrew Jeffery char *tty_vuart_lpc_addr = NULL; 70d3cb9c22SAndrew Jeffery char *tty_device_reldir = NULL; 7130ea6385SAndrew Jeffery char *tty_sysfs_devnode = NULL; 72d3cb9c22SAndrew Jeffery char *tty_kname_real = NULL; 7330ea6385SAndrew Jeffery char *tty_path_input = NULL; 7417217845SJeremy Kerr int rc; 7517217845SJeremy Kerr 7630ea6385SAndrew Jeffery console->tty.type = TTY_DEVICE_UNDEFINED; 7730ea6385SAndrew Jeffery 7830ea6385SAndrew Jeffery assert(console->tty.kname); 7930ea6385SAndrew Jeffery if (!strlen(console->tty.kname)) { 8030ea6385SAndrew Jeffery warnx("TTY kname must not be empty"); 8130ea6385SAndrew Jeffery rc = -1; 8230ea6385SAndrew Jeffery goto out_free; 832834c5b1SAndrew Jeffery } 8417217845SJeremy Kerr 8530ea6385SAndrew Jeffery if (console->tty.kname[0] == '/') { 8630ea6385SAndrew Jeffery tty_path_input = strdup(console->tty.kname); 8730ea6385SAndrew Jeffery if (!tty_path_input) { 8830ea6385SAndrew Jeffery rc = -1; 8930ea6385SAndrew Jeffery goto out_free; 9030ea6385SAndrew Jeffery } 9130ea6385SAndrew Jeffery } else { 9230ea6385SAndrew Jeffery rc = asprintf(&tty_path_input, "/dev/%s", console->tty.kname); 9330ea6385SAndrew Jeffery if (rc < 0) { 9430ea6385SAndrew Jeffery goto out_free; 9530ea6385SAndrew Jeffery } 9630ea6385SAndrew Jeffery } 9730ea6385SAndrew Jeffery 9830ea6385SAndrew Jeffery /* udev may rename the tty name with a symbol link, try to resolve */ 9945ad7676SYi Li tty_path_input_real = realpath(tty_path_input, NULL); 10045ad7676SYi Li if (!tty_path_input_real) { 10130ea6385SAndrew Jeffery warn("Can't find realpath for %s", tty_path_input); 10215792aa7SAndrew Jeffery rc = -1; 10345ad7676SYi Li goto out_free; 10445ad7676SYi Li } 10545ad7676SYi Li 10630ea6385SAndrew Jeffery /* 10730ea6385SAndrew Jeffery * Allow hooking obmc-console-server up to PTYs for testing 10830ea6385SAndrew Jeffery * 10930ea6385SAndrew Jeffery * https://amboar.github.io/notes/2023/05/02/testing-obmc-console-with-socat.html 11030ea6385SAndrew Jeffery */ 11130ea6385SAndrew Jeffery if (!strncmp(DEV_PTS_PATH, tty_path_input_real, strlen(DEV_PTS_PATH))) { 11230ea6385SAndrew Jeffery console->tty.type = TTY_DEVICE_PTY; 11330ea6385SAndrew Jeffery console->tty.dev = strdup(console->tty.kname); 11430ea6385SAndrew Jeffery rc = console->tty.dev ? 0 : -1; 11530ea6385SAndrew Jeffery goto out_free; 11630ea6385SAndrew Jeffery } 11730ea6385SAndrew Jeffery 11845ad7676SYi Li tty_kname_real = basename(tty_path_input_real); 11945ad7676SYi Li if (!tty_kname_real) { 12030ea6385SAndrew Jeffery warn("Can't find real name for %s", console->tty.kname); 12115792aa7SAndrew Jeffery rc = -1; 12245ad7676SYi Li goto out_free; 12345ad7676SYi Li } 12445ad7676SYi Li 125a72711afSAndrew Jeffery rc = asprintf(&tty_class_device_link, "/sys/class/tty/%s", 126a72711afSAndrew Jeffery tty_kname_real); 1272834c5b1SAndrew Jeffery if (rc < 0) { 12845ad7676SYi Li goto out_free; 1292834c5b1SAndrew Jeffery } 13045ad7676SYi Li 13117217845SJeremy Kerr tty_device_tty_dir = realpath(tty_class_device_link, NULL); 13245ad7676SYi Li if (!tty_device_tty_dir) { 13345ad7676SYi Li warn("Can't query sysfs for device %s", tty_kname_real); 13415792aa7SAndrew Jeffery rc = -1; 13517217845SJeremy Kerr goto out_free; 13617217845SJeremy Kerr } 13717217845SJeremy Kerr 13817217845SJeremy Kerr rc = asprintf(&tty_device_reldir, "%s/../../", tty_device_tty_dir); 1392834c5b1SAndrew Jeffery if (rc < 0) { 14017217845SJeremy Kerr goto out_free; 1412834c5b1SAndrew Jeffery } 14217217845SJeremy Kerr 14330ea6385SAndrew Jeffery tty_sysfs_devnode = realpath(tty_device_reldir, NULL); 14430ea6385SAndrew Jeffery if (!tty_sysfs_devnode) { 14545ad7676SYi Li warn("Can't find parent device for %s", tty_kname_real); 1462834c5b1SAndrew Jeffery } 14717217845SJeremy Kerr 14830ea6385SAndrew Jeffery rc = asprintf(&console->tty.dev, "/dev/%s", tty_kname_real); 1492834c5b1SAndrew Jeffery if (rc < 0) { 15017217845SJeremy Kerr goto out_free; 1512834c5b1SAndrew Jeffery } 15217217845SJeremy Kerr 153955d140eSOskar Senft // Default to non-VUART 154955d140eSOskar Senft console->tty.type = TTY_DEVICE_UART; 155955d140eSOskar Senft 15630ea6385SAndrew Jeffery /* Arbitrarily pick an attribute to differentiate UART vs VUART */ 157955d140eSOskar Senft if (tty_sysfs_devnode) { 158955d140eSOskar Senft rc = asprintf(&tty_vuart_lpc_addr, "%s/lpc_address", 159955d140eSOskar Senft tty_sysfs_devnode); 16030ea6385SAndrew Jeffery if (rc < 0) { 16130ea6385SAndrew Jeffery goto out_free; 16230ea6385SAndrew Jeffery } 16330ea6385SAndrew Jeffery 16430ea6385SAndrew Jeffery rc = access(tty_vuart_lpc_addr, F_OK); 165955d140eSOskar Senft if (!rc) { 166955d140eSOskar Senft console->tty.type = TTY_DEVICE_VUART; 167955d140eSOskar Senft console->tty.vuart.sysfs_devnode = 168955d140eSOskar Senft strdup(tty_sysfs_devnode); 169955d140eSOskar Senft } 170955d140eSOskar Senft } 17130ea6385SAndrew Jeffery 17217217845SJeremy Kerr rc = 0; 17317217845SJeremy Kerr 17417217845SJeremy Kerr out_free: 17530ea6385SAndrew Jeffery free(tty_vuart_lpc_addr); 17617217845SJeremy Kerr free(tty_class_device_link); 177*982090d9SAndrew Jeffery free(tty_sysfs_devnode); 17817217845SJeremy Kerr free(tty_device_tty_dir); 17917217845SJeremy Kerr free(tty_device_reldir); 18045ad7676SYi Li free(tty_path_input); 18145ad7676SYi Li free(tty_path_input_real); 18217217845SJeremy Kerr return rc; 18317217845SJeremy Kerr } 18417217845SJeremy Kerr 1851a0e03b4SJeremy Kerr static int tty_set_sysfs_attr(struct console *console, const char *name, 186957818b4SJeremy Kerr int value) 187957818b4SJeremy Kerr { 188957818b4SJeremy Kerr char *path; 189957818b4SJeremy Kerr FILE *fp; 190957818b4SJeremy Kerr int rc; 191957818b4SJeremy Kerr 19230ea6385SAndrew Jeffery assert(console->tty.type == TTY_DEVICE_VUART); 19330ea6385SAndrew Jeffery 19430ea6385SAndrew Jeffery if (!console->tty.vuart.sysfs_devnode) { 19530ea6385SAndrew Jeffery return -1; 19630ea6385SAndrew Jeffery } 19730ea6385SAndrew Jeffery 19830ea6385SAndrew Jeffery rc = asprintf(&path, "%s/%s", console->tty.vuart.sysfs_devnode, name); 1992834c5b1SAndrew Jeffery if (rc < 0) { 200957818b4SJeremy Kerr return -1; 2012834c5b1SAndrew Jeffery } 202957818b4SJeremy Kerr 203957818b4SJeremy Kerr fp = fopen(path, "w"); 204957818b4SJeremy Kerr if (!fp) { 205a72711afSAndrew Jeffery warn("Can't access attribute %s on device %s", name, 20630ea6385SAndrew Jeffery console->tty.kname); 207957818b4SJeremy Kerr rc = -1; 208957818b4SJeremy Kerr goto out_free; 209957818b4SJeremy Kerr } 210957818b4SJeremy Kerr setvbuf(fp, NULL, _IONBF, 0); 211957818b4SJeremy Kerr 212957818b4SJeremy Kerr rc = fprintf(fp, "0x%x", value); 2132834c5b1SAndrew Jeffery if (rc < 0) { 214a72711afSAndrew Jeffery warn("Error writing to %s attribute of device %s", name, 21530ea6385SAndrew Jeffery console->tty.kname); 2162834c5b1SAndrew Jeffery } 217957818b4SJeremy Kerr fclose(fp); 218957818b4SJeremy Kerr 219957818b4SJeremy Kerr out_free: 220957818b4SJeremy Kerr free(path); 221957818b4SJeremy Kerr return rc; 222957818b4SJeremy Kerr } 223957818b4SJeremy Kerr 224d831f960SJeremy Kerr /** 225c7fbcd48SBenjamin Fair * Set termios attributes on the console tty. 22654e9569dSJeremy Kerr */ 227e258e51fSNinad Palsule void tty_init_termios(struct console *console) 22854e9569dSJeremy Kerr { 22954e9569dSJeremy Kerr struct termios termios; 23054e9569dSJeremy Kerr int rc; 23154e9569dSJeremy Kerr 23230ea6385SAndrew Jeffery rc = tcgetattr(console->tty.fd, &termios); 23354e9569dSJeremy Kerr if (rc) { 23454e9569dSJeremy Kerr warn("Can't read tty termios"); 23554e9569dSJeremy Kerr return; 23654e9569dSJeremy Kerr } 23754e9569dSJeremy Kerr 23830ea6385SAndrew Jeffery if (console->tty.type == TTY_DEVICE_UART && console->tty.uart.baud) { 23930ea6385SAndrew Jeffery if (cfsetspeed(&termios, console->tty.uart.baud) < 0) { 24030ea6385SAndrew Jeffery warn("Couldn't set speeds for %s", console->tty.kname); 241c7fbcd48SBenjamin Fair } 2422834c5b1SAndrew Jeffery } 243c7fbcd48SBenjamin Fair 244c7fbcd48SBenjamin Fair /* Set console to raw mode: we don't want any processing to occur on 245c7fbcd48SBenjamin Fair * the underlying terminal input/output. 246c7fbcd48SBenjamin Fair */ 24754e9569dSJeremy Kerr cfmakeraw(&termios); 248c7fbcd48SBenjamin Fair 24930ea6385SAndrew Jeffery rc = tcsetattr(console->tty.fd, TCSANOW, &termios); 2502834c5b1SAndrew Jeffery if (rc) { 25130ea6385SAndrew Jeffery warn("Can't set terminal options for %s", console->tty.kname); 25254e9569dSJeremy Kerr } 2532834c5b1SAndrew Jeffery } 25454e9569dSJeremy Kerr 25554e9569dSJeremy Kerr /** 256d831f960SJeremy Kerr * Open and initialise the serial device 257d831f960SJeremy Kerr */ 25830ea6385SAndrew Jeffery static void tty_init_vuart_io(struct console *console) 259d831f960SJeremy Kerr { 26030ea6385SAndrew Jeffery assert(console->tty.type == TTY_DEVICE_VUART); 26130ea6385SAndrew Jeffery 26230ea6385SAndrew Jeffery if (console->tty.vuart.sirq) { 26330ea6385SAndrew Jeffery tty_set_sysfs_attr(console, "sirq", console->tty.vuart.sirq); 2642834c5b1SAndrew Jeffery } 265957818b4SJeremy Kerr 26630ea6385SAndrew Jeffery if (console->tty.vuart.lpc_addr) { 26730ea6385SAndrew Jeffery tty_set_sysfs_attr(console, "lpc_address", 26830ea6385SAndrew Jeffery console->tty.vuart.lpc_addr); 26930ea6385SAndrew Jeffery } 27030ea6385SAndrew Jeffery } 27130ea6385SAndrew Jeffery 27230ea6385SAndrew Jeffery static int tty_init_io(struct console *console) 27330ea6385SAndrew Jeffery { 27430ea6385SAndrew Jeffery console->tty.fd = open(console->tty.dev, O_RDWR); 27530ea6385SAndrew Jeffery if (console->tty.fd <= 0) { 27630ea6385SAndrew Jeffery warn("Can't open tty %s", console->tty.dev); 277d831f960SJeremy Kerr return -1; 278d831f960SJeremy Kerr } 279d831f960SJeremy Kerr 280d831f960SJeremy Kerr /* Disable character delay. We may want to later enable this when 281d831f960SJeremy Kerr * we detect larger amounts of data 282d831f960SJeremy Kerr */ 28330ea6385SAndrew Jeffery fcntl(console->tty.fd, F_SETFL, FNDELAY); 284d831f960SJeremy Kerr 28554e9569dSJeremy Kerr tty_init_termios(console); 28654e9569dSJeremy Kerr 28730ea6385SAndrew Jeffery console->pollfds[console->n_pollers].fd = console->tty.fd; 288329a35f5SJeremy Kerr console->pollfds[console->n_pollers].events = POLLIN; 289329a35f5SJeremy Kerr 290d831f960SJeremy Kerr return 0; 291d831f960SJeremy Kerr } 292d831f960SJeremy Kerr 29330ea6385SAndrew Jeffery static int tty_init_vuart(struct console *console, struct config *config) 294d66195c1SJeremy Kerr { 295fd883a88SAndrew Jeffery unsigned long parsed; 296d66195c1SJeremy Kerr const char *val; 297d66195c1SJeremy Kerr char *endp; 29830ea6385SAndrew Jeffery 29930ea6385SAndrew Jeffery assert(console->tty.type == TTY_DEVICE_VUART); 300d66195c1SJeremy Kerr 301d66195c1SJeremy Kerr val = config_get_value(config, "lpc-address"); 302d66195c1SJeremy Kerr if (val) { 303fd883a88SAndrew Jeffery errno = 0; 304fd883a88SAndrew Jeffery parsed = strtoul(val, &endp, 0); 305fd883a88SAndrew Jeffery if (parsed == ULONG_MAX && errno == ERANGE) { 306fd883a88SAndrew Jeffery warn("Cannot interpret 'lpc-address' value as an unsigned long: '%s'", 307fd883a88SAndrew Jeffery val); 308fd883a88SAndrew Jeffery return -1; 309fd883a88SAndrew Jeffery } 310fd883a88SAndrew Jeffery 311fd883a88SAndrew Jeffery if (parsed > UINT16_MAX) { 312fd883a88SAndrew Jeffery warn("Invalid LPC address '%s'", val); 313fd883a88SAndrew Jeffery return -1; 314fd883a88SAndrew Jeffery } 315fd883a88SAndrew Jeffery 31630ea6385SAndrew Jeffery console->tty.vuart.lpc_addr = (uint16_t)parsed; 317d66195c1SJeremy Kerr if (endp == optarg) { 318d66195c1SJeremy Kerr warn("Invalid LPC address: '%s'", val); 319d66195c1SJeremy Kerr return -1; 320d66195c1SJeremy Kerr } 321d66195c1SJeremy Kerr } 322d66195c1SJeremy Kerr 323d66195c1SJeremy Kerr val = config_get_value(config, "sirq"); 324d66195c1SJeremy Kerr if (val) { 325fd883a88SAndrew Jeffery errno = 0; 326fd883a88SAndrew Jeffery parsed = strtoul(val, &endp, 0); 327fd883a88SAndrew Jeffery if (parsed == ULONG_MAX && errno == ERANGE) { 328fd883a88SAndrew Jeffery warn("Cannot interpret 'sirq' value as an unsigned long: '%s'", 329fd883a88SAndrew Jeffery val); 330fd883a88SAndrew Jeffery } 331fd883a88SAndrew Jeffery 3322834c5b1SAndrew Jeffery if (parsed > 16) { 333fd883a88SAndrew Jeffery warn("Invalid LPC SERIRQ: '%s'", val); 3342834c5b1SAndrew Jeffery } 335fd883a88SAndrew Jeffery 33630ea6385SAndrew Jeffery console->tty.vuart.sirq = (int)parsed; 3372834c5b1SAndrew Jeffery if (endp == optarg) { 338d66195c1SJeremy Kerr warn("Invalid sirq: '%s'", val); 339d66195c1SJeremy Kerr } 3402834c5b1SAndrew Jeffery } 341d66195c1SJeremy Kerr 34230ea6385SAndrew Jeffery return 0; 3432834c5b1SAndrew Jeffery } 344c7fbcd48SBenjamin Fair 34530ea6385SAndrew Jeffery static int tty_init(struct console *console, struct config *config, 34630ea6385SAndrew Jeffery const char *tty_arg) 34730ea6385SAndrew Jeffery { 34830ea6385SAndrew Jeffery const char *val; 34930ea6385SAndrew Jeffery int rc; 35030ea6385SAndrew Jeffery 351d769eecfSAndrew Jeffery if (tty_arg) { 35230ea6385SAndrew Jeffery console->tty.kname = tty_arg; 353d769eecfSAndrew Jeffery } else if ((val = config_get_value(config, "upstream-tty"))) { 35430ea6385SAndrew Jeffery console->tty.kname = val; 355d769eecfSAndrew Jeffery } else { 356d66195c1SJeremy Kerr warnx("Error: No TTY device specified"); 357d66195c1SJeremy Kerr return -1; 358d66195c1SJeremy Kerr } 359d66195c1SJeremy Kerr 360d66195c1SJeremy Kerr rc = tty_find_device(console); 3612834c5b1SAndrew Jeffery if (rc) { 362d66195c1SJeremy Kerr return rc; 3632834c5b1SAndrew Jeffery } 364d66195c1SJeremy Kerr 36530ea6385SAndrew Jeffery switch (console->tty.type) { 36630ea6385SAndrew Jeffery case TTY_DEVICE_VUART: 36730ea6385SAndrew Jeffery rc = tty_init_vuart(console, config); 36830ea6385SAndrew Jeffery if (rc) { 369d66195c1SJeremy Kerr return rc; 370d66195c1SJeremy Kerr } 371d66195c1SJeremy Kerr 37230ea6385SAndrew Jeffery tty_init_vuart_io(console); 37330ea6385SAndrew Jeffery break; 37430ea6385SAndrew Jeffery case TTY_DEVICE_UART: 37530ea6385SAndrew Jeffery val = config_get_value(config, "baud"); 37630ea6385SAndrew Jeffery if (val) { 37730ea6385SAndrew Jeffery if (config_parse_baud(&console->tty.uart.baud, val)) { 37830ea6385SAndrew Jeffery warnx("Invalid baud rate: '%s'", val); 37930ea6385SAndrew Jeffery } 38030ea6385SAndrew Jeffery } 38130ea6385SAndrew Jeffery break; 38230ea6385SAndrew Jeffery case TTY_DEVICE_PTY: 38330ea6385SAndrew Jeffery break; 38430ea6385SAndrew Jeffery case TTY_DEVICE_UNDEFINED: 38530ea6385SAndrew Jeffery default: 38630ea6385SAndrew Jeffery warnx("Cannot configure unrecognised TTY device"); 38730ea6385SAndrew Jeffery return -1; 38830ea6385SAndrew Jeffery } 38930ea6385SAndrew Jeffery 39030ea6385SAndrew Jeffery return tty_init_io(console); 39130ea6385SAndrew Jeffery } 39230ea6385SAndrew Jeffery 39330ea6385SAndrew Jeffery static void tty_fini(struct console *console) 39430ea6385SAndrew Jeffery { 39530ea6385SAndrew Jeffery if (console->tty.type == TTY_DEVICE_VUART) { 39630ea6385SAndrew Jeffery free(console->tty.vuart.sysfs_devnode); 39730ea6385SAndrew Jeffery } 39830ea6385SAndrew Jeffery free(console->tty.dev); 39930ea6385SAndrew Jeffery } 40030ea6385SAndrew Jeffery 4017dc08baaSZev Weiss static int write_to_path(const char *path, const char *data) 4027dc08baaSZev Weiss { 4037dc08baaSZev Weiss int rc = 0; 4047dc08baaSZev Weiss FILE *f = fopen(path, "w"); 4057dc08baaSZev Weiss if (!f) { 4067dc08baaSZev Weiss return -1; 4077dc08baaSZev Weiss } 4087dc08baaSZev Weiss 4097dc08baaSZev Weiss if (fprintf(f, "%s", data) < 0) { 4107dc08baaSZev Weiss rc = -1; 4117dc08baaSZev Weiss } 4127dc08baaSZev Weiss 4137dc08baaSZev Weiss if (fclose(f)) { 4147dc08baaSZev Weiss rc = -1; 4157dc08baaSZev Weiss } 4167dc08baaSZev Weiss 4177dc08baaSZev Weiss return rc; 4187dc08baaSZev Weiss } 4197dc08baaSZev Weiss 4207dc08baaSZev Weiss #define ASPEED_UART_ROUTING_PATTERN \ 4217dc08baaSZev Weiss "/sys/bus/platform/drivers/aspeed-uart-routing/*.uart-routing" 4227dc08baaSZev Weiss 4237dc08baaSZev Weiss static void uart_routing_init(struct config *config) 4247dc08baaSZev Weiss { 4257dc08baaSZev Weiss const char *muxcfg; 4267dc08baaSZev Weiss const char *p; 4277dc08baaSZev Weiss size_t buflen; 4287dc08baaSZev Weiss char *sink; 4297dc08baaSZev Weiss char *source; 4307dc08baaSZev Weiss char *muxdir; 4317dc08baaSZev Weiss char *path; 4327dc08baaSZev Weiss glob_t globbuf; 4337dc08baaSZev Weiss 4347dc08baaSZev Weiss muxcfg = config_get_value(config, "aspeed-uart-routing"); 4357dc08baaSZev Weiss if (!muxcfg) { 4367dc08baaSZev Weiss return; 4377dc08baaSZev Weiss } 4387dc08baaSZev Weiss 4397dc08baaSZev Weiss /* Find the driver's sysfs directory */ 4407dc08baaSZev Weiss if (glob(ASPEED_UART_ROUTING_PATTERN, GLOB_ERR | GLOB_NOSORT, NULL, 4417dc08baaSZev Weiss &globbuf) != 0) { 4427dc08baaSZev Weiss warn("Couldn't find uart-routing driver directory, cannot apply config"); 4437dc08baaSZev Weiss return; 4447dc08baaSZev Weiss } 4457dc08baaSZev Weiss if (globbuf.gl_pathc != 1) { 4467dc08baaSZev Weiss warnx("Found %zd uart-routing driver directories, cannot apply config", 4477dc08baaSZev Weiss globbuf.gl_pathc); 4487dc08baaSZev Weiss goto out_free_glob; 4497dc08baaSZev Weiss } 4507dc08baaSZev Weiss muxdir = globbuf.gl_pathv[0]; 4517dc08baaSZev Weiss 4527dc08baaSZev Weiss /* 4537dc08baaSZev Weiss * Rather than faff about tracking a bunch of separate buffer sizes, 4547dc08baaSZev Weiss * just use one (worst-case) size for all of them -- +2 for a trailing 4557dc08baaSZev Weiss * NUL and a '/' separator to construct the sysfs file path. 4567dc08baaSZev Weiss */ 4577dc08baaSZev Weiss buflen = strlen(muxdir) + strlen(muxcfg) + 2; 4587dc08baaSZev Weiss 4597dc08baaSZev Weiss sink = malloc(buflen); 4607dc08baaSZev Weiss source = malloc(buflen); 4617dc08baaSZev Weiss path = malloc(buflen); 4627dc08baaSZev Weiss if (!path || !sink || !source) { 4637dc08baaSZev Weiss warnx("Out of memory applying uart routing config"); 4647dc08baaSZev Weiss goto out_free_bufs; 4657dc08baaSZev Weiss } 4667dc08baaSZev Weiss 4677dc08baaSZev Weiss p = muxcfg; 4687dc08baaSZev Weiss while (*p) { 4697dc08baaSZev Weiss ssize_t bytes_scanned; 4707dc08baaSZev Weiss 4717dc08baaSZev Weiss if (sscanf(p, " %[^:/ \t]:%[^: \t] %zn", sink, source, 4727dc08baaSZev Weiss &bytes_scanned) != 2) { 4737dc08baaSZev Weiss warnx("Invalid syntax in aspeed uart config: '%s' not applied", 4747dc08baaSZev Weiss p); 4757dc08baaSZev Weiss break; 4767dc08baaSZev Weiss } 4777dc08baaSZev Weiss p += bytes_scanned; 4787dc08baaSZev Weiss 4797dc08baaSZev Weiss /* 4807dc08baaSZev Weiss * Check that the sink name looks reasonable before proceeding 4817dc08baaSZev Weiss * (there are other writable files in the same directory that 4827dc08baaSZev Weiss * we shouldn't be touching, such as 'driver_override' and 4837dc08baaSZev Weiss * 'uevent'). 4847dc08baaSZev Weiss */ 4857dc08baaSZev Weiss if (strncmp(sink, "io", strlen("io")) != 0 && 4867dc08baaSZev Weiss strncmp(sink, "uart", strlen("uart")) != 0) { 4877dc08baaSZev Weiss warnx("Skipping invalid uart routing name '%s' (must be ioN or uartN)", 4887dc08baaSZev Weiss sink); 4897dc08baaSZev Weiss continue; 4907dc08baaSZev Weiss } 4917dc08baaSZev Weiss 4927dc08baaSZev Weiss snprintf(path, buflen, "%s/%s", muxdir, sink); 4937dc08baaSZev Weiss if (write_to_path(path, source)) { 4947dc08baaSZev Weiss warn("Failed to apply uart-routing config '%s:%s'", 4957dc08baaSZev Weiss sink, source); 4967dc08baaSZev Weiss } 4977dc08baaSZev Weiss } 4987dc08baaSZev Weiss 4997dc08baaSZev Weiss out_free_bufs: 5007dc08baaSZev Weiss free(path); 5017dc08baaSZev Weiss free(source); 5027dc08baaSZev Weiss free(sink); 5037dc08baaSZev Weiss out_free_glob: 5047dc08baaSZev Weiss globfree(&globbuf); 5057dc08baaSZev Weiss } 5067dc08baaSZev Weiss 5071a0e03b4SJeremy Kerr int console_data_out(struct console *console, const uint8_t *data, size_t len) 508d831f960SJeremy Kerr { 50930ea6385SAndrew Jeffery return write_buf_to_fd(console->tty.fd, data, len); 510d831f960SJeremy Kerr } 511d831f960SJeremy Kerr 5125ba20b5bSNinad Palsule /* Prepare a socket name */ 513954be0fbSAndrew Jeffery static int set_socket_info(struct console *console, struct config *config, 514954be0fbSAndrew Jeffery const char *console_id) 515b14ca19cSNinad Palsule { 516b14ca19cSNinad Palsule ssize_t len; 517b14ca19cSNinad Palsule 5185ba20b5bSNinad Palsule /* Get console id */ 5195ba20b5bSNinad Palsule console->console_id = config_resolve_console_id(config, console_id); 520954be0fbSAndrew Jeffery 521b14ca19cSNinad Palsule /* Get the socket name/path */ 5225ba20b5bSNinad Palsule len = console_socket_path(console->socket_name, console->console_id); 523b14ca19cSNinad Palsule if (len < 0) { 524b14ca19cSNinad Palsule warn("Failed to set socket path: %s", strerror(errno)); 525b14ca19cSNinad Palsule return EXIT_FAILURE; 526b14ca19cSNinad Palsule } 527b14ca19cSNinad Palsule 528b14ca19cSNinad Palsule /* Socket name is not a null terminated string hence save the length */ 529b14ca19cSNinad Palsule console->socket_name_len = len; 530b14ca19cSNinad Palsule 531b14ca19cSNinad Palsule return 0; 532b14ca19cSNinad Palsule } 533b14ca19cSNinad Palsule 534d47963e5SJeremy Kerr static void handlers_init(struct console *console, struct config *config) 535d831f960SJeremy Kerr { 536b70f8713SAndrew Jeffery /* NOLINTBEGIN(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) */ 537e2826c7dSJeremy Kerr extern const struct handler_type *const __start_handlers; 538e2826c7dSJeremy Kerr extern const struct handler_type *const __stop_handlers; 539b70f8713SAndrew Jeffery /* NOLINTEND(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) */ 540e2826c7dSJeremy Kerr size_t n_types; 541e2826c7dSJeremy Kerr int j = 0; 542e2826c7dSJeremy Kerr size_t i; 543e2826c7dSJeremy Kerr 544e2826c7dSJeremy Kerr n_types = &__stop_handlers - &__start_handlers; 545e2826c7dSJeremy Kerr console->handlers = calloc(n_types, sizeof(struct handler *)); 546e2826c7dSJeremy Kerr if (!console->handlers) { 547e2826c7dSJeremy Kerr err(EXIT_FAILURE, "malloc(handlers)"); 548e2826c7dSJeremy Kerr } 549e2826c7dSJeremy Kerr 550079fc516SAndrew Jeffery printf("%zu handler type%s\n", n_types, n_types == 1 ? "" : "s"); 551e2826c7dSJeremy Kerr 552e2826c7dSJeremy Kerr for (i = 0; i < n_types; i++) { 553e2826c7dSJeremy Kerr const struct handler_type *type = &__start_handlers[i]; 5541a0e03b4SJeremy Kerr struct handler *handler; 555d831f960SJeremy Kerr 556e2826c7dSJeremy Kerr /* Should be picked up at build time by 557e2826c7dSJeremy Kerr * console_handler_register, but check anyway 558e2826c7dSJeremy Kerr */ 559e2826c7dSJeremy Kerr if (!type->init || !type->fini) { 560e2826c7dSJeremy Kerr errx(EXIT_FAILURE, 561e2826c7dSJeremy Kerr "invalid handler type %s: no init() / fini()", 562e2826c7dSJeremy Kerr type->name); 5632834c5b1SAndrew Jeffery } 564021b91f0SJeremy Kerr 565e2826c7dSJeremy Kerr handler = type->init(type, console, config); 566021b91f0SJeremy Kerr 567e2826c7dSJeremy Kerr printf(" console '%s': handler %s [%sactive]\n", 568e2826c7dSJeremy Kerr console->console_id, type->name, handler ? "" : "in"); 569e2826c7dSJeremy Kerr 570e2826c7dSJeremy Kerr if (handler) { 571e2826c7dSJeremy Kerr handler->type = type; 572e2826c7dSJeremy Kerr console->handlers[j++] = handler; 573d831f960SJeremy Kerr } 574d831f960SJeremy Kerr } 575d831f960SJeremy Kerr 576e2826c7dSJeremy Kerr console->n_handlers = j; 577e2826c7dSJeremy Kerr } 578e2826c7dSJeremy Kerr 5791a0e03b4SJeremy Kerr static void handlers_fini(struct console *console) 580d831f960SJeremy Kerr { 5811a0e03b4SJeremy Kerr struct handler *handler; 5821a0e03b4SJeremy Kerr int i; 5831a0e03b4SJeremy Kerr 5841a0e03b4SJeremy Kerr for (i = 0; i < console->n_handlers; i++) { 5851a0e03b4SJeremy Kerr handler = console->handlers[i]; 586e2826c7dSJeremy Kerr handler->type->fini(handler); 5871a0e03b4SJeremy Kerr } 588e2826c7dSJeremy Kerr 589e2826c7dSJeremy Kerr free(console->handlers); 590e2826c7dSJeremy Kerr console->handlers = NULL; 591e2826c7dSJeremy Kerr console->n_handlers = 0; 5922834c5b1SAndrew Jeffery } 593d831f960SJeremy Kerr 5941cecc5deSJohnathan Mantey static int get_current_time(struct timeval *tv) 5951cecc5deSJohnathan Mantey { 5961cecc5deSJohnathan Mantey struct timespec t; 5971cecc5deSJohnathan Mantey int rc; 5981cecc5deSJohnathan Mantey 5991cecc5deSJohnathan Mantey /* 6001cecc5deSJohnathan Mantey * We use clock_gettime(CLOCK_MONOTONIC) so we're immune to 6011cecc5deSJohnathan Mantey * local time changes. However, a struct timeval is more 6021cecc5deSJohnathan Mantey * convenient for calculations, so convert to that. 6031cecc5deSJohnathan Mantey */ 6041cecc5deSJohnathan Mantey rc = clock_gettime(CLOCK_MONOTONIC, &t); 6052834c5b1SAndrew Jeffery if (rc) { 6061cecc5deSJohnathan Mantey return rc; 6072834c5b1SAndrew Jeffery } 6081cecc5deSJohnathan Mantey 6091cecc5deSJohnathan Mantey tv->tv_sec = t.tv_sec; 6101cecc5deSJohnathan Mantey tv->tv_usec = t.tv_nsec / 1000; 6111cecc5deSJohnathan Mantey 6121cecc5deSJohnathan Mantey return 0; 6131cecc5deSJohnathan Mantey } 6141cecc5deSJohnathan Mantey 615a72711afSAndrew Jeffery struct ringbuffer_consumer * 616a72711afSAndrew Jeffery console_ringbuffer_consumer_register(struct console *console, 617f733c85aSJeremy Kerr ringbuffer_poll_fn_t poll_fn, void *data) 618d831f960SJeremy Kerr { 619f733c85aSJeremy Kerr return ringbuffer_consumer_register(console->rb, poll_fn, data); 620d831f960SJeremy Kerr } 621d831f960SJeremy Kerr 62255c9712dSJeremy Kerr struct poller *console_poller_register(struct console *console, 623a72711afSAndrew Jeffery struct handler *handler, 624a72711afSAndrew Jeffery poller_event_fn_t poller_fn, 6251cecc5deSJohnathan Mantey poller_timeout_fn_t timeout_fn, int fd, 6261cecc5deSJohnathan Mantey int events, void *data) 627d831f960SJeremy Kerr { 628329a35f5SJeremy Kerr struct poller *poller; 6295c359cc6SAndrew Jeffery long n; 630329a35f5SJeremy Kerr 631329a35f5SJeremy Kerr poller = malloc(sizeof(*poller)); 632329a35f5SJeremy Kerr poller->remove = false; 633329a35f5SJeremy Kerr poller->handler = handler; 6341cecc5deSJohnathan Mantey poller->event_fn = poller_fn; 6351cecc5deSJohnathan Mantey poller->timeout_fn = timeout_fn; 636329a35f5SJeremy Kerr poller->data = data; 637329a35f5SJeremy Kerr 638329a35f5SJeremy Kerr /* add one to our pollers array */ 639329a35f5SJeremy Kerr n = console->n_pollers++; 64091b52175SAndrew Jeffery /* 64191b52175SAndrew Jeffery * We're managing an array of pointers to aggregates, so don't warn about sizeof() on a 64291b52175SAndrew Jeffery * pointer type. 64391b52175SAndrew Jeffery */ 64491b52175SAndrew Jeffery /* NOLINTBEGIN(bugprone-sizeof-expression) */ 64591b52175SAndrew Jeffery console->pollers = reallocarray(console->pollers, console->n_pollers, 64691b52175SAndrew Jeffery sizeof(*console->pollers)); 64791b52175SAndrew Jeffery /* NOLINTEND(bugprone-sizeof-expression) */ 648329a35f5SJeremy Kerr 649329a35f5SJeremy Kerr console->pollers[n] = poller; 650329a35f5SJeremy Kerr 651329a35f5SJeremy Kerr /* increase pollfds array too */ 6524e44c790SAndrew Jeffery console->pollfds = reallocarray( 6534e44c790SAndrew Jeffery console->pollfds, (MAX_INTERNAL_POLLFD + console->n_pollers), 65491b52175SAndrew Jeffery sizeof(*console->pollfds)); 655329a35f5SJeremy Kerr 656329a35f5SJeremy Kerr /* shift the end pollfds up by one */ 657a72711afSAndrew Jeffery memcpy(&console->pollfds[n + 1], &console->pollfds[n], 658f9c8f6caSCheng C Yang sizeof(*console->pollfds) * MAX_INTERNAL_POLLFD); 659329a35f5SJeremy Kerr 660329a35f5SJeremy Kerr console->pollfds[n].fd = fd; 6615c359cc6SAndrew Jeffery console->pollfds[n].events = (short)(events & 0x7fff); 662329a35f5SJeremy Kerr 663329a35f5SJeremy Kerr return poller; 664329a35f5SJeremy Kerr } 665329a35f5SJeremy Kerr 666a72711afSAndrew Jeffery void console_poller_unregister(struct console *console, struct poller *poller) 667329a35f5SJeremy Kerr { 668329a35f5SJeremy Kerr int i; 669329a35f5SJeremy Kerr 670329a35f5SJeremy Kerr /* find the entry in our pollers array */ 6712834c5b1SAndrew Jeffery for (i = 0; i < console->n_pollers; i++) { 6722834c5b1SAndrew Jeffery if (console->pollers[i] == poller) { 673329a35f5SJeremy Kerr break; 6742834c5b1SAndrew Jeffery } 6752834c5b1SAndrew Jeffery } 676329a35f5SJeremy Kerr 677329a35f5SJeremy Kerr assert(i < console->n_pollers); 678329a35f5SJeremy Kerr 679329a35f5SJeremy Kerr console->n_pollers--; 680329a35f5SJeremy Kerr 68191b52175SAndrew Jeffery /* 68291b52175SAndrew Jeffery * Remove the item from the pollers array... 68391b52175SAndrew Jeffery * 68491b52175SAndrew Jeffery * We're managing an array of pointers to aggregates, so don't warn about sizeof() on a 68591b52175SAndrew Jeffery * pointer type. 68691b52175SAndrew Jeffery */ 68791b52175SAndrew Jeffery /* NOLINTBEGIN(bugprone-sizeof-expression) */ 688329a35f5SJeremy Kerr memmove(&console->pollers[i], &console->pollers[i + 1], 689a72711afSAndrew Jeffery sizeof(*console->pollers) * (console->n_pollers - i)); 690329a35f5SJeremy Kerr 69191b52175SAndrew Jeffery console->pollers = reallocarray(console->pollers, console->n_pollers, 69291b52175SAndrew Jeffery sizeof(*console->pollers)); 69391b52175SAndrew Jeffery /* NOLINTEND(bugprone-sizeof-expression) */ 694329a35f5SJeremy Kerr 695329a35f5SJeremy Kerr /* ... and the pollfds array */ 696329a35f5SJeremy Kerr memmove(&console->pollfds[i], &console->pollfds[i + 1], 697329a35f5SJeremy Kerr sizeof(*console->pollfds) * 698f9c8f6caSCheng C Yang (MAX_INTERNAL_POLLFD + console->n_pollers - i)); 699329a35f5SJeremy Kerr 7004e44c790SAndrew Jeffery console->pollfds = reallocarray( 7014e44c790SAndrew Jeffery console->pollfds, (MAX_INTERNAL_POLLFD + console->n_pollers), 70291b52175SAndrew Jeffery sizeof(*console->pollfds)); 703329a35f5SJeremy Kerr 704329a35f5SJeremy Kerr free(poller); 705329a35f5SJeremy Kerr } 706329a35f5SJeremy Kerr 7076b1fed27SJeremy Kerr void console_poller_set_events(struct console *console, struct poller *poller, 7086b1fed27SJeremy Kerr int events) 7096b1fed27SJeremy Kerr { 7106b1fed27SJeremy Kerr int i; 7116b1fed27SJeremy Kerr 7126b1fed27SJeremy Kerr /* find the entry in our pollers array */ 7132834c5b1SAndrew Jeffery for (i = 0; i < console->n_pollers; i++) { 7142834c5b1SAndrew Jeffery if (console->pollers[i] == poller) { 7156b1fed27SJeremy Kerr break; 7162834c5b1SAndrew Jeffery } 7172834c5b1SAndrew Jeffery } 7186b1fed27SJeremy Kerr 7195c359cc6SAndrew Jeffery console->pollfds[i].events = (short)(events & 0x7fff); 7206b1fed27SJeremy Kerr } 7216b1fed27SJeremy Kerr 722fd048328SAndrew Jeffery void console_poller_set_timeout(struct console *console __attribute__((unused)), 723fd048328SAndrew Jeffery struct poller *poller, const struct timeval *tv) 7241cecc5deSJohnathan Mantey { 7251cecc5deSJohnathan Mantey struct timeval now; 7261cecc5deSJohnathan Mantey int rc; 7271cecc5deSJohnathan Mantey 7281cecc5deSJohnathan Mantey rc = get_current_time(&now); 7292834c5b1SAndrew Jeffery if (rc) { 7301cecc5deSJohnathan Mantey return; 7312834c5b1SAndrew Jeffery } 7321cecc5deSJohnathan Mantey 7331cecc5deSJohnathan Mantey timeradd(&now, tv, &poller->timeout); 7341cecc5deSJohnathan Mantey } 7351cecc5deSJohnathan Mantey 7365c359cc6SAndrew Jeffery static long get_poll_timeout(struct console *console, struct timeval *cur_time) 7371cecc5deSJohnathan Mantey { 738b70f8713SAndrew Jeffery struct timeval *earliest; 739b70f8713SAndrew Jeffery struct timeval interval; 7401cecc5deSJohnathan Mantey struct poller *poller; 7411cecc5deSJohnathan Mantey int i; 7421cecc5deSJohnathan Mantey 7431cecc5deSJohnathan Mantey earliest = NULL; 7441cecc5deSJohnathan Mantey 7451cecc5deSJohnathan Mantey for (i = 0; i < console->n_pollers; i++) { 7461cecc5deSJohnathan Mantey poller = console->pollers[i]; 7471cecc5deSJohnathan Mantey 7481cecc5deSJohnathan Mantey if (poller->timeout_fn && timerisset(&poller->timeout) && 7491cecc5deSJohnathan Mantey (!earliest || 7501cecc5deSJohnathan Mantey (earliest && timercmp(&poller->timeout, earliest, <)))) { 7511cecc5deSJohnathan Mantey // poller is buffering data and needs the poll 7521cecc5deSJohnathan Mantey // function to timeout. 7531cecc5deSJohnathan Mantey earliest = &poller->timeout; 7541cecc5deSJohnathan Mantey } 7551cecc5deSJohnathan Mantey } 7561cecc5deSJohnathan Mantey 7571cecc5deSJohnathan Mantey if (earliest) { 7581cecc5deSJohnathan Mantey if (timercmp(earliest, cur_time, >)) { 7591cecc5deSJohnathan Mantey /* recalculate the timeout period, time period has 7601cecc5deSJohnathan Mantey * not elapsed */ 7611cecc5deSJohnathan Mantey timersub(earliest, cur_time, &interval); 7621cecc5deSJohnathan Mantey return ((interval.tv_sec * 1000) + 7631cecc5deSJohnathan Mantey (interval.tv_usec / 1000)); 7640b7b0477SAndrew Jeffery } /* return from poll immediately */ 7651cecc5deSJohnathan Mantey return 0; 7660b7b0477SAndrew Jeffery 7670b7b0477SAndrew Jeffery } /* poll indefinitely */ 7681cecc5deSJohnathan Mantey return -1; 7691cecc5deSJohnathan Mantey } 7701cecc5deSJohnathan Mantey 7711cecc5deSJohnathan Mantey static int call_pollers(struct console *console, struct timeval *cur_time) 772329a35f5SJeremy Kerr { 773329a35f5SJeremy Kerr struct poller *poller; 774329a35f5SJeremy Kerr struct pollfd *pollfd; 775329a35f5SJeremy Kerr enum poller_ret prc; 776b70f8713SAndrew Jeffery int i; 777b70f8713SAndrew Jeffery int rc; 778d831f960SJeremy Kerr 7791a0e03b4SJeremy Kerr rc = 0; 7801a0e03b4SJeremy Kerr 781329a35f5SJeremy Kerr /* 782329a35f5SJeremy Kerr * Process poll events by iterating through the pollers and pollfds 783329a35f5SJeremy Kerr * in-step, calling any pollers that we've found revents for. 784329a35f5SJeremy Kerr */ 785329a35f5SJeremy Kerr for (i = 0; i < console->n_pollers; i++) { 786329a35f5SJeremy Kerr poller = console->pollers[i]; 787329a35f5SJeremy Kerr pollfd = &console->pollfds[i]; 7881cecc5deSJohnathan Mantey prc = POLLER_OK; 7891a0e03b4SJeremy Kerr 7901cecc5deSJohnathan Mantey /* process pending events... */ 7911cecc5deSJohnathan Mantey if (pollfd->revents) { 7921cecc5deSJohnathan Mantey prc = poller->event_fn(poller->handler, pollfd->revents, 793329a35f5SJeremy Kerr poller->data); 7942834c5b1SAndrew Jeffery if (prc == POLLER_EXIT) { 795329a35f5SJeremy Kerr rc = -1; 7962834c5b1SAndrew Jeffery } else if (prc == POLLER_REMOVE) { 797329a35f5SJeremy Kerr poller->remove = true; 798329a35f5SJeremy Kerr } 7992834c5b1SAndrew Jeffery } 800329a35f5SJeremy Kerr 8011cecc5deSJohnathan Mantey if ((prc == POLLER_OK) && poller->timeout_fn && 8021cecc5deSJohnathan Mantey timerisset(&poller->timeout) && 8031cecc5deSJohnathan Mantey timercmp(&poller->timeout, cur_time, <=)) { 8041cecc5deSJohnathan Mantey /* One of the ringbuffer consumers is buffering the 8051cecc5deSJohnathan Mantey data stream. The amount of idle time the consumer 8061cecc5deSJohnathan Mantey desired has expired. Process the buffered data for 8071cecc5deSJohnathan Mantey transmission. */ 8081cecc5deSJohnathan Mantey timerclear(&poller->timeout); 8091cecc5deSJohnathan Mantey prc = poller->timeout_fn(poller->handler, poller->data); 8101cecc5deSJohnathan Mantey if (prc == POLLER_EXIT) { 8111cecc5deSJohnathan Mantey rc = -1; 8121cecc5deSJohnathan Mantey } else if (prc == POLLER_REMOVE) { 8131cecc5deSJohnathan Mantey poller->remove = true; 8141cecc5deSJohnathan Mantey } 8151cecc5deSJohnathan Mantey } 8161cecc5deSJohnathan Mantey } 8171cecc5deSJohnathan Mantey 818329a35f5SJeremy Kerr /** 819329a35f5SJeremy Kerr * Process deferred removals; restarting each time we unregister, as 820329a35f5SJeremy Kerr * the array will have changed 821329a35f5SJeremy Kerr */ 822329a35f5SJeremy Kerr for (;;) { 823329a35f5SJeremy Kerr bool removed = false; 824329a35f5SJeremy Kerr 825329a35f5SJeremy Kerr for (i = 0; i < console->n_pollers; i++) { 826329a35f5SJeremy Kerr poller = console->pollers[i]; 827329a35f5SJeremy Kerr if (poller->remove) { 82855c9712dSJeremy Kerr console_poller_unregister(console, poller); 829329a35f5SJeremy Kerr removed = true; 830329a35f5SJeremy Kerr break; 831329a35f5SJeremy Kerr } 832329a35f5SJeremy Kerr } 8332834c5b1SAndrew Jeffery if (!removed) { 834329a35f5SJeremy Kerr break; 8351a0e03b4SJeremy Kerr } 8362834c5b1SAndrew Jeffery } 8371a0e03b4SJeremy Kerr 8381a0e03b4SJeremy Kerr return rc; 8391a0e03b4SJeremy Kerr } 8401a0e03b4SJeremy Kerr 841769cee1aSJeremy Kerr static void sighandler(int signal) 842769cee1aSJeremy Kerr { 8432834c5b1SAndrew Jeffery if (signal == SIGINT) { 844769cee1aSJeremy Kerr sigint = true; 845769cee1aSJeremy Kerr } 8462834c5b1SAndrew Jeffery } 847769cee1aSJeremy Kerr 8486925740dSAlexander Hansen static int run_console_iteration(struct console *console) 8491a0e03b4SJeremy Kerr { 8506925740dSAlexander Hansen uint8_t buf[4096]; 8511cecc5deSJohnathan Mantey struct timeval tv; 8525c359cc6SAndrew Jeffery long timeout; 8535c359cc6SAndrew Jeffery ssize_t rc; 854769cee1aSJeremy Kerr 8557f2bfb9bSMedicine Yeh if (console->rb->size < sizeof(buf)) { 8566925740dSAlexander Hansen fprintf(stderr, "Ringbuffer size should be greater than %zuB\n", 8577f2bfb9bSMedicine Yeh sizeof(buf)); 8586925740dSAlexander Hansen return -1; 8597f2bfb9bSMedicine Yeh } 8601764145dSJeremy Kerr 861769cee1aSJeremy Kerr if (sigint) { 862769cee1aSJeremy Kerr fprintf(stderr, "Received interrupt, exiting\n"); 8636925740dSAlexander Hansen return -1; 864769cee1aSJeremy Kerr } 865769cee1aSJeremy Kerr 8661cecc5deSJohnathan Mantey rc = get_current_time(&tv); 8671cecc5deSJohnathan Mantey if (rc) { 8681cecc5deSJohnathan Mantey warn("Failed to read current time"); 8696925740dSAlexander Hansen return -1; 8701cecc5deSJohnathan Mantey } 8711cecc5deSJohnathan Mantey 8721cecc5deSJohnathan Mantey timeout = get_poll_timeout(console, &tv); 8731cecc5deSJohnathan Mantey 8746925740dSAlexander Hansen rc = poll(console->pollfds, console->n_pollers + MAX_INTERNAL_POLLFD, 8755c359cc6SAndrew Jeffery (int)timeout); 8761cecc5deSJohnathan Mantey 877d831f960SJeremy Kerr if (rc < 0) { 878769cee1aSJeremy Kerr if (errno == EINTR) { 8796925740dSAlexander Hansen return 0; 8800b7b0477SAndrew Jeffery } 881d831f960SJeremy Kerr warn("poll error"); 8826925740dSAlexander Hansen return -1; 883769cee1aSJeremy Kerr } 884d831f960SJeremy Kerr 885329a35f5SJeremy Kerr /* process internal fd first */ 886329a35f5SJeremy Kerr if (console->pollfds[console->n_pollers].revents) { 88730ea6385SAndrew Jeffery rc = read(console->tty.fd, buf, sizeof(buf)); 888d831f960SJeremy Kerr if (rc <= 0) { 889d831f960SJeremy Kerr warn("Error reading from tty device"); 8906925740dSAlexander Hansen return -1; 891d831f960SJeremy Kerr } 892f733c85aSJeremy Kerr rc = ringbuffer_queue(console->rb, buf, rc); 8932834c5b1SAndrew Jeffery if (rc) { 8946925740dSAlexander Hansen return -1; 895d831f960SJeremy Kerr } 8962834c5b1SAndrew Jeffery } 897d831f960SJeremy Kerr 898f9c8f6caSCheng C Yang if (console->pollfds[console->n_pollers + 1].revents) { 899f9c8f6caSCheng C Yang sd_bus_process(console->bus, NULL); 900f9c8f6caSCheng C Yang } 901f9c8f6caSCheng C Yang 902329a35f5SJeremy Kerr /* ... and then the pollers */ 9031cecc5deSJohnathan Mantey rc = call_pollers(console, &tv); 9042834c5b1SAndrew Jeffery if (rc) { 9056925740dSAlexander Hansen return -1; 9066925740dSAlexander Hansen } 9076925740dSAlexander Hansen return 0; 9086925740dSAlexander Hansen } 9096925740dSAlexander Hansen 9106925740dSAlexander Hansen int run_console(struct console *console) 9116925740dSAlexander Hansen { 9126925740dSAlexander Hansen sighandler_t sighandler_save = signal(SIGINT, sighandler); 9136925740dSAlexander Hansen ssize_t rc = 0; 9146925740dSAlexander Hansen 9156925740dSAlexander Hansen for (;;) { 9166925740dSAlexander Hansen rc = run_console_iteration(console); 9176925740dSAlexander Hansen if (rc) { 918769cee1aSJeremy Kerr break; 9191a0e03b4SJeremy Kerr } 9202834c5b1SAndrew Jeffery } 921769cee1aSJeremy Kerr 922769cee1aSJeremy Kerr signal(SIGINT, sighandler_save); 923f9c8f6caSCheng C Yang sd_bus_unref(console->bus); 924769cee1aSJeremy Kerr 925769cee1aSJeremy Kerr return rc ? -1 : 0; 9261a0e03b4SJeremy Kerr } 927d831f960SJeremy Kerr static const struct option options[] = { 928d66195c1SJeremy Kerr { "config", required_argument, 0, 'c' }, 929954be0fbSAndrew Jeffery { "console-id", required_argument, 0, 'i' }, 930f5858b5bSJoel Stanley { 0, 0, 0, 0 }, 931d831f960SJeremy Kerr }; 932d831f960SJeremy Kerr 933d831f960SJeremy Kerr int main(int argc, char **argv) 934d831f960SJeremy Kerr { 9357f2bfb9bSMedicine Yeh size_t buffer_size = default_buffer_size; 936d66195c1SJeremy Kerr const char *config_filename = NULL; 9376221ce94SVishwanatha Subbanna const char *config_tty_kname = NULL; 9387f2bfb9bSMedicine Yeh const char *buffer_size_str = NULL; 939954be0fbSAndrew Jeffery const char *console_id = NULL; 9401a0e03b4SJeremy Kerr struct console *console; 941d66195c1SJeremy Kerr struct config *config; 942d66195c1SJeremy Kerr int rc; 943d831f960SJeremy Kerr 944d831f960SJeremy Kerr for (;;) { 945b70f8713SAndrew Jeffery int c; 946b70f8713SAndrew Jeffery int idx; 947d831f960SJeremy Kerr 948954be0fbSAndrew Jeffery c = getopt_long(argc, argv, "c:i:", options, &idx); 9492834c5b1SAndrew Jeffery if (c == -1) { 950d831f960SJeremy Kerr break; 9512834c5b1SAndrew Jeffery } 952d831f960SJeremy Kerr 953d831f960SJeremy Kerr switch (c) { 954d66195c1SJeremy Kerr case 'c': 955d66195c1SJeremy Kerr config_filename = optarg; 956d831f960SJeremy Kerr break; 957954be0fbSAndrew Jeffery case 'i': 958954be0fbSAndrew Jeffery console_id = optarg; 959954be0fbSAndrew Jeffery break; 960d831f960SJeremy Kerr case 'h': 961d831f960SJeremy Kerr case '?': 962d831f960SJeremy Kerr usage(argv[0]); 963d66195c1SJeremy Kerr return EXIT_SUCCESS; 964d831f960SJeremy Kerr } 965d831f960SJeremy Kerr } 966d831f960SJeremy Kerr 9672834c5b1SAndrew Jeffery if (optind < argc) { 9686221ce94SVishwanatha Subbanna config_tty_kname = argv[optind]; 9692834c5b1SAndrew Jeffery } 9706221ce94SVishwanatha Subbanna 9717f2bfb9bSMedicine Yeh config = config_init(config_filename); 9727f2bfb9bSMedicine Yeh 973d66195c1SJeremy Kerr console = malloc(sizeof(struct console)); 974d66195c1SJeremy Kerr memset(console, 0, sizeof(*console)); 975a72711afSAndrew Jeffery console->pollfds = 976a72711afSAndrew Jeffery calloc(MAX_INTERNAL_POLLFD, sizeof(*console->pollfds)); 9777f2bfb9bSMedicine Yeh buffer_size_str = config_get_value(config, "ringbuffer-size"); 9787f2bfb9bSMedicine Yeh if (buffer_size_str) { 9797f2bfb9bSMedicine Yeh rc = config_parse_bytesize(buffer_size_str, &buffer_size); 9807f2bfb9bSMedicine Yeh if (rc) { 9817f2bfb9bSMedicine Yeh warn("Invalid ringbuffer-size. Default to %zukB", 9827f2bfb9bSMedicine Yeh buffer_size >> 10); 9837f2bfb9bSMedicine Yeh } 9847f2bfb9bSMedicine Yeh } 985f733c85aSJeremy Kerr console->rb = ringbuffer_init(buffer_size); 9863f8d5bebSAndrew Jeffery if (!console->rb) { 9873f8d5bebSAndrew Jeffery rc = -1; 9883f8d5bebSAndrew Jeffery goto out_config_fini; 9893f8d5bebSAndrew Jeffery } 990329a35f5SJeremy Kerr 991954be0fbSAndrew Jeffery if (set_socket_info(console, config, console_id)) { 99229c59c44SAndrew Jeffery rc = -1; 9933f8d5bebSAndrew Jeffery goto out_ringbuffer_fini; 994b14ca19cSNinad Palsule } 995b14ca19cSNinad Palsule 9967dc08baaSZev Weiss uart_routing_init(config); 9977dc08baaSZev Weiss 998d769eecfSAndrew Jeffery rc = tty_init(console, config, config_tty_kname); 9992834c5b1SAndrew Jeffery if (rc) { 10003f8d5bebSAndrew Jeffery goto out_ringbuffer_fini; 10012834c5b1SAndrew Jeffery } 1002d831f960SJeremy Kerr 1003f9c8f6caSCheng C Yang dbus_init(console, config); 1004f9c8f6caSCheng C Yang 1005d47963e5SJeremy Kerr handlers_init(console, config); 1006d831f960SJeremy Kerr 10071a0e03b4SJeremy Kerr rc = run_console(console); 1008d831f960SJeremy Kerr 10091a0e03b4SJeremy Kerr handlers_fini(console); 1010d831f960SJeremy Kerr 101130ea6385SAndrew Jeffery tty_fini(console); 101230ea6385SAndrew Jeffery 10133f8d5bebSAndrew Jeffery out_ringbuffer_fini: 10143f8d5bebSAndrew Jeffery ringbuffer_fini(console->rb); 10153f8d5bebSAndrew Jeffery 1016d66195c1SJeremy Kerr out_config_fini: 1017d66195c1SJeremy Kerr config_fini(config); 1018d66195c1SJeremy Kerr 101989ea8198SJeremy Kerr free(console->pollers); 102089ea8198SJeremy Kerr free(console->pollfds); 10211a0e03b4SJeremy Kerr free(console); 1022d831f960SJeremy Kerr 1023d831f960SJeremy Kerr return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE; 1024d831f960SJeremy Kerr } 1025