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