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;