/* * AcpiDevices.cpp * ACPI device enumeration via AML namespace * Copyright (c) 2026 Daniel Hammer */ #include "AcpiDevices.hpp" #include #include #include #include #include using namespace Kt; namespace Hal { namespace AcpiDevices { // ── EISAID decoding ───────────────────────────────────────────── // ACPI encodes PNP IDs as compressed 32-bit EISAIDs. static void DecodeEisaId(uint32_t id, char* out) { // EISA ID encoding: // Bits 31-16: 3 compressed letters (5 bits each, '@' based) // Bits 15-0: 4 hex digits (product number) out[0] = (char)(((id >> 26) & 0x1F) + '@'); out[1] = (char)(((id >> 21) & 0x1F) + '@'); out[2] = (char)(((id >> 16) & 0x1F) + '@'); // Product ID as 4 hex digits static const char hex[] = "0123456789ABCDEF"; out[3] = hex[(id >> 12) & 0xF]; out[4] = hex[(id >> 8) & 0xF]; out[5] = hex[(id >> 4) & 0xF]; out[6] = hex[(id >> 0) & 0xF]; out[7] = '\0'; } // ── String comparison ─────────────────────────────────────────── static bool StrEqual(const char* a, const char* b) { while (*a && *b) { if (*a != *b) return false; a++; b++; } return *a == *b; } static void StrCopy(char* dst, const char* src, int maxLen) { int i = 0; while (src[i] && i < maxLen - 1) { dst[i] = src[i]; i++; } dst[i] = '\0'; } // ── DeviceList methods ────────────────────────────────────────── const DeviceInfo* DeviceList::FindByHid(const char* hid) const { for (int i = 0; i < Count; i++) { if (StrEqual(Devices[i].HardwareId, hid)) return &Devices[i]; } return nullptr; } const DeviceInfo* DeviceList::FindByPath(const char* path) const { for (int i = 0; i < Count; i++) { if (StrEqual(Devices[i].Path, path)) return &Devices[i]; } return nullptr; } // ── EvaluateSta ───────────────────────────────────────────────── uint32_t EvaluateSta(int32_t deviceNodeIndex) { auto& interp = AML::GetInterpreter(); auto& ns = interp.GetNamespace(); // Look for _STA child int32_t staNode = -1; ns.ForEachChild(deviceNodeIndex, AML::ObjectType::None, [&](int32_t idx, const AML::NamespaceNode* node) -> bool { if (node->Name[0] == '_' && node->Name[1] == 'S' && node->Name[2] == 'T' && node->Name[3] == 'A') { staNode = idx; return false; // stop } return true; }); if (staNode < 0) return STA_DEFAULT; auto* staObj = ns.GetNode(staNode); if (!staObj) return STA_DEFAULT; if (staObj->Obj.Type == AML::ObjectType::Method) { AML::Object result{}; char path[256]; ns.GetNodePath(staNode, path, 256); if (interp.EvaluateObject(path, result)) return (uint32_t)result.Integer; return STA_DEFAULT; } if (staObj->Obj.Type == AML::ObjectType::Integer) return (uint32_t)staObj->Obj.Integer; return STA_DEFAULT; } // ── EvaluateAdr ───────────────────────────────────────────────── uint64_t EvaluateAdr(int32_t deviceNodeIndex) { auto& interp = AML::GetInterpreter(); auto& ns = interp.GetNamespace(); int32_t adrNode = -1; ns.ForEachChild(deviceNodeIndex, AML::ObjectType::None, [&](int32_t idx, const AML::NamespaceNode* node) -> bool { if (node->Name[0] == '_' && node->Name[1] == 'A' && node->Name[2] == 'D' && node->Name[3] == 'R') { adrNode = idx; return false; } return true; }); if (adrNode < 0) return 0; auto* adrObj = ns.GetNode(adrNode); if (!adrObj) return 0; if (adrObj->Obj.Type == AML::ObjectType::Method) { AML::Object result{}; char path[256]; ns.GetNodePath(adrNode, path, 256); if (interp.EvaluateObject(path, result)) return result.Integer; return 0; } if (adrObj->Obj.Type == AML::ObjectType::Integer) return adrObj->Obj.Integer; return 0; } // ── EvaluateHid ───────────────────────────────────────────────── bool EvaluateHid(int32_t deviceNodeIndex, char* outHid, int maxLen) { auto& interp = AML::GetInterpreter(); auto& ns = interp.GetNamespace(); int32_t hidNode = -1; ns.ForEachChild(deviceNodeIndex, AML::ObjectType::None, [&](int32_t idx, const AML::NamespaceNode* node) -> bool { if (node->Name[0] == '_' && node->Name[1] == 'H' && node->Name[2] == 'I' && node->Name[3] == 'D') { hidNode = idx; return false; } return true; }); if (hidNode < 0) return false; AML::Object result{}; char path[256]; ns.GetNodePath(hidNode, path, 256); if (!interp.EvaluateObject(path, result)) return false; if (result.Type == AML::ObjectType::Integer) { // EISA ID DecodeEisaId((uint32_t)result.Integer, outHid); return true; } if (result.Type == AML::ObjectType::String) { StrCopy(outHid, result.String.Data, maxLen); return true; } return false; } // ── EvaluateUid ───────────────────────────────────────────────── bool EvaluateUid(int32_t deviceNodeIndex, char* outUid, int maxLen) { auto& interp = AML::GetInterpreter(); auto& ns = interp.GetNamespace(); int32_t uidNode = -1; ns.ForEachChild(deviceNodeIndex, AML::ObjectType::None, [&](int32_t idx, const AML::NamespaceNode* node) -> bool { if (node->Name[0] == '_' && node->Name[1] == 'U' && node->Name[2] == 'I' && node->Name[3] == 'D') { uidNode = idx; return false; } return true; }); if (uidNode < 0) return false; AML::Object result{}; char path[256]; ns.GetNodePath(uidNode, path, 256); if (!interp.EvaluateObject(path, result)) return false; if (result.Type == AML::ObjectType::Integer) { // Convert integer to string uint64_t val = result.Integer; char buf[21]; int i = 0; if (val == 0) { buf[i++] = '0'; } else { char tmp[21]; int t = 0; while (val > 0) { tmp[t++] = '0' + (val % 10); val /= 10; } for (int j = t - 1; j >= 0; j--) buf[i++] = tmp[j]; } buf[i] = '\0'; StrCopy(outUid, buf, maxLen); return true; } if (result.Type == AML::ObjectType::String) { StrCopy(outUid, result.String.Data, maxLen); return true; } return false; } // ── EvaluateCrs ───────────────────────────────────────────────── bool EvaluateCrs(int32_t deviceNodeIndex, AML::ResourceList& result) { auto& interp = AML::GetInterpreter(); auto& ns = interp.GetNamespace(); int32_t crsNode = -1; ns.ForEachChild(deviceNodeIndex, AML::ObjectType::None, [&](int32_t idx, const AML::NamespaceNode* node) -> bool { if (node->Name[0] == '_' && node->Name[1] == 'C' && node->Name[2] == 'R' && node->Name[3] == 'S') { crsNode = idx; return false; } return true; }); if (crsNode < 0) return false; AML::Object crsResult{}; char path[256]; ns.GetNodePath(crsNode, path, 256); if (!interp.EvaluateObject(path, crsResult)) return false; if (crsResult.Type != AML::ObjectType::Buffer) return false; return AML::ParseResourceTemplate(crsResult.Buffer.Data, crsResult.Buffer.Length, result); } // ── EnumerateAll ──────────────────────────────────────────────── void EnumerateAll(DeviceList& result) { auto& interp = AML::GetInterpreter(); if (!interp.IsInitialized()) return; auto& ns = interp.GetNamespace(); result.Count = 0; // Walk all namespace nodes looking for Device objects ns.WalkDescendants(ns.RootIndex(), AML::ObjectType::Device, [&](int32_t idx, [[maybe_unused]] const AML::NamespaceNode* node) { if (result.Count >= MaxDevices) return; auto& dev = result.Devices[result.Count]; dev.NodeIndex = idx; // Get the path ns.GetNodePath(idx, dev.Path, 128); // Evaluate _STA dev.Status = EvaluateSta(idx); dev.IsPresent = (dev.Status & STA_PRESENT) && (dev.Status & STA_FUNCTIONAL); // Evaluate _HID if (!EvaluateHid(idx, dev.HardwareId, 16)) dev.HardwareId[0] = '\0'; // Evaluate _UID if (!EvaluateUid(idx, dev.UniqueId, 16)) dev.UniqueId[0] = '\0'; // Evaluate _ADR dev.Address = EvaluateAdr(idx); result.Count++; }); KernelLogStream(OK, "ACPI") << "Enumerated " << base::dec << (uint64_t)result.Count << " ACPI devices"; // Log discovered devices for (int i = 0; i < result.Count; i++) { auto& dev = result.Devices[i]; if (dev.HardwareId[0]) { KernelLogStream(DEBUG, "ACPI") << " " << dev.Path << " HID=" << dev.HardwareId << (dev.IsPresent ? " [present]" : " [not present]"); } else if (dev.Address != 0) { KernelLogStream(DEBUG, "ACPI") << " " << dev.Path << " ADR=" << base::hex << dev.Address << (dev.IsPresent ? " [present]" : " [not present]"); } } } // ── GetSleepState ─────────────────────────────────────────────── SleepState GetSleepState(int state) { SleepState result{}; result.Valid = false; if (state < 0 || state > 5) return result; char path[8] = "\\_Sx_"; path[2] = 'S'; path[3] = '0' + (char)state; path[4] = '_'; path[5] = '\0'; auto& interp = AML::GetInterpreter(); AML::Object obj{}; if (!interp.EvaluateObject(path, obj)) return result; // The sleep state object should be a Package with at least 2 integers. // During loading, we stored Package data as a raw buffer. // For the common case, the \_S5_ was already extracted by the old parser. // Try to get it from namespace directly. auto& ns = interp.GetNamespace(); int32_t node = ns.FindNode(path); if (node < 0) return result; auto* nsNode = ns.GetNode(node); if (!nsNode) return result; if (nsNode->Obj.Type == AML::ObjectType::Package) { // Parse the raw package buffer to extract SLP_TYP values const uint8_t* data = nsNode->Obj.Buffer.Data; uint32_t len = nsNode->Obj.Buffer.Length; if (len < 2) return result; // NumElements uint8_t numElements = data[0]; uint32_t pos = 1; if (numElements >= 1 && pos < len) { // Decode first element uint8_t elem0 = data[pos]; if (elem0 == AML::ZeroOp) { result.SLP_TYPa = 0; pos++; } else if (elem0 == AML::OneOp) { result.SLP_TYPa = 1; pos++; } else if (elem0 == AML::BytePrefix && pos + 1 < len) { result.SLP_TYPa = data[pos + 1]; pos += 2; } else { pos++; } } if (numElements >= 2 && pos < len) { uint8_t elem1 = data[pos]; if (elem1 == AML::ZeroOp) { result.SLP_TYPb = 0; pos++; } else if (elem1 == AML::OneOp) { result.SLP_TYPb = 1; pos++; } else if (elem1 == AML::BytePrefix && pos + 1 < len) { result.SLP_TYPb = data[pos + 1]; pos += 2; } else { pos++; } } result.Valid = true; } else if (nsNode->Obj.Type == AML::ObjectType::Integer) { result.SLP_TYPa = (uint16_t)nsNode->Obj.Integer; result.SLP_TYPb = 0; result.Valid = true; } return result; } }; };