/* 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); // }