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 */
loadkeys(const char * filename)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 */
write_serial(char data)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
pm_write_serial(com32sys_t * regs)105 void pm_write_serial(com32sys_t *regs)
106 {
107 write_serial(regs->eax.b[0]);
108 }
109
serialcfg(uint16_t * iobase,uint16_t * divisor,uint16_t * flowctl)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
pm_serialcfg(com32sys_t * regs)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 */
write_serial_str(char * data)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 */
bios_pollchar(void)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
pollchar(void)191 __export int pollchar(void)
192 {
193 return firmware->i_ops->pollchar();
194 }
195
pm_pollchar(com32sys_t * regs)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
bios_getchar(char * hi)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
bios_shiftflags(void)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
kbd_shiftflags(void)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 */
getchar(char * hi)308 __export char getchar(char *hi)
309 {
310 return firmware->i_ops->getchar(hi);
311 }
312
pm_getchar(com32sys_t * regs)313 void pm_getchar(com32sys_t *regs)
314 {
315 regs->eax.b[0] = getchar((char *)®s->eax.b[1]);
316 }
317