[e16e8f2] | 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 | } |
---|