serenity/Kernel/Devices/DeviceManagement.h

80 lines
2.6 KiB
C
Raw Normal View History

/*
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Badge.h>
#include <AK/Error.h>
#include <AK/OwnPtr.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/Device.h>
#include <Kernel/Devices/Generic/ConsoleDevice.h>
#include <Kernel/Devices/Generic/DeviceControlDevice.h>
#include <Kernel/Devices/Generic/NullDevice.h>
#include <Kernel/Library/LockRefPtr.h>
#include <Kernel/UnixTypes.h>
namespace Kernel {
class DeviceManagement {
public:
DeviceManagement();
static void initialize();
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&);
void after_inserting_device(Badge<Device>, Device&);
void before_device_removal(Badge<Device>, Device&);
void for_each(Function<void(Device&)>);
ErrorOr<void> try_for_each(Function<ErrorOr<void>(Device&)>);
RefPtr<Device> get_device(MajorNumber major, MinorNumber minor);
NullDevice const& null_device() const;
NullDevice& null_device();
ConsoleDevice const& console_device() const;
ConsoleDevice& console_device();
template<typename DeviceType, typename... Args>
2022-10-16 22:06:11 +00:00
static inline ErrorOr<NonnullLockRefPtr<DeviceType>> try_create_device(Args&&... args)
requires(requires(Args... args) { DeviceType::try_create(args...); })
{
auto device = TRY(DeviceType::try_create(forward<Args>(args)...));
TRY(device->after_inserting());
return device;
}
template<typename DeviceType, typename... Args>
static inline ErrorOr<NonnullLockRefPtr<DeviceType>> try_create_device(Args&&... args)
{
auto device = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) DeviceType(forward<Args>(args)...)));
TRY(device->after_inserting());
return device;
}
Kernel+Services: Enable barebones hot-plug handling capabilities Userspace initially didn't have any sort of mechanism to handle device hotplug (either removing or inserting a device). This meant that after a short term of scanning all known devices, by fetching device events (DeviceEvent packets) from /dev/devctl, we basically never try to read it again after SystemServer initialization code. To accommodate hotplug needs, we change SystemServer by ensuring it will generate a known set of device nodes at their location during the its main initialization code. This includes devices like /dev/mem, /dev/zero and /dev/full, etc. The actual responsible userspace program to handle hotplug events is a new userspace program called DeviceMapper, with following key points: - Its current task is to to constantly read the /dev/devctl device node. Because we already created generic devices, we only handle devices that are dynamically-generated in nature, like storage devices, audio channels, etc. - Since dynamically-generated device nodes could have an infinite minor numbers, but major numbers are decoded to a device type, we create an internal registry based on two structures - DeviceNodeFamily, and RegisteredDeviceNode. DeviceNodeFamily objects are attached in the main logic code, when handling a DeviceEvent device insertion packet. A DeviceNodeFamily object has an internal HashTable to hold objects of RegisteredDeviceNode class. - Because some device nodes could still share the same major number (TTY and serial TTY devices), we have two modes of allocation - limited allocation (so a range is defined for a major number), or infinite range. Therefore, two (or more) separate DeviceNodeFamily objects can can exist albeit sharing the same major number, but they are required to allocate from a different minor numbers' range to ensure there are no collisions. - As for KCOV, we handle this device differently. In case the user compiled the kernel with such support - this happens to be a singular device node that we usually don't need, so it's dynamically-generated too, and because it has only one instance, we don't register it in our internal registry to not make it complicated needlessly. The Kernel code is modified to allow proper blocking in case of no events in the DeviceControlDevice class, because otherwise we will need to poll periodically the device to check if a new event is available, which would waste CPU time for no good reason.
2023-04-29 18:17:20 +00:00
SpinlockProtected<CircularQueue<DeviceEvent, 100>, LockRank::None>& event_queue(Badge<DeviceControlDevice>);
private:
LockRefPtr<NullDevice> m_null_device;
LockRefPtr<ConsoleDevice> m_console_device;
LockRefPtr<DeviceControlDevice> m_device_control_device;
SpinlockProtected<HashMap<u64, Device*>, LockRank::None> m_devices {};
Kernel+Services: Enable barebones hot-plug handling capabilities Userspace initially didn't have any sort of mechanism to handle device hotplug (either removing or inserting a device). This meant that after a short term of scanning all known devices, by fetching device events (DeviceEvent packets) from /dev/devctl, we basically never try to read it again after SystemServer initialization code. To accommodate hotplug needs, we change SystemServer by ensuring it will generate a known set of device nodes at their location during the its main initialization code. This includes devices like /dev/mem, /dev/zero and /dev/full, etc. The actual responsible userspace program to handle hotplug events is a new userspace program called DeviceMapper, with following key points: - Its current task is to to constantly read the /dev/devctl device node. Because we already created generic devices, we only handle devices that are dynamically-generated in nature, like storage devices, audio channels, etc. - Since dynamically-generated device nodes could have an infinite minor numbers, but major numbers are decoded to a device type, we create an internal registry based on two structures - DeviceNodeFamily, and RegisteredDeviceNode. DeviceNodeFamily objects are attached in the main logic code, when handling a DeviceEvent device insertion packet. A DeviceNodeFamily object has an internal HashTable to hold objects of RegisteredDeviceNode class. - Because some device nodes could still share the same major number (TTY and serial TTY devices), we have two modes of allocation - limited allocation (so a range is defined for a major number), or infinite range. Therefore, two (or more) separate DeviceNodeFamily objects can can exist albeit sharing the same major number, but they are required to allocate from a different minor numbers' range to ensure there are no collisions. - As for KCOV, we handle this device differently. In case the user compiled the kernel with such support - this happens to be a singular device node that we usually don't need, so it's dynamically-generated too, and because it has only one instance, we don't register it in our internal registry to not make it complicated needlessly. The Kernel code is modified to allow proper blocking in case of no events in the DeviceControlDevice class, because otherwise we will need to poll periodically the device to check if a new event is available, which would waste CPU time for no good reason.
2023-04-29 18:17:20 +00:00
SpinlockProtected<CircularQueue<DeviceEvent, 100>, LockRank::None> m_event_queue {};
};
}