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
module_verbose_set(struct env_var * ev,int flags,const void * value)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
cons_probe(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
getchar(void)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
ischar(void)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
putchar(int c)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
cons_find(const char * name)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
cons_set(struct env_var * ev,int flags,const void * value)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
cons_check(const char * string)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
cons_change(const char * string)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
twiddle_set(struct env_var * ev,int flags,const void * value)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