diff --git a/Kernel/API/DeviceEvent.h b/Kernel/API/DeviceEvent.h new file mode 100644 index 0000000000..e209f8d088 --- /dev/null +++ b/Kernel/API/DeviceEvent.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2021, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +struct DeviceEvent { + int state; + int is_block_device; + unsigned major_number; + unsigned minor_number; + + enum State { + Removed = 0x01, + Inserted = 0x02, + Recovered = 0x03, + FatalError = 0x04, + }; +}; diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index b887683496..c433b27fda 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -51,6 +51,7 @@ set(KERNEL_SOURCES Devices/CharacterDevice.cpp Devices/ConsoleDevice.cpp Devices/Device.cpp + Devices/DeviceControlDevice.cpp Devices/DeviceManagement.cpp Devices/FullDevice.cpp Devices/KCOVDevice.cpp diff --git a/Kernel/Devices/DeviceControlDevice.cpp b/Kernel/Devices/DeviceControlDevice.cpp new file mode 100644 index 0000000000..5eeff02c43 --- /dev/null +++ b/Kernel/Devices/DeviceControlDevice.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2021, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +namespace Kernel { + +UNMAP_AFTER_INIT NonnullRefPtr DeviceControlDevice::must_create() +{ + auto device_control_device_or_error = DeviceManagement::try_create_device(); + // FIXME: Find a way to propagate errors + VERIFY(!device_control_device_or_error.is_error()); + return device_control_device_or_error.release_value(); +} + +bool DeviceControlDevice::can_read(const OpenFileDescription&, size_t) const +{ + return true; +} + +UNMAP_AFTER_INIT DeviceControlDevice::DeviceControlDevice() + : CharacterDevice(2, 10) +{ +} + +UNMAP_AFTER_INIT DeviceControlDevice::~DeviceControlDevice() +{ +} + +ErrorOr DeviceControlDevice::read(OpenFileDescription&, u64, UserOrKernelBuffer& buffer, size_t size) +{ + auto device_event = DeviceManagement::the().dequeue_top_device_event({}); + if (!device_event.has_value()) + return 0; + + if (size < sizeof(DeviceEvent)) + return Error::from_errno(EOVERFLOW); + size_t nread = 0; + TRY(buffer.write(&device_event.value(), nread, sizeof(DeviceEvent))); + nread += sizeof(DeviceEvent); + return nread; +} + +ErrorOr DeviceControlDevice::ioctl(OpenFileDescription&, unsigned, Userspace) +{ + return Error::from_errno(ENOTSUP); +} + +} diff --git a/Kernel/Devices/DeviceControlDevice.h b/Kernel/Devices/DeviceControlDevice.h new file mode 100644 index 0000000000..16fdc73e7a --- /dev/null +++ b/Kernel/Devices/DeviceControlDevice.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace Kernel { + +class DeviceControlDevice final : public CharacterDevice { + friend class DeviceManagement; + +public: + static NonnullRefPtr must_create(); + virtual ~DeviceControlDevice() override; + +private: + DeviceControlDevice(); + + // ^CharacterDevice + virtual ErrorOr ioctl(OpenFileDescription&, unsigned request, Userspace arg) override; + virtual ErrorOr read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override; + virtual ErrorOr write(OpenFileDescription&, u64, const UserOrKernelBuffer&, size_t) override { return Error::from_errno(ENOTSUP); } + virtual bool can_read(const OpenFileDescription&, size_t) const override; + virtual bool can_write(const OpenFileDescription&, size_t) const override { return false; } + virtual StringView class_name() const override { return "DeviceControlDevice"sv; } +}; + +} diff --git a/Kernel/Devices/DeviceManagement.cpp b/Kernel/Devices/DeviceManagement.cpp index 5f68d1cfb4..3315c80e21 100644 --- a/Kernel/Devices/DeviceManagement.cpp +++ b/Kernel/Devices/DeviceManagement.cpp @@ -37,6 +37,11 @@ UNMAP_AFTER_INIT void DeviceManagement::attach_null_device(NullDevice const& dev m_null_device = device; } +UNMAP_AFTER_INIT void DeviceManagement::attach_device_control_device(DeviceControlDevice const& device) +{ + m_device_control_device = device; +} + DeviceManagement& DeviceManagement::the() { return *s_the; @@ -52,6 +57,14 @@ Device* DeviceManagement::get_device(MajorNumber major, MinorNumber minor) }); } +Optional DeviceManagement::dequeue_top_device_event(Badge) +{ + SpinlockLocker locker(m_event_queue_lock); + if (m_event_queue.is_empty()) + return {}; + return m_event_queue.dequeue(); +} + void DeviceManagement::before_device_removal(Badge, Device& device) { u64 device_id = encoded_device(device.major(), device.minor()); @@ -59,6 +72,14 @@ void DeviceManagement::before_device_removal(Badge, Device& device) VERIFY(map.contains(device_id)); map.remove(encoded_device(device.major(), device.minor())); }); + + { + DeviceEvent event { DeviceEvent::State::Removed, device.is_block_device(), device.major().value(), device.minor().value() }; + SpinlockLocker locker(m_event_queue_lock); + m_event_queue.enqueue(event); + } + if (m_device_control_device) + m_device_control_device->evaluate_block_conditions(); } void DeviceManagement::after_inserting_device(Badge, Device& device) @@ -75,6 +96,14 @@ void DeviceManagement::after_inserting_device(Badge, Device& device) VERIFY_NOT_REACHED(); } }); + + { + DeviceEvent event { DeviceEvent::State::Inserted, device.is_block_device(), device.major().value(), device.minor().value() }; + SpinlockLocker locker(m_event_queue_lock); + m_event_queue.enqueue(event); + } + if (m_device_control_device) + m_device_control_device->evaluate_block_conditions(); } void DeviceManagement::for_each(Function callback) diff --git a/Kernel/Devices/DeviceManagement.h b/Kernel/Devices/DeviceManagement.h index 18431232fd..a16181c57b 100644 --- a/Kernel/Devices/DeviceManagement.h +++ b/Kernel/Devices/DeviceManagement.h @@ -13,11 +13,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -31,12 +33,17 @@ public: static DeviceManagement& the(); void attach_null_device(NullDevice const&); + void attach_device_control_device(DeviceControlDevice const&); + bool is_console_device_attached() const { return !m_console_device.is_null(); } void attach_console_device(ConsoleDevice const&); // FIXME: Once we have a singleton for managing many sound cards, remove this from here void attach_audio_device(CharacterDevice const&); + bool is_device_event_queue_ready_to_read() const; + Optional dequeue_top_device_event(Badge); + void after_inserting_device(Badge, Device&); void before_device_removal(Badge, Device&); @@ -60,9 +67,13 @@ public: private: RefPtr m_null_device; RefPtr m_console_device; + RefPtr m_device_control_device; // FIXME: Once we have a singleton for managing many sound cards, remove this from here NonnullRefPtrVector m_audio_devices; MutexProtected> m_devices; + + mutable Spinlock m_event_queue_lock; + CircularQueue m_event_queue; }; } diff --git a/Kernel/init.cpp b/Kernel/init.cpp index ae6c095392..9895895721 100644 --- a/Kernel/init.cpp +++ b/Kernel/init.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -185,6 +186,7 @@ extern "C" [[noreturn]] UNMAP_AFTER_INIT void init(BootInfo const& boot_info) SysFSComponentRegistry::initialize(); DeviceManagement::the().attach_null_device(*NullDevice::must_initialize()); DeviceManagement::the().attach_console_device(*ConsoleDevice::must_create()); + DeviceManagement::the().attach_device_control_device(*DeviceControlDevice::must_create()); s_bsp_processor.initialize(0); CommandLine::initialize();