Home | History | Annotate | Download | only in core
      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(&regs->eax.w[0], &regs->ecx.w[0], &regs->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(&reg, 0, sizeof reg);
    277 	reg.eax.b[1] = 0x12;
    278 	__intcall(0x16, &reg, &reg);
    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 *)&regs->eax.b[1]);
    316 }
    317