reorg: move device data modules into library/device/<domain>/
First step of the device-code reorganization (plan: group device code by domain, split each domain into a shareable data module + a logic module). This moves the pure-data modules — the enums, wire types, and taxonomies that any layer including the kernel can import cheaply — out of system/devices/ and into their domain home: device-abi -> library/device/model/device-abi.zig pci-class -> library/device/pci/pci-class.zig usb-abi -> library/device/usb/usb-abi.zig usb-ids -> library/device/usb/usb-ids.zig acpi-ids -> library/device/acpi/acpi-ids.zig aml/ -> library/device/acpi/aml/ Module names are unchanged, so this is a pure file move: only the b.addModule paths and the host-test file list in build.zig change; no importer is touched. system/devices/ now holds only the kernel-internal device model (device-model, platform, acpi, device-tree, power). The device *logic* (the pci Function helper, the usb transfer client) and the kernel's cosmetic taxonomy dependency are handled in following commits. zig build + zig build test green.
This commit is contained in:
@@ -0,0 +1,181 @@
|
||||
//! The ACPI namespace the AML parser builds: a tree of named nodes, plus the name
|
||||
//! resolution rules the parser needs while it walks (so a method invocation can be
|
||||
//! resolved to its declaration to learn its argument count).
|
||||
//!
|
||||
//! Nodes are individually allocated and linked intrusively (first-child /
|
||||
//! next-sibling), the same shape as the device tree in `device.zig`.
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
pub const NodeKind = enum {
|
||||
root,
|
||||
scope,
|
||||
device,
|
||||
method,
|
||||
name,
|
||||
region, // OperationRegion
|
||||
field, // a Field unit
|
||||
mutex,
|
||||
event,
|
||||
processor,
|
||||
power_resource,
|
||||
thermal_zone,
|
||||
alias,
|
||||
external,
|
||||
other,
|
||||
};
|
||||
|
||||
pub const Node = struct {
|
||||
/// The 4-byte NameSeg identifying this node within its parent. The root uses
|
||||
/// all-zero.
|
||||
segment: [4]u8 = .{ 0, 0, 0, 0 },
|
||||
kind: NodeKind = .other,
|
||||
/// For Method / External: the declared argument count (0..7). Used to resolve
|
||||
/// how many TermArgs a method invocation consumes.
|
||||
arg_count: u8 = 0,
|
||||
/// For Name: the AML bytes of its DataReferenceObject (so a value like a sleep
|
||||
/// state's (`_Sx`) Package can be parsed on demand). For Method: the AML bytes of the body,
|
||||
/// interpreted on demand by the evaluator. Empty otherwise.
|
||||
value: []const u8 = &.{},
|
||||
|
||||
// OperationRegion metadata (kind == .region): the address space, plus the AML
|
||||
// of the offset/length expressions (evaluated lazily, usually constants).
|
||||
region_space: u8 = 0,
|
||||
region_offset_aml: []const u8 = &.{},
|
||||
region_len_aml: []const u8 = &.{},
|
||||
|
||||
// Field-unit metadata (kind == .field): which region it lives in and its bit
|
||||
// position/width/access, so the evaluator can read/write it.
|
||||
region: ?*Node = null,
|
||||
bit_offset: u32 = 0,
|
||||
bit_width: u32 = 0,
|
||||
access_type: u8 = 0,
|
||||
|
||||
parent: ?*Node = null,
|
||||
first_child: ?*Node = null,
|
||||
next_sibling: ?*Node = null,
|
||||
|
||||
/// Depth-first count of this node and everything under it.
|
||||
pub fn subtreeCount(self: *const Node) usize {
|
||||
var n: usize = 1;
|
||||
var c = self.first_child;
|
||||
while (c) |child| : (c = child.next_sibling) n += child.subtreeCount();
|
||||
return n;
|
||||
}
|
||||
};
|
||||
|
||||
pub const Namespace = struct {
|
||||
allocator: std.mem.Allocator,
|
||||
root: *Node,
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator) !Namespace {
|
||||
const root = try allocator.create(Node);
|
||||
root.* = .{ .kind = .root };
|
||||
return .{ .allocator = allocator, .root = root };
|
||||
}
|
||||
|
||||
pub fn nodeCount(self: *const Namespace) usize {
|
||||
return self.root.subtreeCount();
|
||||
}
|
||||
|
||||
fn findChild(parent: *Node, segment: [4]u8) ?*Node {
|
||||
var c = parent.first_child;
|
||||
while (c) |child| : (c = child.next_sibling) {
|
||||
if (std.mem.eql(u8, &child.segment, &segment)) return child;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// The direct child of `node` named `segment`, or null. Unlike `resolve`, this does
|
||||
/// not apply the search-rule walk-up — it looks only at immediate children (for
|
||||
/// reading a device's own hardware ID (`_HID`) / current resource settings (`_CRS`)).
|
||||
pub fn childOf(node: *Node, segment: [4]u8) ?*Node {
|
||||
return findChild(node, segment);
|
||||
}
|
||||
|
||||
fn newChild(self: *Namespace, parent: *Node, segment: [4]u8, kind: NodeKind) !*Node {
|
||||
const n = try self.allocator.create(Node);
|
||||
n.* = .{ .segment = segment, .kind = kind, .parent = parent };
|
||||
// Append at the tail so a dump reads in declaration order.
|
||||
if (parent.first_child == null) {
|
||||
parent.first_child = n;
|
||||
} else {
|
||||
var current = parent.first_child.?;
|
||||
while (current.next_sibling) |sib| current = sib;
|
||||
current.next_sibling = n;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/// Create a Field unit node directly under `scope` (field units live in the
|
||||
/// scope of the Field/IndexField/BankField, not under the region).
|
||||
pub fn newFieldUnit(self: *Namespace, scope: *Node, segment: [4]u8) !*Node {
|
||||
return self.findOrCreate(scope, segment, .field);
|
||||
}
|
||||
|
||||
fn findOrCreate(self: *Namespace, parent: *Node, segment: [4]u8, kind: NodeKind) !*Node {
|
||||
if (findChild(parent, segment)) |existing| {
|
||||
// Reopening a scope (e.g. Scope(\_SB) after Device \_SB) keeps the more
|
||||
// specific kind rather than downgrading to a plain scope.
|
||||
if (existing.kind == .scope and kind != .scope) existing.kind = kind;
|
||||
return existing;
|
||||
}
|
||||
return self.newChild(parent, segment, kind);
|
||||
}
|
||||
|
||||
/// The node a definition's NameString names, creating any intermediate scopes.
|
||||
/// The final segment is created (or found) with `kind`; intermediates are
|
||||
/// scopes. Returns the namespace root for a NullName (empty path).
|
||||
pub fn place(
|
||||
self: *Namespace,
|
||||
current: *Node,
|
||||
rooted: bool,
|
||||
parents: u8,
|
||||
segments: []const [4]u8,
|
||||
kind: NodeKind,
|
||||
) !*Node {
|
||||
var base = startNode(self, current, rooted, parents);
|
||||
if (segments.len == 0) return base;
|
||||
var i: usize = 0;
|
||||
while (i + 1 < segments.len) : (i += 1) {
|
||||
base = try self.findOrCreate(base, segments[i], .scope);
|
||||
}
|
||||
return self.findOrCreate(base, segments[segments.len - 1], kind);
|
||||
}
|
||||
|
||||
/// Resolve a NameString *reference* to an existing node, or null. A single
|
||||
/// relative segment uses the ACPI search rule (walk up the ancestors); any
|
||||
/// rooted, parented, or multi-segment path is resolved exactly.
|
||||
pub fn resolve(
|
||||
self: *Namespace,
|
||||
current: *Node,
|
||||
rooted: bool,
|
||||
parents: u8,
|
||||
segments: []const [4]u8,
|
||||
) ?*Node {
|
||||
if (segments.len == 0) return null;
|
||||
|
||||
if (!rooted and parents == 0 and segments.len == 1) {
|
||||
// Search rule: this scope, then each ancestor up to the root.
|
||||
var scope: ?*Node = current;
|
||||
while (scope) |s| : (scope = s.parent) {
|
||||
if (findChild(s, segments[0])) |n| return n;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
var base = startNode(self, current, rooted, parents);
|
||||
for (segments) |segment| {
|
||||
base = findChild(base, segment) orelse return null;
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
fn startNode(self: *Namespace, current: *Node, rooted: bool, parents: u8) *Node {
|
||||
if (rooted) return self.root;
|
||||
var base = current;
|
||||
var up = parents;
|
||||
while (up > 0) : (up -= 1) base = base.parent orelse self.root;
|
||||
return base;
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user