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