From ba9b3dc65651d6aed8094deb7ce4d17ccfb28f01 Mon Sep 17 00:00:00 2001 From: Idan Horowitz Date: Fri, 23 Apr 2021 17:26:52 +0300 Subject: [PATCH] Kernel: Implement a PCI Serial Device driver This simple driver simply finds a device in a device definitions list and then sets up a SerialDevice instance based on the definition. The driver currently only supports "WCH CH382 2S" pci serial boards, as that is the only device available for me to test with, but most other pci serial devices should be as easily addable as adding a board_definitions entry. --- Kernel/CMakeLists.txt | 1 + Kernel/Devices/PCISerialDevice.cpp | 50 ++++++++++++++++++++++++++++++ Kernel/Devices/PCISerialDevice.h | 39 +++++++++++++++++++++++ Kernel/Devices/SerialDevice.h | 2 ++ Kernel/PCI/IDs.h | 1 + Kernel/init.cpp | 2 ++ Kernel/kprintf.cpp | 4 +++ 7 files changed, 99 insertions(+) create mode 100644 Kernel/Devices/PCISerialDevice.cpp create mode 100644 Kernel/Devices/PCISerialDevice.h diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index b2068ade5c..c95951e18b 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -33,6 +33,7 @@ set(KERNEL_SOURCES Devices/FullDevice.cpp Devices/MemoryDevice.cpp Devices/NullDevice.cpp + Devices/PCISerialDevice.cpp Devices/PCSpeaker.cpp Devices/RandomDevice.cpp Devices/SB16.cpp diff --git a/Kernel/Devices/PCISerialDevice.cpp b/Kernel/Devices/PCISerialDevice.cpp new file mode 100644 index 0000000000..f9849ab23f --- /dev/null +++ b/Kernel/Devices/PCISerialDevice.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021, Idan Horowitz + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include + +namespace Kernel { + +static SerialDevice* s_the = nullptr; + +void PCISerialDevice::detect() +{ + PCI::enumerate([&](const PCI::Address& address, PCI::ID id) { + if (address.is_null()) + return; + + // HACK: There's currently no way to break out of PCI::enumerate, so we just early return if we already initialized the pci serial device + if (is_available()) + return; + + for (auto& board_definition : board_definitions) { + if (board_definition.device_id != id) + continue; + + auto bar_base = PCI::get_BAR(address, board_definition.pci_bar) & ~1; + // FIXME: We should support more than 1 PCI serial port (per card/multiple devices) + s_the = new SerialDevice(IOAddress(bar_base + board_definition.first_offset), 64); + if (board_definition.baud_rate != SerialDevice::Baud::Baud38400) // non-default baud + s_the->set_baud(board_definition.baud_rate); + + dmesgln("PCISerialDevice: Found {} @ {}", board_definition.name, address); + return; + } + }); +} + +SerialDevice& PCISerialDevice::the() +{ + VERIFY(s_the); + return *s_the; +} + +bool PCISerialDevice::is_available() +{ + return s_the; +} + +} diff --git a/Kernel/Devices/PCISerialDevice.h b/Kernel/Devices/PCISerialDevice.h new file mode 100644 index 0000000000..61f1e32afb --- /dev/null +++ b/Kernel/Devices/PCISerialDevice.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021, Idan Horowitz + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include + +namespace Kernel { + +class PCISerialDevice { + AK_MAKE_ETERNAL +public: + static void detect(); + static SerialDevice& the(); + static bool is_available(); + +private: + struct BoardDefinition { + PCI::ID device_id; + StringView name; + u32 port_count { 0 }; + u32 pci_bar { 0 }; + u32 first_offset { 0 }; + u32 port_size { 0 }; + SerialDevice::Baud baud_rate { SerialDevice::Baud::Baud38400 }; + }; + + static constexpr BoardDefinition board_definitions[1] = { + { { (u16)PCIVendorID::WCH, 0x3253 }, "WCH CH382 2S", 2, 0, 0xC0, 8, SerialDevice::Baud::Baud115200 } + }; +}; + +} diff --git a/Kernel/Devices/SerialDevice.h b/Kernel/Devices/SerialDevice.h index 5a7182d972..11c36f32df 100644 --- a/Kernel/Devices/SerialDevice.h +++ b/Kernel/Devices/SerialDevice.h @@ -111,6 +111,8 @@ public: virtual String device_name() const override; private: + friend class PCISerialDevice; + // ^CharacterDevice virtual const char* class_name() const override { return "SerialDevice"; } diff --git a/Kernel/PCI/IDs.h b/Kernel/PCI/IDs.h index 38c0912c5c..ad55269073 100644 --- a/Kernel/PCI/IDs.h +++ b/Kernel/PCI/IDs.h @@ -11,6 +11,7 @@ namespace Kernel { enum class PCIVendorID { VirtIO = 0x1af4, Intel = 0x8086, + WCH = 0x1c00, }; enum class PCIDeviceID { diff --git a/Kernel/init.cpp b/Kernel/init.cpp index 207242df66..ec6552f924 100644 --- a/Kernel/init.cpp +++ b/Kernel/init.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -146,6 +147,7 @@ extern "C" UNMAP_AFTER_INIT [[noreturn]] void init() // Initialize the PCI Bus as early as possible, for early boot (PCI based) serial logging PCI::initialize(); + PCISerialDevice::detect(); VFS::initialize(); diff --git a/Kernel/kprintf.cpp b/Kernel/kprintf.cpp index a01329abe0..7765707957 100644 --- a/Kernel/kprintf.cpp +++ b/Kernel/kprintf.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -34,6 +35,9 @@ int get_serial_debug() static void serial_putch(char ch) { + if (PCISerialDevice::is_available()) + return PCISerialDevice::the().put_char(ch); + static bool serial_ready = false; static bool was_cr = false;