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