summaryrefslogtreecommitdiffstats
path: root/src/core/console.c
blob: 1ea3dc13e6952198e224bdb4b5360193cc6c9831 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#include "stddef.h"
#include "console.h"
#include <gpxe/process.h>
#include <gpxe/nap.h>

/** @file */

/**
 * Write a single character to each console device.
 *
 * @v character		Character to be written
 * @ret None		-
 * @err None		-
 *
 * The character is written out to all enabled console devices, using
 * each device's console_driver::putchar() method.
 *
 */
void putchar ( int character ) {
	struct console_driver *console;

	/* Automatic LF -> CR,LF translation */
	if ( character == '\n' )
		putchar ( '\r' );

	for_each_table_entry ( console, CONSOLES ) {
		if ( ( ! console->disabled ) && console->putchar )
			console->putchar ( character );
	}
}

/**
 * Check to see if any input is available on any console.
 *
 * @v None		-
 * @ret console		Console device that has input available, if any.
 * @ret NULL		No console device has input available.
 * @err None		-
 *
 * All enabled console devices are checked once for available input
 * using each device's console_driver::iskey() method.  The first
 * console device that has available input will be returned, if any.
 *
 */
static struct console_driver * has_input ( void ) {
	struct console_driver *console;

	for_each_table_entry ( console, CONSOLES ) {
		if ( ( ! console->disabled ) && console->iskey ) {
			if ( console->iskey () )
				return console;
		}
	}
	return NULL;
}

/**
 * Read a single character from any console.
 *
 * @v None		-
 * @ret character	Character read from a console.
 * @err None		-
 *
 * A character will be read from the first enabled console device that
 * has input available using that console's console_driver::getchar()
 * method.  If no console has input available to be read, this method
 * will block.  To perform a non-blocking read, use something like
 *
 * @code
 *
 *   int key = iskey() ? getchar() : -1;
 *
 * @endcode
 *
 * The character read will not be echoed back to any console.
 *
 */
int getchar ( void ) {
	struct console_driver *console;
	int character;

	while ( 1 ) {
		console = has_input();
		if ( console && console->getchar ) {
			character = console->getchar ();
			break;
		}

		/* Doze for a while (until the next interrupt).  This works
		 * fine, because the keyboard is interrupt-driven, and the
		 * timer interrupt (approx. every 50msec) takes care of the
		 * serial port, which is read by polling.  This reduces the
		 * power dissipation of a modern CPU considerably, and also
		 * makes Etherboot waiting for user interaction waste a lot
		 * less CPU time in a VMware session.
		 */
		cpu_nap();

		/* Keep processing background tasks while we wait for
		 * input.
		 */
		step();
	}

	/* CR -> LF translation */
	if ( character == '\r' )
		character = '\n';

	return character;
}

/** Check for available input on any console.
 *
 * @v None		-
 * @ret True		Input is available on a console
 * @ret False		Input is not available on any console
 * @err None		-
 *
 * All enabled console devices are checked once for available input
 * using each device's console_driver::iskey() method.  If any console
 * device has input available, this call will return True.  If this
 * call returns True, you can then safely call getchar() without
 * blocking.
 *
 */
int iskey ( void ) {
	return has_input() ? 1 : 0;
}