mirror of
https://github.com/justinian/jsix.git
synced 2025-12-10 00:14:32 -08:00
[kernel] Make serial driver interrupt based
Move the in-kernel serial driver code to be completely interrupt based. The next step will be to move this to a userspace driver.
This commit is contained in:
@@ -12,6 +12,7 @@
|
|||||||
#include "kernel_memory.h"
|
#include "kernel_memory.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "objects/endpoint.h"
|
#include "objects/endpoint.h"
|
||||||
|
#include "serial.h"
|
||||||
|
|
||||||
|
|
||||||
static endpoint * const ignore_endpoint = reinterpret_cast<endpoint*>(-1ull);
|
static endpoint * const ignore_endpoint = reinterpret_cast<endpoint*>(-1ull);
|
||||||
@@ -50,17 +51,6 @@ acpi_table_header::validate(uint32_t expected_type) const
|
|||||||
return !expected_type || (expected_type == type);
|
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() :
|
device_manager::device_manager() :
|
||||||
m_lapic_base(0)
|
m_lapic_base(0)
|
||||||
@@ -269,6 +259,9 @@ device_manager::load_apic(const acpi_table_header *header)
|
|||||||
|
|
||||||
p += length;
|
p += length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_ioapics[0].mask(3, false);
|
||||||
|
m_ioapics[0].mask(4, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -384,6 +377,11 @@ device_manager::init_drivers()
|
|||||||
bool
|
bool
|
||||||
device_manager::dispatch_irq(unsigned irq)
|
device_manager::dispatch_irq(unsigned irq)
|
||||||
{
|
{
|
||||||
|
if (irq == 4) {
|
||||||
|
g_com1.handle_interrupt();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (irq >= m_irqs.count())
|
if (irq >= m_irqs.count())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|||||||
@@ -183,6 +183,7 @@ kernel_main(init::args *args)
|
|||||||
unsigned num_cpus = start_aps(*apic, apic_ids, args->pml4);
|
unsigned num_cpus = start_aps(*apic, apic_ids, args->pml4);
|
||||||
|
|
||||||
interrupts_enable();
|
interrupts_enable();
|
||||||
|
g_com1.handle_interrupt();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
block_device *disk = devices->get_block_device(0);
|
block_device *disk = devices->get_block_device(0);
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
|
#include "kutil/assert.h"
|
||||||
|
#include "kutil/memory.h"
|
||||||
#include "kutil/no_construct.h"
|
#include "kutil/no_construct.h"
|
||||||
|
#include "interrupts.h"
|
||||||
#include "io.h"
|
#include "io.h"
|
||||||
#include "serial.h"
|
#include "serial.h"
|
||||||
|
|
||||||
@@ -8,35 +11,152 @@
|
|||||||
static kutil::no_construct<serial_port> __g_com1_storage;
|
static kutil::no_construct<serial_port> __g_com1_storage;
|
||||||
serial_port &g_com1 = __g_com1_storage.value;
|
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() :
|
serial_port::serial_port() :
|
||||||
|
m_writing(false),
|
||||||
m_port(0)
|
m_port(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
serial_port::serial_port(uint16_t port) :
|
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 + IER, 0x00); // Disable all interrupts
|
||||||
outb(port + 3, 0x80); // Enable the Divisor Latch Access Bit
|
outb(port + LCR, 0x80); // Enable the Divisor Latch Access Bit
|
||||||
outb(port + 0, 0x01); // Divisor low bit: 1 (115200 baud)
|
outb(port + DLL, 0x01); // Divisor low byte: 1 (115200 baud)
|
||||||
outb(port + 1, 0x00); // Divisor high bit
|
outb(port + DLH, 0x00); // Divisor high byte
|
||||||
outb(port + 3, 0x03); // 8-N-1
|
outb(port + LCR, 0x03); // 8-N-1, diable DLAB
|
||||||
outb(port + 2, 0xe7); // Clear and enable FIFO, enable 64 byte, 56 byte trigger
|
outb(port + FCR, 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 + 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; }
|
inline bool read_ready(uint16_t port) { return (inb(port + LSR) & 0x01) != 0; }
|
||||||
bool serial_port::write_ready() { return (inb(m_port + 5) & 0x20) != 0; }
|
inline bool write_ready(uint16_t port) { return (inb(port + LSR) & 0x20) != 0; }
|
||||||
|
|
||||||
char
|
void
|
||||||
serial_port::read() {
|
serial_port::handle_interrupt()
|
||||||
while (!read_ready());
|
{
|
||||||
return inb(m_port);
|
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
|
void
|
||||||
serial_port::write(char c) {
|
serial_port::do_read()
|
||||||
while (!write_ready());
|
{
|
||||||
outb(m_port, c);
|
size_t used = 0;
|
||||||
|
uint8_t *data = nullptr;
|
||||||
|
size_t avail = m_in_buffer.reserve(fifo_size, reinterpret_cast<void**>(&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<void**>(&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<void**>(&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<void**>(&data));
|
||||||
|
if (!avail)
|
||||||
|
return;
|
||||||
|
*data = c;
|
||||||
|
m_out_buffer.commit(1);
|
||||||
|
|
||||||
|
if (!m_writing)
|
||||||
|
do_write();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
/// Declarations related to serial ports.
|
/// Declarations related to serial ports.
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "kutil/bip_buffer.h"
|
||||||
|
|
||||||
class serial_port
|
class serial_port
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -15,11 +17,17 @@ public:
|
|||||||
void write(char c);
|
void write(char c);
|
||||||
char read();
|
char read();
|
||||||
|
|
||||||
private:
|
void handle_interrupt();
|
||||||
uint16_t m_port;
|
|
||||||
|
|
||||||
bool read_ready();
|
private:
|
||||||
bool write_ready();
|
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;
|
extern serial_port &g_com1;
|
||||||
|
|||||||
Reference in New Issue
Block a user