summaryrefslogtreecommitdiff
path: root/Kernel/Devices
diff options
context:
space:
mode:
authorConrad Pankoff <deoxxa@fknsrs.biz>2019-06-08 23:24:34 +1000
committerAndreas Kling <awesomekling@gmail.com>2019-06-08 18:12:20 +0200
commit8b1154f5f24fe775aaa23c85bec676c5d0131b91 (patch)
treeba4f9f35144c17719150e86f7e65e898e6213c0b /Kernel/Devices
parent7b04c7dc48a123378e44a8a93e685968ca788311 (diff)
downloadserenity-8b1154f5f24fe775aaa23c85bec676c5d0131b91.zip
Kernel: Implement serial port driver
This implements a basic 8250 UART serial port driver. It does not currently handle (or enable) interrupts, nor any runtime configuration.
Diffstat (limited to 'Kernel/Devices')
-rw-r--r--Kernel/Devices/SerialDevice.cpp110
-rw-r--r--Kernel/Devices/SerialDevice.h120
2 files changed, 230 insertions, 0 deletions
diff --git a/Kernel/Devices/SerialDevice.cpp b/Kernel/Devices/SerialDevice.cpp
new file mode 100644
index 0000000000..d945e4c1d4
--- /dev/null
+++ b/Kernel/Devices/SerialDevice.cpp
@@ -0,0 +1,110 @@
+#include <Kernel/Devices/SerialDevice.h>
+#include <Kernel/IO.h>
+
+SerialDevice::SerialDevice(int base_addr, unsigned minor)
+ : CharacterDevice(4, minor)
+ , m_base_addr(base_addr)
+{
+ initialize();
+}
+
+SerialDevice::~SerialDevice()
+{
+}
+
+bool SerialDevice::can_read(FileDescription&) const
+{
+ return (get_line_status() & DataReady) != 0;
+}
+
+ssize_t SerialDevice::read(FileDescription&, byte* buffer, ssize_t size)
+{
+ if (!size)
+ return 0;
+
+ if (!(get_line_status() & DataReady))
+ return 0;
+
+ buffer[0] = IO::in8(m_base_addr);
+
+ return 1;
+}
+
+bool SerialDevice::can_write(FileDescription&) const
+{
+ return (get_line_status() & EmptyTransmitterHoldingRegister) != 0;
+}
+
+ssize_t SerialDevice::write(FileDescription&, const byte* buffer, ssize_t size)
+{
+ if (!size)
+ return 0;
+
+ if (!(get_line_status() & EmptyTransmitterHoldingRegister))
+ return 0;
+
+ IO::out8(m_base_addr, buffer[0]);
+
+ return 1;
+}
+
+void SerialDevice::initialize()
+{
+ set_interrupts(0);
+ set_baud(Baud38400);
+ set_line_control(None, One, EightBits);
+ set_fifo_control(EnableFIFO | ClearReceiveFIFO | ClearTransmitFIFO | TriggerLevel4);
+ set_modem_control(RequestToSend | DataTerminalReady);
+}
+
+void SerialDevice::set_interrupts(char interrupt_enable)
+{
+ m_interrupt_enable = interrupt_enable;
+
+ IO::out8(m_base_addr + 1, interrupt_enable);
+}
+
+void SerialDevice::set_baud(Baud baud)
+{
+ m_baud = baud;
+
+ IO::out8(m_base_addr + 3, IO::in8(m_base_addr + 3) | 0x80); // turn on DLAB
+ IO::out8(m_base_addr + 0, ((char)(baud)) >> 2); // lower half of divisor
+ IO::out8(m_base_addr + 1, ((char)(baud)) & 0xff); // upper half of divisor
+ IO::out8(m_base_addr + 3, IO::in8(m_base_addr + 3) & 0x7f); // turn off DLAB
+}
+
+void SerialDevice::set_fifo_control(char fifo_control)
+{
+ m_fifo_control = fifo_control;
+
+ IO::out8(m_base_addr + 2, fifo_control);
+}
+
+void SerialDevice::set_line_control(ParitySelect parity_select, StopBits stop_bits, WordLength word_length)
+{
+ m_parity_select = parity_select;
+ m_stop_bits = stop_bits;
+ m_word_length = word_length;
+
+ IO::out8(m_base_addr + 3, IO::in8(m_base_addr + 3) & (0xc0 | parity_select | stop_bits | word_length));
+}
+
+void SerialDevice::set_break_enable(bool break_enable)
+{
+ m_break_enable = break_enable;
+
+ IO::out8(m_base_addr + 3, IO::in8(m_base_addr + 3) & (break_enable ? 0xff : 0xbf));
+}
+
+void SerialDevice::set_modem_control(char modem_control)
+{
+ m_modem_control = modem_control;
+
+ IO::out8(m_base_addr + 4, modem_control);
+}
+
+char SerialDevice::get_line_status() const
+{
+ return IO::in8(m_base_addr + 5);
+}
diff --git a/Kernel/Devices/SerialDevice.h b/Kernel/Devices/SerialDevice.h
new file mode 100644
index 0000000000..3cccf0c91a
--- /dev/null
+++ b/Kernel/Devices/SerialDevice.h
@@ -0,0 +1,120 @@
+#include <Kernel/Devices/CharacterDevice.h>
+
+#define SERIAL_COM1_ADDR 0x3F8
+#define SERIAL_COM2_ADDR 0x2F8
+#define SERIAL_COM3_ADDR 0x3E8
+#define SERIAL_COM4_ADDR 0x2E8
+
+class SerialDevice final : public CharacterDevice {
+ AK_MAKE_ETERNAL
+public:
+ SerialDevice(int base_addr, unsigned minor);
+ virtual ~SerialDevice() override;
+
+ // ^CharacterDevice
+ virtual bool can_read(FileDescription&) const override;
+ virtual ssize_t read(FileDescription&, byte*, ssize_t) override;
+ virtual bool can_write(FileDescription&) const override;
+ virtual ssize_t write(FileDescription&, const byte*, ssize_t) override;
+
+ enum InterruptEnable {
+ LowPowerMode = 0x01 << 5,
+ SleepMode = 0x01 << 4,
+ ModemStatusInterrupt = 0x01 << 3,
+ ReceiverLineStatusInterrupt = 0x01 << 2,
+ TransmitterHoldingRegisterEmptyInterrupt = 0x01 << 1,
+ ReceivedDataAvailableInterrupt = 0x01 << 0
+ };
+
+ enum Baud {
+ Baud50 = 2304,
+ Baud110 = 1047,
+ Baud220 = 524,
+ Baud300 = 384,
+ Baud600 = 192,
+ Baud1200 = 96,
+ Baud2400 = 48,
+ Baud4800 = 24,
+ Baud9600 = 12,
+ Baud19200 = 6,
+ Baud38400 = 3,
+ Baud57600 = 2,
+ Baud115200 = 1
+ };
+
+ enum ParitySelect {
+ None = 0x00 << 3,
+ Odd = 0x01 << 3,
+ Even = 0x03 << 3,
+ Mark = 0x05 << 3,
+ Space = 0x07 << 3
+ };
+
+ enum StopBits {
+ One = 0x00 << 2,
+ Two = 0x01 << 2
+ };
+
+ enum WordLength {
+ FiveBits = 0x00,
+ SixBits = 0x01,
+ SevenBits = 0x02,
+ EightBits = 0x03
+ };
+
+ enum FIFOControl {
+ EnableFIFO = 0x01 << 0,
+ ClearReceiveFIFO = 0x01 << 1,
+ ClearTransmitFIFO = 0x01 << 2,
+ Enable64ByteFIFO = 0x01 << 5,
+ TriggerLevel1 = 0x00 << 6,
+ TriggerLevel2 = 0x01 << 6,
+ TriggerLevel3 = 0x02 << 6,
+ TriggerLevel4 = 0x03 << 6
+ };
+
+ enum ModemControl {
+ AutoflowControlEnabled = 0x01 << 5,
+ LoopbackMode = 0x01 << 4,
+ AuxiliaryOutput2 = 0x01 << 3,
+ AuxiliaryOutput1 = 0x01 << 2,
+ RequestToSend = 0x01 << 1,
+ DataTerminalReady = 0x01 << 0
+ };
+
+ enum LineStatus {
+ ErrorInReceivedFIFO = 0x01 << 7,
+ EmptyDataHoldingRegisters = 0x01 << 6,
+ EmptyTransmitterHoldingRegister = 0x01 << 5,
+ BreakInterrupt = 0x01 << 4,
+ FramingError = 0x01 << 3,
+ ParityError = 0x01 << 2,
+ OverrunError = 0x01 << 1,
+ DataReady = 0x01 << 0
+ };
+
+private:
+ // ^CharacterDevice
+ virtual const char* class_name() const override { return "SerialDevice"; }
+
+ void initialize();
+ void set_interrupts(char interrupt_enable);
+ void set_baud(Baud);
+ void set_fifo_control(char fifo_control);
+ void set_line_control(ParitySelect, StopBits, WordLength);
+ void set_break_enable(bool break_enable);
+ void set_modem_control(char modem_control);
+ char get_line_status() const;
+ bool rx_ready();
+ bool tx_ready();
+
+ int m_base_addr;
+ char m_interrupt_enable;
+ char m_fifo_control;
+ Baud m_baud;
+ ParitySelect m_parity_select;
+ StopBits m_stop_bits;
+ WordLength m_word_length;
+ bool m_break_enable;
+ char m_modem_control;
+};