Sfoglia il codice sorgente

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.
Liav A 3 anni fa
parent
commit
b1ca39411b

+ 23 - 0
Kernel/API/DeviceEvent.h

@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/Types.h>
+
+struct DeviceEvent {
+    int state;
+    int is_block_device;
+    unsigned major_number;
+    unsigned minor_number;
+
+    enum State {
+        Removed = 0x01,
+        Inserted = 0x02,
+        Recovered = 0x03,
+        FatalError = 0x04,
+    };
+};

+ 1 - 0
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

+ 53 - 0
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);
+}
+
+}

+ 32 - 0
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; }
+};
+
+}

+ 29 - 0
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)

+ 11 - 0
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;
 };
 
 }

+ 2 - 0
Kernel/init.cpp

@@ -15,6 +15,7 @@
 #include <Kernel/CommandLine.h>
 #include <Kernel/Devices/Audio/AC97.h>
 #include <Kernel/Devices/Audio/SB16.h>
+#include <Kernel/Devices/DeviceControlDevice.h>
 #include <Kernel/Devices/DeviceManagement.h>
 #include <Kernel/Devices/FullDevice.h>
 #include <Kernel/Devices/HID/HIDManagement.h>
@@ -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();