Files
ipxe/src/core/serial.c
Michael Brown 03c80c12b8 [iSCSI] Support Windows Server 2008 direct iSCSI installation
Add yet another ugly hack to iscsiboot.c, this time to allow the user to
inhibit the shutdown/removal of the iSCSI INT13 device (and the network
devices, since they are required for the iSCSI device to function).

On the plus side, the fact that shutdown() now takes flags to
differentiate between shutdown-for-exit and shutdown-for-boot means that
another ugly hack (to allow returning via the PXE stack on BIOSes that
have broken INT 18 calls) will be easier.

I feel dirty.
2008-07-17 17:45:17 +01:00

253 lines
6.3 KiB
C

/*
* The serial port interface routines implement a simple polled i/o
* interface to a standard serial port. Due to the space restrictions
* for the boot blocks, no BIOS support is used (since BIOS requires
* expensive real/protected mode switches), instead the rudimentary
* BIOS support is duplicated here.
*
* The base address and speed for the i/o port are passed from the
* Makefile in the COMCONSOLE and CONSPEED preprocessor macros. The
* line control parameters are currently hard-coded to 8 bits, no
* parity, 1 stop bit (8N1). This can be changed in init_serial().
*/
#include "stddef.h"
#include <gpxe/init.h>
#include "io.h"
#include <unistd.h>
#include <gpxe/serial.h>
#include "config/serial.h"
/* Set default values if none specified */
#ifndef COMCONSOLE
#define COMCONSOLE 0x3f8
#endif
#ifndef COMSPEED
#define COMSPEED 9600
#endif
#ifndef COMDATA
#define COMDATA 8
#endif
#ifndef COMPARITY
#define COMPARITY 0
#endif
#ifndef COMSTOP
#define COMSTOP 1
#endif
#undef UART_BASE
#define UART_BASE ( COMCONSOLE )
#undef UART_BAUD
#define UART_BAUD ( COMSPEED )
#if ((115200%UART_BAUD) != 0)
#error Bad ttys0 baud rate
#endif
#define COMBRD (115200/UART_BAUD)
/* Line Control Settings */
#define UART_LCS ( ( ( (COMDATA) - 5 ) << 0 ) | \
( ( (COMPARITY) ) << 3 ) | \
( ( (COMSTOP) - 1 ) << 2 ) )
/* Data */
#define UART_RBR 0x00
#define UART_TBR 0x00
/* Control */
#define UART_IER 0x01
#define UART_IIR 0x02
#define UART_FCR 0x02
#define UART_LCR 0x03
#define UART_MCR 0x04
#define UART_DLL 0x00
#define UART_DLM 0x01
/* Status */
#define UART_LSR 0x05
#define UART_LSR_TEMPT 0x40 /* Transmitter empty */
#define UART_LSR_THRE 0x20 /* Transmit-hold-register empty */
#define UART_LSR_BI 0x10 /* Break interrupt indicator */
#define UART_LSR_FE 0x08 /* Frame error indicator */
#define UART_LSR_PE 0x04 /* Parity error indicator */
#define UART_LSR_OE 0x02 /* Overrun error indicator */
#define UART_LSR_DR 0x01 /* Receiver data ready */
#define UART_MSR 0x06
#define UART_SCR 0x07
#if defined(UART_MEM)
#define uart_readb(addr) readb((addr))
#define uart_writeb(val,addr) writeb((val),(addr))
#else
#define uart_readb(addr) inb((addr))
#define uart_writeb(val,addr) outb((val),(addr))
#endif
/*
* void serial_putc(int ch);
* Write character `ch' to port UART_BASE.
*/
void serial_putc ( int ch ) {
int i;
int status;
i = 1000; /* timeout */
while(--i > 0) {
status = uart_readb(UART_BASE + UART_LSR);
if (status & UART_LSR_THRE) {
/* TX buffer emtpy */
uart_writeb(ch, UART_BASE + UART_TBR);
break;
}
mdelay(2);
}
}
/*
* int serial_getc(void);
* Read a character from port UART_BASE.
*/
int serial_getc ( void ) {
int status;
int ch;
do {
status = uart_readb(UART_BASE + UART_LSR);
} while((status & 1) == 0);
ch = uart_readb(UART_BASE + UART_RBR); /* fetch (first) character */
ch &= 0x7f; /* remove any parity bits we get */
if (ch == 0x7f) { /* Make DEL... look like BS */
ch = 0x08;
}
return ch;
}
/*
* int serial_ischar(void);
* If there is a character in the input buffer of port UART_BASE,
* return nonzero; otherwise return 0.
*/
int serial_ischar ( void ) {
int status;
status = uart_readb(UART_BASE + UART_LSR); /* line status reg; */
return status & 1; /* rx char available */
}
/*
* int serial_init(void);
* Initialize port UART_BASE to speed COMSPEED, line settings 8N1.
*/
static void serial_init ( void ) {
int status;
int divisor, lcs;
DBG ( "Serial port %#x initialising\n", UART_BASE );
divisor = COMBRD;
lcs = UART_LCS;
#ifdef COMPRESERVE
lcs = uart_readb(UART_BASE + UART_LCR) & 0x7f;
uart_writeb(0x80 | lcs, UART_BASE + UART_LCR);
divisor = (uart_readb(UART_BASE + UART_DLM) << 8) | uart_readb(UART_BASE + UART_DLL);
uart_writeb(lcs, UART_BASE + UART_LCR);
#endif
/* Set Baud Rate Divisor to COMSPEED, and test to see if the
* serial port appears to be present.
*/
uart_writeb(0x80 | lcs, UART_BASE + UART_LCR);
uart_writeb(0xaa, UART_BASE + UART_DLL);
if (uart_readb(UART_BASE + UART_DLL) != 0xaa) {
DBG ( "Serial port %#x UART_DLL failed\n", UART_BASE );
goto out;
}
uart_writeb(0x55, UART_BASE + UART_DLL);
if (uart_readb(UART_BASE + UART_DLL) != 0x55) {
DBG ( "Serial port %#x UART_DLL failed\n", UART_BASE );
goto out;
}
uart_writeb(divisor & 0xff, UART_BASE + UART_DLL);
if (uart_readb(UART_BASE + UART_DLL) != (divisor & 0xff)) {
DBG ( "Serial port %#x UART_DLL failed\n", UART_BASE );
goto out;
}
uart_writeb(0xaa, UART_BASE + UART_DLM);
if (uart_readb(UART_BASE + UART_DLM) != 0xaa) {
DBG ( "Serial port %#x UART_DLM failed\n", UART_BASE );
goto out;
}
uart_writeb(0x55, UART_BASE + UART_DLM);
if (uart_readb(UART_BASE + UART_DLM) != 0x55) {
DBG ( "Serial port %#x UART_DLM failed\n", UART_BASE );
goto out;
}
uart_writeb((divisor >> 8) & 0xff, UART_BASE + UART_DLM);
if (uart_readb(UART_BASE + UART_DLM) != ((divisor >> 8) & 0xff)) {
DBG ( "Serial port %#x UART_DLM failed\n", UART_BASE );
goto out;
}
uart_writeb(lcs, UART_BASE + UART_LCR);
/* disable interrupts */
uart_writeb(0x0, UART_BASE + UART_IER);
/* disable fifo's */
uart_writeb(0x00, UART_BASE + UART_FCR);
/* Set clear to send, so flow control works... */
uart_writeb((1<<1), UART_BASE + UART_MCR);
/* Flush the input buffer. */
do {
/* rx buffer reg
* throw away (unconditionally the first time)
*/
(void) uart_readb(UART_BASE + UART_RBR);
/* line status reg */
status = uart_readb(UART_BASE + UART_LSR);
} while(status & UART_LSR_DR);
out:
return;
}
/*
* void serial_fini(void);
* Cleanup our use of the serial port, in particular flush the
* output buffer so we don't accidentially lose characters.
*/
static void serial_fini ( int flags __unused ) {
int i, status;
/* Flush the output buffer to avoid dropping characters,
* if we are reinitializing the serial port.
*/
i = 10000; /* timeout */
do {
status = uart_readb(UART_BASE + UART_LSR);
} while((--i > 0) && !(status & UART_LSR_TEMPT));
/* Don't mark it as disabled; it's still usable */
}
/**
* Serial driver initialisation function
*
* Initialise serial port early on so that it is available to capture
* early debug messages.
*/
struct init_fn serial_init_fn __init_fn ( INIT_SERIAL ) = {
.initialise = serial_init,
};
/** Serial driver startup function */
struct startup_fn serial_startup_fn __startup_fn ( STARTUP_EARLY ) = {
.shutdown = serial_fini,
};