aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorAqua-sama <aqua@iserlohn-fortress.net>2021-02-12 17:55:31 +0200
committerAqua-sama <aqua@iserlohn-fortress.net>2021-02-12 17:55:31 +0200
commit46a5f1ee4db70a037af5038deaa1d64ad0f7862c (patch)
tree0250b88f2eb69a7a0987a78d23a06e2125df74a2 /drivers
parentAdd comments to explain CGA ports better (diff)
downloadkernel.cpp-46a5f1ee4db70a037af5038deaa1d64ad0f7862c.tar.xz
Add serial0 console output
Diffstat (limited to 'drivers')
-rw-r--r--drivers/makefile3
-rw-r--r--drivers/ports.h24
-rw-r--r--drivers/serial.cc88
-rw-r--r--drivers/serial.h47
4 files changed, 153 insertions, 9 deletions
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<T, uint8_t>::value || is_same<T, uint16_t>::value
template <uint16_t port_num, port_data_t T>
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<uint8_t>(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 <stdlib.h>
+#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;
+};