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