From 4165209842edf74bd786f6525a58b7b0bb9790d5 Mon Sep 17 00:00:00 2001
From: dzwdz
Date: Mon, 9 Aug 2021 22:20:32 +0200
Subject: implement serial output

---
 src/kernel/arch/i386/tty/serial.c | 44 +++++++++++++++++++++++++++++++++++++++
 src/kernel/arch/i386/tty/serial.h |  5 +++++
 src/kernel/arch/i386/tty/tty.c    |  3 +++
 3 files changed, 52 insertions(+)
 create mode 100644 src/kernel/arch/i386/tty/serial.c
 create mode 100644 src/kernel/arch/i386/tty/serial.h

(limited to 'src/kernel/arch/i386')

diff --git a/src/kernel/arch/i386/tty/serial.c b/src/kernel/arch/i386/tty/serial.c
new file mode 100644
index 0000000..bf55c41
--- /dev/null
+++ b/src/kernel/arch/i386/tty/serial.c
@@ -0,0 +1,44 @@
+#include <kernel/arch/i386/tty/serial.h>
+#include <stdint.h>
+
+const int COM1 = 0x3f8;
+
+// TODO move to some header file
+inline void port_outb(uint16_t port, uint8_t val) {
+	asm volatile("outb %0, %1" : : "a" (val), "Nd" (port));
+}
+
+inline uint8_t port_inb(uint16_t port) {
+	uint8_t val;
+	asm volatile("inb %1, %0" : "=a" (val) : "Nd" (port));
+	return val;
+}
+
+void serial_init() {
+	// see https://www.sci.muni.cz/docs/pc/serport.txt
+
+	port_outb(COM1 + 1, 0x00); // disable interrupts, we won't be using them
+
+	// set baud rate divisor
+	port_outb(COM1 + 3, 0b10000000); // enable DLAB
+	port_outb(COM1 + 0, 0x01);       // divisor = 1 (low  byte)
+	port_outb(COM1 + 1, 0x00);       //             (high byte)
+
+	port_outb(COM1 + 3, 0b00000011); // 8 bits, no parity, one stop bit
+	port_outb(COM1 + 2, 0b11000111); // enable FIFO with 14-bit trigger level (???)
+	port_outb(COM1 + 4, 0b00001111); // enable everything in the MCR
+
+	/* the example on the OSDEV wiki does a selftest of the serial connection
+	 * i don't really see the point as long as i'm not using it for input?
+	 * if i start using serial for input, TODO selftest */
+}
+
+void serial_putchar(char c) {
+	while ((port_inb(COM1 + 5) & 0x20) == 0); // wait for THRE
+	port_outb(COM1, c);
+}
+
+void serial_write(const char *buf, size_t len) {
+	for (size_t i = 0; i < len; i++)
+		serial_putchar(buf[i]);
+}
diff --git a/src/kernel/arch/i386/tty/serial.h b/src/kernel/arch/i386/tty/serial.h
new file mode 100644
index 0000000..ac7131b
--- /dev/null
+++ b/src/kernel/arch/i386/tty/serial.h
@@ -0,0 +1,5 @@
+#pragma once
+#include <stddef.h>
+
+void serial_write(const char *buf, size_t len);
+void serial_init();
diff --git a/src/kernel/arch/i386/tty/tty.c b/src/kernel/arch/i386/tty/tty.c
index 670dd5b..300bbcc 100644
--- a/src/kernel/arch/i386/tty/tty.c
+++ b/src/kernel/arch/i386/tty/tty.c
@@ -1,10 +1,13 @@
+#include <kernel/arch/i386/tty/serial.h>
 #include <kernel/arch/i386/tty/vga.h>
 #include <kernel/arch/log.h>
 
 void tty_init() {
 	vga_clear();
+	serial_init();
 }
 
 void tty_write(const char *buf, size_t len) {
 	vga_write(buf, len);
+	serial_write(buf, len);
 }
-- 
cgit v1.2.3