fix: add backlight modulation support for hybrid graphics laptops
This commit is contained in:
1 parent
7979acc2eb
commit
f9e21e0f13
6 files changed
+381
-12
No files matched your search
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* AcpiWmi.cpp
|
||||
* ACPI-WMI mapper (PNP0C14): find a WMI GUID's data block in _WDG and call
|
||||
* its WMxx method
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
#include "AcpiWmi.hpp"
|
||||
#include "AcpiDevices.hpp"
|
||||
#include <ACPI/AML/AmlInterpreter.hpp>
|
||||
#include <Libraries/Memory.hpp>
|
||||
#include <CppLib/Stream.hpp>
|
||||
#include <Terminal/Terminal.hpp>
|
||||
|
||||
using namespace Kt;
|
||||
|
||||
namespace Hal {
|
||||
namespace AcpiWmi {
|
||||
|
||||
static constexpr const char* HidWmi = "PNP0C14";
|
||||
static constexpr uint32_t WdgEntrySize = 20; // GUID, id, count, flags
|
||||
|
||||
static int HexDigit(char c) {
|
||||
if (c >= '0' && c <= '9') return c - '0';
|
||||
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
|
||||
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool ParseGuid(const char* text, uint8_t out[16]) {
|
||||
// Bytes in text order, then the first three fields byte-swapped.
|
||||
uint8_t bytes[16];
|
||||
int count = 0;
|
||||
for (int i = 0; text[i]; i++) {
|
||||
if (text[i] == '-') continue;
|
||||
int hi = HexDigit(text[i]);
|
||||
int lo = text[i + 1] ? HexDigit(text[i + 1]) : -1;
|
||||
if (hi < 0 || lo < 0 || count == 16) return false;
|
||||
bytes[count++] = (uint8_t)(hi << 4 | lo);
|
||||
i++;
|
||||
}
|
||||
if (count != 16) return false;
|
||||
static constexpr int order[16] = {3, 2, 1, 0, 5, 4, 7, 6,
|
||||
8, 9, 10, 11, 12, 13, 14, 15};
|
||||
for (int i = 0; i < 16; i++) out[i] = bytes[order[i]];
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool SearchDevice(int32_t device, const uint8_t guid[16], Block& out) {
|
||||
auto& interp = AML::GetInterpreter();
|
||||
AML::Object wdg{};
|
||||
if (!interp.EvaluateChild(device, "_WDG", nullptr, 0, wdg)
|
||||
|| wdg.Type != AML::ObjectType::Buffer)
|
||||
return false;
|
||||
|
||||
// The interpreter caps buffers; a _WDG that fills the cap may have
|
||||
// lost entries past it.
|
||||
if (wdg.Buffer.Length >= (uint32_t)AML::MaxBufferLen) {
|
||||
char path[128];
|
||||
interp.GetNamespace().GetNodePath(device, path, sizeof(path));
|
||||
KernelLogStream(WARNING, "WMI") << path << "._WDG may be truncated ("
|
||||
<< base::dec << (uint64_t)wdg.Buffer.Length << " bytes read)";
|
||||
}
|
||||
|
||||
for (uint32_t off = 0; off + WdgEntrySize <= wdg.Buffer.Length; off += WdgEntrySize) {
|
||||
const uint8_t* entry = wdg.Buffer.Data + off;
|
||||
if (memcmp(entry, guid, 16) != 0) continue;
|
||||
out.Device = device;
|
||||
out.ObjectId[0] = (char)entry[16];
|
||||
out.ObjectId[1] = (char)entry[17];
|
||||
out.InstanceCount = entry[18];
|
||||
out.Flags = entry[19];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Find(const uint8_t guid[16], Block& out) {
|
||||
auto& interp = AML::GetInterpreter();
|
||||
if (!interp.IsInitialized()) return false;
|
||||
auto& ns = interp.GetNamespace();
|
||||
|
||||
bool found = false;
|
||||
ns.WalkDescendants(ns.RootIndex(), AML::ObjectType::Device,
|
||||
[&](int32_t idx, const AML::NamespaceNode*) {
|
||||
if (found) return;
|
||||
char hid[16];
|
||||
if (!AcpiDevices::EvaluateHid(idx, hid, sizeof(hid))) return;
|
||||
if (memcmp(hid, HidWmi, sizeof("PNP0C14")) != 0) return;
|
||||
if (!(AcpiDevices::EvaluateSta(idx) & AcpiDevices::STA_PRESENT)) return;
|
||||
found = SearchDevice(idx, guid, out);
|
||||
});
|
||||
return found;
|
||||
}
|
||||
|
||||
bool EvaluateMethod(const Block& block, uint8_t instance, uint32_t methodId,
|
||||
const uint8_t* input, uint32_t inputLength,
|
||||
AML::Object& result) {
|
||||
if (!(block.Flags & FlagMethod) || instance >= block.InstanceCount)
|
||||
return false;
|
||||
|
||||
AML::Object args[3];
|
||||
args[0].Type = AML::ObjectType::Integer;
|
||||
args[0].Integer = instance;
|
||||
args[1].Type = AML::ObjectType::Integer;
|
||||
args[1].Integer = methodId;
|
||||
if (block.Flags & FlagString) {
|
||||
uint32_t n = inputLength;
|
||||
if (n >= (uint32_t)AML::MaxStringLen) n = AML::MaxStringLen - 1;
|
||||
args[2].Type = AML::ObjectType::String;
|
||||
if (n) memcpy(args[2].String.Data, input, n);
|
||||
args[2].String.Data[n] = '\0';
|
||||
args[2].String.Length = (uint16_t)n;
|
||||
} else {
|
||||
uint32_t n = inputLength;
|
||||
if (n > (uint32_t)AML::MaxBufferLen) n = AML::MaxBufferLen;
|
||||
args[2].Type = AML::ObjectType::Buffer;
|
||||
if (n) memcpy(args[2].Buffer.Data, input, n);
|
||||
args[2].Buffer.Length = n;
|
||||
}
|
||||
|
||||
char method[5] = {'W', 'M', block.ObjectId[0], block.ObjectId[1], '\0'};
|
||||
return AML::GetInterpreter().EvaluateChild(block.Device, method, args, 3, result);
|
||||
}
|
||||
|
||||
};
|
||||
};
|
||||
Reference in new issue
Block a user