windows-nt/Source/XPSP1/NT/base/boot/detect/ia64/comlptc.c
2020-09-26 16:20:57 +08:00

1650 lines
46 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1990 Microsoft Corporation
Module Name:
comport.c
Abstract:
This module contains C code to determine comport and LPT configuration in
syste.
Author:
Shie-Lin Tzong (shielint) Dec-23-1991
Revision History:
--*/
#include "hwdetect.h"
#include "comlpt.h"
#include "bios.h"
#include "string.h"
#define LOWEST_IRQ 3
#define MASTER_IRQ_MASK_BITS 0xf8
#define SLAVE_IRQ_MASK_BITS 0xfe
//
// ComPortAddress[] is a global array to remember which comports have
// been detected and their I/O port addresses.
//
#if defined(NEC_98)
#define NEC_MAX_COM_PORTS 15
#define NEC_MAX_IO_PATTERN 38
#define FIFO_INT_ID_PRS1 0x40
#define FIFO_INT_ID_PRS 0x60
#define COUNTRY_JAPAN 81
#define NEC1500 0x1500 // Old mode
#define NEC1501 0x1501 // SuperIO
#define NEC1502 0x1502 // SuperIO2
#define NEC1503 0x1503 // 2ndCCU 16550 Controller
#define NEC8071 0x8071 // Extended Board of 1stCCU Compatibility Controller
#define NEC0C01 0x0C01 // Extended Board of 16550 Compatibility Controller
//
// PC-9801-94 ports
//
#define TOKI_CONTROL 0x549
#define READ_DIVISOR_LATCH( PDesiredDivisor ) \
do \
{ \
PUCHAR Address; \
PSHORT PDivisor = PDesiredDivisor; \
UCHAR LineControl; \
UCHAR Lsb; \
UCHAR Msb; \
LineControl = READ_PORT_UCHAR( (PUCHAR)0x23b ); \
WRITE_PORT_UCHAR((PUCHAR)0x23b,(UCHAR)0x80 ); \
Lsb = READ_PORT_UCHAR( (PUCHAR)0x238 ); \
Msb = READ_PORT_UCHAR( (PUCHAR)0x239 ); \
*PDivisor = Lsb; \
*PDivisor = *PDivisor | (((USHORT)Msb) << 8); \
WRITE_PORT_UCHAR((PUCHAR)0x23b,(UCHAR)0x0 ); \
} while (0)
#define WRITE_DIVISOR_LATCH(BaseAddress,DesiredDivisor) \
do \
{ \
PUCHAR Address = BaseAddress; \
SHORT Divisor = DesiredDivisor; \
UCHAR LineControl; \
LineControl = READ_PORT_UCHAR(Address+LINE_CONTROL_REGISTER); \
WRITE_PORT_UCHAR( \
Address+LINE_CONTROL_REGISTER, \
(UCHAR)(LineControl | SERIAL_LCR_DLAB) \
); \
WRITE_PORT_UCHAR( \
Address+DIVISOR_LATCH_LSB, \
(UCHAR)(Divisor & 0xff) \
); \
WRITE_PORT_UCHAR( \
Address+DIVISOR_LATCH_MSB, \
(UCHAR)((Divisor & 0xff00) >> 8) \
); \
WRITE_PORT_UCHAR( \
Address+LINE_CONTROL_REGISTER, \
LineControl \
); \
} while (0)
#define NextComPortNumber(ComportName, PortNumber) \
do \
{ \
if (PortNumber < 9){ \
ComportName[3] = PortNumber + (UCHAR)'1'; \
ComportName[4] = '\0'; \
}else{ \
ComportName[3] = (UCHAR)'1'; \
ComportName[4] = (PortNumber - 10) + (UCHAR)'1'; \
ComportName[5] = '\0'; \
} \
++PortNumber; \
}while(0) \
USHORT ComPortAddress[NEC_MAX_COM_PORTS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
#else // NEC_98
USHORT ComPortAddress[MAX_COM_PORTS] = {0, 0, 0, 0};
#endif // NEC_98
#if defined(NEC_98)
VOID
NEC2ndCCUInitialize (
VOID
)
/*++
Routine Description:
We have to initialize 2ndCCU(NS16550A) after system reset.
Because the initial value of this chip is instability.
Arguments:
None.
Return Value:
None.
--*/
{
_asm { cli }
WRITE_PORT_UCHAR( (PUCHAR)0x0411,(UCHAR)0x91 );
IoDelay(1);
WRITE_PORT_UCHAR( (PUCHAR)0x0413,(UCHAR)0x01 );
IoDelay(1);
//
// set baudrate.
//
WRITE_PORT_UCHAR( (PUCHAR)0x023B,(UCHAR)0x80 );
IoDelay(1);
WRITE_DIVISOR_LATCH( (PUCHAR)0x0238,(SHORT)0x0060 );
IoDelay(1);
WRITE_PORT_UCHAR( (PUCHAR)0x023B,(UCHAR)0x00 );
IoDelay(1);
WRITE_PORT_UCHAR( (PUCHAR)0x0413,(UCHAR)0x08 );
//
// wait 5 micro sec
//
IoDelay(5);
WRITE_PORT_UCHAR( (PUCHAR)0x0413,(UCHAR)0x01 );
IoDelay(1);
_asm { sti }
//
// initialize - END
//
}
BOOLEAN
FifoPresenceCheck (
PUCHAR PortAddress
)
/*++
Routine Description:
This routine reports whether the resource have FIFO faculty.
Arguments:
None.
Return Value:
Will return true if the resource have, otherwise it
will return false.
--*/
{
BOOLEAN FifoStatus = TRUE;
USHORT cnt;
UCHAR tmp;
WRITE_PORT_UCHAR( (PUCHAR)(PortAddress - 0x1) , (UCHAR)(0x03) );
IoDelay(10);
tmp = READ_PORT_UCHAR( (PUCHAR)PortAddress );
IoDelay(10);
if ((tmp & FIFO_INT_ID_PRS1) == 0) {
tmp = READ_PORT_UCHAR( (PUCHAR)PortAddress );
IoDelay(10);
}
for (cnt = 0;cnt < 2;cnt++){
tmp = READ_PORT_UCHAR( (PUCHAR)PortAddress );
IoDelay(10);
if ((tmp & FIFO_INT_ID_PRS) != 0){
FifoStatus = FALSE;
break;
}
tmp = READ_PORT_UCHAR( (PUCHAR)PortAddress );
IoDelay(10);
if ((tmp & FIFO_INT_ID_PRS) != 0x40){
FifoStatus = FALSE;
break;
}
}
return(FifoStatus);
}
BOOLEAN
DoesPortExistNEC101(
SHORT PortNumber,
PUSHORT InterruptNumber,
PUSHORT PortLength
)
{
BOOLEAN PortExist = FALSE;
USHORT Port = PortNumber;
USHORT NEC_y = 0;
USHORT IsCommonInterrupt;
USHORT temp1,temp2;
USHORT Ch_2_IRQ_Tbl[4] = {3,5,6,9};
USHORT Ch_3_IRQ_Tbl[4] = {3,10,12,13};
USHORT COMMON_IRQ_Tbl[7] = {3,5,6,9,10,12,13};
NEC_y = ( ( Port ) & (0x0f00) );
switch (Port & 0x00ff) {
case 0x00b0:
if ( READ_PORT_UCHAR((PUCHAR)(NEC_y | 0x00b3)) != 0xff ) {
PortExist = TRUE;
}
break;
case 0x00b2:
if ( READ_PORT_UCHAR((PUCHAR)(NEC_y | 0x00bb)) != 0xff ) {
PortExist = TRUE;
}
break;
default:
break;
}
//
// This check is necessary to know detecting PC-9861K is not support.
//
if (( PortExist == TRUE ) &&
( FifoPresenceCheck((PUCHAR)(Port | 0x0005)) == FALSE)){
PortExist = FALSE;
}
if (PortExist == TRUE){
IsCommonInterrupt = ( (0x04) & (READ_PORT_UCHAR((PUCHAR)(NEC_y | 0x00b8))) );
IoDelay(10);
if( IsCommonInterrupt ){
temp1 = ( (0x03) & ( READ_PORT_UCHAR((PUCHAR)(NEC_y | Port))) );
IoDelay(10);
if( ( Port & (0x00ff) ) == 0x00b0 ){
*InterruptNumber = Ch_2_IRQ_Tbl[temp1];
}else{
*InterruptNumber = Ch_3_IRQ_Tbl[temp1];
}
}else{
temp1 = ( 0x01 & (READ_PORT_UCHAR((PUCHAR)(NEC_y | 0x00b0))) );
IoDelay(10);
temp2 = ( 0x03 & (READ_PORT_UCHAR((PUCHAR)(NEC_y | 0x00b2))) );
IoDelay(10);
*InterruptNumber = COMMON_IRQ_Tbl[(((temp1 << 2) | temp2) & 0x07)];
}
*PortLength = 1;
}
return(PortExist);
}
BOOLEAN
DoesPortExistNECFaxModem(
SHORT PortNumber,
PUSHORT InterruptNumber,
PUSHORT PortLength
)
{
BOOLEAN PortExist = FALSE;
USHORT Port = PortNumber;
USHORT temp;
temp = READ_PORT_UCHAR( (PUCHAR)(Port | 0x000f) );
if ((temp & 0x0f0) == 0) {
switch ( temp & 0x0f ) {
case 0x01:
*InterruptNumber = 3;
break;
case 0x02:
*InterruptNumber = 5;
break;
case 0x04:
*InterruptNumber = 6;
break;
case 0x08:
*InterruptNumber = 12;
break;
default:
break;
}
if (*InterruptNumber != 0) {
PortExist = TRUE;
}
*PortLength = 8;
}
return(PortExist);
}
#endif // NEC_98
#if !defined(_GAMBIT_)
VOID
SerialInterruptRequest (
PUCHAR PortAddress
)
/*++
Routine Description:
This routine generates an interrupt on the interrupt line for
com port.
Arguments:
PortAddress - the port address of the desired com port.
Return Value:
None.
--*/
{
USHORT i;
UCHAR Temp;
WRITE_PORT_UCHAR(
PortAddress + MODEM_CONTROL_REGISTER,
8
);
WRITE_PORT_UCHAR(
PortAddress + INTERRUPT_ENABLE_REGISTER,
0
);
WRITE_PORT_UCHAR(
PortAddress + INTERRUPT_ENABLE_REGISTER,
0xf
);
//
// Add some delay
//
for (i = 0; i < 5 ; i++ ) {
Temp = READ_PORT_UCHAR((PUCHAR) PIC1_PORT1);
Temp = READ_PORT_UCHAR((PUCHAR) PIC2_PORT1);
}
}
VOID
SerialInterruptDismiss (
PUCHAR PortAddress
)
/*++
Routine Description:
This routine dismisses an interrupt on the interrupt line for
com port.
Arguments:
PortAddress - the port address of the desired com port.
Return Value:
None.
--*/
{
USHORT i;
UCHAR Temp;
Temp = READ_PORT_UCHAR(
PortAddress + INTERRUPT_IDENT_REGISTER
);
WRITE_PORT_UCHAR(
PortAddress + INTERRUPT_ENABLE_REGISTER,
0
);
//
// Add some delay
//
for (i = 0; i < 5 ; i++ ) {
Temp = READ_PORT_UCHAR((PUCHAR) PIC1_PORT1);
Temp = READ_PORT_UCHAR((PUCHAR) PIC2_PORT1);
}
}
BOOLEAN
DoesPortExist(
IN PUCHAR Address
)
/*++
Routine Description:
This routine examines several of what might be the serial device
registers. It ensures that the bits that should be zero are zero.
It will then attempt to set the device to 19200 baud. If the
will then attempt to read that baud. If it is still 19200 then
we can feel pretty safe that this is a serial device.
NOTE: If there is indeed a serial port at the address specified
it will absolutely have interrupts inhibited upon return
from this routine.
Arguments:
Address - address of hw port.
Return Value:
TRUE - Port exists. Party on.
FALSE - Port doesn't exist. Don't use it.
History:
7/23/97 a-paulbr fixed bug 95050. Init LineControl to 0x00
--*/
{
UCHAR IerContents;
UCHAR BaudRateMsb, BaudRateLsb;
BOOLEAN ReturnValue = FALSE;
UCHAR LineControl = 0x00;
UCHAR LineControl_Save;
UCHAR Temp;
//
// Save the original LCR, so we can restore it later
// We won't use it, because the port could be handing us
// a bad initial value. We will use 0x00 instead.
//
LineControl_Save = READ_PORT_UCHAR(Address+LINE_CONTROL_REGISTER);
//
// Read original baud rate divisor and save it.
//
WRITE_PORT_UCHAR(
Address+LINE_CONTROL_REGISTER,
(UCHAR)(LineControl | SERIAL_LCR_DLAB)
);
BaudRateMsb = READ_PORT_UCHAR(Address+DIVISOR_LATCH_MSB);
BaudRateLsb = READ_PORT_UCHAR(Address+DIVISOR_LATCH_LSB);
//
// Change baud rate to 9600.
//
WRITE_PORT_UCHAR(Address+DIVISOR_LATCH_MSB, BAUD_RATE_9600_MSB);
WRITE_PORT_UCHAR(Address+DIVISOR_LATCH_LSB, BAUD_RATE_9600_LSB);
//
// Read IER and save it away.
//
WRITE_PORT_UCHAR(
Address+LINE_CONTROL_REGISTER,
(UCHAR)(LineControl & ~SERIAL_LCR_DLAB)
);
IerContents = READ_PORT_UCHAR(
Address + INTERRUPT_ENABLE_REGISTER
);
WRITE_PORT_UCHAR(
Address + INTERRUPT_ENABLE_REGISTER,
IER_TEST_VALUE
);
//
// Read baud rate divisor. The values we read should be equal to the
// values we set earlier.
//
WRITE_PORT_UCHAR(
Address+LINE_CONTROL_REGISTER,
(UCHAR)(LineControl | SERIAL_LCR_DLAB)
);
Temp = READ_PORT_UCHAR(Address+DIVISOR_LATCH_MSB);
if (Temp != BAUD_RATE_9600_MSB) {
goto AllDone;
}
Temp = READ_PORT_UCHAR(Address+DIVISOR_LATCH_LSB);
if (Temp != BAUD_RATE_9600_LSB) {
goto AllDone;
}
//
// Read IER and it should be equal to the value we set earlier.
//
WRITE_PORT_UCHAR(
Address+LINE_CONTROL_REGISTER,
(UCHAR)(LineControl & ~SERIAL_LCR_DLAB)
);
Temp = READ_PORT_UCHAR(
Address + INTERRUPT_ENABLE_REGISTER
);
if (Temp != IER_TEST_VALUE) {
goto AllDone;
}
ReturnValue = TRUE;
AllDone:
//
// Restore registers which we destroyed .
//
WRITE_PORT_UCHAR(
Address+LINE_CONTROL_REGISTER,
(UCHAR)(LineControl & ~SERIAL_LCR_DLAB)
);
WRITE_PORT_UCHAR(
Address + INTERRUPT_ENABLE_REGISTER,
IerContents
);
WRITE_PORT_UCHAR(
Address+LINE_CONTROL_REGISTER,
(UCHAR)(LineControl | SERIAL_LCR_DLAB)
);
WRITE_PORT_UCHAR(Address+DIVISOR_LATCH_MSB, BaudRateMsb);
WRITE_PORT_UCHAR(Address+DIVISOR_LATCH_LSB, BaudRateLsb);
WRITE_PORT_UCHAR(
Address+LINE_CONTROL_REGISTER,
LineControl_Save
);
return ReturnValue;
}
BOOLEAN
HwInterruptDetection(
IN PUCHAR BasePort,
IN VOID (*InterruptRequestRoutine)(),
IN VOID (*InterruptDismissRoutine)(),
OUT PUSHORT Vector
)
/*++
Routine Description:
This routine attempts to locate the interrupt vector for which
the device is configured. The allowable vectors are
3 - 7, and 9 - 15. If no interrupt vector is found, or more than
one is found, the routine returns FALSE. Otherwise, TRUE is returned.
Note that we diddle the i8259 interrupt controllers here.
Arguments:
BasePort - the I/O port base for the device.
InterruptRequestRoutine - A pointer to a routine to generate
desired interrupt.
InterruptDismissRoutine - A pointer to a routine to dismiss the interrupt
generated by InterruptRequestRoutine.
Vector - Pointer to the location to store the mouse interrupt vector.
Return Value:
Returns TRUE if the Inport interrupt vector was located; otherwise,
FALSE is returned.
--*/
{
UCHAR OldMasterMask, OldSlaveMask;
UCHAR MasterMask, SlaveMask;
UCHAR InterruptBits;
UCHAR PossibleInterruptBits;
int i;
int NumberOfIRQs;
BOOLEAN VectorFound = FALSE;
//
// Get the i8259 interrupt masks.
//
OldMasterMask = READ_PORT_UCHAR((PUCHAR) PIC1_PORT1);
OldSlaveMask = READ_PORT_UCHAR((PUCHAR) PIC2_PORT1);
//
// Raise IRQL to the highest priority IRQL the inport would use.
//
WRITE_PORT_UCHAR(
(PUCHAR) PIC1_PORT1,
(UCHAR) 0xff ^ ((UCHAR)(1 << LOWEST_IRQ) - 1)
);
WRITE_PORT_UCHAR(
(PUCHAR) PIC2_PORT1,
(UCHAR) 0xfe
);
//
// Get the master i8259 interrupt mask.
//
MasterMask = READ_PORT_UCHAR((PUCHAR) PIC1_PORT1);
//
// Disable potential device interrupts.
//
WRITE_PORT_UCHAR(
(PUCHAR) PIC1_PORT1,
(UCHAR) (MasterMask | MASTER_IRQ_MASK_BITS)
);
//
// Attempt to locate the interrupt line on the master i8259.
// Why try this 10 times? It's magic...
//
PossibleInterruptBits = MASTER_IRQ_MASK_BITS;
for (i = 0; i < 10; i++) {
//
// Generate a 0 on the master 8259 interrupt line
//
(*InterruptDismissRoutine)(BasePort);
//
// Read the interrupt bits off the master i8259. Only bits
// 3 - 7 are of interest. Eliminate non-functional
// IRQs. Only continue looking at the master i8259 if there
// is at least one functional IRQ.
//
_asm {cli}
WRITE_PORT_UCHAR((PUCHAR) PIC1_PORT0, OCW3_READ_IRR);
InterruptBits = READ_PORT_UCHAR((PUCHAR) PIC1_PORT0);
_asm {sti}
InterruptBits &= MASTER_IRQ_MASK_BITS;
InterruptBits ^= MASTER_IRQ_MASK_BITS;
PossibleInterruptBits &= InterruptBits;
if (!PossibleInterruptBits) {
break;
}
//
// Generate an interrupt from the desired device.
//
(*InterruptRequestRoutine)(BasePort);
//
// Read the interrupt bits off the master i8259. Only bits
// 3 - 7 are of interest. Eliminate non-functional
// IRQs. Only continue looking at the master i8259 if there
// is at least one functional IRQ.
//
_asm {cli}
WRITE_PORT_UCHAR((PUCHAR) PIC1_PORT0, OCW3_READ_IRR);
InterruptBits = READ_PORT_UCHAR((PUCHAR) PIC1_PORT0);
_asm {sti}
InterruptBits &= MASTER_IRQ_MASK_BITS;
PossibleInterruptBits &= InterruptBits;
if (!PossibleInterruptBits) {
break;
}
}
if (PossibleInterruptBits) {
//
// We found at least one IRQ on the master i8259 that could belong
// to the Inport mouse. Count how many we found. If there is
// more than one, we haven't found the vector. Otherwise, we've
// successfully located the Inport interrupt vector on the master
// i8259 (provided the interrupt vector is 3, 4, 5, or 7).
//
PossibleInterruptBits >>= 3;
NumberOfIRQs = 0;
for (i = 3; i <= 7; i++) {
if (PossibleInterruptBits & 1) {
NumberOfIRQs += 1;
*Vector = (CCHAR) i;
}
PossibleInterruptBits >>= 1;
}
if (NumberOfIRQs == 1) {
VectorFound = TRUE;
}
}
//
// If we didn't locate the interrupt vector on the master i8259, attempt
// to locate it on the slave i8259.
//
if (!VectorFound) {
//
// Get the slave i8259 interrupt mask.
//
SlaveMask = READ_PORT_UCHAR((PUCHAR) PIC2_PORT1);
//
// Attempt to locate the interupt line on the slave i8259.
// Why try this 20 times? It's magic...
//
PossibleInterruptBits = SLAVE_IRQ_MASK_BITS;
for (i = 0; i < 20; i++) {
//
// Generate a 0 on the Inport IRQ on the slave i8259.
//
(*InterruptDismissRoutine)(BasePort);
//
// Read the interrupt bits off the slave i8259.
// Eliminate non-functional IRQs. Only continue
// looking at the slave i8259 if there is at least one
// functional IRQ.
//
_asm {cli}
WRITE_PORT_UCHAR((PUCHAR) PIC2_PORT0, OCW3_READ_IRR);
InterruptBits = READ_PORT_UCHAR((PUCHAR) PIC2_PORT0);
_asm {sti}
InterruptBits &= SLAVE_IRQ_MASK_BITS;
InterruptBits ^= SLAVE_IRQ_MASK_BITS;
PossibleInterruptBits &= InterruptBits;
if (!PossibleInterruptBits) {
break;
}
//
// Generate a 1 on the Inport IRQ on the slave i8259.
//
(*InterruptRequestRoutine)(BasePort);
//
// Read the interrupt bits off the slave i8259.
// Eliminate non-functional IRQs. Only continue
// looking at the slave i8259 if there is at least one
// functional IRQ.
//
_asm {cli}
WRITE_PORT_UCHAR((PUCHAR) PIC2_PORT0, OCW3_READ_IRR);
InterruptBits = READ_PORT_UCHAR((PUCHAR) PIC2_PORT0);
_asm {sti}
InterruptBits &= SLAVE_IRQ_MASK_BITS;
PossibleInterruptBits &= InterruptBits;
if (!PossibleInterruptBits) {
break;
}
}
if (PossibleInterruptBits) {
//
// We found at least one IRQ on the slave i8259 that could belong
// to the device. Count how many we found. If there is
// more than one, we haven't found the vector. Otherwise, we've
// successfully located the device interrupt vector on the slave
// i8259.
//
PossibleInterruptBits >>= 1;
NumberOfIRQs = 0;
for (i = 9; i <= 15; i++) {
if (PossibleInterruptBits & 1) {
NumberOfIRQs += 1;
*Vector = (CCHAR) i;
}
PossibleInterruptBits >>= 1;
}
if (NumberOfIRQs == 1) {
VectorFound = TRUE;
}
}
//
// Restore the i8259 slave.
//
WRITE_PORT_UCHAR((PUCHAR) PIC2_PORT0, OCW3_READ_ISR);
//
// Restore the i8259 slave interrupt mask.
//
WRITE_PORT_UCHAR((PUCHAR) PIC2_PORT1, SlaveMask);
}
//
// Dismiss interrupt on the device
//
(*InterruptDismissRoutine)(BasePort);
//
// Restore the i8259 master.
//
WRITE_PORT_UCHAR((PUCHAR) PIC1_PORT0, OCW3_READ_ISR);
//
// Restore the i8259 master interrupt mask.
//
WRITE_PORT_UCHAR((PUCHAR) PIC1_PORT1, MasterMask);
//
// Restore the previous IRQL.
//
WRITE_PORT_UCHAR(
(PUCHAR) PIC1_PORT1,
OldMasterMask
);
WRITE_PORT_UCHAR(
(PUCHAR) PIC2_PORT1,
OldSlaveMask
);
return(VectorFound);
}
#endif // _GAMBIT_
FPFWCONFIGURATION_COMPONENT_DATA
GetComportInformation (
VOID
)
/*++
Routine Description:
This routine will attempt to detect the comports information
for the system. The information includes port address, irq
level.
Note that this routine can only detect up to 4 comports and
it assumes that if MCA, COM3 and COM4 use irq 4. Otherwise,
COM3 uses irq 4 and COM4 uses irq 3. Also, the number of ports
for COMPORT is set to 8 (for example, COM2 uses ports 2F8 - 2FF)
Arguments:
None.
Return Value:
A pointer to a stucture of type FWCONFIGURATION_COMPONENT_DATA
which is the root of comport component list.
If no comport exists, a value of NULL is returned.
--*/
{
#if defined(NEC_98)
FPFWCONFIGURATION_COMPONENT_DATA CurrentEntry, PreviousEntry = NULL;
FPFWCONFIGURATION_COMPONENT_DATA FirstComport = NULL;
FPFWCONFIGURATION_COMPONENT Component;
HWCONTROLLER_DATA ControlData;
UCHAR i, j, z;
SHORT Port;
UCHAR ComportName[] = "COM??";
CM_SERIAL_DEVICE_DATA SerialData;
ULONG BaudClock = 1843200;
USHORT Vector;
BOOLEAN PortExist;
USHORT IoPorts[NEC_MAX_IO_PATTERN] = {0x030, 0x238,
0x00b0, 0x00b2, 0x01b0, 0x01b2, 0x02b0, 0x02b2,
0x03b0, 0x03b2, 0x04b0, 0x04b2, 0x05b0, 0x05b2,
0x06b0, 0x06b2, 0x07b0, 0x07b2, 0x08b0, 0x08b2,
0x09b0, 0x09b2, 0x0ab0, 0x0ab2, 0x0bb0, 0x0bb2,
0x0cb0, 0x0cb2, 0x0db0, 0x0db2, 0x0eb0, 0x0eb2,
0x0fb0, 0x0fb2,
0x8b0, 0x9b0, 0xab0, 0xbb0};
USHORT PortLength;
USHORT InterruptNumber;
USHORT NEC_COM_number = 0;
//
// Check BIOS ROM data is right or not.
// if not, we don't detect CCU.
//
if ( (COM_ID_L != 0x98) || (COM_ID_H != 0x21) ){
return(FirstComport);
}
//
// Initialize serial device specific data
//
SerialData.Version = COUNTRY_JAPAN;
SerialData.Revision = NEC1500;
SerialData.BaudClock = 153600;
for (i = 0; i < NEC_MAX_IO_PATTERN; i++) {
PortExist = FALSE;
//
// Initialize Controller data
//
ControlData.NumberPortEntries = 0;
ControlData.NumberIrqEntries = 0;
ControlData.NumberMemoryEntries = 0;
ControlData.NumberDmaEntries = 0;
z = 0;
//
// Load the port address from the BIOS data area, if it exists
//
Port = IoPorts[i];
InterruptNumber = 0;
switch(i){
case 0:
//
// internal 1st CCU
//
if ( (ROM_FLAG7 & LOCKED_CCU1) == 0 ) {
PortExist = TRUE;
InterruptNumber = 4;
PortLength = 1;
SerialData.Revision = NEC1501;
if (ROM_FLAG5 & 0x10) {
SerialData.Revision = NEC1502;
}
}
break;
case 1:
//
// internal 2nd CCU
//
if ( (ROM_FLAG5 & 0x08) && ((ROM_FLAG7 & LOCKED_CCU2) == 0) ) {
PortExist = DoesPortExist( (PUCHAR)Port );
InterruptNumber = 5;
PortLength = 7;
SerialData.Revision = NEC1503;
SerialData.BaudClock = 1843200;
}
break;
case 34:
case 35:
case 36:
case 37:
//
// external FAX/Modem board
//
PortExist = DoesPortExistNECFaxModem(Port,&InterruptNumber,&PortLength);
SerialData.Revision = NEC0C01;
SerialData.BaudClock = 1843200;
break;
default:
//
// PC-9801-101 borad
//
PortExist = DoesPortExistNEC101(Port,&InterruptNumber,&PortLength);
SerialData.Revision = NEC8071;
SerialData.BaudClock = 153600;
break;
}
if (PortExist) {
//
// Remember the port address in our global variable
// such that other detection code (e.g. Serial Mouse) can
// get the information.
//
ComPortAddress[NEC_COM_number] = Port;
CurrentEntry = (FPFWCONFIGURATION_COMPONENT_DATA)HwAllocateHeap (
sizeof(FWCONFIGURATION_COMPONENT_DATA), TRUE);
if (!FirstComport) {
FirstComport = CurrentEntry;
}
Component = &CurrentEntry->ComponentEntry;
Component->Class = ControllerClass;
Component->Type = SerialController;
Component->Flags.ConsoleOut = 1;
Component->Flags.ConsoleIn = 1;
Component->Flags.Output = 1;
Component->Flags.Input = 1;
Component->Version = 0;
Component->Key = NEC_COM_number;
Component->AffinityMask = 0xffffffff;
//
// Set up type string.
//
NextComPortNumber(ComportName, NEC_COM_number);
// ComportName[3] = NEC_COM_number + (UCHAR)'1';
// NEC_COM_number++;
//
// Set up Port information
//
ControlData.NumberPortEntries = 1;
ControlData.DescriptorList[z].Type = RESOURCE_PORT;
ControlData.DescriptorList[z].ShareDisposition =
CmResourceShareDeviceExclusive;
ControlData.DescriptorList[z].Flags = CM_RESOURCE_PORT_IO;
ControlData.DescriptorList[z].u.Port.Start.LowPart = (ULONG)Port;
ControlData.DescriptorList[z].u.Port.Start.HighPart = 0;
ControlData.DescriptorList[z].u.Port.Length = PortLength;
z++;
//
// Set up Irq information
//
ControlData.NumberIrqEntries = 1;
ControlData.DescriptorList[z].Type = RESOURCE_INTERRUPT;
ControlData.DescriptorList[z].ShareDisposition =
CmResourceShareUndetermined;
ControlData.DescriptorList[z].Flags = EDGE_TRIGGERED;
ControlData.DescriptorList[z].u.Interrupt.Level = InterruptNumber;
ControlData.DescriptorList[z].u.Interrupt.Vector = InterruptNumber;
ControlData.DescriptorList[z].u.Interrupt.Affinity = ALL_PROCESSORS;
CurrentEntry->ConfigurationData =
HwSetUpResourceDescriptor(Component,
ComportName,
&ControlData,
sizeof(SerialData),
(PUCHAR)&SerialData
);
if (PreviousEntry) {
PreviousEntry->Sibling = CurrentEntry;
}
PreviousEntry = CurrentEntry;
if (Port == 0x238){
NEC2ndCCUInitialize ();
}
}
}
return(FirstComport);
#else // NEC_98
FPFWCONFIGURATION_COMPONENT_DATA CurrentEntry, PreviousEntry = NULL;
FPFWCONFIGURATION_COMPONENT_DATA FirstComport = NULL;
FPFWCONFIGURATION_COMPONENT Component;
HWCONTROLLER_DATA ControlData;
UCHAR i, j, z;
SHORT Port;
UCHAR ComportName[] = "COM?";
CM_SERIAL_DEVICE_DATA SerialData;
ULONG BaudClock = 1843200;
USHORT Vector;
BOOLEAN PortExist;
USHORT IoPorts[MAX_COM_PORTS] = {0x3f8, 0x2f8, 0x3e8, 0x2e8};
#if !defined(_GAMBIT_)
//
// BIOS DATA area 40:0 is the port address of the first valid COM port
//
USHORT far *pPortAddress = (USHORT far *)0x00400000;
#endif
//
// Initialize serial device specific data
//
SerialData.Version = 0;
SerialData.Revision = 0;
SerialData.BaudClock = 1843200;
#if !defined(_GAMBIT_)
//
// Initialize default COM port address.
// Some BIOS puts incorrect comport address to the 40:0 area.
// To cope with this problem, we test the port address supplied
// by BIOS first. If it fail, we try our default port.
//
for (i = 0; i < MAX_COM_PORTS; i++) {
for (j = 0; j < MAX_COM_PORTS; j++) {
if (IoPorts[i] == *(pPortAddress + j)) {
IoPorts[i] = 0;
break;
}
}
}
#endif
#if defined(_GAMBIT_)
for (i = 0; i < 1; i++) {
#else
for (i = 0; i < MAX_COM_PORTS; i++) {
#endif
PortExist = FALSE;
//
// Initialize Controller data
//
ControlData.NumberPortEntries = 0;
ControlData.NumberIrqEntries = 0;
ControlData.NumberMemoryEntries = 0;
ControlData.NumberDmaEntries = 0;
z = 0;
#if _GAMBIT_
Port = IoPorts[i];
PortExist = TRUE;
#else
//
// Load the port address from the BIOS data area, if it exists
//
Port = *(pPortAddress + i);
//
// Determine if the port exists
//
if (Port != 0) {
if (DoesPortExist((PUCHAR)Port)) {
PortExist = TRUE;
}
}
if (!PortExist && (Port = IoPorts[i])) {
if (PortExist = DoesPortExist((PUCHAR)Port)) {
IoPorts[i] = 0;
*(pPortAddress+i) = (USHORT)Port;
}
}
#endif //_GAMBIT_
if (PortExist) {
//
// Remember the port address in our global variable
// such that other detection code (e.g. Serial Mouse) can
// get the information.
//
ComPortAddress[i] = Port;
CurrentEntry = (FPFWCONFIGURATION_COMPONENT_DATA)HwAllocateHeap (
sizeof(FWCONFIGURATION_COMPONENT_DATA), TRUE);
if (!FirstComport) {
FirstComport = CurrentEntry;
}
Component = &CurrentEntry->ComponentEntry;
Component->Class = ControllerClass;
Component->Type = SerialController;
Component->Flags.ConsoleOut = 1;
Component->Flags.ConsoleIn = 1;
Component->Flags.Output = 1;
Component->Flags.Input = 1;
Component->Version = 0;
Component->Key = i;
Component->AffinityMask = 0xffffffff;
//
// Set up type string.
//
ComportName[3] = i + (UCHAR)'1';
//
// Set up Port information
//
ControlData.NumberPortEntries = 1;
ControlData.DescriptorList[z].Type = RESOURCE_PORT;
ControlData.DescriptorList[z].ShareDisposition =
CmResourceShareDeviceExclusive;
ControlData.DescriptorList[z].Flags = CM_RESOURCE_PORT_IO;
ControlData.DescriptorList[z].u.Port.Start.LowPart = (ULONG)Port;
ControlData.DescriptorList[z].u.Port.Start.HighPart = 0;
ControlData.DescriptorList[z].u.Port.Length = 7;
z++;
//
// Set up Irq information
//
ControlData.NumberIrqEntries = 1;
ControlData.DescriptorList[z].Type = RESOURCE_INTERRUPT;
ControlData.DescriptorList[z].ShareDisposition =
CmResourceShareUndetermined;
if (HwBusType == MACHINE_TYPE_MCA) {
ControlData.DescriptorList[z].Flags = LEVEL_SENSITIVE;
if (i == 0) { // COM1 - irql4; COM2 - COM3 - irq3
ControlData.DescriptorList[z].u.Interrupt.Level = 4;
ControlData.DescriptorList[z].u.Interrupt.Vector = 4;
} else {
ControlData.DescriptorList[z].u.Interrupt.Level = 3;
ControlData.DescriptorList[z].u.Interrupt.Vector = 3;
}
} else {
//
// For EISA the LevelTriggered is temporarily set to FALSE.
// COM1 and COM3 use irq 4; COM2 and COM4 use irq3
//
ControlData.DescriptorList[z].Flags = EDGE_TRIGGERED;
if (Port == 0x3f8 || Port == 0x3e8) {
ControlData.DescriptorList[z].u.Interrupt.Level = 4;
ControlData.DescriptorList[z].u.Interrupt.Vector = 4;
} else if (Port == 0x2f8 || Port == 0x2e8) {
ControlData.DescriptorList[z].u.Interrupt.Level = 3;
ControlData.DescriptorList[z].u.Interrupt.Vector = 3;
} else if (i == 0 || i == 2) {
ControlData.DescriptorList[z].u.Interrupt.Level = 4;
ControlData.DescriptorList[z].u.Interrupt.Vector = 4;
} else {
ControlData.DescriptorList[z].u.Interrupt.Level = 3;
ControlData.DescriptorList[z].u.Interrupt.Vector = 3;
}
}
ControlData.DescriptorList[z].u.Interrupt.Affinity = ALL_PROCESSORS;
//
// Try to determine the interrupt vector. If we success, the
// new vector will be used to replace the default value.
//
#if !defined(_GAMBIT_)
if (HwInterruptDetection((PUCHAR)Port,
SerialInterruptRequest,
SerialInterruptDismiss,
&Vector)) {
ControlData.DescriptorList[z].u.Interrupt.Level =
(ULONG)Vector;
ControlData.DescriptorList[z].u.Interrupt.Vector =
(ULONG)Vector;
}
//
// Since the com port interrupt detection destryed some
// of the com port registers, here we do the clean up.
//
WRITE_PORT_UCHAR ((PUCHAR)(Port + INTERRUPT_ENABLE_REGISTER), 0);
WRITE_PORT_UCHAR ((PUCHAR)(Port + MODEM_CONTROL_REGISTER), 0);
#endif //_GAMBIT_
CurrentEntry->ConfigurationData =
HwSetUpResourceDescriptor(Component,
ComportName,
&ControlData,
sizeof(SerialData),
(PUCHAR)&SerialData
);
if (PreviousEntry) {
PreviousEntry->Sibling = CurrentEntry;
}
PreviousEntry = CurrentEntry;
}
}
return(FirstComport);
#endif // NEC_98
}
#if !defined(_GAMBIT_)
FPFWCONFIGURATION_COMPONENT_DATA
GetLptInformation (
VOID
)
/*++
Routine Description:
This routine will attempt to detect the parallel printer port
information for the system. The information includes port address,
irq level.
Note if this code is run after user established NETWORK LPT
connection. The Network LPT will be counted as regular parallel
port.
Arguments:
None.
Return Value:
A pointer to a stucture of type PONENT_DATA
which is the root of Parallel component list.
If no comport exists, a value of NULL is returned.
--*/
{
FPFWCONFIGURATION_COMPONENT_DATA CurrentEntry, PreviousEntry = NULL;
FPFWCONFIGURATION_COMPONENT_DATA FirstLptPort = NULL;
FPFWCONFIGURATION_COMPONENT Component;
HWCONTROLLER_DATA ControlData;
UCHAR LptPortName[] = "PARALLEL?";
USHORT i, z;
USHORT LptStatus;
ULONG Port;
#if defined(NEC_98)
ULONG Portnec;
int lptportnumnec;
UCHAR level;
UCHAR j = 0;
UCHAR tmp;
USHORT PortAddress[MAX_LPT_PORTS] = { 0, 0, 0 };
USHORT far *pPortAddress = &PortAddress;
//
// check the internal LPT port is locked.
//
if ( COM_ID_L == 0x98 && COM_ID_H == 0x21 &&
ROM_FLAG7 & LOCKED_LPT ){
// The LPT was locked .
// skip the internal LPT port.
} else {
//
// try to set full centro mode
//
_asm {
push ax
mov ah, 17h
int 1Ah
mov tmp, ah
pop ax
}
if ( BIOS_FLAG5 & PRTMODE_FULL_CENTRO ){
PortAddress[0] = 0x140; // Full centro-mode
} else {
PortAddress[0] = 0x40; // Old type.
}
j++;
}
//
// check the external LPT ports.
//
// NOTE:
// There is check codes that external port is starting LPT1 or LPT2.
// at here in PC-98's old original source.
// It read 0x054F(Int Ch Read) port and check CAON (bit2).
// If the bit is clear, then the port is starting LPT1.
// I could not understand its raison d'e^tre.
//
tmp = READ_PORT_UCHAR( (PUCHAR)TOKI_CONTROL );
if ( tmp == 0xFF ){
//
// There is no external LPT ports.
//
PortAddress[j] = 0;
PortAddress[j+1] = 0;
} else {
//
// There is a extended card (PC-9801-94).
// It has 2 LPT ports.
// Change them to Full centro mode.
//
PortAddress[j] = 0x540;
WRITE_PORT_UCHAR( (PUCHAR)0x0549, (UCHAR)0x10 );
WRITE_PORT_UCHAR( (PUCHAR)0x054E, (UCHAR)0x00 );
WRITE_PORT_UCHAR( (PUCHAR)0x0542, (UCHAR)0x04 );
PortAddress[j+1] = 0xD40;
WRITE_PORT_UCHAR( (PUCHAR)0x0D49, (UCHAR)0x10 );
WRITE_PORT_UCHAR( (PUCHAR)0x0D4E, (UCHAR)0x00 );
WRITE_PORT_UCHAR( (PUCHAR)0x0D42, (UCHAR)0x04 );
//
// get int level
//
tmp = READ_PORT_UCHAR( (PUCHAR)0x54F );
switch( tmp & 0x3 ){
case 0x00:
level = 3; // Extended slot INT 0
break;
case 0x01:
level = 5; // Extended slot INT 1
break;
case 0x10:
level = 6; // Extended slot INT 2
break;
case 0x11:
level = 13; // Extended slot INT 6
}
}
#else // NEC_98
//
// BIOS DATA area 40:8 is the port address of the first valid COM port
//
USHORT far *pPortAddress = (USHORT far *)0x00400008;
#endif // NEC_98
for (i = 0; i < MAX_LPT_PORTS; i++) {
Port = (ULONG)*(pPortAddress + i);
if (Port == 0) {
continue;
#if defined(NEC_98)
#else
} else {
//
// If we think we have a lpt, we will initialize it to
// a known state. In order to make printing work under
// nt, the arbitration level must be disabled. The BIOS
// init function seems to do the trick.
//
_asm {
mov ah, 1
mov dx, i
int 17h
}
#endif
}
//
// Initialize Controller data
//
ControlData.NumberPortEntries = 0;
ControlData.NumberIrqEntries = 0;
ControlData.NumberMemoryEntries = 0;
ControlData.NumberDmaEntries = 0;
z = 0;
#if defined(NEC_98)
//
// We do not have to determine if the port exists.
// Because it was already done.
//
LptStatus = 0;
#else // NEC_98
//
// Determine if the port exists
//
LptStatus = _bios_printer(_PRINTER_STATUS, i , 0);
#endif // NEC_98
if (!(LptStatus & 6)){
CurrentEntry = (FPFWCONFIGURATION_COMPONENT_DATA)HwAllocateHeap (
sizeof(FWCONFIGURATION_COMPONENT_DATA), TRUE);
if (!FirstLptPort) {
FirstLptPort = CurrentEntry;
}
Component = &CurrentEntry->ComponentEntry;
Component->Class = ControllerClass;
Component->Type = ParallelController;
Component->Flags.Output = 1;
Component->Version = 0;
Component->Key = i;
Component->AffinityMask = 0xffffffff;
//
// Set up type string.
//
LptPortName[8] = (UCHAR)i + (UCHAR)'1';
//
// Set up Port information
//
Port = (ULONG)*(pPortAddress + i);
#if defined(NEC_98)
if (Port == 0x40){
ControlData.NumberPortEntries = 4;
for ( j = 0; j < ControlData.NumberPortEntries; j++ ){
ControlData.DescriptorList[z].Type = RESOURCE_PORT;
ControlData.DescriptorList[z].ShareDisposition =
CmResourceShareDeviceExclusive;
ControlData.DescriptorList[z].Flags = CM_RESOURCE_PORT_IO;
ControlData.DescriptorList[z].u.Port.Start.LowPart = Port + (j * 2);
ControlData.DescriptorList[z].u.Port.Start.HighPart = 0;
ControlData.DescriptorList[z].u.Port.Length = 1;
z++;
}
}else{
#endif // NEC_98
ControlData.NumberPortEntries = 1;
ControlData.DescriptorList[z].Type = RESOURCE_PORT;
ControlData.DescriptorList[z].ShareDisposition =
CmResourceShareDeviceExclusive;
ControlData.DescriptorList[z].Flags = CM_RESOURCE_PORT_IO;
ControlData.DescriptorList[z].u.Port.Start.LowPart = Port;
ControlData.DescriptorList[z].u.Port.Start.HighPart = 0;
ControlData.DescriptorList[z].u.Port.Length = 3;
z++;
#if defined(NEC_98)
}
#endif // NEC_98
//
// Set up Irq information
//
ControlData.NumberIrqEntries = 1;
ControlData.DescriptorList[z].Type = RESOURCE_INTERRUPT;
ControlData.DescriptorList[z].ShareDisposition =
CmResourceShareUndetermined;
ControlData.DescriptorList[z].u.Interrupt.Affinity = ALL_PROCESSORS;
#if defined(NEC_98)
if (i ==0) {
ControlData.DescriptorList[z].u.Interrupt.Level = 14;
ControlData.DescriptorList[z].u.Interrupt.Vector = 14;
} else {
ControlData.DescriptorList[z].u.Interrupt.Level = level;
ControlData.DescriptorList[z].u.Interrupt.Vector = level;
}
#else // NEC_98
if (i ==0) {
ControlData.DescriptorList[z].u.Interrupt.Level = 7;
ControlData.DescriptorList[z].u.Interrupt.Vector = 7;
} else {
ControlData.DescriptorList[z].u.Interrupt.Level = 5;
ControlData.DescriptorList[z].u.Interrupt.Vector = 5;
}
#endif // NEC_98
if (HwBusType == MACHINE_TYPE_MCA) {
ControlData.DescriptorList[z].Flags = LEVEL_SENSITIVE;
} else {
//
// For EISA the LevelTriggered is temporarily set to FALSE.
//
ControlData.DescriptorList[z].Flags = EDGE_TRIGGERED;
}
CurrentEntry->ConfigurationData =
HwSetUpResourceDescriptor(Component,
LptPortName,
&ControlData,
0,
NULL
);
if (PreviousEntry) {
PreviousEntry->Sibling = CurrentEntry;
}
PreviousEntry = CurrentEntry;
}
}
return(FirstLptPort);
}
#endif //_GAMBIT_