diff --git a/input/aip/aip.h b/input/aip/aip.h index 95ea010..ed4cc77 100644 --- a/input/aip/aip.h +++ b/input/aip/aip.h @@ -34,5 +34,6 @@ void PS2MouseInterruptHandler(TrapFrame *); int InitializeMouse(); int FinalizeMouse(); int DetectPS2Mouse(); +int DetectUART(); #endif // !__FENNIX_DRIVER_AIP_H__ diff --git a/input/aip/main.c b/input/aip/main.c index 606927c..aa53058 100644 --- a/input/aip/main.c +++ b/input/aip/main.c @@ -194,10 +194,11 @@ int DriverProbe() int kbd = DetectPS2Keyboard(); int mouse = DetectPS2Mouse(); + int uart = DetectUART(); UnregisterAllInterruptHandlers(__intStub); - if (kbd != 0 && mouse != 0) + if (kbd != 0 && mouse != 0 && uart != 0) return -ENODEV; if (kbd == 0) @@ -219,11 +220,11 @@ int DriverProbe() } KernelPrint("PS/2 Port 1: %s (0x%X 0x%X)", - GetPS2DeviceName(Device1ID[0], Device1ID[1]), - Device1ID[0], Device1ID[1]); + GetPS2DeviceName(Device1ID[0], Device1ID[1]), + Device1ID[0], Device1ID[1]); KernelPrint("PS/2 Port 2: %s (0x%X 0x%X)", - GetPS2DeviceName(Device2ID[0], Device2ID[1]), - Device2ID[0], Device2ID[1]); + GetPS2DeviceName(Device2ID[0], Device2ID[1]), + Device2ID[0], Device2ID[1]); return 0; } diff --git a/input/aip/uart.c b/input/aip/uart.c new file mode 100644 index 0000000..832ef8f --- /dev/null +++ b/input/aip/uart.c @@ -0,0 +1,646 @@ +/* + 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 . +*/ + +#include "aip.h" + +#include +#include +#include +#include +#include + +#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); +// }