#include #include #include #include #include /* I/O ports */ /** * All the I/O ports are calculated relative to their base. * The ports (COM1, COM2, etc) have their ports in the same order, * but they start at different values. */ #define SERIAL_COM1_BASE 0x3F8 #define SERIAL_DATA_PORT(base) (base) #define SERIAL_FIFO_COMMAND_PORT(base) (base + 2) #define SERIAL_LINE_COMMAND_PORT(base) (base + 3) #define SERIAL_MODEM_COMMAND_PORT(base) (base + 4) #define SERIAL_LINE_STATUS_PORT(base) (base + 5) /* I/O port commands */ /** * SERIAL_LINE_ENABLE_DLAB: * Tells the serial port to expect first the highest 8 bits on the data port, * then the lowest 8 bits will follow */ #define SERIAL_LINE_ENABLE_DLAB 0x80 void serial_configure_baud_rate(unsigned short com, unsigned short divisor) { outb(SERIAL_LINE_COMMAND_PORT(com), SERIAL_LINE_ENABLE_DLAB); outb(SERIAL_DATA_PORT(com), (divisor >> 8) & 0x00FF); outb(SERIAL_DATA_PORT(com), divisor & 0x00FF); } void serial_configure_line(unsigned short com) { /** * Bit: | 7 | 6 | 5 4 3 | 2 | 1 0 | * Content: | d | b | prty | s | dl | * d - Enabled (d = 1) or disables (d = 0) DLAB * b - If break control is enabled (b = 1) or disabled (b = 0) * prty - The number of parity bits to use * s - The number of stop bits to use (s = 0 equals 1, s = 1 equals 1.5 or 2) * dl - Describes the length of the data * Value: | 0 | 0 | 0 0 0 | 0 | 1 1 | = 0x03 */ outb(SERIAL_LINE_COMMAND_PORT(com), 0x03); } void serial_configure_buffers(unsigned short com) { /** * Bit: | 7 6 | 5 | 4 | 3 | 2 | 1 | 0 | * Content: | lvl | bs | r | dma | clt | clr | e | * lvl - How many bytes should be stored in the FIFO buffers * bs - If the buffers should be 16 or 64 bytes large * r - Reserved for future use * dma - How the serial port data should be accessed * clt - Clear the transmission FIFO buffer * clr - Clear the receiver FIFO buffer * e - If the FIFO buffer should be enabled or not * Value: | 1 1 | 0 | 0 | 0 | 1 | 1 | 1 | */ outb(SERIAL_FIFO_COMMAND_PORT(com), 0xC7); } void serial_configure_modem(unsigned short com) { /** * Bit: | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | * Content: | r | r | af | lb | ao2 | ao1 | rts | dtr | * r - Reserved * af - Autoflow control enabled * lb - Loopback mode (used for debugging serial ports) * ao2 - Auxiliary output 2, used for receiving interrupts * ao1 - Auxiliary output 1 * rts - Ready to transmit * dtr - Data Terminal ready * Value: | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | */ outb(SERIAL_MODEM_COMMAND_PORT(com), 0x03); } int serial_is_transmit_fifo_empty(unsigned int com) { /* 0x20 = 0010 0000 */ return inb(SERIAL_LINE_STATUS_PORT(com)) & 0x20; } void serial_initialize(void) { serial_configure_baud_rate(SERIAL_COM1_BASE, 2); serial_configure_line(SERIAL_COM1_BASE); serial_configure_buffers(SERIAL_COM1_BASE); serial_configure_modem(SERIAL_COM1_BASE); } void serial_write_byte(unsigned short com, char c) { while (!serial_is_transmit_fifo_empty(com)); outb(SERIAL_DATA_PORT(com), c); } void serial_write(const char* buf, size_t len) { for (size_t i = 0; i < len; i++) serial_write_byte(SERIAL_COM1_BASE, buf[i]); } void serial_writestring(const char* buf) { serial_write(buf, strlen(buf)); }