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