diff --git a/src/kernel/device_manager.cpp b/src/kernel/device_manager.cpp index 2cb82f0..89ea101 100644 --- a/src/kernel/device_manager.cpp +++ b/src/kernel/device_manager.cpp @@ -12,6 +12,7 @@ #include "kernel_memory.h" #include "log.h" #include "objects/endpoint.h" +#include "serial.h" static endpoint * const ignore_endpoint = reinterpret_cast(-1ull); @@ -50,17 +51,6 @@ acpi_table_header::validate(uint32_t expected_type) const return !expected_type || (expected_type == type); } -void irq2_callback(void *) -{ -} - -void irq4_callback(void *) -{ - // TODO: move this to a real serial driver - console *cons = console::get(); - cons->echo(); -} - device_manager::device_manager() : m_lapic_base(0) @@ -269,6 +259,9 @@ device_manager::load_apic(const acpi_table_header *header) p += length; } + + m_ioapics[0].mask(3, false); + m_ioapics[0].mask(4, false); } void @@ -384,6 +377,11 @@ device_manager::init_drivers() bool device_manager::dispatch_irq(unsigned irq) { + if (irq == 4) { + g_com1.handle_interrupt(); + return true; + } + if (irq >= m_irqs.count()) return false; diff --git a/src/kernel/main.cpp b/src/kernel/main.cpp index ff64ee2..a4f18f3 100644 --- a/src/kernel/main.cpp +++ b/src/kernel/main.cpp @@ -183,6 +183,7 @@ kernel_main(init::args *args) unsigned num_cpus = start_aps(*apic, apic_ids, args->pml4); interrupts_enable(); + g_com1.handle_interrupt(); /* block_device *disk = devices->get_block_device(0); diff --git a/src/kernel/serial.cpp b/src/kernel/serial.cpp index 27301a5..a639034 100644 --- a/src/kernel/serial.cpp +++ b/src/kernel/serial.cpp @@ -1,4 +1,7 @@ +#include "kutil/assert.h" +#include "kutil/memory.h" #include "kutil/no_construct.h" +#include "interrupts.h" #include "io.h" #include "serial.h" @@ -8,35 +11,152 @@ static kutil::no_construct __g_com1_storage; serial_port &g_com1 = __g_com1_storage.value; +constexpr size_t fifo_size = 64; + +// register offsets +constexpr uint16_t THR = 0; // Write +constexpr uint16_t RBR = 0; // Read +constexpr uint16_t IER = 1; +constexpr uint16_t FCR = 2; // Write +constexpr uint16_t IIR = 2; // Read +constexpr uint16_t LCR = 3; +constexpr uint16_t MCR = 4; +constexpr uint16_t LSR = 5; +constexpr uint16_t MSR = 6; + +constexpr uint16_t DLL = 0; // DLAB == 1 +constexpr uint16_t DLH = 1; // DLAB == 1 + +uint8_t com1_out_buffer[4096*4]; +uint8_t com1_in_buffer[512]; + serial_port::serial_port() : + m_writing(false), m_port(0) { } serial_port::serial_port(uint16_t port) : - m_port(port) + m_writing(false), + m_port(port), + m_out_buffer(com1_out_buffer, sizeof(com1_out_buffer)), + m_in_buffer(com1_in_buffer, sizeof(com1_in_buffer)) { - outb(port + 1, 0x00); // Disable all interrupts - outb(port + 3, 0x80); // Enable the Divisor Latch Access Bit - outb(port + 0, 0x01); // Divisor low bit: 1 (115200 baud) - outb(port + 1, 0x00); // Divisor high bit - outb(port + 3, 0x03); // 8-N-1 - outb(port + 2, 0xe7); // Clear and enable FIFO, enable 64 byte, 56 byte trigger - outb(port + 4, 0x0b); // Data terminal ready, Request to send, aux output 2 (irq enable) + outb(port + IER, 0x00); // Disable all interrupts + outb(port + LCR, 0x80); // Enable the Divisor Latch Access Bit + outb(port + DLL, 0x01); // Divisor low byte: 1 (115200 baud) + outb(port + DLH, 0x00); // Divisor high byte + outb(port + LCR, 0x03); // 8-N-1, diable DLAB + outb(port + FCR, 0xe7); // Clear and enable FIFO, enable 64 byte, 56 byte trigger + outb(port + MCR, 0x0b); // Data terminal ready, Request to send, aux output 2 (irq enable) + outb(port + IER, 0x03); // Enable interrupts + + // Clear out pending interrupts + handle_interrupt(); } -bool serial_port::read_ready() { return (inb(m_port + 5) & 0x01) != 0; } -bool serial_port::write_ready() { return (inb(m_port + 5) & 0x20) != 0; } +inline bool read_ready(uint16_t port) { return (inb(port + LSR) & 0x01) != 0; } +inline bool write_ready(uint16_t port) { return (inb(port + LSR) & 0x20) != 0; } -char -serial_port::read() { - while (!read_ready()); - return inb(m_port); +void +serial_port::handle_interrupt() +{ + interrupts_disable(); + uint8_t iir = inb(m_port + IIR); + + while ((iir & 1) == 0) { + uint8_t reg = 0; + switch ((iir>>1) & 0x3) { + case 0: // Modem status change + reg = inb(m_port + MSR); + handle_error(MSR, reg); + break; + + case 1: // Transmit buffer empty + do_write(); + break; + + case 2: // Received data available + do_read(); + break; + + case 3: // Line status change + reg = inb(m_port + LSR); + handle_error(LSR, reg); + break; + } + + iir = inb(m_port + IIR); + } + interrupts_enable(); } void -serial_port::write(char c) { - while (!write_ready()); - outb(m_port, c); +serial_port::do_read() +{ + size_t used = 0; + uint8_t *data = nullptr; + size_t avail = m_in_buffer.reserve(fifo_size, reinterpret_cast(&data)); + + while (used < avail && read_ready(m_port)) { + *data++ = inb(m_port); + used++; + } + + m_in_buffer.commit(used); +} + +void +serial_port::do_write() +{ + uint8_t *data = nullptr; + uint8_t tmp[fifo_size]; + + size_t n = m_out_buffer.get_block(reinterpret_cast(&data)); + + m_writing = (n > 0); + if (!m_writing) + return; + + if (n > fifo_size) + n = fifo_size; + + kutil::memcpy(tmp, data, n); + m_out_buffer.consume(n); + + for (size_t i = 0; i < n; ++i) + outb(m_port, data[i]); + +} + +void +serial_port::handle_error(uint16_t reg, uint8_t value) +{ + kassert(false, "serial line error"); +} + +char +serial_port::read() +{ + uint8_t *data = nullptr; + size_t n = m_in_buffer.get_block(reinterpret_cast(&data)); + if (!n) return 0; + char c = *data; + m_in_buffer.consume(1); + return c; +} + +void +serial_port::write(char c) +{ + uint8_t *data = nullptr; + size_t avail = m_out_buffer.reserve(1, reinterpret_cast(&data)); + if (!avail) + return; + *data = c; + m_out_buffer.commit(1); + + if (!m_writing) + do_write(); } diff --git a/src/kernel/serial.h b/src/kernel/serial.h index ba0b934..ff31776 100644 --- a/src/kernel/serial.h +++ b/src/kernel/serial.h @@ -3,6 +3,8 @@ /// Declarations related to serial ports. #include +#include "kutil/bip_buffer.h" + class serial_port { public: @@ -15,11 +17,17 @@ public: void write(char c); char read(); -private: - uint16_t m_port; + void handle_interrupt(); - bool read_ready(); - bool write_ready(); +private: + bool m_writing; + uint16_t m_port; + kutil::bip_buffer m_out_buffer; + kutil::bip_buffer m_in_buffer; + + void do_read(); + void do_write(); + void handle_error(uint16_t reg, uint8_t value); }; extern serial_port &g_com1;