mirror of
https://github.com/Fennix-Project/Drivers.git
synced 2025-05-25 22:14:31 +00:00
647 lines
13 KiB
C
647 lines
13 KiB
C
/*
|
|
This file is part of Fennix Drivers.
|
|
|
|
Fennix Drivers 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, either version 3 of
|
|
the License, or (at your option) any later version.
|
|
|
|
Fennix Drivers is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with Fennix Drivers. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "aip.h"
|
|
|
|
#include <errno.h>
|
|
#include <base.h>
|
|
#include <fs.h>
|
|
#include <input.h>
|
|
#include <io.h>
|
|
|
|
#define SERIAL_ENABLE_DLAB 0x80
|
|
#define SERIAL_BUFFER_EMPTY 0x20
|
|
|
|
enum Ports
|
|
{
|
|
COM1 = 0x3F8,
|
|
COM2 = 0x2F8,
|
|
COM3 = 0x3E8,
|
|
COM4 = 0x2E8,
|
|
COM5 = 0x5F8,
|
|
COM6 = 0x4F8,
|
|
COM7 = 0x5E8,
|
|
COM8 = 0x4E8,
|
|
|
|
LPT1 = 0x378,
|
|
LPT2 = 0x278,
|
|
LPT3 = 0x3BC
|
|
};
|
|
|
|
enum SerialSpeed
|
|
{
|
|
RATE_50_HI = 0x09,
|
|
RATE_50_LO = 0x00,
|
|
|
|
RATE_300_HI = 0x01,
|
|
RATE_300_LO = 0x80,
|
|
|
|
RATE_600_HI = 0x00,
|
|
RATE_600_LO = 0xC0,
|
|
|
|
RATE_2400_HI = 0x00,
|
|
RATE_2400_LO = 0x30,
|
|
|
|
RATE_4800_HI = 0x00,
|
|
RATE_4800_LO = 0x18,
|
|
|
|
RATE_9600_HI = 0x00,
|
|
RATE_9600_LO = 0x0C,
|
|
|
|
RATE_19200_HI = 0x00,
|
|
RATE_19200_LO = 0x06,
|
|
|
|
RATE_38400_HI = 0x00,
|
|
RATE_38400_LO = 0x03,
|
|
|
|
RATE_57600_HI = 0x00,
|
|
RATE_57600_LO = 0x02,
|
|
|
|
RATE_115200_HI = 0x00,
|
|
RATE_115200_LO = 0x01
|
|
};
|
|
|
|
/*
|
|
. Table of Registers .
|
|
/---------------------------------------------------------------------\
|
|
| Base Address | DLAB | R/W | Abr | Register Name |
|
|
|---------------------------------------------------------------------|
|
|
| +0 | =0 | W | - | Transmitter Holding Buffer |
|
|
| | =0 | R | - | Receiver Buffer |
|
|
| | =1 | R/W | - | Divisor Latch Low Byte |
|
|
| +1 | =0 | R/W | IER | Interrupt Enable Register |
|
|
| | =1 | R/W | - | Divisor Latch High Byte |
|
|
| +2 | - | R | IIR | Interrupt Identification Register |
|
|
| | - | W | FCR | FIFO Control Register |
|
|
| +3 | - | R/W | LCR | Line Control Register |
|
|
| +4 | - | R/W | MCR | Modem Control Register |
|
|
| +5 | - | R | LSR | Line Status Register |
|
|
| +6 | - | R | MSR | Modem Status Register |
|
|
| +7 | - | R/W | - | Scratch Register |
|
|
\---------------------------------------------------------------------/
|
|
|
|
Source:
|
|
Interfacing the Serial / RS232 Port V5.0
|
|
Table 5 : Table of Registers
|
|
*/
|
|
|
|
/** Interrupt Enable Register */
|
|
typedef union
|
|
{
|
|
struct
|
|
{
|
|
/* Enable Received Data Available Interrupt */
|
|
uint8_t InterruptOnReceive : 1;
|
|
|
|
/* Enable Transmitter Holding Register Empty Interrupt */
|
|
uint8_t InterruptOnTransmitter : 1;
|
|
|
|
/* Enable Receiver Line Status Interrupt */
|
|
uint8_t LineStatusInterrupt : 1;
|
|
|
|
/* Enable Modem Status Interrupt */
|
|
uint8_t ModemStatusInterrupt : 1;
|
|
|
|
/* Enables Sleep Mode (16750) */
|
|
uint8_t SleepMode : 1;
|
|
|
|
/* Enables Low Power Mode (16750) */
|
|
uint8_t LowPowerMode : 1;
|
|
|
|
/* Reserved */
|
|
uint8_t __reserved : 2;
|
|
};
|
|
uint8_t raw;
|
|
} IER;
|
|
|
|
/** Interrupt Identification Register */
|
|
typedef union
|
|
{
|
|
struct
|
|
{
|
|
/* Interrupt pending */
|
|
uint8_t InterruptPending : 1;
|
|
|
|
/**
|
|
* Interrupt Status
|
|
*
|
|
* 00b = Modem Status Interrupt
|
|
* 01b = Transmitter Holding Register Empty Interrupt
|
|
* 10b = Received Data Available Interrupt
|
|
* 11b = Receiver Line Status Interrupt
|
|
*/
|
|
uint8_t InterruptStatus : 2;
|
|
|
|
/**
|
|
* 16550 Time-out Interrupt Pending
|
|
*
|
|
* @note Reserved on 8250, 16450
|
|
*/
|
|
uint8_t TimeOutIP : 1;
|
|
|
|
/** Reserved */
|
|
uint8_t __reserved : 1;
|
|
|
|
/** 64 Byte Fifo Enabled (16750 only) */
|
|
uint8_t FIFO64 : 1;
|
|
|
|
/**
|
|
* Enable FIFO
|
|
*
|
|
* 00b = No FIFO
|
|
* 01b = FIFO Enabled but Unusable
|
|
* 11b = FIFO Enabled
|
|
*/
|
|
uint8_t FIFO : 2;
|
|
};
|
|
uint8_t raw;
|
|
} IIR;
|
|
|
|
/** First In / First Out Control Register */
|
|
typedef union
|
|
{
|
|
struct
|
|
{
|
|
/** Enable FIFO's */
|
|
uint8_t FIFO : 1;
|
|
|
|
/** Clear Receive FIFO */
|
|
uint8_t ClearRX : 1;
|
|
|
|
/** Clear Transmit FIFO */
|
|
uint8_t ClearTX : 1;
|
|
|
|
/** DMA Mode Select.
|
|
*
|
|
* Change status of RXRDY & TXRDY pins from mode 1 to mode 2.
|
|
*/
|
|
uint8_t DMAMode : 1;
|
|
|
|
/** Reserved */
|
|
uint8_t __reserved : 1;
|
|
|
|
/** Enable 64 Byte FIFO (16750 only) */
|
|
uint8_t FIFO64 : 1;
|
|
|
|
/** Interrupt Trigger Level
|
|
*
|
|
* 00b = 1 Byte
|
|
* 01b = 4 Bytes
|
|
* 10b = 8 Bytes
|
|
* 11b = 14 Bytes
|
|
*/
|
|
uint8_t TriggerLevel : 2;
|
|
};
|
|
uint8_t raw;
|
|
} FCR;
|
|
|
|
/** Line Control Register */
|
|
typedef union
|
|
{
|
|
struct
|
|
{
|
|
/** Word Length
|
|
*
|
|
* 00b = 5 bits
|
|
* 01b = 6 bits
|
|
* 10b = 7 bits
|
|
* 11b = 8 bits
|
|
*/
|
|
uint8_t WordLength : 2;
|
|
|
|
/** Length of Stop Bit
|
|
*
|
|
* 0b = One Stop Bit
|
|
* 1b = 2 Stop bits for words of length 6,7 or 8 bits or 1.5 Stop Bits for Word lengths of 5 bits.
|
|
*/
|
|
uint8_t StopBit : 1;
|
|
|
|
/** Parity Select
|
|
*
|
|
* 0b = No Parity
|
|
* 001b = Odd Parity
|
|
* 011b = Even Parity
|
|
* 101b = High Parity (Sticky)
|
|
* 111b = Low Parity (Sticky)
|
|
*/
|
|
uint8_t Parity : 3;
|
|
|
|
/** Set Break Enable */
|
|
uint8_t SetBreak : 1;
|
|
|
|
/**
|
|
* Divisor Latch Access
|
|
*
|
|
* 0b = Access to Receiver buffer, Transmitter buffer & Interrupt Enable Register
|
|
* 1b = Divisor Latch Access Bit
|
|
*/
|
|
uint8_t DLAB : 1;
|
|
};
|
|
uint8_t raw;
|
|
} LCR;
|
|
|
|
/** Modem Control Register */
|
|
typedef union
|
|
{
|
|
struct
|
|
{
|
|
/** Force Data Terminal Ready */
|
|
uint8_t DataTerminalReady : 1;
|
|
|
|
/** Force Request to Send */
|
|
uint8_t RequestToSend : 1;
|
|
|
|
/** Auxiliary Output 1 */
|
|
uint8_t Out1 : 1;
|
|
|
|
/** Auxiliary Output 2 */
|
|
uint8_t Out2 : 1;
|
|
|
|
/** Loopback Mode */
|
|
uint8_t Loopback : 1;
|
|
|
|
/** Autoflow Control Enabled (16750 only) */
|
|
uint8_t Autoflow : 1;
|
|
|
|
/** Reserved */
|
|
uint8_t __reserved : 2;
|
|
};
|
|
uint8_t raw;
|
|
} MCR;
|
|
|
|
/** Line Status Register */
|
|
typedef union
|
|
{
|
|
struct
|
|
{
|
|
/** Data Ready */
|
|
uint8_t DataReady : 1;
|
|
|
|
/** Overrun Error */
|
|
uint8_t OverrunError : 1;
|
|
|
|
/** Parity Error */
|
|
uint8_t ParityError : 1;
|
|
|
|
/** Framing Error */
|
|
uint8_t FramingError : 1;
|
|
|
|
/** Break Interrupt */
|
|
uint8_t BreakInterrupt : 1;
|
|
|
|
/** Empty Transmitter Holding Register */
|
|
uint8_t EmptyTransmitterHolding : 1;
|
|
|
|
/** Empty Data Holding Registers */
|
|
uint8_t EmptyDataHolding : 1;
|
|
|
|
/** Error in Received FIFO */
|
|
uint8_t ErrorReceivedFIFO : 1;
|
|
};
|
|
uint8_t raw;
|
|
} LSR;
|
|
|
|
/** Modem Status Register */
|
|
typedef union
|
|
{
|
|
struct
|
|
{
|
|
/** Delta Clear to Send */
|
|
uint8_t DeltaClearToSend : 1;
|
|
|
|
/** Delta Data Set Ready */
|
|
uint8_t DeltaDataSetReady : 1;
|
|
|
|
/** Trailing Edge Ring Indicator */
|
|
uint8_t TrailingEdgeRingIndicator : 1;
|
|
|
|
/** Delta Data Carrier Detect */
|
|
uint8_t DeltaDataCarrierDetect : 1;
|
|
|
|
/** Clear To Send */
|
|
uint8_t ClearToSend : 1;
|
|
|
|
/** Data Set Ready */
|
|
uint8_t DataSetReady : 1;
|
|
|
|
/** Ring Indicator */
|
|
uint8_t RingIndicator : 1;
|
|
|
|
/** Carrier Detect */
|
|
uint8_t CarrierDetect : 1;
|
|
};
|
|
uint8_t raw;
|
|
} MSR;
|
|
|
|
union UARTs
|
|
{
|
|
struct
|
|
{
|
|
uint8_t com1 : 1;
|
|
uint8_t com2 : 1;
|
|
uint8_t com3 : 1;
|
|
uint8_t com4 : 1;
|
|
uint8_t com5 : 1;
|
|
uint8_t com6 : 1;
|
|
uint8_t com7 : 1;
|
|
uint8_t com8 : 1;
|
|
|
|
uint8_t lpt1 : 1;
|
|
uint8_t lpt2 : 1;
|
|
uint8_t lpt3 : 1;
|
|
|
|
uint8_t __reserved : 5;
|
|
};
|
|
uint16_t raw;
|
|
} uart;
|
|
|
|
bool IsDataReady(uint16_t Port)
|
|
{
|
|
LSR lsr;
|
|
lsr.raw = inb(Port + 5);
|
|
return lsr.DataReady;
|
|
}
|
|
|
|
bool IsTransmitEmpty(uint16_t Port)
|
|
{
|
|
LSR lsr;
|
|
lsr.raw = inb(Port + 5);
|
|
return lsr.EmptyTransmitterHolding;
|
|
}
|
|
|
|
char ReadSerial(uint16_t Port)
|
|
{
|
|
while (!IsDataReady(Port))
|
|
Yield();
|
|
return inb(Port);
|
|
}
|
|
|
|
void WriteSerial(uint16_t Port, char Character)
|
|
{
|
|
while (!IsTransmitEmpty(Port))
|
|
Yield();
|
|
outb(Port, Character);
|
|
}
|
|
|
|
void ReportSerialReceived(uint8_t Data)
|
|
{
|
|
DebugLog("%c", Data);
|
|
}
|
|
|
|
void UartCOM24(TrapFrame *)
|
|
{
|
|
LSR lsr2, lsr4;
|
|
do
|
|
{
|
|
lsr2.raw = inb(COM2 + 5);
|
|
if (lsr2.DataReady)
|
|
ReportSerialReceived(inb(COM2));
|
|
lsr4.raw = inb(COM4 + 5);
|
|
if (lsr4.DataReady)
|
|
ReportSerialReceived(inb(COM4));
|
|
} while (lsr2.DataReady || lsr4.DataReady);
|
|
}
|
|
|
|
void UartCOM13(TrapFrame *)
|
|
{
|
|
LSR lsr1, lsr3;
|
|
do
|
|
{
|
|
lsr1.raw = inb(COM1 + 5);
|
|
if (lsr1.DataReady)
|
|
ReportSerialReceived(inb(COM1));
|
|
lsr3.raw = inb(COM3 + 5);
|
|
if (lsr3.DataReady)
|
|
ReportSerialReceived(inb(COM3));
|
|
} while (lsr1.DataReady || lsr3.DataReady);
|
|
}
|
|
|
|
bool InitializePort(uint16_t Port)
|
|
{
|
|
ECS;
|
|
LCR lcr = {0};
|
|
IER ier = {0};
|
|
FCR fcr = {0};
|
|
MCR mcr = {0};
|
|
|
|
outb(Port + 3, lcr.raw);
|
|
outb(Port + 1, ier.raw);
|
|
|
|
lcr.DLAB = 1;
|
|
outb(Port + 3, lcr.raw);
|
|
|
|
outb(Port + 0, RATE_115200_LO);
|
|
outb(Port + 1, RATE_115200_HI);
|
|
|
|
lcr.DLAB = 0;
|
|
lcr.WordLength = 0b11;
|
|
outb(Port + 3, lcr.raw);
|
|
|
|
fcr.FIFO = 1;
|
|
fcr.ClearRX = 1;
|
|
fcr.ClearTX = 1;
|
|
fcr.TriggerLevel = 0b11;
|
|
outb(Port + 2, fcr.raw);
|
|
|
|
mcr.DataTerminalReady = 1;
|
|
mcr.RequestToSend = 1;
|
|
mcr.Out2 = 1;
|
|
mcr.Loopback = 1;
|
|
outb(Port + 4, mcr.raw);
|
|
|
|
/* Test the serial port */
|
|
outb(Port + 0, 0x48);
|
|
uint8_t result = inb(Port + 0);
|
|
if (result != 0x48)
|
|
{
|
|
/* FIXME: DETECT BAUD RATE
|
|
Do multiple test to check if the output is garbage.
|
|
If so, reduce the baud rate until it works. */
|
|
|
|
LCS;
|
|
KernelPrint("Port %#X test failed!", Port);
|
|
return false;
|
|
}
|
|
|
|
/* Set normal operation mode */
|
|
mcr.DataTerminalReady = 1;
|
|
mcr.RequestToSend = 1;
|
|
mcr.Out1 = 1;
|
|
mcr.Out2 = 1;
|
|
mcr.Loopback = 0;
|
|
outb(Port + 4, mcr.raw);
|
|
|
|
/* Enable interrupts on receive */
|
|
ier.InterruptOnReceive = 1;
|
|
outb(Port + 1, ier.raw);
|
|
RegisterInterruptHandler(3, UartCOM24);
|
|
RegisterInterruptHandler(4, UartCOM13);
|
|
|
|
LCS;
|
|
KernelPrint("Port %#X initialized", Port);
|
|
return true;
|
|
}
|
|
|
|
int DetectUART()
|
|
{
|
|
uart.com1 = inb(COM1) != 0xFF ? true : false;
|
|
uart.com2 = inb(COM2) != 0xFF ? true : false;
|
|
uart.com3 = inb(COM3) != 0xFF ? true : false;
|
|
uart.com4 = inb(COM4) != 0xFF ? true : false;
|
|
uart.com5 = inb(COM5) != 0xFF ? true : false;
|
|
uart.com6 = inb(COM6) != 0xFF ? true : false;
|
|
uart.com7 = inb(COM7) != 0xFF ? true : false;
|
|
uart.com8 = inb(COM8) != 0xFF ? true : false;
|
|
|
|
uart.lpt1 = inb(LPT1) != 0xFF ? true : false;
|
|
uart.lpt2 = inb(LPT2) != 0xFF ? true : false;
|
|
uart.lpt3 = inb(LPT3) != 0xFF ? true : false;
|
|
|
|
if (uart.com1 == true)
|
|
if (InitializePort(COM1) == false)
|
|
uart.com1 = false;
|
|
|
|
if (uart.com2 == true)
|
|
if (InitializePort(COM2) == false)
|
|
uart.com1 = false;
|
|
|
|
if (uart.com3 == true)
|
|
if (InitializePort(COM3) == false)
|
|
uart.com1 = false;
|
|
|
|
if (uart.com4 == true)
|
|
if (InitializePort(COM4) == false)
|
|
uart.com1 = false;
|
|
|
|
if (uart.com5 == true)
|
|
if (InitializePort(COM5) == false)
|
|
uart.com1 = false;
|
|
|
|
if (uart.com6 == true)
|
|
if (InitializePort(COM6) == false)
|
|
uart.com1 = false;
|
|
|
|
if (uart.com7 == true)
|
|
if (InitializePort(COM7) == false)
|
|
uart.com1 = false;
|
|
|
|
if (uart.com8 == true)
|
|
if (InitializePort(COM8) == false)
|
|
uart.com1 = false;
|
|
|
|
if (uart.lpt1 == true)
|
|
KernelPrint("LPT1 is present");
|
|
|
|
if (uart.lpt2 == true)
|
|
KernelPrint("LPT2 is present");
|
|
|
|
if (uart.lpt3 == true)
|
|
KernelPrint("LPT3 is present");
|
|
return 0;
|
|
}
|
|
|
|
// static int once = 0;
|
|
// static uint8_t com4 = 0xFF;
|
|
// if (!once++)
|
|
// com4 = inb(0x2E8);
|
|
// if (com4 == 0xFF)
|
|
// CPU::Halt(true);
|
|
// char UserInputBuffer[256]{'\0'};
|
|
// int BackSpaceLimit = 0;
|
|
// while (true)
|
|
// {
|
|
// while ((inb(0x2E8 + 5) & 1) == 0)
|
|
// CPU::Pause();
|
|
// char key = inb(0x2E8);
|
|
// // debug("key: %d", key);
|
|
// if (key == '\x7f') /* Backspace (DEL) */
|
|
// {
|
|
// if (BackSpaceLimit <= 0)
|
|
// continue;
|
|
// char keyBuf[5] = {'\b', '\x1b', '[', 'K', '\0'};
|
|
// ExPrint(keyBuf);
|
|
// backspace(UserInputBuffer);
|
|
// BackSpaceLimit--;
|
|
// continue;
|
|
// }
|
|
// else if (key == '\x0d') /* Enter (CR) */
|
|
// {
|
|
// UserInput(UserInputBuffer);
|
|
// BackSpaceLimit = 0;
|
|
// UserInputBuffer[0] = '\0';
|
|
// continue;
|
|
// }
|
|
// else if (key == '\x1b') /* Escape */
|
|
// {
|
|
// char tmp[16]{'\0'};
|
|
// append(tmp, key);
|
|
// while ((inb(0x2E8 + 5) & 1) == 0)
|
|
// CPU::Pause();
|
|
// char key = inb(0x2E8);
|
|
// append(tmp, key);
|
|
// if (key == '[')
|
|
// {
|
|
// // 27 91
|
|
// // < 68
|
|
// // > 67
|
|
// // down 66
|
|
// // up 65
|
|
// while ((inb(0x2E8 + 5) & 1) == 0)
|
|
// CPU::Pause();
|
|
// key = inb(0x2E8);
|
|
// append(tmp, key);
|
|
// switch (key)
|
|
// {
|
|
// case 'A':
|
|
// key = KEY_D_UP;
|
|
// break;
|
|
// case 'B':
|
|
// key = KEY_D_DOWN;
|
|
// break;
|
|
// case 'C':
|
|
// key = KEY_D_RIGHT;
|
|
// break;
|
|
// case 'D':
|
|
// key = KEY_D_LEFT;
|
|
// break;
|
|
// default:
|
|
// {
|
|
// for (size_t i = 0; i < strlen(tmp); i++)
|
|
// {
|
|
// if ((int)sizeof(UserInputBuffer) <= BackSpaceLimit)
|
|
// continue;
|
|
// append(UserInputBuffer, tmp[i]);
|
|
// BackSpaceLimit++;
|
|
// char keyBuf[2] = {(char)tmp[i], '\0'};
|
|
// ExPrint(keyBuf);
|
|
// }
|
|
// continue;
|
|
// }
|
|
// }
|
|
// ArrowInput(key);
|
|
// continue;
|
|
// }
|
|
// }
|
|
|
|
// if ((int)sizeof(UserInputBuffer) <= BackSpaceLimit)
|
|
// continue;
|
|
// append(UserInputBuffer, key);
|
|
// BackSpaceLimit++;
|
|
// char keyBuf[2] = {(char)key, '\0'};
|
|
// ExPrint(keyBuf);
|
|
// }
|