1 /*- 2 * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 #include <sys/param.h> 29 #include <stand.h> 30 #include <string.h> 31 32 #include "bootstrap.h" 33 /* 34 * Core console support 35 */ 36 37 static int cons_set(struct env_var *ev, int flags, const void *value); 38 static int cons_find(const char *name); 39 static int cons_check(const char *string); 40 static int cons_change(const char *string); 41 static int twiddle_set(struct env_var *ev, int flags, const void *value); 42 43 #ifndef MODULE_VERBOSE 44 # define MODULE_VERBOSE MODULE_VERBOSE_TWIDDLE 45 #endif 46 int module_verbose = MODULE_VERBOSE; 47 48 static int 49 module_verbose_set(struct env_var *ev, int flags, const void *value) 50 { 51 u_long v; 52 char *eptr; 53 54 v = strtoul(value, &eptr, 0); 55 if (*(const char *)value == 0 || *eptr != 0) { 56 printf("invalid module_verbose '%s'\n", (const char *)value); 57 return (CMD_ERROR); 58 } 59 module_verbose = (int)v; 60 if (module_verbose < MODULE_VERBOSE_TWIDDLE) { 61 /* A hack for now; we do not want twiddling */ 62 twiddle_divisor(UINT_MAX); 63 } 64 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 65 66 return (CMD_OK); 67 } 68 69 /* 70 * Detect possible console(s) to use. If preferred console(s) have been 71 * specified, mark them as active. Else, mark the first probed console 72 * as active. Also create the console variable. 73 */ 74 void 75 cons_probe(void) 76 { 77 int cons; 78 int active; 79 char *prefconsole; 80 char module_verbose_buf[8]; 81 82 TSENTER(); 83 84 /* We want a callback to install the new value when these vars change. */ 85 snprintf(module_verbose_buf, sizeof(module_verbose_buf), "%d", 86 module_verbose); 87 env_setenv("module_verbose", EV_VOLATILE, module_verbose_buf, 88 module_verbose_set, env_nounset); 89 env_setenv("twiddle_divisor", EV_VOLATILE, "16", twiddle_set, 90 env_nounset); 91 92 /* Do all console probes */ 93 for (cons = 0; consoles[cons] != NULL; cons++) { 94 consoles[cons]->c_flags = 0; 95 consoles[cons]->c_probe(consoles[cons]); 96 } 97 /* Now find the first working one */ 98 active = -1; 99 for (cons = 0; consoles[cons] != NULL && active == -1; cons++) { 100 consoles[cons]->c_flags = 0; 101 consoles[cons]->c_probe(consoles[cons]); 102 if (consoles[cons]->c_flags == (C_PRESENTIN | C_PRESENTOUT)) 103 active = cons; 104 } 105 /* Force a console even if all probes failed */ 106 if (active == -1) 107 active = 0; 108 109 /* Check to see if a console preference has already been registered */ 110 prefconsole = getenv("console"); 111 if (prefconsole != NULL) 112 prefconsole = strdup(prefconsole); 113 if (prefconsole != NULL) { 114 unsetenv("console"); /* we want to replace this */ 115 cons_change(prefconsole); 116 } else { 117 consoles[active]->c_flags |= C_ACTIVEIN | C_ACTIVEOUT; 118 consoles[active]->c_init(0); 119 prefconsole = strdup(consoles[active]->c_name); 120 } 121 122 printf("Consoles: "); 123 for (cons = 0; consoles[cons] != NULL; cons++) 124 if (consoles[cons]->c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) 125 printf("%s ", consoles[cons]->c_desc); 126 printf("\n"); 127 128 if (prefconsole != NULL) { 129 env_setenv("console", EV_VOLATILE, prefconsole, cons_set, 130 env_nounset); 131 free(prefconsole); 132 } 133 134 TSEXIT(); 135 } 136 137 int 138 getchar(void) 139 { 140 int cons; 141 int rv; 142 143 /* Loop forever polling all active consoles */ 144 for (;;) { 145 for (cons = 0; consoles[cons] != NULL; cons++) { 146 if ((consoles[cons]->c_flags & 147 (C_PRESENTIN | C_ACTIVEIN)) == 148 (C_PRESENTIN | C_ACTIVEIN) && 149 ((rv = consoles[cons]->c_in()) != -1)) 150 return (rv); 151 } 152 } 153 } 154 155 int 156 ischar(void) 157 { 158 int cons; 159 160 for (cons = 0; consoles[cons] != NULL; cons++) 161 if ((consoles[cons]->c_flags & (C_PRESENTIN | C_ACTIVEIN)) == 162 (C_PRESENTIN | C_ACTIVEIN) && 163 (consoles[cons]->c_ready() != 0)) 164 return (1); 165 return (0); 166 } 167 168 void 169 putchar(int c) 170 { 171 int cons; 172 173 /* Expand newlines */ 174 if (c == '\n') 175 putchar('\r'); 176 177 for (cons = 0; consoles[cons] != NULL; cons++) { 178 if ((consoles[cons]->c_flags & (C_PRESENTOUT | C_ACTIVEOUT)) == 179 (C_PRESENTOUT | C_ACTIVEOUT)) 180 consoles[cons]->c_out(c); 181 } 182 } 183 184 /* 185 * Find the console with the specified name. 186 */ 187 static int 188 cons_find(const char *name) 189 { 190 int cons; 191 192 for (cons = 0; consoles[cons] != NULL; cons++) 193 if (strcmp(consoles[cons]->c_name, name) == 0) 194 return (cons); 195 return (-1); 196 } 197 198 /* 199 * Select one or more consoles. 200 */ 201 static int 202 cons_set(struct env_var *ev, int flags, const void *value) 203 { 204 int ret; 205 206 if ((value == NULL) || (cons_check(value) == 0)) { 207 /* 208 * Return CMD_OK instead of CMD_ERROR to prevent forth syntax 209 * error, which would prevent it processing any further 210 * loader.conf entries. 211 */ 212 return (CMD_OK); 213 } 214 215 ret = cons_change(value); 216 if (ret != CMD_OK) 217 return (ret); 218 219 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 220 return (CMD_OK); 221 } 222 223 /* 224 * Check that at least one the consoles listed in *string is valid 225 */ 226 static int 227 cons_check(const char *string) 228 { 229 int cons, found, failed; 230 char *curpos, *dup, *next; 231 232 dup = next = strdup(string); 233 found = failed = 0; 234 while (next != NULL) { 235 curpos = strsep(&next, " ,"); 236 if (*curpos != '\0') { 237 cons = cons_find(curpos); 238 if (cons == -1) { 239 printf("console %s is unavailable\n", curpos); 240 failed++; 241 } else { 242 found++; 243 } 244 } 245 } 246 247 free(dup); 248 249 if (found == 0) 250 printf("no valid consoles!\n"); 251 252 if (found == 0 && failed != 0) { 253 printf("Available consoles:\n"); 254 for (cons = 0; consoles[cons] != NULL; cons++) 255 printf(" %s\n", consoles[cons]->c_name); 256 } 257 258 return (found); 259 } 260 261 /* 262 * Activate all the valid consoles listed in *string and disable all others. 263 */ 264 static int 265 cons_change(const char *string) 266 { 267 int cons, active; 268 char *curpos, *dup, *next; 269 270 /* Disable all consoles */ 271 for (cons = 0; consoles[cons] != NULL; cons++) { 272 consoles[cons]->c_flags &= ~(C_ACTIVEIN | C_ACTIVEOUT); 273 } 274 275 /* Enable selected consoles */ 276 dup = next = strdup(string); 277 active = 0; 278 while (next != NULL) { 279 curpos = strsep(&next, " ,"); 280 if (*curpos == '\0') 281 continue; 282 cons = cons_find(curpos); 283 if (cons >= 0) { 284 consoles[cons]->c_flags |= C_ACTIVEIN | C_ACTIVEOUT; 285 consoles[cons]->c_init(0); 286 if ((consoles[cons]->c_flags & 287 (C_PRESENTIN | C_PRESENTOUT)) == 288 (C_PRESENTIN | C_PRESENTOUT)) { 289 active++; 290 continue; 291 } 292 293 if (active != 0) { 294 /* 295 * If no consoles have initialised we 296 * wouldn't see this. 297 */ 298 printf("console %s failed to initialize\n", 299 consoles[cons]->c_name); 300 } 301 } 302 } 303 304 free(dup); 305 306 if (active == 0) { 307 /* 308 * All requested consoles failed to initialise, 309 * try to recover. 310 */ 311 for (cons = 0; consoles[cons] != NULL; cons++) { 312 consoles[cons]->c_flags |= C_ACTIVEIN | C_ACTIVEOUT; 313 consoles[cons]->c_init(0); 314 if ((consoles[cons]->c_flags & 315 (C_PRESENTIN | C_PRESENTOUT)) == 316 (C_PRESENTIN | C_PRESENTOUT)) 317 active++; 318 } 319 320 if (active == 0) 321 return (CMD_ERROR); /* Recovery failed. */ 322 } 323 324 return (CMD_OK); 325 } 326 327 /* 328 * Change the twiddle divisor. 329 * 330 * The user can set the twiddle_divisor variable to directly control how fast 331 * the progress twiddle spins, useful for folks with slow serial consoles. The 332 * code to monitor changes to the variable and propagate them to the twiddle 333 * routines has to live somewhere. Twiddling is console-related so it's here. 334 */ 335 static int 336 twiddle_set(struct env_var *ev, int flags, const void *value) 337 { 338 u_long tdiv; 339 char *eptr; 340 341 tdiv = strtoul(value, &eptr, 0); 342 if (*(const char *)value == 0 || *eptr != 0) { 343 printf("invalid twiddle_divisor '%s'\n", (const char *)value); 344 return (CMD_ERROR); 345 } 346 twiddle_divisor((u_int)tdiv); 347 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 348 349 return (CMD_OK); 350 } 351