#include #include #include #include #include #include #include #include "console-server.h" #include "console-mux.h" #include "config.h" struct console_gpio { char *name; struct gpiod_line *line; }; struct console_mux { struct console_gpio *mux_gpios; size_t n_mux_gpios; }; static const char *key_mux_gpios = "mux-gpios"; static const char *key_mux_index = "mux-index"; __attribute__((nonnull)) static size_t strtokcnt(const char *str, const char sep) { ssize_t n = 1; while (*str) { if (*str == sep) { n++; } str++; } return n; } __attribute__((nonnull)) static size_t count_mux_gpios(const char *config_mux_gpios) { return strtokcnt(config_mux_gpios, ','); } __attribute__((nonnull)) static char * extract_mux_gpio_name(const char **config_gpio_names) { const char *current; const char *comma; ptrdiff_t length; assert(*config_gpio_names); current = *config_gpio_names; comma = strchrnul(current, ','); length = comma - current; if (length == 0) { return NULL; } char *word = calloc(length + 1, 1); if (!word) { return NULL; } strncpy(word, current, length); *config_gpio_names = comma + !!(*comma); return word; } __attribute__((nonnull)) static struct console_gpio * console_mux_find_gpio_by_index(struct console_gpio *gpio, const char **config_gpio_names) { assert(*config_gpio_names); gpio->name = extract_mux_gpio_name(config_gpio_names); if (gpio->name == NULL) { warnx("could not extract mux gpio name from config '%s'", *config_gpio_names); return NULL; } gpio->line = gpiod_line_find(gpio->name); if (gpio->line == NULL) { warnx("libgpiod: could not find line %s", gpio->name); free(gpio->name); return NULL; } return gpio; } __attribute__((nonnull)) static void console_mux_release_gpio_lines(struct console_server *server) { for (unsigned long i = 0; i < server->mux->n_mux_gpios; i++) { struct console_gpio *gpio = &server->mux->mux_gpios[i]; gpiod_line_release(gpio->line); gpiod_line_close_chip(gpio->line); free(gpio->name); gpio->name = NULL; } } __attribute__((nonnull)) static int console_mux_request_gpio_lines(struct console_server *server, const char *config_gpio_names) { const char *current = config_gpio_names; struct console_gpio *gpio; int status = 0; for (server->mux->n_mux_gpios = 0; *current; server->mux->n_mux_gpios++) { size_t i = server->mux->n_mux_gpios; gpio = console_mux_find_gpio_by_index( &server->mux->mux_gpios[i], ¤t); if (gpio == NULL) { console_mux_release_gpio_lines(server); return -1; } status = gpiod_line_request_output( gpio->line, program_invocation_short_name, 0); if (status != 0) { warnx("could not set line %s as output", gpio->name); warnx("releasing all lines already requested"); console_mux_release_gpio_lines(server); return -1; } } return 0; } int console_server_mux_init(struct console_server *server) { const char *config_gpio_names; size_t max_ngpios; size_t ngpios; config_gpio_names = config_get_value(server->config, key_mux_gpios); if (!config_gpio_names) { return 0; } ngpios = count_mux_gpios(config_gpio_names); max_ngpios = sizeof(((struct console *)0)->mux_index) * CHAR_BIT; if (ngpios > max_ngpios) { return -1; } server->mux = calloc(1, sizeof(struct console_mux)); if (!server->mux) { return -1; } server->mux->n_mux_gpios = 0; server->mux->mux_gpios = calloc(ngpios, sizeof(struct console_gpio)); if (!server->mux->mux_gpios) { return -1; } return console_mux_request_gpio_lines(server, config_gpio_names); } void console_server_mux_fini(struct console_server *server) { if (!server->mux) { return; } console_mux_release_gpio_lines(server); free(server->mux->mux_gpios); server->mux->mux_gpios = NULL; free(server->mux); server->mux = NULL; } int console_mux_init(struct console *console, struct config *config) { if (!console->server->mux) { return 0; } if (console->server->mux->n_mux_gpios == 0) { return 0; } const char *gpio_value = config_get_section_value( config, console->console_id, key_mux_index); if (gpio_value == NULL) { warnx("console %s does not have property %s in config", console->console_id, key_mux_index); return -1; } errno = 0; console->mux_index = strtoul(gpio_value, NULL, 0); if (errno == ERANGE) { return -1; } return 0; } static int console_timestamp(char *buffer, size_t size) { size_t status; time_t rawtime; struct tm *timeinfo; time(&rawtime); timeinfo = gmtime(&rawtime); status = strftime(buffer, size, "%Y-%m-%d %H:%M:%S UTC", timeinfo); return !status; } static int console_print_timestamped(struct console *console, const char *message) { #define TIMESTAMP_MAX_SIZE 32 char buf_timestamp[TIMESTAMP_MAX_SIZE]; int status; char *buf; status = console_timestamp(buf_timestamp, sizeof(buf_timestamp)); if (status != 0) { warnx("Error: unable to print timestamp"); return status; } status = asprintf(&buf, "[obmc-console] %s %s\n", buf_timestamp, message); if (status == -1) { return -1; } ringbuffer_queue(console->rb, (uint8_t *)buf, strlen(buf)); free(buf); return 0; } static int console_mux_set_lines(struct console *console) { int status = 0; for (size_t i = 0; i < console->server->mux->n_mux_gpios; i++) { struct console_gpio *gpio = &console->server->mux->mux_gpios[i]; const uint8_t value = (console->mux_index >> i) & 0x1; status = gpiod_line_set_value(gpio->line, value); if (status != 0) { warnx("could not set line %s", gpio->name); return -1; } } return 0; } int console_mux_activate(struct console *console) { struct console_server *server = console->server; const bool first_activation = server->active == NULL; const bool is_active = server->active == console; int status = 0; if (is_active) { return 0; } if (server->mux) { status = console_mux_set_lines(console); } if (status != 0) { warnx("Error: unable to set mux gpios"); return status; } server->active = console; /* Don't print disconnect/connect events on startup */ if (first_activation) { return 0; } for (size_t i = 0; i < server->n_consoles; i++) { struct console *other = server->consoles[i]; if (other == console) { continue; } console_print_timestamped(other, "DISCONNECTED"); for (long j = 0; j < other->n_handlers; j++) { struct handler *h = other->handlers[j]; if (h->type->deselect) { h->type->deselect(h); } } } console_print_timestamped(console, "CONNECTED"); return 0; }