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