Kernel: You can now read serial output from the /dev/ttyS*

This commit is contained in:
Bananymous 2023-09-05 00:05:49 +03:00
parent 33c81f00b7
commit 4a0652684c
4 changed files with 85 additions and 17 deletions

View File

@ -43,6 +43,8 @@ namespace Kernel
virtual uint32_t height() const override;
virtual void putchar(uint8_t) override;
virtual void update() override;
private:
SerialTTY(Serial);
bool initialize();

View File

@ -29,6 +29,7 @@ namespace Kernel
static void initialize_devices();
void on_key_event(Input::KeyEvent);
void handle_input(const uint8_t* ch);
virtual bool is_tty() const override { return true; }
@ -50,7 +51,7 @@ namespace Kernel
void do_backspace();
protected:
mutable Kernel::SpinLock m_lock;
mutable Kernel::RecursiveSpinLock m_lock;
TerminalDriver::Color m_foreground { TerminalColor::BRIGHT_WHITE };
TerminalDriver::Color m_background { TerminalColor::BLACK };

View File

@ -1,4 +1,6 @@
#include <BAN/Array.h>
#include <BAN/CircularQueue.h>
#include <kernel/CriticalScope.h>
#include <kernel/FS/DevFS/FileSystem.h>
#include <kernel/IDT.h>
#include <kernel/InterruptController.h>
@ -24,6 +26,11 @@
#define PARITY_MARK (0b101 << 3)
#define PARITY_SPACE (0b111 << 3)
#define COM1_PORT 0x3F8
#define COM2_PORT 0x2F8
#define COM1_IRQ 4
#define COM2_IRQ 3
namespace Kernel
{
@ -31,20 +38,35 @@ namespace Kernel
static BAN::Array<Serial, sizeof(s_serial_ports) / sizeof(*s_serial_ports)> s_serial_drivers;
static bool s_has_devices { false };
static BAN::CircularQueue<uint8_t, 128> s_com1_input;
static BAN::CircularQueue<uint8_t, 128> s_com2_input;
static BAN::RefPtr<SerialTTY> s_com1;
static BAN::RefPtr<SerialTTY> s_com2;
static void irq3_handler()
{
if (!s_serial_drivers[1].is_valid())
return;
uint8_t ch = IO::inb(s_serial_drivers[1].port());
Debug::putchar(ch);
uint8_t ch = IO::inb(COM2_PORT);
if (s_com2_input.full())
{
dwarnln("COM2 buffer full");
s_com2_input.pop();
}
s_com2_input.push(ch);
}
static void irq4_handler()
{
if (!s_serial_drivers[0].is_valid())
return;
uint8_t ch = IO::inb(s_serial_drivers[0].port());
Debug::putchar(ch);
uint8_t ch = IO::inb(COM1_PORT);
if (s_com1_input.full())
{
dwarnln("COM1 buffer full");
s_com1_input.pop();
}
s_com1_input.push(ch);
}
static dev_t next_rdev()
@ -71,7 +93,8 @@ namespace Kernel
s_has_devices = !!count;
for (auto& driver : s_serial_drivers)
dprintln("{}x{} serial device at 0x{H}", driver.width(), driver.height(), driver.port());
if (driver.is_valid())
dprintln("{}x{} serial device at 0x{H}", driver.width(), driver.height(), driver.port());
}
void Serial::initialize_devices()
@ -192,17 +215,17 @@ namespace Kernel
ASSERT(tty);
// Enable interrupts for COM1 and COM2
if (serial.port() == s_serial_ports[0])
if (serial.port() == COM1_PORT)
{
IO::outb(serial.port() + 1, 1);
InterruptController::get().enable_irq(4);
IDT::register_irq_handler(4, irq4_handler);
IO::outb(COM1_PORT + 1, 1);
InterruptController::get().enable_irq(COM1_IRQ);
IDT::register_irq_handler(COM1_IRQ, irq4_handler);
}
else if (serial.port() == s_serial_ports[1])
else if (serial.port() == COM2_PORT)
{
IO::outb(serial.port() + 1, 1);
InterruptController::get().enable_irq(3);
IDT::register_irq_handler(3, irq3_handler);
IO::outb(COM2_PORT + 1, 1);
InterruptController::get().enable_irq(COM2_IRQ);
IDT::register_irq_handler(COM2_IRQ, irq3_handler);
}
ASSERT(minor(tty->rdev()) < 10);
@ -210,8 +233,45 @@ namespace Kernel
auto ref_ptr = BAN::RefPtr<SerialTTY>::adopt(tty);
DevFileSystem::get().add_device(name, ref_ptr);
if (serial.port() == COM1_PORT)
s_com1 = ref_ptr;
if (serial.port() == COM2_PORT)
s_com2 = ref_ptr;
return ref_ptr;
}
void SerialTTY::update()
{
if (m_serial.port() != COM1_PORT && m_serial.port() != COM2_PORT)
return;
auto update_com =
[&](auto& device, auto& input_queue)
{
if (input_queue.empty())
return;
uint8_t buffer[128];
uint8_t* ptr = buffer;
while (!input_queue.empty())
{
*ptr = input_queue.front();
if (*ptr == '\r')
*ptr = '\n';
if (*ptr == 127)
*ptr++ = '\b', *ptr++ = ' ', *ptr = '\b';
input_queue.pop();
ptr++;
}
*ptr = '\0';
device->handle_input(buffer);
};
CriticalScope _;
if (m_serial.port() == COM1_PORT)
update_com(s_com1, s_com1_input);
if (m_serial.port() == COM2_PORT)
update_com(s_com2, s_com2_input);
}
uint32_t SerialTTY::width() const
{

View File

@ -132,11 +132,16 @@ namespace Kernel
}
}
const uint8_t* ansi = (const uint8_t*)ansi_c_str;
handle_input((const uint8_t*)ansi_c_str);
}
void TTY::handle_input(const uint8_t* ansi)
{
LockGuard _(m_lock);
bool eof = ansi && (
ansi[0] == '\x04' || // ^D
ansi[0] == '\n' // \n
ansi[0] == '\x04' || // ^D
ansi[0] == '\n' // \n
);
if (ansi && m_termios.canonical)