diff options
author | Liav A <liavalb@gmail.com> | 2021-12-20 11:10:35 +0200 |
---|---|---|
committer | Linus Groh <mail@linusgroh.de> | 2022-01-23 00:38:02 +0000 |
commit | b1ca39411b42b4164def6bf6f4e138e5ca7cffb0 (patch) | |
tree | 9ee7951e38dd4e5b8c3604e5bed2130c8fa6a584 /Kernel/Devices | |
parent | b60e19fd3406dec707c4933d840286526e4058d1 (diff) | |
download | serenity-b1ca39411b42b4164def6bf6f4e138e5ca7cffb0.zip |
Kernel/Devices: Introduce the Device Control Device
This device will assist userspace to manage hotplug events.
A userspace application reads a DeviceEvent entry until the return value
is zero which indicates no events that are queued and waiting for
processing.
Trying to read with a buffer smaller than sizeof(DeviceEvent) results in
EOVERFLOW.
For now, there's no ioctl mechanism for this device but in the future an
acknowledgement mechanism can be implemented via ioctl(2) interface.
Diffstat (limited to 'Kernel/Devices')
-rw-r--r-- | Kernel/Devices/DeviceControlDevice.cpp | 53 | ||||
-rw-r--r-- | Kernel/Devices/DeviceControlDevice.h | 32 | ||||
-rw-r--r-- | Kernel/Devices/DeviceManagement.cpp | 29 | ||||
-rw-r--r-- | Kernel/Devices/DeviceManagement.h | 11 |
4 files changed, 125 insertions, 0 deletions
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. <liavalb@hotmail.co.il> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <Kernel/Devices/DeviceControlDevice.h> +#include <Kernel/Devices/DeviceManagement.h> + +namespace Kernel { + +UNMAP_AFTER_INIT NonnullRefPtr<DeviceControlDevice> DeviceControlDevice::must_create() +{ + auto device_control_device_or_error = DeviceManagement::try_create_device<DeviceControlDevice>(); + // 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<size_t> 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<void> DeviceControlDevice::ioctl(OpenFileDescription&, unsigned, Userspace<void*>) +{ + 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. <liavalb@hotmail.co.il> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <Kernel/Devices/CharacterDevice.h> + +namespace Kernel { + +class DeviceControlDevice final : public CharacterDevice { + friend class DeviceManagement; + +public: + static NonnullRefPtr<DeviceControlDevice> must_create(); + virtual ~DeviceControlDevice() override; + +private: + DeviceControlDevice(); + + // ^CharacterDevice + virtual ErrorOr<void> ioctl(OpenFileDescription&, unsigned request, Userspace<void*> arg) override; + virtual ErrorOr<size_t> read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override; + virtual ErrorOr<size_t> 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<DeviceEvent> DeviceManagement::dequeue_top_device_event(Badge<DeviceControlDevice>) +{ + 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& device) { u64 device_id = encoded_device(device.major(), device.minor()); @@ -59,6 +72,14 @@ void DeviceManagement::before_device_removal(Badge<Device>, 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& device) @@ -75,6 +96,14 @@ void DeviceManagement::after_inserting_device(Badge<Device>, 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<void(Device&)> 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 <AK/RefPtr.h> #include <AK/Time.h> #include <AK/Types.h> +#include <Kernel/API/DeviceEvent.h> #include <Kernel/API/TimePage.h> #include <Kernel/Arch/RegisterState.h> #include <Kernel/Devices/CharacterDevice.h> #include <Kernel/Devices/ConsoleDevice.h> #include <Kernel/Devices/Device.h> +#include <Kernel/Devices/DeviceControlDevice.h> #include <Kernel/Devices/NullDevice.h> #include <Kernel/UnixTypes.h> @@ -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<DeviceEvent> dequeue_top_device_event(Badge<DeviceControlDevice>); + void after_inserting_device(Badge<Device>, Device&); void before_device_removal(Badge<Device>, Device&); @@ -60,9 +67,13 @@ public: private: RefPtr<NullDevice> m_null_device; RefPtr<ConsoleDevice> m_console_device; + RefPtr<DeviceControlDevice> m_device_control_device; // FIXME: Once we have a singleton for managing many sound cards, remove this from here NonnullRefPtrVector<CharacterDevice, 1> m_audio_devices; MutexProtected<HashMap<u64, Device*>> m_devices; + + mutable Spinlock m_event_queue_lock; + CircularQueue<DeviceEvent, 100> m_event_queue; }; } |