From 46a5f1ee4db70a037af5038deaa1d64ad0f7862c Mon Sep 17 00:00:00 2001 From: Aqua-sama Date: Fri, 12 Feb 2021 17:55:31 +0200 Subject: Add serial0 console output --- .clang-format | 1 + drivers/makefile | 3 +- drivers/ports.h | 24 +++++++++----- drivers/serial.cc | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/serial.h | 47 +++++++++++++++++++++++++++ libk/stdlib.h | 37 +++++++++++++++------ libk/stdlib/console.cc | 12 ++++--- src/kernel.cc | 6 +++- 8 files changed, 194 insertions(+), 24 deletions(-) create mode 100644 drivers/serial.cc create mode 100644 drivers/serial.h diff --git a/.clang-format b/.clang-format index ee8a219..ec6cc2c 100644 --- a/.clang-format +++ b/.clang-format @@ -2,5 +2,6 @@ BasedOnStyle: Chromium ColumnLimit: '120' AccessModifierOffset: '-2' +AllowShortBlocksOnASingleLine: true ... diff --git a/drivers/makefile b/drivers/makefile index b76f7c5..0d9071a 100644 --- a/drivers/makefile +++ b/drivers/makefile @@ -1 +1,2 @@ -CXX_OBJ += drivers/cga.o +CXX_OBJ += drivers/cga.o \ + drivers/serial.o diff --git a/drivers/ports.h b/drivers/ports.h index 338c3b8..8671f19 100644 --- a/drivers/ports.h +++ b/drivers/ports.h @@ -6,34 +6,36 @@ concept port_data_t = (is_same::value || is_same::value template class Port { public: - [[nodiscard]] T read() { + [[nodiscard]] T read(uint16_t offset = 0) { + const uint16_t port = port_num + offset; T result; switch (sizeof(T)) { case 1: - asm volatile("inb %1, %0" : "=a"(result) : "Nd"(port_num)); + asm volatile("inb %1, %0" : "=a"(result) : "Nd"(port)); break; case 2: - asm volatile("inw %1, %0" : "=a"(result) : "Nd"(port_num)); + asm volatile("inw %1, %0" : "=a"(result) : "Nd"(port)); break; case 4: - asm volatile("inl %1, %0" : "=a"(result) : "Nd"(port_num)); + asm volatile("inl %1, %0" : "=a"(result) : "Nd"(port)); break; } return result; } - void write(T data) { + void write(T data, uint16_t offset = 0) { + const uint16_t port = port_num + offset; switch (sizeof(T)) { case 1: - asm volatile("outb %0, %1" : : "a"(data), "Nd"(port_num)); + asm volatile("outb %0, %1" : : "a"(data), "Nd"(port)); break; case 2: - asm volatile("outw %0, %1" : : "a"(data), "Nd"(port_num)); + asm volatile("outw %0, %1" : : "a"(data), "Nd"(port)); break; case 4: - asm volatile("outl %0, %1" : : "a"(data), "Nd"(port_num)); + asm volatile("outl %0, %1" : : "a"(data), "Nd"(port)); break; } } @@ -45,3 +47,9 @@ typedef Port<0x3d5, uint8_t> cga_dat_port; // 0x3d8 : mode select register // 0x3d8 : color control register // 0x3da : status register + +/* Serial ports */ +typedef Port<0x3f8, uint8_t> com1_port_t; +typedef Port<0x2f8, uint8_t> com2_port_t; +typedef Port<0x3e8, uint8_t> com3_port_t; +typedef Port<0x2e8, uint8_t> com4_port_t; diff --git a/drivers/serial.cc b/drivers/serial.cc new file mode 100644 index 0000000..84dd4b0 --- /dev/null +++ b/drivers/serial.cc @@ -0,0 +1,88 @@ +#include "serial.h" + +/* Line Control + * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + * |dla| | parity | s | data | + */ +enum LineControlRegister : uint8_t { + // data bits + d5bit = 0b00000000, + d6bit = 0b00000001, + d7bit = 0b00000010, + d8bit = 0b00000011, + // parity bits + none = 0b00000000, + odd = 0b00001000, + even = 0b00011000, + mark = 0b00101000, + space = 0b00111000, + // stop bits + s1bit = 0b00000000, + s2bit = 0b00000100, // 1.5 for 5bit data; 2 otherwise + // divisor latch access bit + dlab = 0b10000000, +}; + +/* Line Status Register */ +enum LineStatusRegister : uint8_t { + DR = 0b00000001, // data ready: see if there is data to read + OE = 0b00000010, // overrun error: see if there has been data lost + PE = 0b00000100, // parity error: see if there was error in transmission + FE = 0b00001000, // framing error: see if a stop bit was missing + BI = 0b00010000, // break indicator: see if there is a break in data input + THRE = 0b00100000, // transmitter holding register empty: see if transmission buffer is empty + TEMT = 0b01000000, // transmitter empty: see if transmitter is not doing anything + ERRO = 0b10000000, // impending error: see if there is an error with a word in the input buffer +}; + +SerialPort::SerialPort() { + /* + * The serial controller (UART) has a baud rate of 115200 ticks per second; to limit the speed of the port set the + * divisor: + * 1. set DLAB on LineControl + * 2. set BaudDiv_l + * 3. set BaudDiv_h + * 4. clear LineControl + */ + + port.write(0x00, InterruptControl); // disable all interrupts + + port.write(dlab, LineControl); + port.write(0x03, BaudDiv_l); // div_l: 0x03 38400 baud + port.write(0x00, BaudDiv_h); // div_h: 0x00 + port.write(d8bit | none | s1bit, LineControl); // 8 bits, no parity, one stop bit + + port.write(0xc7, FifoControl); // enable FIFO, clear, with 14-byte threshold + port.write(0x0b, ModemControl); // IRQ enabled, RTS/DSR set +} + +bool SerialPort::ready_read() { + return (port.read(LineStatus) & DR); +} + +uint8_t SerialPort::read() { + while (!ready_read()) { + asm volatile("nop"); + } + return port.read(); +} + +bool SerialPort::ready_write() { + return (port.read(LineStatus) & THRE); +} + +void SerialPort::write(char c) { + while (!ready_write()) { + asm volatile("nop"); + } + + switch (c) { + case '\n': + port.write('\n'); + port.write('\r'); + break; + default: + port.write(static_cast(c)); + } +} + diff --git a/drivers/serial.h b/drivers/serial.h new file mode 100644 index 0000000..0579dd4 --- /dev/null +++ b/drivers/serial.h @@ -0,0 +1,47 @@ +#pragma once + +#include +#include "ports.h" + +/* + * Serial Port + * + * useful links: + * https://wiki.osdev.org/Serial_Ports + * https://www.lowlevel.eu/wiki/Serielle_Schnittstelle + */ + +class SerialPort : public Console { +public: + SerialPort(); + ~SerialPort() = default; + + bool ready_read(); + uint8_t read(); + + bool ready_write(); + void write(char c) override; + void write(ViewIterator& iter) override { + while (iter) { + write(iter.next()); + } + } + + void update_cursor() override {} + +private: + enum PortOffset : uint16_t { + BaudDiv_l = 0, // if dlab is set + BaudDiv_h = 1, // if dlab is set + Data = 0, // read from receive buffer / write to transmit buffer + InterruptControl = 1, // interrupt enable + FifoControl = 2, // interrupt ID and FIFO control + LineControl = 3, // most significant bit is the DLAB + ModemControl = 4, + LineStatus = 5, + ModemStatus = 6, + Scratch = 7, + }; + + com1_port_t port; +}; diff --git a/libk/stdlib.h b/libk/stdlib.h index a336586..7213384 100644 --- a/libk/stdlib.h +++ b/libk/stdlib.h @@ -2,15 +2,32 @@ #include +template +class Iterator { +public: + T next() { return buffer[reverse ? pos-- : pos++]; } + operator bool() const { return reverse ? (pos >= 0) : (pos < length); } + + explicit Iterator(T* b, size_t l) : buffer(b), length(l) { + if (reverse) + pos = length - 1; + }; + +private: + ssize_t pos = 0; + T* const buffer; + const size_t length; +}; + class Console { - public: - virtual ~Console() = default; - virtual void write(char c) = 0; - virtual void write(ViewIterator& msg) = 0; - virtual void update_cursor() = 0; - - static void set(Console* ptr); - static Console* get(); +public: + virtual ~Console() = default; + virtual void write(char c) = 0; + virtual void write(ViewIterator& msg) = 0; + virtual void update_cursor() = 0; + + static void set(Console* ptr); + static Iterator begin(); }; template @@ -37,7 +54,9 @@ void print_c(Console* console, const T& a, const Args&... x) { template void printk(const T& a, const Args&... x) { - if (auto* c = Console::get()) { + auto iter = Console::begin(); + while (iter) { + auto* c = iter.next(); print_c(c, a, x...); c->update_cursor(); } diff --git a/libk/stdlib/console.cc b/libk/stdlib/console.cc index 308f055..a234e6b 100644 --- a/libk/stdlib/console.cc +++ b/libk/stdlib/console.cc @@ -1,15 +1,17 @@ #include "stdlib.h" -static Console* global_console = nullptr; +constexpr size_t max_consoles = 4; +static Console* global_console[max_consoles] = {nullptr}; +static size_t last_console = 0; void Console::set(Console* ptr) { - if (ptr != nullptr) { - global_console = ptr; + if (ptr != nullptr && last_console < max_consoles) { + global_console[last_console++] = ptr; } } -Console* Console::get() { - return global_console; +Iterator Console::begin() { + return Iterator(global_console, last_console); } void abort() { diff --git a/src/kernel.cc b/src/kernel.cc index 4c17969..624f472 100644 --- a/src/kernel.cc +++ b/src/kernel.cc @@ -12,18 +12,22 @@ #include #include "cga.h" #include "gdt.h" +#include "serial.h" extern "C" { void dump_multiboot(uint32_t mb_magic, uint32_t mb_addr); void dump_gdt(); void kernel_main([[maybe_unused]] uint32_t mb_magic, [[maybe_unused]] uint32_t mb_addr) { + SerialPort s0; + Console::set(&s0); + CGA terminal; Console::set(&terminal); printk("Hello, kernel World!\n"); - // dump_multiboot(mb_magic, mb_addr); + dump_multiboot(mb_magic, mb_addr); dump_gdt(); printk("GDT::SegmentDescriptor tests\n"); -- cgit v1.2.1