2021-09-11 09:19:20 +03:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <AK/HashTable.h>
|
|
|
|
#include <AK/Singleton.h>
|
|
|
|
#include <Kernel/Devices/DeviceManagement.h>
|
|
|
|
#include <Kernel/FileSystem/InodeMetadata.h>
|
|
|
|
#include <Kernel/Sections.h>
|
|
|
|
|
|
|
|
namespace Kernel {
|
|
|
|
|
|
|
|
static Singleton<DeviceManagement> s_the;
|
|
|
|
|
|
|
|
UNMAP_AFTER_INIT DeviceManagement::DeviceManagement()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
UNMAP_AFTER_INIT void DeviceManagement::initialize()
|
|
|
|
{
|
|
|
|
s_the.ensure_instance();
|
|
|
|
}
|
|
|
|
|
2021-09-16 22:23:25 +03:00
|
|
|
UNMAP_AFTER_INIT void DeviceManagement::attach_console_device(ConsoleDevice const& device)
|
|
|
|
{
|
|
|
|
m_console_device = device;
|
|
|
|
}
|
|
|
|
|
2021-09-11 09:19:20 +03:00
|
|
|
UNMAP_AFTER_INIT void DeviceManagement::attach_null_device(NullDevice const& device)
|
|
|
|
{
|
|
|
|
m_null_device = device;
|
|
|
|
}
|
|
|
|
|
2021-12-20 12:10:35 +03:00
|
|
|
UNMAP_AFTER_INIT void DeviceManagement::attach_device_control_device(DeviceControlDevice const& device)
|
|
|
|
{
|
|
|
|
m_device_control_device = device;
|
|
|
|
}
|
|
|
|
|
2021-09-11 09:19:20 +03:00
|
|
|
DeviceManagement& DeviceManagement::the()
|
|
|
|
{
|
|
|
|
return *s_the;
|
|
|
|
}
|
|
|
|
|
2024-03-04 22:19:31 +03:00
|
|
|
RefPtr<Device> DeviceManagement::get_device(MajorNumber major, MinorNumber minor)
|
2021-09-11 09:19:20 +03:00
|
|
|
{
|
2024-03-04 22:19:31 +03:00
|
|
|
return m_devices.with([&](auto& map) -> RefPtr<Device> {
|
2021-12-23 21:08:18 +03:00
|
|
|
auto it = map.find(encoded_device(major.value(), minor.value()));
|
2021-09-11 09:19:20 +03:00
|
|
|
if (it == map.end())
|
|
|
|
return nullptr;
|
2024-03-04 22:19:31 +03:00
|
|
|
return *it->value;
|
2021-09-11 09:19:20 +03:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
void DeviceManagement::before_device_removal(Badge<Device>, Device& device)
|
|
|
|
{
|
2021-12-23 21:08:18 +03:00
|
|
|
u64 device_id = encoded_device(device.major(), device.minor());
|
2022-02-03 03:41:01 +03:00
|
|
|
m_devices.with([&](auto& map) -> void {
|
2021-09-11 09:19:20 +03:00
|
|
|
VERIFY(map.contains(device_id));
|
|
|
|
map.remove(encoded_device(device.major(), device.minor()));
|
|
|
|
});
|
2021-12-20 12:10:35 +03:00
|
|
|
|
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 21:17:20 +03:00
|
|
|
m_event_queue.with([&](auto& queue) {
|
2021-12-20 12:10:35 +03:00
|
|
|
DeviceEvent event { DeviceEvent::State::Removed, device.is_block_device(), device.major().value(), device.minor().value() };
|
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 21:17:20 +03:00
|
|
|
queue.enqueue(event);
|
|
|
|
});
|
|
|
|
|
2021-12-20 12:10:35 +03:00
|
|
|
if (m_device_control_device)
|
|
|
|
m_device_control_device->evaluate_block_conditions();
|
2021-09-11 09:19:20 +03:00
|
|
|
}
|
|
|
|
|
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 21:17:20 +03:00
|
|
|
SpinlockProtected<CircularQueue<DeviceEvent, 100>, LockRank::None>& DeviceManagement::event_queue(Badge<DeviceControlDevice>)
|
|
|
|
{
|
|
|
|
return m_event_queue;
|
|
|
|
}
|
|
|
|
|
2021-09-11 09:19:20 +03:00
|
|
|
void DeviceManagement::after_inserting_device(Badge<Device>, Device& device)
|
|
|
|
{
|
2021-12-23 21:08:18 +03:00
|
|
|
u64 device_id = encoded_device(device.major(), device.minor());
|
2022-02-03 03:41:01 +03:00
|
|
|
m_devices.with([&](auto& map) -> void {
|
2021-09-11 09:19:20 +03:00
|
|
|
if (map.contains(device_id)) {
|
|
|
|
dbgln("Already registered {},{}: {}", device.major(), device.minor(), device.class_name());
|
|
|
|
VERIFY_NOT_REACHED();
|
|
|
|
}
|
|
|
|
auto result = map.set(device_id, &device);
|
|
|
|
if (result != AK::HashSetResult::InsertedNewEntry) {
|
|
|
|
dbgln("Failed to register {},{}: {}", device.major(), device.minor(), device.class_name());
|
|
|
|
VERIFY_NOT_REACHED();
|
|
|
|
}
|
|
|
|
});
|
2021-12-20 12:10:35 +03:00
|
|
|
|
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 21:17:20 +03:00
|
|
|
m_event_queue.with([&](auto& queue) {
|
2021-12-20 12:10:35 +03:00
|
|
|
DeviceEvent event { DeviceEvent::State::Inserted, device.is_block_device(), device.major().value(), device.minor().value() };
|
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 21:17:20 +03:00
|
|
|
queue.enqueue(event);
|
|
|
|
});
|
|
|
|
|
2021-12-20 12:10:35 +03:00
|
|
|
if (m_device_control_device)
|
|
|
|
m_device_control_device->evaluate_block_conditions();
|
2021-09-11 09:19:20 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void DeviceManagement::for_each(Function<void(Device&)> callback)
|
|
|
|
{
|
2022-02-03 03:41:01 +03:00
|
|
|
m_devices.with([&](auto& map) -> void {
|
2021-09-11 09:19:20 +03:00
|
|
|
for (auto& entry : map)
|
|
|
|
callback(*entry.value);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-02-24 21:03:26 +03:00
|
|
|
ErrorOr<void> DeviceManagement::try_for_each(Function<ErrorOr<void>(Device&)> callback)
|
|
|
|
{
|
|
|
|
return m_devices.with([&](auto& map) -> ErrorOr<void> {
|
|
|
|
for (auto& entry : map)
|
|
|
|
TRY(callback(*entry.value));
|
|
|
|
return {};
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-09-11 09:19:20 +03:00
|
|
|
NullDevice& DeviceManagement::null_device()
|
|
|
|
{
|
|
|
|
return *m_null_device;
|
|
|
|
}
|
|
|
|
|
|
|
|
NullDevice const& DeviceManagement::null_device() const
|
|
|
|
{
|
|
|
|
return *m_null_device;
|
|
|
|
}
|
|
|
|
|
2021-09-16 22:23:25 +03:00
|
|
|
ConsoleDevice const& DeviceManagement::console_device() const
|
|
|
|
{
|
|
|
|
return *m_console_device;
|
|
|
|
}
|
|
|
|
ConsoleDevice& DeviceManagement::console_device()
|
|
|
|
{
|
|
|
|
return *m_console_device;
|
|
|
|
}
|
|
|
|
|
2021-09-11 09:19:20 +03:00
|
|
|
}
|