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