Kernel: Add support for ACPI Control Method Batteries
The implementation is kinda weird but it exposes some battery information to userspace!
This commit is contained in:
parent
b9fe564d78
commit
4e364bd2f6
|
@ -1,6 +1,7 @@
|
||||||
set(KERNEL_SOURCES
|
set(KERNEL_SOURCES
|
||||||
font/prefs.psf.o
|
font/prefs.psf.o
|
||||||
kernel/ACPI/ACPI.cpp
|
kernel/ACPI/ACPI.cpp
|
||||||
|
kernel/ACPI/BatterySystem.cpp
|
||||||
kernel/ACPI/AML/Namespace.cpp
|
kernel/ACPI/AML/Namespace.cpp
|
||||||
kernel/ACPI/AML/Node.cpp
|
kernel/ACPI/AML/Node.cpp
|
||||||
kernel/ACPI/AML/OpRegion.cpp
|
kernel/ACPI/AML/OpRegion.cpp
|
||||||
|
|
|
@ -28,6 +28,8 @@ namespace Kernel::ACPI
|
||||||
// 2: SAPIC
|
// 2: SAPIC
|
||||||
BAN::ErrorOr<void> enter_acpi_mode(uint8_t mode);
|
BAN::ErrorOr<void> enter_acpi_mode(uint8_t mode);
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> initialize_acpi_devices();
|
||||||
|
|
||||||
BAN::ErrorOr<void> poweroff();
|
BAN::ErrorOr<void> poweroff();
|
||||||
BAN::ErrorOr<void> reset();
|
BAN::ErrorOr<void> reset();
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <kernel/ACPI/AML/Namespace.h>
|
||||||
|
#include <kernel/FS/TmpFS/Inode.h>
|
||||||
|
|
||||||
|
namespace Kernel::ACPI
|
||||||
|
{
|
||||||
|
|
||||||
|
class BatterySystem
|
||||||
|
{
|
||||||
|
BAN_NON_COPYABLE(BatterySystem);
|
||||||
|
BAN_NON_MOVABLE(BatterySystem);
|
||||||
|
public:
|
||||||
|
static BAN::ErrorOr<void> initialize(AML::Namespace& acpi_namespace);
|
||||||
|
|
||||||
|
private:
|
||||||
|
BatterySystem(AML::Namespace&);
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> initialize_impl();
|
||||||
|
|
||||||
|
private:
|
||||||
|
AML::Namespace& m_acpi_namespace;
|
||||||
|
BAN::RefPtr<TmpDirectoryInode> m_directory;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
#include <BAN/ScopeGuard.h>
|
#include <BAN/ScopeGuard.h>
|
||||||
#include <BAN/StringView.h>
|
#include <BAN/StringView.h>
|
||||||
#include <kernel/ACPI/ACPI.h>
|
#include <kernel/ACPI/ACPI.h>
|
||||||
|
#include <kernel/ACPI/BatterySystem.h>
|
||||||
#include <kernel/ACPI/AML/OpRegion.h>
|
#include <kernel/ACPI/AML/OpRegion.h>
|
||||||
#include <kernel/BootInfo.h>
|
#include <kernel/BootInfo.h>
|
||||||
#include <kernel/InterruptController.h>
|
#include <kernel/InterruptController.h>
|
||||||
|
@ -774,6 +775,13 @@ acpi_release_global_lock:
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> ACPI::initialize_acpi_devices()
|
||||||
|
{
|
||||||
|
ASSERT(m_namespace);
|
||||||
|
TRY(BatterySystem::initialize(*m_namespace));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
void ACPI::acpi_event_task()
|
void ACPI::acpi_event_task()
|
||||||
{
|
{
|
||||||
auto get_fixed_event =
|
auto get_fixed_event =
|
||||||
|
|
|
@ -0,0 +1,150 @@
|
||||||
|
#include <kernel/ACPI/BatterySystem.h>
|
||||||
|
#include <kernel/FS/DevFS//FileSystem.h>
|
||||||
|
#include <kernel/Timer/Timer.h>
|
||||||
|
|
||||||
|
namespace Kernel::ACPI
|
||||||
|
{
|
||||||
|
|
||||||
|
static BAN::UniqPtr<BatterySystem> s_instance;
|
||||||
|
|
||||||
|
class BatteryInfoInode final : public TmpInode
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static BAN::ErrorOr<BAN::RefPtr<BatteryInfoInode>> create_new(
|
||||||
|
AML::Namespace& acpi_namespace,
|
||||||
|
const AML::Scope& battery_path,
|
||||||
|
BAN::StringView method,
|
||||||
|
size_t index,
|
||||||
|
mode_t mode, uid_t uid, gid_t gid)
|
||||||
|
{
|
||||||
|
auto inode_info = create_inode_info(mode | Mode::IFREG, uid, gid);
|
||||||
|
auto ino = TRY(DevFileSystem::get().allocate_inode(inode_info));
|
||||||
|
|
||||||
|
auto battery_path_copy = TRY(battery_path.copy());
|
||||||
|
auto method_copy = TRY(AML::NameString::from_string(method));
|
||||||
|
|
||||||
|
auto* inode_ptr = new BatteryInfoInode(acpi_namespace, BAN::move(battery_path_copy), BAN::move(method_copy), index, ino, inode_info);
|
||||||
|
if (inode_ptr == nullptr)
|
||||||
|
return BAN::Error::from_errno(ENOMEM);
|
||||||
|
return BAN::RefPtr<BatteryInfoInode>::adopt(inode_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
BAN::ErrorOr<size_t> read_impl(off_t offset, BAN::ByteSpan buffer) override
|
||||||
|
{
|
||||||
|
if (offset < 0)
|
||||||
|
return BAN::Error::from_errno(EINVAL);
|
||||||
|
|
||||||
|
if (SystemTimer::get().ms_since_boot() > m_last_read_ms + 1000)
|
||||||
|
{
|
||||||
|
auto [method_path, method_ref] = TRY(m_acpi_namespace.find_named_object(m_battery_path, m_method_name));
|
||||||
|
if (method_ref == nullptr)
|
||||||
|
return BAN::Error::from_errno(EFAULT);
|
||||||
|
|
||||||
|
auto result = TRY(AML::method_call(method_path, method_ref->node, {}));
|
||||||
|
if (result.type != AML::Node::Type::Package || result.as.package->num_elements < m_result_index)
|
||||||
|
return BAN::Error::from_errno(EFAULT);
|
||||||
|
|
||||||
|
auto& target_elem = result.as.package->elements[m_result_index];
|
||||||
|
if (!target_elem.resolved || !target_elem.value.node)
|
||||||
|
return BAN::Error::from_errno(EFAULT);
|
||||||
|
|
||||||
|
auto target_conv = AML::convert_node(TRY(target_elem.value.node->copy()), AML::ConvInteger, sizeof(uint64_t));
|
||||||
|
if (target_conv.is_error())
|
||||||
|
return BAN::Error::from_errno(EFAULT);
|
||||||
|
|
||||||
|
m_last_read_ms = SystemTimer::get().ms_since_boot();
|
||||||
|
m_last_value = target_conv.value().as.integer.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto target_str = TRY(BAN::String::formatted("{}", m_last_value));
|
||||||
|
|
||||||
|
if (static_cast<size_t>(offset) >= target_str.size())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
const size_t ncopy = BAN::Math::min(buffer.size(), target_str.size() - offset);
|
||||||
|
memcpy(buffer.data(), target_str.data() + offset, ncopy);
|
||||||
|
return ncopy;
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<size_t> write_impl(off_t, BAN::ConstByteSpan) override { return BAN::Error::from_errno(EINVAL); }
|
||||||
|
BAN::ErrorOr<void> truncate_impl(size_t) override { return BAN::Error::from_errno(EINVAL); }
|
||||||
|
|
||||||
|
bool can_read_impl() const override { return true; }
|
||||||
|
bool can_write_impl() const override { return false; }
|
||||||
|
bool has_error_impl() const override { return false; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
BatteryInfoInode(AML::Namespace& acpi_namespace, AML::Scope&& battery_path, AML::NameString&& method, size_t index, ino_t ino, const TmpInodeInfo& info)
|
||||||
|
: TmpInode(DevFileSystem::get(), ino, info)
|
||||||
|
, m_acpi_namespace(acpi_namespace)
|
||||||
|
, m_battery_path(BAN::move(battery_path))
|
||||||
|
, m_method_name(BAN::move(method))
|
||||||
|
, m_result_index(index)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
private:
|
||||||
|
AML::Namespace& m_acpi_namespace;
|
||||||
|
AML::Scope m_battery_path;
|
||||||
|
AML::NameString m_method_name;
|
||||||
|
size_t m_result_index;
|
||||||
|
|
||||||
|
uint64_t m_last_read_ms = 0;
|
||||||
|
uint64_t m_last_value = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> BatterySystem::initialize(AML::Namespace& acpi_namespace)
|
||||||
|
{
|
||||||
|
ASSERT(!s_instance);
|
||||||
|
|
||||||
|
auto* battery_system = new BatterySystem(acpi_namespace);
|
||||||
|
if (battery_system == nullptr)
|
||||||
|
return BAN::Error::from_errno(ENOMEM);
|
||||||
|
s_instance = BAN::UniqPtr<BatterySystem>::adopt(battery_system);
|
||||||
|
|
||||||
|
TRY(s_instance->initialize_impl());
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
BatterySystem::BatterySystem(AML::Namespace& acpi_namespace)
|
||||||
|
: m_acpi_namespace(acpi_namespace)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> BatterySystem::initialize_impl()
|
||||||
|
{
|
||||||
|
auto base_inode = TRY(TmpDirectoryInode::create_new(DevFileSystem::get(), 0555, 0, 0, static_cast<TmpInode&>(*DevFileSystem::get().root_inode())));
|
||||||
|
DevFileSystem::get().add_inode("batteries", base_inode);
|
||||||
|
|
||||||
|
auto batteries = TRY(m_acpi_namespace.find_device_with_eisa_id("PNP0C0A"_sv));
|
||||||
|
for (const auto& battery : batteries)
|
||||||
|
{
|
||||||
|
auto [_1, bif_ref] = TRY(m_acpi_namespace.find_named_object(battery, TRY(AML::NameString::from_string("_BIF"_sv))));
|
||||||
|
if (!bif_ref || bif_ref->node.type != AML::Node::Type::Method || bif_ref->node.as.method.arg_count != 0)
|
||||||
|
{
|
||||||
|
dwarnln("Battery {} does not have _BIF or it is invalid", battery);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto [_2, bst_ref] = TRY(m_acpi_namespace.find_named_object(battery, TRY(AML::NameString::from_string("_BST"_sv))));
|
||||||
|
if (!bst_ref || bst_ref->node.type != AML::Node::Type::Method || bst_ref->node.as.method.arg_count != 0)
|
||||||
|
{
|
||||||
|
dwarnln("Battery {} does not have _BST or it is invalid", battery);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto battery_name = BAN::StringView(reinterpret_cast<const char*>(&battery.parts.back()), 4);
|
||||||
|
auto battery_inode = TRY(TmpDirectoryInode::create_new(DevFileSystem::get(), 0555, 0, 0, *base_inode));
|
||||||
|
TRY(base_inode->link_inode(*battery_inode, battery_name));
|
||||||
|
|
||||||
|
auto cap_full_inode = TRY(BatteryInfoInode::create_new(m_acpi_namespace, battery, "_BIF"_sv, 2, 0444, 0, 0));
|
||||||
|
TRY(battery_inode->link_inode(*cap_full_inode, "capacity_full"_sv));
|
||||||
|
|
||||||
|
auto cap_now_inode = TRY(BatteryInfoInode::create_new(m_acpi_namespace, battery, "_BST"_sv, 2, 0444, 0, 0));
|
||||||
|
TRY(battery_inode->link_inode(*cap_now_inode, "capacity_now"_sv));
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -231,6 +231,9 @@ static void init2(void*)
|
||||||
if (!cmdline.disable_acpi && ACPI::ACPI::get().enter_acpi_mode(InterruptController::get().is_using_apic()).is_error())
|
if (!cmdline.disable_acpi && ACPI::ACPI::get().enter_acpi_mode(InterruptController::get().is_using_apic()).is_error())
|
||||||
dprintln("Failed to enter ACPI mode");
|
dprintln("Failed to enter ACPI mode");
|
||||||
|
|
||||||
|
if (auto ret = ACPI::ACPI::get().initialize_acpi_devices(); ret.is_error())
|
||||||
|
dwarnln("Could not initialize ACPI devices: {}", ret.error());
|
||||||
|
|
||||||
DevFileSystem::get().initialize_device_updater();
|
DevFileSystem::get().initialize_device_updater();
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
|
|
Loading…
Reference in New Issue