1 #include <assert.h> 2 #include <err.h> 3 #include <errno.h> 4 #include <gpiod.h> 5 #include <limits.h> 6 #include <stddef.h> 7 #include <stdlib.h> 8 9 #include "console-server.h" 10 #include "console-mux.h" 11 #include "config.h" 12 13 struct console_gpio { 14 char *name; 15 struct gpiod_line *line; 16 }; 17 18 struct console_mux { 19 struct console_gpio *mux_gpios; 20 size_t n_mux_gpios; 21 }; 22 23 static const char *key_mux_gpios = "mux-gpios"; 24 static const char *key_mux_index = "mux-index"; 25 26 __attribute__((nonnull)) static size_t strtokcnt(const char *str, 27 const char sep) 28 { 29 ssize_t n = 1; 30 31 while (*str) { 32 if (*str == sep) { 33 n++; 34 } 35 str++; 36 } 37 38 return n; 39 } 40 41 __attribute__((nonnull)) static size_t 42 count_mux_gpios(const char *config_mux_gpios) 43 { 44 return strtokcnt(config_mux_gpios, ','); 45 } 46 47 __attribute__((nonnull)) static char * 48 extract_mux_gpio_name(const char **config_gpio_names) 49 { 50 const char *current; 51 const char *comma; 52 ptrdiff_t length; 53 54 assert(*config_gpio_names); 55 current = *config_gpio_names; 56 comma = strchrnul(current, ','); 57 length = comma - current; 58 59 if (length == 0) { 60 return NULL; 61 } 62 63 char *word = calloc(length + 1, 1); 64 if (!word) { 65 return NULL; 66 } 67 68 strncpy(word, current, length); 69 70 *config_gpio_names = comma + !!(*comma); 71 72 return word; 73 } 74 75 __attribute__((nonnull)) static struct console_gpio * 76 console_mux_find_gpio_by_index(struct console_gpio *gpio, 77 const char **config_gpio_names) 78 { 79 assert(*config_gpio_names); 80 81 gpio->name = extract_mux_gpio_name(config_gpio_names); 82 if (gpio->name == NULL) { 83 warnx("could not extract mux gpio name from config '%s'", 84 *config_gpio_names); 85 return NULL; 86 } 87 88 gpio->line = gpiod_line_find(gpio->name); 89 if (gpio->line == NULL) { 90 warnx("libgpiod: could not find line %s", gpio->name); 91 free(gpio->name); 92 return NULL; 93 } 94 95 return gpio; 96 } 97 98 __attribute__((nonnull)) static void 99 console_mux_release_gpio_lines(struct console_server *server) 100 { 101 for (unsigned long i = 0; i < server->mux->n_mux_gpios; i++) { 102 struct console_gpio *gpio = &server->mux->mux_gpios[i]; 103 gpiod_line_release(gpio->line); 104 gpiod_line_close_chip(gpio->line); 105 106 free(gpio->name); 107 gpio->name = NULL; 108 } 109 } 110 111 __attribute__((nonnull)) static int 112 console_mux_request_gpio_lines(struct console_server *server, 113 const char *config_gpio_names) 114 { 115 const char *current = config_gpio_names; 116 struct console_gpio *gpio; 117 int status = 0; 118 119 for (server->mux->n_mux_gpios = 0; *current; 120 server->mux->n_mux_gpios++) { 121 size_t i = server->mux->n_mux_gpios; 122 gpio = console_mux_find_gpio_by_index( 123 &server->mux->mux_gpios[i], ¤t); 124 if (gpio == NULL) { 125 console_mux_release_gpio_lines(server); 126 return -1; 127 } 128 129 status = gpiod_line_request_output( 130 gpio->line, program_invocation_short_name, 0); 131 if (status != 0) { 132 warnx("could not set line %s as output", gpio->name); 133 warnx("releasing all lines already requested"); 134 console_mux_release_gpio_lines(server); 135 return -1; 136 } 137 } 138 139 return 0; 140 } 141 142 int console_server_mux_init(struct console_server *server) 143 { 144 const char *config_gpio_names; 145 size_t max_ngpios; 146 size_t ngpios; 147 148 config_gpio_names = config_get_value(server->config, key_mux_gpios); 149 if (!config_gpio_names) { 150 return 0; 151 } 152 153 ngpios = count_mux_gpios(config_gpio_names); 154 max_ngpios = sizeof(((struct console *)0)->mux_index) * CHAR_BIT; 155 if (ngpios > max_ngpios) { 156 return -1; 157 } 158 159 server->mux = calloc(1, sizeof(struct console_mux)); 160 if (!server->mux) { 161 return -1; 162 } 163 164 server->mux->n_mux_gpios = 0; 165 server->mux->mux_gpios = calloc(ngpios, sizeof(struct console_gpio)); 166 if (!server->mux->mux_gpios) { 167 return -1; 168 } 169 170 return console_mux_request_gpio_lines(server, config_gpio_names); 171 } 172 173 void console_server_mux_fini(struct console_server *server) 174 { 175 if (!server->mux) { 176 return; 177 } 178 179 console_mux_release_gpio_lines(server); 180 181 free(server->mux->mux_gpios); 182 server->mux->mux_gpios = NULL; 183 184 free(server->mux); 185 server->mux = NULL; 186 } 187 188 int console_mux_init(struct console *console, struct config *config) 189 { 190 if (!console->server->mux) { 191 return 0; 192 } 193 194 if (console->server->mux->n_mux_gpios == 0) { 195 return 0; 196 } 197 198 const char *gpio_value = config_get_section_value( 199 config, console->console_id, key_mux_index); 200 201 if (gpio_value == NULL) { 202 warnx("console %s does not have property %s in config", 203 console->console_id, key_mux_index); 204 return -1; 205 } 206 207 errno = 0; 208 console->mux_index = strtoul(gpio_value, NULL, 0); 209 if (errno == ERANGE) { 210 return -1; 211 } 212 213 return 0; 214 } 215 216 static int console_timestamp(char *buffer, size_t size) 217 { 218 size_t status; 219 time_t rawtime; 220 struct tm *timeinfo; 221 222 time(&rawtime); 223 timeinfo = gmtime(&rawtime); 224 225 status = strftime(buffer, size, "%Y-%m-%d %H:%M:%S UTC", timeinfo); 226 return !status; 227 } 228 229 static int console_print_timestamped(struct console *console, 230 const char *message) 231 { 232 #define TIMESTAMP_MAX_SIZE 32 233 char buf_timestamp[TIMESTAMP_MAX_SIZE]; 234 int status; 235 char *buf; 236 237 status = console_timestamp(buf_timestamp, sizeof(buf_timestamp)); 238 if (status != 0) { 239 warnx("Error: unable to print timestamp"); 240 return status; 241 } 242 243 status = asprintf(&buf, "[obmc-console] %s %s\n", buf_timestamp, 244 message); 245 if (status == -1) { 246 return -1; 247 } 248 249 ringbuffer_queue(console->rb, (uint8_t *)buf, strlen(buf)); 250 251 free(buf); 252 253 return 0; 254 } 255 256 static int console_mux_set_lines(struct console *console) 257 { 258 int status = 0; 259 260 for (size_t i = 0; i < console->server->mux->n_mux_gpios; i++) { 261 struct console_gpio *gpio = &console->server->mux->mux_gpios[i]; 262 const uint8_t value = (console->mux_index >> i) & 0x1; 263 264 status = gpiod_line_set_value(gpio->line, value); 265 if (status != 0) { 266 warnx("could not set line %s", gpio->name); 267 return -1; 268 } 269 } 270 271 return 0; 272 } 273 274 int console_mux_activate(struct console *console) 275 { 276 struct console_server *server = console->server; 277 const bool first_activation = server->active == NULL; 278 const bool is_active = server->active == console; 279 int status = 0; 280 281 if (is_active) { 282 return 0; 283 } 284 285 if (server->mux) { 286 status = console_mux_set_lines(console); 287 } 288 289 if (status != 0) { 290 warnx("Error: unable to set mux gpios"); 291 return status; 292 } 293 294 server->active = console; 295 296 /* Don't print disconnect/connect events on startup */ 297 if (first_activation) { 298 return 0; 299 } 300 301 for (size_t i = 0; i < server->n_consoles; i++) { 302 struct console *other = server->consoles[i]; 303 if (other == console) { 304 continue; 305 } 306 console_print_timestamped(other, "DISCONNECTED"); 307 308 for (long j = 0; j < other->n_handlers; j++) { 309 struct handler *h = other->handlers[j]; 310 311 if (h->type->deselect) { 312 h->type->deselect(h); 313 } 314 } 315 } 316 317 console_print_timestamped(console, "CONNECTED"); 318 319 return 0; 320 } 321