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
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);
42
43/*
44 * Detect possible console(s) to use.  If preferred console(s) have been
45 * specified, mark them as active. Else, mark the first probed console
46 * as active.  Also create the console variable.
47 */
48void
49cons_probe(void)
50{
51    int			cons;
52    int			active;
53    char		*prefconsole;
54
55    /* Do all console probes */
56    for (cons = 0; consoles[cons] != NULL; cons++) {
57	consoles[cons]->c_flags = 0;
58 	consoles[cons]->c_probe(consoles[cons]);
59    }
60    /* Now find the first working one */
61    active = -1;
62    for (cons = 0; consoles[cons] != NULL && active == -1; cons++) {
63	consoles[cons]->c_flags = 0;
64 	consoles[cons]->c_probe(consoles[cons]);
65	if (consoles[cons]->c_flags == (C_PRESENTIN | C_PRESENTOUT))
66	    active = cons;
67    }
68    /* Force a console even if all probes failed */
69    if (active == -1)
70	active = 0;
71
72    /* Check to see if a console preference has already been registered */
73    prefconsole = getenv("console");
74    if (prefconsole != NULL)
75	prefconsole = strdup(prefconsole);
76    if (prefconsole != NULL) {
77	unsetenv("console");		/* we want to replace this */
78	cons_change(prefconsole);
79    } else {
80	consoles[active]->c_flags |= C_ACTIVEIN | C_ACTIVEOUT;
81	consoles[active]->c_init(0);
82	prefconsole = strdup(consoles[active]->c_name);
83    }
84
85    printf("Consoles: ");
86    for (cons = 0; consoles[cons] != NULL; cons++)
87	if (consoles[cons]->c_flags & (C_ACTIVEIN | C_ACTIVEOUT))
88	    printf("%s  ", consoles[cons]->c_desc);
89    printf("\n");
90
91    if (prefconsole != NULL) {
92	env_setenv("console", EV_VOLATILE, prefconsole, cons_set,
93	    env_nounset);
94	free(prefconsole);
95    }
96}
97
98int
99getchar(void)
100{
101    int		cons;
102    int		rv;
103
104    /* Loop forever polling all active consoles */
105    for(;;)
106	for (cons = 0; consoles[cons] != NULL; cons++)
107	    if ((consoles[cons]->c_flags & (C_PRESENTIN | C_ACTIVEIN)) ==
108		(C_PRESENTIN | C_ACTIVEIN) &&
109		((rv = consoles[cons]->c_in()) != -1))
110		return(rv);
111}
112
113int
114ischar(void)
115{
116    int		cons;
117
118    for (cons = 0; consoles[cons] != NULL; cons++)
119	if ((consoles[cons]->c_flags & (C_PRESENTIN | C_ACTIVEIN)) ==
120	    (C_PRESENTIN | C_ACTIVEIN) &&
121	    (consoles[cons]->c_ready() != 0))
122		return(1);
123    return(0);
124}
125
126void
127putchar(int c)
128{
129    int		cons;
130
131    /* Expand newlines */
132    if (c == '\n')
133	putchar('\r');
134
135    for (cons = 0; consoles[cons] != NULL; cons++)
136	if ((consoles[cons]->c_flags & (C_PRESENTOUT | C_ACTIVEOUT)) ==
137	    (C_PRESENTOUT | C_ACTIVEOUT))
138	    consoles[cons]->c_out(c);
139}
140
141/*
142 * Find the console with the specified name.
143 */
144static int
145cons_find(const char *name)
146{
147    int		cons;
148
149    for (cons = 0; consoles[cons] != NULL; cons++)
150	if (!strcmp(consoles[cons]->c_name, name))
151	    return (cons);
152    return (-1);
153}
154
155/*
156 * Select one or more consoles.
157 */
158static int
159cons_set(struct env_var *ev, int flags, const void *value)
160{
161    int		cons;
162
163    if ((value == NULL) || (cons_check(value) == -1)) {
164	if (value != NULL)
165	    printf("no such console!\n");
166	printf("Available consoles:\n");
167	for (cons = 0; consoles[cons] != NULL; cons++)
168	    printf("    %s\n", consoles[cons]->c_name);
169	return(CMD_ERROR);
170    }
171
172    cons_change(value);
173
174    env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
175    return(CMD_OK);
176}
177
178/*
179 * Check that all of the consoles listed in *string are valid consoles
180 */
181static int
182cons_check(const char *string)
183{
184    int		cons;
185    char	*curpos, *dup, *next;
186
187    dup = next = strdup(string);
188    cons = -1;
189    while (next != NULL) {
190	curpos = strsep(&next, " ,");
191	if (*curpos != '\0') {
192	    cons = cons_find(curpos);
193	    if (cons == -1)
194		break;
195	}
196    }
197
198    free(dup);
199    return (cons);
200}
201
202/*
203 * Activate all of the consoles listed in *string and disable all the others.
204 */
205static void
206cons_change(const char *string)
207{
208    int		cons;
209    char	*curpos, *dup, *next;
210
211    /* Disable all consoles */
212    for (cons = 0; consoles[cons] != NULL; cons++) {
213	consoles[cons]->c_flags &= ~(C_ACTIVEIN | C_ACTIVEOUT);
214    }
215
216    /* Enable selected consoles */
217    dup = next = strdup(string);
218    while (next != NULL) {
219	curpos = strsep(&next, " ,");
220	if (*curpos == '\0')
221		continue;
222	cons = cons_find(curpos);
223	if (cons >= 0) {
224	    consoles[cons]->c_flags |= C_ACTIVEIN | C_ACTIVEOUT;
225	    consoles[cons]->c_init(0);
226	    if ((consoles[cons]->c_flags & (C_PRESENTIN | C_PRESENTOUT)) !=
227		(C_PRESENTIN | C_PRESENTOUT))
228		printf("console %s failed to initialize\n",
229		    consoles[cons]->c_name);
230	}
231    }
232
233    free(dup);
234}
235