diff options
Diffstat (limited to 'src/kernel/serial.c')
-rw-r--r-- | src/kernel/serial.c | 121 |
1 files changed, 121 insertions, 0 deletions
diff --git a/src/kernel/serial.c b/src/kernel/serial.c new file mode 100644 index 0000000..c5a6d95 --- /dev/null +++ b/src/kernel/serial.c @@ -0,0 +1,121 @@ +#include "serial.h" +#include "string.h" +#include "stdlib.h" + +// COM1 - I/O ports +#define COM1_PORT_BASE 0x3F8 + +// RDR - Receive buffer register (on port read, if DLAB=0) +// THR - Transmitter holding register (on port write, if DLAB=0) +// DL - Divisor Latch (on port write, LSB, if DLAB=1) +#define COM1_TRANSMIT_PORT COM1_PORT_BASE + +// IER - Interrupt Enable Register (if DLAB=0) +// DL - Divisor Latch (on port write, MSB, if DLAB=1) +#define COM1_INTERRUPT_ENABLE_PORT COM1_PORT_BASE + 1 + +// IIR - Interrupt Identification Register (on read) +// FCR - FIFO Control Register (write only) +#define COM1_FIFO_CONTROL_PORT COM1_PORT_BASE + 2 + +// LCR - Line Control Register +#define COM1_LCR_PORT COM1_PORT_BASE + 3 + +// MCR - Modem Control Register +#define COM1_MCR_PORT COM1_PORT_BASE + 4 + +// LSR - Line Status Register +#define COM1_LSR_PORT COM1_PORT_BASE + 5 + +// IER - Interrupt Enable Register +#define IER_ENABLE_IRQ_DATA_READY 0x01 +#define IER_ENABLE_IRQ_WRITE_READY 0x02 +#define IER_ENABLE_IRQ_STATUS 0x04 +#define IER_ENABLE_IRQ_MODEM_STATUS 0x08 + +// LSR - Line Status Register +#define LSR_THRE 0x20 // Transmitter Holding Register Empty + +// LCR - Line Control Register +#define LCR_DATA_BITS_5 0x00 +#define LCR_DATA_BITS_6 0x01 +#define LCR_DATA_BITS_7 0x02 +#define LCR_DATA_BITS_8 0x03 +#define LCR_ONE_STOPBIT 0x00 +#define LCR_TWO_STOPBITS 0x04 +#define LCR_PARITY_ENABLE 0x08 +#define LCR_PARITY_EVEN 0x10 +#define LCR_PARITY_STICKY 0x20 +#define LCR_SET_BREAK 0x40 +#define LCR_DLAB_ENABLE 0x80 // DLAB=1/0, baud rate DL register selector + +// MCR - Modem Control Register +#define MCR_DTR_LOW 0x01 +#define MCR_DTR_RTS_LOW 0x02 +#define MCR_DTS_OUT2_LOW 0x04 +#define MCR_LOCAL_LOOPBACK 0x08 + +// baud rate helpers (division of 115200 by divisor results in baud rate) +#define BAUD_RATE_DIVISOR_115200 1 +#define BAUD_RATE_DIVISOR_57600 2 +#define BAUD_RATE_DIVISOR_38400 3 +#define BAUD_RATE_DIVISOR_19200 6 +#define BAUD_RATE_DIVISOR_14400 8 +#define BAUD_RATE_DIVISOR_9600 12 +#define BAUD_RATE_DIVISOR_4800 24 +#define BAUD_RATE_DIVISOR_2400 48 +#define BAUD_RATE_DIVISOR_1200 96 +#define BAUD_RATE_DIVISOR_300 384 + +void serial_init( serial_t *serial ) +{ + memset( serial, 0, sizeof( serial_t ) ); + + port8_init( &serial->port_transmit, COM1_TRANSMIT_PORT ); + port8_init( &serial->port_interrupt_enable, COM1_INTERRUPT_ENABLE_PORT ); + port8_init( &serial->port_fifo_control, COM1_FIFO_CONTROL_PORT ); + port8_init( &serial->port_line_control, COM1_LCR_PORT ); + port8_init( &serial->port_modem_control, COM1_MCR_PORT ); + port8_init( &serial->port_line_status, COM1_LSR_PORT ); + + // disable all interrupts + port8_write( &serial->port_interrupt_enable, 0x00 ); + + // set baud rate, enable DLAB so we can access the DL registers + port8_write( &serial->port_line_control, + LCR_DATA_BITS_8 | LCR_DLAB_ENABLE ); + port8_write( &serial->port_transmit, BAUD_RATE_DIVISOR_9600 ); + port8_write( &serial->port_interrupt_enable, 0 ); + port8_write( &serial->port_line_control, + LCR_DATA_BITS_8 ); + + // disable FIFO (16550+ only, TODO: probe?) + port8_write( &serial->port_fifo_control, 0x00 ); + + // No loopback + // TODO: more? + port8_write( &serial->port_modem_control, 0x00 ); +} + +void serial_put_char( serial_t *serial, const char c ) +{ + uint8_t status; + do { + status = port8_read( &serial->port_line_status ); + } while( ( status & LSR_THRE ) == 0 ); + + port8_write( &serial->port_transmit, c ); +} + +void serial_put_string( serial_t *serial, const char *s ) +{ + for( size_t i = 0; i < strlen( s ); i++ ) { + serial_put_char( serial, s[i] ); + } +} + +void serial_put_newline( serial_t *serial ) +{ + serial_put_char( serial, '\r' ); + serial_put_char( serial, '\n' ); +} |