1 | /* |
---|
2 | * ----------------------------------------------------------------------- |
---|
3 | * |
---|
4 | * Copyright 1994-2008 H. Peter Anvin - All Rights Reserved |
---|
5 | * Copyright 2009-2014 Intel Corporation; author: H. Peter Anvin |
---|
6 | * |
---|
7 | * This program is free software; you can redistribute it and/or modify |
---|
8 | * it under the terms of the GNU General Public License as published by |
---|
9 | * the Free Software Foundation, Inc., 53 Temple Place Ste 330, |
---|
10 | * Boston MA 02111-1307, USA; either version 2 of the License, or |
---|
11 | * (at your option) any later version; incorporated herein by reference. |
---|
12 | * |
---|
13 | * ----------------------------------------------------------------------- |
---|
14 | * |
---|
15 | * |
---|
16 | * conio.c |
---|
17 | * |
---|
18 | * Console I/O code, except: |
---|
19 | * writechr, writestr_early - module-dependent |
---|
20 | * writestr, crlf - writestr.inc |
---|
21 | * writehex* - writehex.inc |
---|
22 | */ |
---|
23 | #include <sys/io.h> |
---|
24 | #include <stddef.h> |
---|
25 | #include <stdio.h> |
---|
26 | #include <string.h> |
---|
27 | #include <fs.h> |
---|
28 | #include <com32.h> |
---|
29 | #include <sys/cpu.h> |
---|
30 | #include <syslinux/firmware.h> |
---|
31 | |
---|
32 | #include "bios.h" |
---|
33 | #include "graphics.h" |
---|
34 | |
---|
35 | union screen _cursor; |
---|
36 | union screen _screensize; |
---|
37 | |
---|
38 | /* |
---|
39 | * Serial console stuff. |
---|
40 | */ |
---|
41 | __export uint16_t SerialPort = 0; /* Serial port base (or 0 for no serial port) */ |
---|
42 | __export uint8_t FlowInput = 0; /* Input bits for serial flow */ |
---|
43 | __export uint16_t BaudDivisor = 115200/9600; /* Baud rate divisor */ |
---|
44 | __export uint8_t FlowIgnore = 0; /* Ignore input unless these bits set */ |
---|
45 | __export uint16_t DisplayCon = 0x01; /* Display console enabled */ |
---|
46 | __export uint8_t FlowOutput = 0; /* Output to assert for serial flow */ |
---|
47 | |
---|
48 | __export uint8_t DisplayMask = 0x07; /* Display modes mask */ |
---|
49 | |
---|
50 | uint8_t ScrollAttribute = 0x07; /* Grey on white (normal text color) */ |
---|
51 | |
---|
52 | /* |
---|
53 | * loadkeys: Load a LILO-style keymap |
---|
54 | * |
---|
55 | * Returns 0 on success, or -1 on error. |
---|
56 | */ |
---|
57 | __export int loadkeys(const char *filename) |
---|
58 | { |
---|
59 | FILE *f; |
---|
60 | |
---|
61 | f = fopen(filename, "r"); |
---|
62 | if (!f) |
---|
63 | return -1; |
---|
64 | |
---|
65 | fread(KbdMap, 1, sizeof(KbdMap), f); |
---|
66 | |
---|
67 | fclose(f); |
---|
68 | return 0; |
---|
69 | } |
---|
70 | |
---|
71 | /* |
---|
72 | * write_serial: If serial output is enabled, write character on |
---|
73 | * serial port. |
---|
74 | */ |
---|
75 | __export void write_serial(char data) |
---|
76 | { |
---|
77 | if (!SerialPort) |
---|
78 | return; |
---|
79 | |
---|
80 | if (!(DisplayMask & 0x04)) |
---|
81 | return; |
---|
82 | |
---|
83 | while (1) { |
---|
84 | char ch; |
---|
85 | |
---|
86 | ch = inb(SerialPort + 5); /* LSR */ |
---|
87 | |
---|
88 | /* Wait for space in transmit register */ |
---|
89 | if (!(ch & 0x20)) |
---|
90 | continue; |
---|
91 | |
---|
92 | /* Wait for input flow control */ |
---|
93 | ch = inb(SerialPort + 6); |
---|
94 | ch &= FlowInput; |
---|
95 | if (ch != FlowInput) |
---|
96 | continue; |
---|
97 | |
---|
98 | break; |
---|
99 | } |
---|
100 | |
---|
101 | outb(data, SerialPort); /* Send data */ |
---|
102 | io_delay(); |
---|
103 | } |
---|
104 | |
---|
105 | void pm_write_serial(com32sys_t *regs) |
---|
106 | { |
---|
107 | write_serial(regs->eax.b[0]); |
---|
108 | } |
---|
109 | |
---|
110 | void serialcfg(uint16_t *iobase, uint16_t *divisor, uint16_t *flowctl) |
---|
111 | { |
---|
112 | uint8_t al, ah; |
---|
113 | |
---|
114 | *iobase = SerialPort; |
---|
115 | *divisor = BaudDivisor; |
---|
116 | |
---|
117 | al = FlowOutput; |
---|
118 | ah = FlowInput; |
---|
119 | |
---|
120 | al |= ah; |
---|
121 | ah = FlowIgnore; |
---|
122 | ah >>= 4; |
---|
123 | |
---|
124 | if (!DisplayCon) |
---|
125 | ah |= 0x80; |
---|
126 | |
---|
127 | *flowctl = al | (ah << 8); |
---|
128 | } |
---|
129 | |
---|
130 | void pm_serialcfg(com32sys_t *regs) |
---|
131 | { |
---|
132 | serialcfg(®s->eax.w[0], ®s->ecx.w[0], ®s->ebx.w[0]); |
---|
133 | } |
---|
134 | |
---|
135 | /* |
---|
136 | * write_serial_str: write_serial for strings |
---|
137 | */ |
---|
138 | __export void write_serial_str(char *data) |
---|
139 | { |
---|
140 | char ch; |
---|
141 | |
---|
142 | while ((ch = *data++)) |
---|
143 | write_serial(ch); |
---|
144 | } |
---|
145 | |
---|
146 | /* |
---|
147 | * pollchar: check if we have an input character pending |
---|
148 | * |
---|
149 | * Returns 1 if character pending. |
---|
150 | */ |
---|
151 | int bios_pollchar(void) |
---|
152 | { |
---|
153 | com32sys_t ireg, oreg; |
---|
154 | uint8_t data = 0; |
---|
155 | |
---|
156 | memset(&ireg, 0, sizeof(ireg)); |
---|
157 | |
---|
158 | ireg.eax.b[1] = 0x11; /* Poll keyboard */ |
---|
159 | __intcall(0x16, &ireg, &oreg); |
---|
160 | |
---|
161 | if (!(oreg.eflags.l & EFLAGS_ZF)) |
---|
162 | return 1; |
---|
163 | |
---|
164 | if (SerialPort) { |
---|
165 | cli(); |
---|
166 | |
---|
167 | /* Already-queued input? */ |
---|
168 | if (SerialTail == SerialHead) { |
---|
169 | /* LSR */ |
---|
170 | data = inb(SerialPort + 5) & 1; |
---|
171 | if (data) { |
---|
172 | /* MSR */ |
---|
173 | data = inb(SerialPort + 6); |
---|
174 | |
---|
175 | /* Required status bits */ |
---|
176 | data &= FlowIgnore; |
---|
177 | |
---|
178 | if (data == FlowIgnore) |
---|
179 | data = 1; |
---|
180 | else |
---|
181 | data = 0; |
---|
182 | } |
---|
183 | } else |
---|
184 | data = 1; |
---|
185 | sti(); |
---|
186 | } |
---|
187 | |
---|
188 | return data; |
---|
189 | } |
---|
190 | |
---|
191 | __export int pollchar(void) |
---|
192 | { |
---|
193 | return firmware->i_ops->pollchar(); |
---|
194 | } |
---|
195 | |
---|
196 | void pm_pollchar(com32sys_t *regs) |
---|
197 | { |
---|
198 | if (pollchar()) |
---|
199 | regs->eflags.l &= ~EFLAGS_ZF; |
---|
200 | else |
---|
201 | regs->eflags.l |= EFLAGS_ZF; |
---|
202 | } |
---|
203 | |
---|
204 | char bios_getchar(char *hi) |
---|
205 | { |
---|
206 | com32sys_t ireg, oreg; |
---|
207 | unsigned char data; |
---|
208 | |
---|
209 | memset(&ireg, 0, sizeof(ireg)); |
---|
210 | memset(&oreg, 0, sizeof(oreg)); |
---|
211 | while (1) { |
---|
212 | __idle(); |
---|
213 | |
---|
214 | ireg.eax.b[1] = 0x11; /* Poll keyboard */ |
---|
215 | __intcall(0x16, &ireg, &oreg); |
---|
216 | |
---|
217 | if (oreg.eflags.l & EFLAGS_ZF) { |
---|
218 | if (!SerialPort) |
---|
219 | continue; |
---|
220 | |
---|
221 | cli(); |
---|
222 | if (SerialTail != SerialHead) { |
---|
223 | /* serial queued */ |
---|
224 | sti(); /* We already know we'll consume data */ |
---|
225 | data = *SerialTail++; |
---|
226 | |
---|
227 | if (SerialTail > SerialHead + serial_buf_size) |
---|
228 | SerialTail = SerialHead; |
---|
229 | } else { |
---|
230 | /* LSR */ |
---|
231 | data = inb(SerialPort + 5) & 1; |
---|
232 | if (!data) { |
---|
233 | sti(); |
---|
234 | continue; |
---|
235 | } |
---|
236 | data = inb(SerialPort + 6); |
---|
237 | data &= FlowIgnore; |
---|
238 | if (data != FlowIgnore) { |
---|
239 | sti(); |
---|
240 | continue; |
---|
241 | } |
---|
242 | |
---|
243 | data = inb(SerialPort); |
---|
244 | sti(); |
---|
245 | break; |
---|
246 | } |
---|
247 | } else { |
---|
248 | /* Keyboard input? */ |
---|
249 | ireg.eax.b[1] = 0x10; /* Get keyboard input */ |
---|
250 | __intcall(0x16, &ireg, &oreg); |
---|
251 | |
---|
252 | data = oreg.eax.b[0]; |
---|
253 | *hi = oreg.eax.b[1]; |
---|
254 | |
---|
255 | if (data == 0xE0) |
---|
256 | data = 0; |
---|
257 | |
---|
258 | if (data) { |
---|
259 | /* Convert character sets */ |
---|
260 | data = KbdMap[data]; |
---|
261 | } |
---|
262 | } |
---|
263 | |
---|
264 | break; |
---|
265 | } |
---|
266 | |
---|
267 | reset_idle(); /* Character received */ |
---|
268 | return data; |
---|
269 | } |
---|
270 | |
---|
271 | uint8_t bios_shiftflags(void) |
---|
272 | { |
---|
273 | com32sys_t reg; |
---|
274 | uint8_t ah, al; |
---|
275 | |
---|
276 | memset(®, 0, sizeof reg); |
---|
277 | reg.eax.b[1] = 0x12; |
---|
278 | __intcall(0x16, ®, ®); |
---|
279 | ah = reg.eax.b[1]; |
---|
280 | al = reg.eax.b[0]; |
---|
281 | |
---|
282 | /* |
---|
283 | * According to the Interrupt List, "many machines" don't correctly |
---|
284 | * fold the Alt state, presumably because it might be AltGr. |
---|
285 | * Explicitly fold the Alt and Ctrl states; it fits our needs |
---|
286 | * better. |
---|
287 | */ |
---|
288 | |
---|
289 | if (ah & 0x0a) |
---|
290 | al |= 0x08; |
---|
291 | if (ah & 0x05) |
---|
292 | al |= 0x04; |
---|
293 | |
---|
294 | return al; |
---|
295 | } |
---|
296 | |
---|
297 | __export uint8_t kbd_shiftflags(void) |
---|
298 | { |
---|
299 | if (firmware->i_ops->shiftflags) |
---|
300 | return firmware->i_ops->shiftflags(); |
---|
301 | else |
---|
302 | return 0; /* Unavailable on this firmware */ |
---|
303 | } |
---|
304 | |
---|
305 | /* |
---|
306 | * getchar: Read a character from keyboard or serial port |
---|
307 | */ |
---|
308 | __export char getchar(char *hi) |
---|
309 | { |
---|
310 | return firmware->i_ops->getchar(hi); |
---|
311 | } |
---|
312 | |
---|
313 | void pm_getchar(com32sys_t *regs) |
---|
314 | { |
---|
315 | regs->eax.b[0] = getchar((char *)®s->eax.b[1]); |
---|
316 | } |
---|