138465Smsmith/*- 238465Smsmith * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> 338465Smsmith * All rights reserved. 438465Smsmith * 538465Smsmith * Redistribution and use in source and binary forms, with or without 638465Smsmith * modification, are permitted provided that the following conditions 738465Smsmith * are met: 838465Smsmith * 1. Redistributions of source code must retain the above copyright 938465Smsmith * notice, this list of conditions and the following disclaimer. 1038465Smsmith * 2. Redistributions in binary form must reproduce the above copyright 1138465Smsmith * notice, this list of conditions and the following disclaimer in the 1238465Smsmith * documentation and/or other materials provided with the distribution. 1338465Smsmith * 1438465Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1538465Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1638465Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1738465Smsmith * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1838465Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1938465Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2038465Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2138465Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2238465Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2338465Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2438465Smsmith * SUCH DAMAGE. 2538465Smsmith */ 2638465Smsmith 27119483Sobrien#include <sys/cdefs.h> 28119483Sobrien__FBSDID("$FreeBSD$"); 29119483Sobrien 3038465Smsmith#include <stand.h> 3138465Smsmith#include <string.h> 3238465Smsmith 3338465Smsmith#include "bootstrap.h" 3438465Smsmith/* 3538465Smsmith * Core console support 3638465Smsmith */ 3738465Smsmith 38146698Sjhbstatic int cons_set(struct env_var *ev, int flags, const void *value); 39146698Sjhbstatic int cons_find(const char *name); 40146698Sjhbstatic int cons_check(const char *string); 41294986Ssmhstatic int cons_change(const char *string); 42278602Sianstatic int twiddle_set(struct env_var *ev, int flags, const void *value); 4338465Smsmith 4438465Smsmith/* 45146698Sjhb * Detect possible console(s) to use. If preferred console(s) have been 46146698Sjhb * specified, mark them as active. Else, mark the first probed console 47146698Sjhb * as active. Also create the console variable. 4838465Smsmith */ 4938465Smsmithvoid 50294986Ssmhcons_probe(void) 5138465Smsmith{ 5238465Smsmith int cons; 5338465Smsmith int active; 5439660Smsmith char *prefconsole; 55294986Ssmh 56278602Sian /* We want a callback to install the new value when this var changes. */ 57278602Sian env_setenv("twiddle_divisor", EV_VOLATILE, "1", twiddle_set, env_nounset); 58278602Sian 5939660Smsmith /* Do all console probes */ 6040214Speter for (cons = 0; consoles[cons] != NULL; cons++) { 6138465Smsmith consoles[cons]->c_flags = 0; 6238465Smsmith consoles[cons]->c_probe(consoles[cons]); 6338465Smsmith } 6440214Speter /* Now find the first working one */ 6540214Speter active = -1; 6640214Speter for (cons = 0; consoles[cons] != NULL && active == -1; cons++) { 6740214Speter consoles[cons]->c_flags = 0; 6840214Speter consoles[cons]->c_probe(consoles[cons]); 6940214Speter if (consoles[cons]->c_flags == (C_PRESENTIN | C_PRESENTOUT)) 7040214Speter active = cons; 7140214Speter } 72146698Sjhb /* Force a console even if all probes failed */ 73146698Sjhb if (active == -1) 74146698Sjhb active = 0; 7539660Smsmith 7639660Smsmith /* Check to see if a console preference has already been registered */ 7740214Speter prefconsole = getenv("console"); 7840214Speter if (prefconsole != NULL) 7940214Speter prefconsole = strdup(prefconsole); 8039660Smsmith if (prefconsole != NULL) { 8139660Smsmith unsetenv("console"); /* we want to replace this */ 82146698Sjhb cons_change(prefconsole); 83146698Sjhb } else { 84146698Sjhb consoles[active]->c_flags |= C_ACTIVEIN | C_ACTIVEOUT; 85146698Sjhb consoles[active]->c_init(0); 86146698Sjhb prefconsole = strdup(consoles[active]->c_name); 87146698Sjhb } 88146698Sjhb 89146698Sjhb printf("Consoles: "); 90146698Sjhb for (cons = 0; consoles[cons] != NULL; cons++) 91146698Sjhb if (consoles[cons]->c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) 92146698Sjhb printf("%s ", consoles[cons]->c_desc); 93146698Sjhb printf("\n"); 94146698Sjhb 95146698Sjhb if (prefconsole != NULL) { 96146698Sjhb env_setenv("console", EV_VOLATILE, prefconsole, cons_set, 97146698Sjhb env_nounset); 9839660Smsmith free(prefconsole); 9939660Smsmith } 10038465Smsmith} 10138465Smsmith 10238465Smsmithint 10338465Smsmithgetchar(void) 10438465Smsmith{ 10538465Smsmith int cons; 10638465Smsmith int rv; 107241299Savg 10838465Smsmith /* Loop forever polling all active consoles */ 10938465Smsmith for(;;) 11038465Smsmith for (cons = 0; consoles[cons] != NULL; cons++) 111241299Savg if ((consoles[cons]->c_flags & (C_PRESENTIN | C_ACTIVEIN)) == 112241299Savg (C_PRESENTIN | C_ACTIVEIN) && 11338465Smsmith ((rv = consoles[cons]->c_in()) != -1)) 11438465Smsmith return(rv); 11538465Smsmith} 11638465Smsmith 11738465Smsmithint 11838465Smsmithischar(void) 11938465Smsmith{ 12038465Smsmith int cons; 12138465Smsmith 12238465Smsmith for (cons = 0; consoles[cons] != NULL; cons++) 123241299Savg if ((consoles[cons]->c_flags & (C_PRESENTIN | C_ACTIVEIN)) == 124241299Savg (C_PRESENTIN | C_ACTIVEIN) && 12538465Smsmith (consoles[cons]->c_ready() != 0)) 12638465Smsmith return(1); 12738465Smsmith return(0); 12838465Smsmith} 12938465Smsmith 13038465Smsmithvoid 13138465Smsmithputchar(int c) 13238465Smsmith{ 13338465Smsmith int cons; 134241299Savg 13538465Smsmith /* Expand newlines */ 13638465Smsmith if (c == '\n') 13738465Smsmith putchar('\r'); 138241299Savg 13938465Smsmith for (cons = 0; consoles[cons] != NULL; cons++) 140241299Savg if ((consoles[cons]->c_flags & (C_PRESENTOUT | C_ACTIVEOUT)) == 141241299Savg (C_PRESENTOUT | C_ACTIVEOUT)) 14238465Smsmith consoles[cons]->c_out(c); 14338465Smsmith} 14438465Smsmith 145146698Sjhb/* 146146698Sjhb * Find the console with the specified name. 147146698Sjhb */ 14838465Smsmithstatic int 149146698Sjhbcons_find(const char *name) 15038465Smsmith{ 15138465Smsmith int cons; 152146698Sjhb 15338465Smsmith for (cons = 0; consoles[cons] != NULL; cons++) 15438465Smsmith if (!strcmp(consoles[cons]->c_name, name)) 155146698Sjhb return (cons); 156146698Sjhb return (-1); 15738465Smsmith} 15838465Smsmith 15938465Smsmith/* 160146698Sjhb * Select one or more consoles. 16138465Smsmith */ 16238465Smsmithstatic int 163146698Sjhbcons_set(struct env_var *ev, int flags, const void *value) 16438465Smsmith{ 165294986Ssmh int ret; 16638465Smsmith 167294986Ssmh if ((value == NULL) || (cons_check(value) == 0)) { 168294986Ssmh /* 169294986Ssmh * Return CMD_OK instead of CMD_ERROR to prevent forth syntax error, 170294986Ssmh * which would prevent it processing any further loader.conf entries. 171294986Ssmh */ 172294986Ssmh return (CMD_OK); 17338465Smsmith } 17438465Smsmith 175294986Ssmh ret = cons_change(value); 176294986Ssmh if (ret != CMD_OK) 177294986Ssmh return (ret); 17840214Speter 17938465Smsmith env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 180294986Ssmh return (CMD_OK); 18138465Smsmith} 182146698Sjhb 183146698Sjhb/* 184294986Ssmh * Check that at least one the consoles listed in *string is valid 185146698Sjhb */ 186146698Sjhbstatic int 187146698Sjhbcons_check(const char *string) 188146698Sjhb{ 189294986Ssmh int cons, found, failed; 190146698Sjhb char *curpos, *dup, *next; 191146698Sjhb 192146698Sjhb dup = next = strdup(string); 193294986Ssmh found = failed = 0; 194146698Sjhb while (next != NULL) { 195146698Sjhb curpos = strsep(&next, " ,"); 196146698Sjhb if (*curpos != '\0') { 197146698Sjhb cons = cons_find(curpos); 198294986Ssmh if (cons == -1) { 199294986Ssmh printf("console %s is invalid!\n", curpos); 200294986Ssmh failed++; 201294986Ssmh } else { 202294986Ssmh found++; 203294986Ssmh } 204146698Sjhb } 205146698Sjhb } 206146698Sjhb 207146698Sjhb free(dup); 208294986Ssmh 209294986Ssmh if (found == 0) 210294986Ssmh printf("no valid consoles!\n"); 211294986Ssmh 212294986Ssmh if (found == 0 || failed != 0) { 213294986Ssmh printf("Available consoles:\n"); 214294986Ssmh for (cons = 0; consoles[cons] != NULL; cons++) 215294986Ssmh printf(" %s\n", consoles[cons]->c_name); 216294986Ssmh } 217294986Ssmh 218294986Ssmh return (found); 219146698Sjhb} 220146698Sjhb 221146698Sjhb/* 222294986Ssmh * Activate all the valid consoles listed in *string and disable all others. 223146698Sjhb */ 224294986Ssmhstatic int 225146698Sjhbcons_change(const char *string) 226146698Sjhb{ 227294986Ssmh int cons, active; 228146698Sjhb char *curpos, *dup, *next; 229146698Sjhb 230146698Sjhb /* Disable all consoles */ 231146698Sjhb for (cons = 0; consoles[cons] != NULL; cons++) { 232146698Sjhb consoles[cons]->c_flags &= ~(C_ACTIVEIN | C_ACTIVEOUT); 233146698Sjhb } 234146698Sjhb 235146698Sjhb /* Enable selected consoles */ 236146698Sjhb dup = next = strdup(string); 237294986Ssmh active = 0; 238146698Sjhb while (next != NULL) { 239146698Sjhb curpos = strsep(&next, " ,"); 240146698Sjhb if (*curpos == '\0') 241146698Sjhb continue; 242146698Sjhb cons = cons_find(curpos); 243148516Sbrian if (cons >= 0) { 244146698Sjhb consoles[cons]->c_flags |= C_ACTIVEIN | C_ACTIVEOUT; 245146698Sjhb consoles[cons]->c_init(0); 246294986Ssmh if ((consoles[cons]->c_flags & (C_PRESENTIN | C_PRESENTOUT)) == 247294986Ssmh (C_PRESENTIN | C_PRESENTOUT)) { 248294986Ssmh active++; 249294986Ssmh continue; 250294986Ssmh } 251294986Ssmh 252294986Ssmh if (active != 0) { 253294986Ssmh /* If no consoles have initialised we wouldn't see this. */ 254294986Ssmh printf("console %s failed to initialize\n", consoles[cons]->c_name); 255294986Ssmh } 256294986Ssmh } 257294986Ssmh } 258294986Ssmh 259294986Ssmh free(dup); 260294986Ssmh 261294986Ssmh if (active == 0) { 262294986Ssmh /* All requested consoles failed to initialise, try to recover. */ 263294986Ssmh for (cons = 0; consoles[cons] != NULL; cons++) { 264294986Ssmh consoles[cons]->c_flags |= C_ACTIVEIN | C_ACTIVEOUT; 265294986Ssmh consoles[cons]->c_init(0); 266294986Ssmh if ((consoles[cons]->c_flags & 267294986Ssmh (C_PRESENTIN | C_PRESENTOUT)) == 268241299Savg (C_PRESENTIN | C_PRESENTOUT)) 269294986Ssmh active++; 270146698Sjhb } 271294986Ssmh 272294986Ssmh if (active == 0) 273294986Ssmh return (CMD_ERROR); /* Recovery failed. */ 274146698Sjhb } 275146698Sjhb 276294986Ssmh return (CMD_OK); 277146698Sjhb} 278278602Sian 279278602Sian/* 280278602Sian * Change the twiddle divisor. 281278602Sian * 282278602Sian * The user can set the twiddle_divisor variable to directly control how fast 283278602Sian * the progress twiddle spins, useful for folks with slow serial consoles. The 284278602Sian * code to monitor changes to the variable and propagate them to the twiddle 285278602Sian * routines has to live somewhere. Twiddling is console-related so it's here. 286278602Sian */ 287278602Sianstatic int 288278602Siantwiddle_set(struct env_var *ev, int flags, const void *value) 289278602Sian{ 290278602Sian u_long tdiv; 291278602Sian char * eptr; 292278602Sian 293278602Sian tdiv = strtoul(value, &eptr, 0); 294278602Sian if (*(const char *)value == 0 || *eptr != 0) { 295278602Sian printf("invalid twiddle_divisor '%s'\n", (const char *)value); 296278602Sian return (CMD_ERROR); 297278602Sian } 298278602Sian twiddle_divisor((u_int)tdiv); 299278602Sian env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 300278602Sian 301278602Sian return(CMD_OK); 302278602Sian} 303