317 lines
5.9 KiB
C
317 lines
5.9 KiB
C
/*
|
|
* -----------------------------------------------------------------------
|
|
*
|
|
* Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
|
|
* Copyright 2009-2014 Intel Corporation; author: H. Peter Anvin
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, Inc., 53 Temple Place Ste 330,
|
|
* Boston MA 02111-1307, USA; either version 2 of the License, or
|
|
* (at your option) any later version; incorporated herein by reference.
|
|
*
|
|
* -----------------------------------------------------------------------
|
|
*
|
|
*
|
|
* conio.c
|
|
*
|
|
* Console I/O code, except:
|
|
* writechr, writestr_early - module-dependent
|
|
* writestr, crlf - writestr.inc
|
|
* writehex* - writehex.inc
|
|
*/
|
|
#include <sys/io.h>
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <fs.h>
|
|
#include <com32.h>
|
|
#include <sys/cpu.h>
|
|
#include <syslinux/firmware.h>
|
|
|
|
#include "bios.h"
|
|
#include "graphics.h"
|
|
|
|
union screen _cursor;
|
|
union screen _screensize;
|
|
|
|
/*
|
|
* Serial console stuff.
|
|
*/
|
|
__export uint16_t SerialPort = 0; /* Serial port base (or 0 for no serial port) */
|
|
__export uint8_t FlowInput = 0; /* Input bits for serial flow */
|
|
__export uint16_t BaudDivisor = 115200/9600; /* Baud rate divisor */
|
|
__export uint8_t FlowIgnore = 0; /* Ignore input unless these bits set */
|
|
__export uint16_t DisplayCon = 0x01; /* Display console enabled */
|
|
__export uint8_t FlowOutput = 0; /* Output to assert for serial flow */
|
|
|
|
__export uint8_t DisplayMask = 0x07; /* Display modes mask */
|
|
|
|
uint8_t ScrollAttribute = 0x07; /* Grey on white (normal text color) */
|
|
|
|
/*
|
|
* loadkeys: Load a LILO-style keymap
|
|
*
|
|
* Returns 0 on success, or -1 on error.
|
|
*/
|
|
__export int loadkeys(const char *filename)
|
|
{
|
|
FILE *f;
|
|
|
|
f = fopen(filename, "r");
|
|
if (!f)
|
|
return -1;
|
|
|
|
fread(KbdMap, 1, sizeof(KbdMap), f);
|
|
|
|
fclose(f);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* write_serial: If serial output is enabled, write character on
|
|
* serial port.
|
|
*/
|
|
__export void write_serial(char data)
|
|
{
|
|
if (!SerialPort)
|
|
return;
|
|
|
|
if (!(DisplayMask & 0x04))
|
|
return;
|
|
|
|
while (1) {
|
|
char ch;
|
|
|
|
ch = inb(SerialPort + 5); /* LSR */
|
|
|
|
/* Wait for space in transmit register */
|
|
if (!(ch & 0x20))
|
|
continue;
|
|
|
|
/* Wait for input flow control */
|
|
ch = inb(SerialPort + 6);
|
|
ch &= FlowInput;
|
|
if (ch != FlowInput)
|
|
continue;
|
|
|
|
break;
|
|
}
|
|
|
|
outb(data, SerialPort); /* Send data */
|
|
io_delay();
|
|
}
|
|
|
|
void pm_write_serial(com32sys_t *regs)
|
|
{
|
|
write_serial(regs->eax.b[0]);
|
|
}
|
|
|
|
void serialcfg(uint16_t *iobase, uint16_t *divisor, uint16_t *flowctl)
|
|
{
|
|
uint8_t al, ah;
|
|
|
|
*iobase = SerialPort;
|
|
*divisor = BaudDivisor;
|
|
|
|
al = FlowOutput;
|
|
ah = FlowInput;
|
|
|
|
al |= ah;
|
|
ah = FlowIgnore;
|
|
ah >>= 4;
|
|
|
|
if (!DisplayCon)
|
|
ah |= 0x80;
|
|
|
|
*flowctl = al | (ah << 8);
|
|
}
|
|
|
|
void pm_serialcfg(com32sys_t *regs)
|
|
{
|
|
serialcfg(®s->eax.w[0], ®s->ecx.w[0], ®s->ebx.w[0]);
|
|
}
|
|
|
|
/*
|
|
* write_serial_str: write_serial for strings
|
|
*/
|
|
__export void write_serial_str(char *data)
|
|
{
|
|
char ch;
|
|
|
|
while ((ch = *data++))
|
|
write_serial(ch);
|
|
}
|
|
|
|
/*
|
|
* pollchar: check if we have an input character pending
|
|
*
|
|
* Returns 1 if character pending.
|
|
*/
|
|
int bios_pollchar(void)
|
|
{
|
|
com32sys_t ireg, oreg;
|
|
uint8_t data = 0;
|
|
|
|
memset(&ireg, 0, sizeof(ireg));
|
|
|
|
ireg.eax.b[1] = 0x11; /* Poll keyboard */
|
|
__intcall(0x16, &ireg, &oreg);
|
|
|
|
if (!(oreg.eflags.l & EFLAGS_ZF))
|
|
return 1;
|
|
|
|
if (SerialPort) {
|
|
cli();
|
|
|
|
/* Already-queued input? */
|
|
if (SerialTail == SerialHead) {
|
|
/* LSR */
|
|
data = inb(SerialPort + 5) & 1;
|
|
if (data) {
|
|
/* MSR */
|
|
data = inb(SerialPort + 6);
|
|
|
|
/* Required status bits */
|
|
data &= FlowIgnore;
|
|
|
|
if (data == FlowIgnore)
|
|
data = 1;
|
|
else
|
|
data = 0;
|
|
}
|
|
} else
|
|
data = 1;
|
|
sti();
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
__export int pollchar(void)
|
|
{
|
|
return firmware->i_ops->pollchar();
|
|
}
|
|
|
|
void pm_pollchar(com32sys_t *regs)
|
|
{
|
|
if (pollchar())
|
|
regs->eflags.l &= ~EFLAGS_ZF;
|
|
else
|
|
regs->eflags.l |= EFLAGS_ZF;
|
|
}
|
|
|
|
char bios_getchar(char *hi)
|
|
{
|
|
com32sys_t ireg, oreg;
|
|
unsigned char data;
|
|
|
|
memset(&ireg, 0, sizeof(ireg));
|
|
memset(&oreg, 0, sizeof(oreg));
|
|
while (1) {
|
|
__idle();
|
|
|
|
ireg.eax.b[1] = 0x11; /* Poll keyboard */
|
|
__intcall(0x16, &ireg, &oreg);
|
|
|
|
if (oreg.eflags.l & EFLAGS_ZF) {
|
|
if (!SerialPort)
|
|
continue;
|
|
|
|
cli();
|
|
if (SerialTail != SerialHead) {
|
|
/* serial queued */
|
|
sti(); /* We already know we'll consume data */
|
|
data = *SerialTail++;
|
|
|
|
if (SerialTail > SerialHead + serial_buf_size)
|
|
SerialTail = SerialHead;
|
|
} else {
|
|
/* LSR */
|
|
data = inb(SerialPort + 5) & 1;
|
|
if (!data) {
|
|
sti();
|
|
continue;
|
|
}
|
|
data = inb(SerialPort + 6);
|
|
data &= FlowIgnore;
|
|
if (data != FlowIgnore) {
|
|
sti();
|
|
continue;
|
|
}
|
|
|
|
data = inb(SerialPort);
|
|
sti();
|
|
break;
|
|
}
|
|
} else {
|
|
/* Keyboard input? */
|
|
ireg.eax.b[1] = 0x10; /* Get keyboard input */
|
|
__intcall(0x16, &ireg, &oreg);
|
|
|
|
data = oreg.eax.b[0];
|
|
*hi = oreg.eax.b[1];
|
|
|
|
if (data == 0xE0)
|
|
data = 0;
|
|
|
|
if (data) {
|
|
/* Convert character sets */
|
|
data = KbdMap[data];
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
reset_idle(); /* Character received */
|
|
return data;
|
|
}
|
|
|
|
uint8_t bios_shiftflags(void)
|
|
{
|
|
com32sys_t reg;
|
|
uint8_t ah, al;
|
|
|
|
memset(®, 0, sizeof reg);
|
|
reg.eax.b[1] = 0x12;
|
|
__intcall(0x16, ®, ®);
|
|
ah = reg.eax.b[1];
|
|
al = reg.eax.b[0];
|
|
|
|
/*
|
|
* According to the Interrupt List, "many machines" don't correctly
|
|
* fold the Alt state, presumably because it might be AltGr.
|
|
* Explicitly fold the Alt and Ctrl states; it fits our needs
|
|
* better.
|
|
*/
|
|
|
|
if (ah & 0x0a)
|
|
al |= 0x08;
|
|
if (ah & 0x05)
|
|
al |= 0x04;
|
|
|
|
return al;
|
|
}
|
|
|
|
__export uint8_t kbd_shiftflags(void)
|
|
{
|
|
if (firmware->i_ops->shiftflags)
|
|
return firmware->i_ops->shiftflags();
|
|
else
|
|
return 0; /* Unavailable on this firmware */
|
|
}
|
|
|
|
/*
|
|
* getchar: Read a character from keyboard or serial port
|
|
*/
|
|
__export char getchar(char *hi)
|
|
{
|
|
return firmware->i_ops->getchar(hi);
|
|
}
|
|
|
|
void pm_getchar(com32sys_t *regs)
|
|
{
|
|
regs->eax.b[0] = getchar((char *)®s->eax.b[1]);
|
|
}
|