#ifndef SERIAL_H #define SERIAL_H #include #include "bda.h" enum SERIAL_UART_REGS { SERIAL_TX = 0, SERIAL_RX = 0, /** (only with DLAB enabled) LSB of divisor/baud rate */ SERIAL_DIVISOR_LO = 0, /** (only with DLAB enabled) MSB of divisor/baud rate */ SERIAL_DIVISOR_HI = 1, /** Interrupt Enable Register(read/write) */ SERIAL_IER = 1, /** Fifo Control Register (write-only) */ SERIAL_FCR = 2, /** Interrupt Identification Register (read-only) */ SERIAL_IIR = 2, /** Line Control Register (read/write), contains DLAB bit */ SERIAL_LCR = 3, /** Modem Control Register (read/write) */ SERIAL_MCR = 4, /** Line Status Register (read-only) */ SERIAL_LSR = 5, /** Modem Status Register (read-only) */ SERIAL_MSR = 6 }; #if 0 // Comes straight from https://wiki.osdev.org/Serial_Ports#Initialization outp(DLOG_TARGET_PORT + 1, 0x00); // Disable all interrupts outp(DLOG_TARGET_PORT + 3, 0x80); // Enable DLAB (set baud rate divisor) outp(DLOG_TARGET_PORT + 0, 0x01); // Set divisor to 1 (lo byte) 115200 baud outp(DLOG_TARGET_PORT + 1, 0x00); // (hi byte) outp(DLOG_TARGET_PORT + 3, 0x03); // 8 bits, no parity, one stop bit outp(DLOG_TARGET_PORT + 2, 0xC7); // Enable FIFO, clear them, with 14-byte threshold outp(DLOG_TARGET_PORT + 4, 0x03); // RTS/DSR set, IRQs disabled #endif enum SERIAL_DIVISOR { SERIAL_DIVISOR_115200 = 1, SEIRAL_DIVISOR_9600 = 12, SERIAL_DIVISOR_1200 = 96 }; enum SERIAL_FCR { SERIAL_FCR_RX_TRIGER_1 = 0 << 6, SERIAL_FCR_RX_TRIGER_4 = 1 << 6, SERIAL_FCR_RX_TRIGER_8 = 2 << 6, SERIAL_FCR_RX_TRIGER_14 = 3 << 6, SERIAL_FCR_DMA_SEL = 1 << 3, SERIAL_FCR_TX_RESET = 1 << 2, SERIAL_FCR_RX_RESET = 1 << 1, SERIAL_FCR_FIFO_ENABLE = 1 << 0, }; enum SERIAL_LCR { /** Word Length = 5 bits */ SERIAL_LCR_WL_5 = 0, SERIAL_LCR_WL_6 = 1, SERIAL_LCR_WL_7 = 2, SERIAL_LCR_WL_8 = 3, /** STop Bit */ SERIAL_LCR_STB = 1 << 2, /** Parity ENable */ SERIAL_LCR_PEN = 1 << 3, /** Even Parity Select. */ SERIAL_LCR_EPS = 1 << 4, /** Stick Parity */ SERIAL_LCR_SP = 1 << 5, SERIAL_LCR_PAR_NONE = 0, SERIAL_LCR_PAR_ODD = SERIAL_LCR_PEN, SERIAL_LCR_PAR_EVEN = SERIAL_LCR_PEN | SERIAL_LCR_EPS, SERIAL_LCR_PAR_MARK = SERIAL_LCR_PEN | SERIAL_LCR_SP, SERIAL_LCR_PAR_SPACE = SERIAL_LCR_PEN | SERIAL_LCR_EPS | SERIAL_LCR_SP, /** Set Break */ SERIAL_LCR_SB = 1 << 6, /** Divisor Latch Access Bit */ SERIAL_LCR_DLAB = 1 << 7, }; enum SERIAL_MCR { SERIAL_MCR_DTR = 1 << 0, SERIAL_MCR_RTS = 1 << 1, SERIAL_MCR_OUT1 = 1 << 2, SERIAL_MCR_OUT2 = 1 << 3, SERIAL_MCR_LOOP = 1 << 4, }; enum SERIAL_LSR { /** Data Ready (i.e., data ready to be read) */ SERIAL_LSR_DR = 1 << 0, SERIAL_LSR_OE = 1 << 1, SERIAL_LSR_PE = 1 << 2, SERIAL_LSR_FE = 1 << 3, SERIAL_LSR_BI = 1 << 4, /** Transmitter Holding Register Empty (i.e. space to send data is available) */ SERIAL_LSR_THRE = 1 << 5, SERIAL_LSR_TEMT = 1 << 6, SERIAL_LSR_RXERR = 1 << 7, }; enum SERIAL_MSR { SERIAL_MSR_DCD = 1 << 7, SERIAL_MSR_RI = 1 << 6, SERIAL_MSR_DSR = 1 << 5, SERIAL_MSR_CTS = 1 << 4, SERIAL_MSR_DDCD = 1 << 3, SERIAL_MSR_TERI = 1 << 2, SERIAL_MSR_DDSR = 1 << 1, SERIAL_MSR_DCTS = 1 << 0 }; struct serial_config { uint8_t divisor_lo, divisor_hi; uint8_t ier, lcr, mcr; }; static unsigned serial_num_ports() { uint8_t equipment = bda_get_byte(0x11); uint8_t numports = (equipment & 0xE) >> 1; return numports; } static uint16_t serial_get_iobase(unsigned port) { uint16_t bdaport = bda_get_word((port - 1) * 2); if (bdaport > 10) { return bdaport; } else if (port == 1) { // Even if BIOS says no, hardcode the basic COM1 port at least. return 0x3F8; } return 0; } static void serial_save_config(unsigned iobase, struct serial_config *config) { config->ier = inp(iobase + SERIAL_IER); config->lcr = inp(iobase + SERIAL_LCR); config->mcr = inp(iobase + SERIAL_MCR); // Set DLAB so that we can read divisor outp(iobase + SERIAL_LCR, config->lcr | SERIAL_LCR_DLAB); config->divisor_lo = inp(iobase + SERIAL_DIVISOR_LO); config->divisor_hi = inp(iobase + SERIAL_DIVISOR_HI); // Restore DLAB to whatever it was outp(iobase + SERIAL_LCR, config->lcr); } static void serial_restore_config(unsigned iobase, const struct serial_config *config) { outp(iobase + SERIAL_LCR, config->lcr | SERIAL_LCR_DLAB); outp(iobase + SERIAL_DIVISOR_LO, config->divisor_lo); outp(iobase + SERIAL_DIVISOR_HI, config->divisor_hi); outp(iobase + SERIAL_LCR, config->lcr); outp(iobase + SERIAL_MCR, config->mcr); outp(iobase + SERIAL_IER, config->ier); } static void serial_configure(unsigned iobase, uint16_t divisor, uint8_t lcr) { outp(iobase + SERIAL_IER, 0); // Disable all interrupts outp(iobase + SERIAL_LCR, SERIAL_LCR_DLAB); // Enable DLAB and clear all other flags outp(iobase + SERIAL_DIVISOR_LO, divisor); outp(iobase + SERIAL_DIVISOR_HI, divisor >> 8); outp(iobase + SERIAL_LCR, lcr); // This also disables DLAB // Enable and reset both FIFOs outp(iobase + SERIAL_FCR, SERIAL_FCR_RX_TRIGER_14|SERIAL_FCR_FIFO_ENABLE|SERIAL_FCR_RX_RESET|SERIAL_FCR_TX_RESET); } static inline void serial_set_modem_control(unsigned iobase, uint8_t mcr) { outp(iobase + SERIAL_MCR, mcr); } static inline void serial_set_modem_status(unsigned iobase, uint8_t msr) { outp(iobase + SERIAL_MSR, msr); } static inline uint8_t serial_get_line_status(unsigned iobase) { return inp(iobase + SERIAL_LSR); } static inline bool serial_data_ready(unsigned iobase) { return serial_get_line_status(iobase) & SERIAL_LSR_DR; } static inline uint8_t serial_read_data(unsigned iobase) { return inp(iobase + SERIAL_RX); } static inline bool serial_tx_ready(unsigned iobase) { return serial_get_line_status(iobase) & SERIAL_LSR_THRE; } static inline void serial_send_data(unsigned iobase, uint8_t data) { outp(iobase + SERIAL_TX, data); } #endif // SERIAL_H