Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/stand/common/console.c
34677 views
1
/*-
2
* Copyright (c) 1998 Michael Smith <[email protected]>
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
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
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
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
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
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
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
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
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
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
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
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
374