console.c revision 278602
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: stable/10/sys/boot/common/console.c 278602 2015-02-11 22:55:24Z ian $"); 29 30#include <stand.h> 31#include <string.h> 32 33#include "bootstrap.h" 34/* 35 * Core console support 36 */ 37 38static int cons_set(struct env_var *ev, int flags, const void *value); 39static int cons_find(const char *name); 40static int cons_check(const char *string); 41static void cons_change(const char *string); 42static 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 */ 49void 50cons_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, env_nounset); 58 59 /* Do all console probes */ 60 for (cons = 0; consoles[cons] != NULL; cons++) { 61 consoles[cons]->c_flags = 0; 62 consoles[cons]->c_probe(consoles[cons]); 63 } 64 /* Now find the first working one */ 65 active = -1; 66 for (cons = 0; consoles[cons] != NULL && active == -1; cons++) { 67 consoles[cons]->c_flags = 0; 68 consoles[cons]->c_probe(consoles[cons]); 69 if (consoles[cons]->c_flags == (C_PRESENTIN | C_PRESENTOUT)) 70 active = cons; 71 } 72 /* Force a console even if all probes failed */ 73 if (active == -1) 74 active = 0; 75 76 /* Check to see if a console preference has already been registered */ 77 prefconsole = getenv("console"); 78 if (prefconsole != NULL) 79 prefconsole = strdup(prefconsole); 80 if (prefconsole != NULL) { 81 unsetenv("console"); /* we want to replace this */ 82 cons_change(prefconsole); 83 } else { 84 consoles[active]->c_flags |= C_ACTIVEIN | C_ACTIVEOUT; 85 consoles[active]->c_init(0); 86 prefconsole = strdup(consoles[active]->c_name); 87 } 88 89 printf("Consoles: "); 90 for (cons = 0; consoles[cons] != NULL; cons++) 91 if (consoles[cons]->c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) 92 printf("%s ", consoles[cons]->c_desc); 93 printf("\n"); 94 95 if (prefconsole != NULL) { 96 env_setenv("console", EV_VOLATILE, prefconsole, cons_set, 97 env_nounset); 98 free(prefconsole); 99 } 100} 101 102int 103getchar(void) 104{ 105 int cons; 106 int rv; 107 108 /* Loop forever polling all active consoles */ 109 for(;;) 110 for (cons = 0; consoles[cons] != NULL; cons++) 111 if ((consoles[cons]->c_flags & (C_PRESENTIN | C_ACTIVEIN)) == 112 (C_PRESENTIN | C_ACTIVEIN) && 113 ((rv = consoles[cons]->c_in()) != -1)) 114 return(rv); 115} 116 117int 118ischar(void) 119{ 120 int cons; 121 122 for (cons = 0; consoles[cons] != NULL; cons++) 123 if ((consoles[cons]->c_flags & (C_PRESENTIN | C_ACTIVEIN)) == 124 (C_PRESENTIN | C_ACTIVEIN) && 125 (consoles[cons]->c_ready() != 0)) 126 return(1); 127 return(0); 128} 129 130void 131putchar(int c) 132{ 133 int cons; 134 135 /* Expand newlines */ 136 if (c == '\n') 137 putchar('\r'); 138 139 for (cons = 0; consoles[cons] != NULL; cons++) 140 if ((consoles[cons]->c_flags & (C_PRESENTOUT | C_ACTIVEOUT)) == 141 (C_PRESENTOUT | C_ACTIVEOUT)) 142 consoles[cons]->c_out(c); 143} 144 145/* 146 * Find the console with the specified name. 147 */ 148static int 149cons_find(const char *name) 150{ 151 int cons; 152 153 for (cons = 0; consoles[cons] != NULL; cons++) 154 if (!strcmp(consoles[cons]->c_name, name)) 155 return (cons); 156 return (-1); 157} 158 159/* 160 * Select one or more consoles. 161 */ 162static int 163cons_set(struct env_var *ev, int flags, const void *value) 164{ 165 int cons; 166 167 if ((value == NULL) || (cons_check(value) == -1)) { 168 if (value != NULL) 169 printf("no such console!\n"); 170 printf("Available consoles:\n"); 171 for (cons = 0; consoles[cons] != NULL; cons++) 172 printf(" %s\n", consoles[cons]->c_name); 173 return(CMD_ERROR); 174 } 175 176 cons_change(value); 177 178 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 179 return(CMD_OK); 180} 181 182/* 183 * Check that all of the consoles listed in *string are valid consoles 184 */ 185static int 186cons_check(const char *string) 187{ 188 int cons; 189 char *curpos, *dup, *next; 190 191 dup = next = strdup(string); 192 cons = -1; 193 while (next != NULL) { 194 curpos = strsep(&next, " ,"); 195 if (*curpos != '\0') { 196 cons = cons_find(curpos); 197 if (cons == -1) 198 break; 199 } 200 } 201 202 free(dup); 203 return (cons); 204} 205 206/* 207 * Activate all of the consoles listed in *string and disable all the others. 208 */ 209static void 210cons_change(const char *string) 211{ 212 int cons; 213 char *curpos, *dup, *next; 214 215 /* Disable all consoles */ 216 for (cons = 0; consoles[cons] != NULL; cons++) { 217 consoles[cons]->c_flags &= ~(C_ACTIVEIN | C_ACTIVEOUT); 218 } 219 220 /* Enable selected consoles */ 221 dup = next = strdup(string); 222 while (next != NULL) { 223 curpos = strsep(&next, " ,"); 224 if (*curpos == '\0') 225 continue; 226 cons = cons_find(curpos); 227 if (cons >= 0) { 228 consoles[cons]->c_flags |= C_ACTIVEIN | C_ACTIVEOUT; 229 consoles[cons]->c_init(0); 230 if ((consoles[cons]->c_flags & (C_PRESENTIN | C_PRESENTOUT)) != 231 (C_PRESENTIN | C_PRESENTOUT)) 232 printf("console %s failed to initialize\n", 233 consoles[cons]->c_name); 234 } 235 } 236 237 free(dup); 238} 239 240/* 241 * Change the twiddle divisor. 242 * 243 * The user can set the twiddle_divisor variable to directly control how fast 244 * the progress twiddle spins, useful for folks with slow serial consoles. The 245 * code to monitor changes to the variable and propagate them to the twiddle 246 * routines has to live somewhere. Twiddling is console-related so it's here. 247 */ 248static int 249twiddle_set(struct env_var *ev, int flags, const void *value) 250{ 251 u_long tdiv; 252 char * eptr; 253 254 tdiv = strtoul(value, &eptr, 0); 255 if (*(const char *)value == 0 || *eptr != 0) { 256 printf("invalid twiddle_divisor '%s'\n", (const char *)value); 257 return (CMD_ERROR); 258 } 259 twiddle_divisor((u_int)tdiv); 260 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 261 262 return(CMD_OK); 263} 264