diff --git a/drivers/serial.c b/drivers/serial.c index ec921962..67d5bd80 100644 --- a/drivers/serial.c +++ b/drivers/serial.c @@ -97,16 +97,15 @@ static inline void set_dlab(io_port_t port, bool dlab) { static inline void set_port_mode(uart_config_t *cfg) { lcr_t lcr = {0}; - lcr.stop_bit = cfg->stop_bit; - lcr.width = cfg->frame_size; - lcr.parity = cfg->parity; - - outb(cfg->port + UART_LCR_REG_OFFSET, lcr.reg); - /* Set baud speed by applying divisor to DLL+DLH */ set_dlab(cfg->port, true); outw(cfg->port + UART_DLL_REG_OFFSET, DEFAULT_BAUD_SPEED / cfg->baud); set_dlab(cfg->port, false); + + lcr.stop_bit = cfg->stop_bit; + lcr.parity = cfg->parity; + lcr.width = cfg->frame_size; + outb(cfg->port + UART_LCR_REG_OFFSET, lcr.reg); } static inline bool thr_empty(io_port_t port) { @@ -127,20 +126,33 @@ static inline bool receiver_ready(io_port_t port) { void __text_init init_uart(uart_config_t *cfg) { mcr_t mcr = {0}; + fcr_t fcr = {0}; + ier_t ier = {0}; /* Enable interrupts for received data available */ - outb(cfg->port + UART_IER_REG_OFFSET, 0x01); - - /* Disable FIFO control */ - outb(cfg->port + UART_FCR_REG_OFFSET, 0x00); + ier.rx_avl = 1; + ier.rx_lsr_change = 0; + ier.msr_change = 1; + outb(cfg->port + UART_IER_REG_OFFSET, ier.reg); - /* Set 8n1 mode */ + /* Set port mode */ set_port_mode(cfg); + /* Enable FIFO control */ + fcr.enable = 1; + fcr.clear_rx = 1; + fcr.clear_tx = 1; + fcr.int_lvl = FIFO_INT_TRIGGER_LEVEL_1; + outb(cfg->port + UART_FCR_REG_OFFSET, fcr.reg); + /* Set tx/rx ready state */ mcr.dtr = 1; mcr.rts = 1; + mcr.aux = 2; outb(cfg->port + UART_MCR_REG_OFFSET, mcr.reg); + + if (com_ports[0] == NO_COM_PORT) + com_ports[0] = cfg->port; } void __text_init init_uart_input(uint8_t dst_cpus) { @@ -210,10 +222,16 @@ int serial_write(io_port_t port, const char *buf, size_t len) { } void uart_interrupt_handler(void) { - unsigned int i; - for (i = 0; i < ARRAY_SIZE(com_ports); ++i) { - uint8_t status = inb(com_ports[i] + UART_IIR_REG_OFFSET); - if ((status & UART_IIR_STATUS_MASK) == UART_IIR_RBR_READY) { + for (unsigned int i = 0; i < ARRAY_SIZE(com_ports); ++i) { + com_port_t com_port = com_ports[i]; + iir_t iir; + + if (com_port == NO_COM_PORT) + continue; + + iir.reg = inb(com_port + UART_IIR_REG_OFFSET); + if (iir.event == UART_IIR_EVENT_RXD_AVAIL || + iir.event == UART_IIR_EVENT_CHAR_TIMEOUT) { uint8_t input = inb(com_ports[i] + UART_RBR_REG_OFFSET); input_state.buf[input_state.curr] = input; diff --git a/include/drivers/serial.h b/include/drivers/serial.h index a8d5906f..6145135b 100644 --- a/include/drivers/serial.h +++ b/include/drivers/serial.h @@ -28,14 +28,6 @@ #include #include -union line_control_register { - uint8_t reg; - struct __packed { - uint8_t width : 2, stop_bit : 1, parity : 3, break_ctrl : 1, DLAB : 1; - }; -}; -typedef union line_control_register lcr_t; - enum com_idx { COM1 = 0, COM2 = 1, @@ -45,6 +37,7 @@ enum com_idx { typedef enum com_idx com_idx_t; enum com_port { + NO_COM_PORT = 0x0, COM1_PORT = 0x3f8, COM2_PORT = 0x2f8, COM3_PORT = 0x3e8, @@ -99,6 +92,14 @@ struct uart_config { }; typedef struct uart_config uart_config_t; +union line_control_register { + uint8_t reg; + struct __packed { + uint8_t width : 2, stop_bit : 1, parity : 3, break_ctrl : 1, DLAB : 1; + }; +}; +typedef union line_control_register lcr_t; + union modem_control_register { uint8_t reg; struct __packed { @@ -148,6 +149,24 @@ union interrupt_enable_register { }; typedef union interrupt_enable_register ier_t; +union interrupt_identification_register { + uint8_t reg; + struct __packed { + uint8_t no_int_pend : 1, event : 3, rsvd : 1, fifo_64b : 1, fifo_status : 2; + }; +}; +typedef union interrupt_identification_register iir_t; + +#define UART_IIR_EVENT_MSR_CHANGE 0x0 +#define UART_IIR_EVENT_THR_EMPTY 0x1 +#define UART_IIR_EVENT_RXD_AVAIL 0x2 +#define UART_IIR_EVENT_LSR_CHANGE 0x3 +#define UART_IIR_EVENT_CHAR_TIMEOUT 0x6 + +#define UART_IIR_FIFO_NO_FIFO 0x0 +#define UART_IIR_FIFO_UNUSABLE_FIFO 0x1 +#define UART_IIR_FIFO_ENABLED 0x3 + enum com_irq { COM1_IRQ = 4, /* IRQ 4 */ COM2_IRQ = 3, /* IRQ 3 */ @@ -172,9 +191,6 @@ typedef enum com_irq com_irq_t; #define UART_MSR_REG_OFFSET 0x06 #define UART_SCR_REG_OFFSET 0x07 -#define UART_IIR_STATUS_MASK 0x0E /* bits 3, 2, 1 */ -#define UART_IIR_RBR_READY 0x04 - /* External declarations */ extern io_port_t com_ports[4];