device-manager: data-driven driver matching via /etc/devices.csv
Replace the three hardcoded switch tables (pciDriverForIdentity, hidDriverFor, usbDriverForIdentity) with an authoritative, human-readable device registry the manager reads at boot. Matching is most-specific-wins across base/subclass/prog_if/vendor/device/subsystem/hid, so a precise vendor:device rule and a generic class rule coexist; an unmatched device is logged, never guessed. This resolves docs' "matching stays code until the third bus". - ABI: child_added and DeviceDescriptor gain vendor/device/subsystem; child_added gains a bus discriminator (BusKind) so PCI and USB class triples match against the right namespace. - pci-bus reads vendor/device (config 0x00) and subsystem (0x2C, type-0) and reports them. - library/device/registry: freestanding CSV parser + matchDriver() with specificity scoring; 5 unit tests wired into `zig build test`. - etc/devices.csv bundled into the initrd; the kernel serves /etc directly, so the manager reads it before any filesystem service is up (fat starts later). - virtio-gpu: drop the now-redundant post-spawn 1AF4:1050 re-confirm, since the registry binds this driver by exact identity. - Remove the orphaned system/drivers/display driver (unreferenced by build or registry). - docs: new devices-csv.md; device-manager.md "matching stays code" resolved. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KJqSiLLchDUUCoXn5jsiwd
This commit is contained in:
@@ -1,59 +0,0 @@
|
||||
//! /system/drivers/display - the generic display engine driver.
|
||||
//! This driver is a non official driver for GPU vendors like Intel, NVIDIA, AMD. It provides basic
|
||||
//! display engine features to the display engine protocol used by the display server, compositor
|
||||
//! and graphical user interface libraries like Zooeee.
|
||||
//!
|
||||
//! This driver is acts like BUS driver, in that it detects the GPU, its capabilities and loads
|
||||
//! sub-drivers for each device detected. Similar The device manager
|
||||
//! finds display adaptor e.g. over the PCI/ACPI, and passes the buck on to this driver to handle.
|
||||
//!
|
||||
//! The display driver provides the low level part of identifying the device and launching the
|
||||
//! generic device driver for a GPU vendor.
|
||||
//!
|
||||
//! It takes over the framebuffer feature that was setup during system boot.
|
||||
const std = @import("std");
|
||||
const device = @import("driver");
|
||||
const ipc = @import("ipc");
|
||||
const process = @import("process");
|
||||
const service = @import("service");
|
||||
const device_manager = @import("driver");
|
||||
const logging = @import("logging");
|
||||
const mmio = @import("mmio");
|
||||
const display_protocol = @import("display-protocol");
|
||||
const scanout_protocol = @import("scanout-protocol");
|
||||
var device_id: u64 = 0;
|
||||
|
||||
fn initialise(endpoint: ipc.Handle) bool {
|
||||
_ = endpoint;
|
||||
// Hello the device manager (role: device — we claim one GPU's PCI function
|
||||
// and serve its display engine; we report no children). Best-effort: without a
|
||||
// manager the driver still runs standalone; when present, the manager marks us
|
||||
// up before the hello deadline and restarts us if we die.
|
||||
_ = device_manager.hello(.device, device_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
fn onMessage(message: []const u8, reply: []u8, sender: u32, capability: ?ipc.Handle) usize {
|
||||
_ = sender;
|
||||
_ = capability;
|
||||
_ = reply;
|
||||
|
||||
if (message.len < scanout_protocol.request_size) return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
pub fn main(init: process.Init) void {
|
||||
const argument = init.arguments.get(1) orelse {
|
||||
_ = logging.write("display: missing device id (argv[1])\n");
|
||||
return;
|
||||
};
|
||||
device_id = std.fmt.parseInt(u64, argument, 10) catch {
|
||||
std.log.info("malformed device id '{s}'", .{argument});
|
||||
return;
|
||||
};
|
||||
service.run(256, .{
|
||||
.service = .scanout,
|
||||
.init = initialise,
|
||||
.on_message = onMessage,
|
||||
});
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
//! /system/drivers/display/intel-integrated - the intel 985 family display engine driver.
|
||||
const std = @import("std");
|
||||
const device = @import("driver");
|
||||
const ipc = @import("ipc");
|
||||
const process = @import("process");
|
||||
const service = @import("service");
|
||||
const logging = @import("logging");
|
||||
const mmio = @import("mmio");
|
||||
const display_protocol = @import("display-protocol");
|
||||
const scanout_protocol = @import("scanout-protocol");
|
||||
const device_manager_protocol = @import("device-manager-protocol");
|
||||
var device_id: u64 = 0;
|
||||
|
||||
|
||||
fn initialise(endpoint: ipc.Handle) bool {
|
||||
_ = endpoint;
|
||||
return true;
|
||||
}
|
||||
|
||||
fn onMessage(message: []const u8, reply: []u8, sender: u32, capability: ?ipc.Handle) usize {
|
||||
_ = sender;
|
||||
_ = capability;
|
||||
_ = reply;
|
||||
|
||||
if (message.len < scanout_protocol.request_size) return 0;
|
||||
const request = std.mem.bytesToValue(scanout_protocol.Request, message[0..scanout_protocol.request_size]);
|
||||
switch (request.operation) {
|
||||
_ => return 0,
|
||||
// TODO:
|
||||
// @intFromEnum(sp.Operation.present) => return scanoutStatus(reply, presentFull()),
|
||||
// @intFromEnum(sp.Operation.get_modes) => {
|
||||
// var response = sp.ModesReply{ .status = 0, .count = offered_modes.len, .modes = undefined };
|
||||
// for (0..sp.max_modes) |i| {
|
||||
// response.modes[i] = if (i < offered_modes.len)
|
||||
// .{ .width = offered_modes[i].width, .height = offered_modes[i].height }
|
||||
// else
|
||||
// .{ .width = 0, .height = 0 };
|
||||
// }
|
||||
// @memcpy(reply[0..sp.modes_reply_size], std.mem.asBytes(&response));
|
||||
// return sp.modes_reply_size;
|
||||
// },
|
||||
// @intFromEnum(sp.Operation.set_mode) => {
|
||||
// const w = request.width;
|
||||
// const h = request.height;
|
||||
// if (w == 0 or h == 0 or w > max_width or h > max_height) return scanoutStatus(reply, false);
|
||||
// current_width = w;
|
||||
// current_height = h;
|
||||
// return scanoutStatus(reply, setScanoutRect());
|
||||
// },
|
||||
else => return 0,
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
pub fn main(init: process.Init) void {
|
||||
const argument = init.arguments.get(1) orelse {
|
||||
_ = logging.write("display/intel-985: missing device id (argv[1])\n");
|
||||
return;
|
||||
};
|
||||
device_id = std.fmt.parseInt(u64, argument, 10) catch {
|
||||
std.log.info("malformed device id '{s}'", .{argument});
|
||||
return;
|
||||
};
|
||||
service.run(256, .{
|
||||
.service = .scanout,
|
||||
.init = initialise,
|
||||
.on_message = onMessage,
|
||||
});
|
||||
}
|
||||
@@ -153,6 +153,12 @@ fn registerAndReport(bus: u64, dev: u64, function: u64, class_triple: u32) void
|
||||
var descriptor = std.mem.zeroes(device.DeviceDescriptor);
|
||||
descriptor.class = @intFromEnum(device.DeviceClass.pci_device);
|
||||
descriptor.pci_class = class_triple;
|
||||
// Vendor/device from the first config dword (0x00): low half vendor, high half
|
||||
// device. These carry to the manager's /etc/devices.csv matcher so a function
|
||||
// can bind on its exact 1AF4:1050 identity, not just its class triple.
|
||||
const vendor_device = configRead(bus, dev, function, 0x00);
|
||||
descriptor.vendor = @truncate(vendor_device);
|
||||
descriptor.device = @truncate(vendor_device >> 16);
|
||||
descriptor.resources[0] = .{
|
||||
.kind = @intFromEnum(device.ResourceKind.memory),
|
||||
.start = ecam_physical + (((bus - start_bus) << 20) | (dev << 15) | (function << 12)),
|
||||
@@ -164,6 +170,11 @@ fn registerAndReport(bus: u64, dev: u64, function: u64, class_triple: u32) void
|
||||
// write all-ones, read the writable mask back, restore. Header type 0 only.
|
||||
const header_type = (configRead(bus, dev, function, 0x0C) >> 16) & 0x7F;
|
||||
if (header_type == 0) {
|
||||
// Subsystem id lives at 0x2C only on type-0 (device) headers, not on
|
||||
// bridges: dword low half is subsystem-vendor, high half subsystem-device.
|
||||
// Repack vendor-first so it reads like the CSV's `ssvid:ssid`.
|
||||
const subsystem_dword = configRead(bus, dev, function, 0x2C);
|
||||
descriptor.subsystem = (@as(u32, @truncate(subsystem_dword)) << 16) | @as(u32, @truncate(subsystem_dword >> 16));
|
||||
const command = configRead16(bus, dev, function, 0x04);
|
||||
configWrite16(bus, dev, function, 0x04, command & ~@as(u16, 0b11));
|
||||
var i: u64 = 0;
|
||||
@@ -215,10 +226,14 @@ fn registerAndReport(bus: u64, dev: u64, function: u64, class_triple: u32) void
|
||||
return;
|
||||
};
|
||||
const report = device_manager_protocol.ChildAdded{
|
||||
.bus = @intFromEnum(device_manager_protocol.BusKind.pci),
|
||||
.parent = bridge_id,
|
||||
.bus_address = (bus << 8) | (dev << 3) | function,
|
||||
.identity = class_triple,
|
||||
.device_id = registered,
|
||||
.vendor = descriptor.vendor,
|
||||
.device = descriptor.device,
|
||||
.subsystem = descriptor.subsystem,
|
||||
};
|
||||
var reply: [device_manager_protocol.message_maximum]u8 = undefined;
|
||||
_ = ipc.call(manager_handle, std.mem.asBytes(&report), &reply) catch {
|
||||
|
||||
@@ -379,6 +379,7 @@ fn reportInterface(manager: ipc.Handle, port: u32, interface: library.InterfaceI
|
||||
};
|
||||
|
||||
const report = device_manager_protocol.ChildAdded{
|
||||
.bus = @intFromEnum(device_manager_protocol.BusKind.usb),
|
||||
.parent = controller_id,
|
||||
.bus_address = (@as(u64, port) << 8) | interface.number,
|
||||
.identity = identity,
|
||||
|
||||
@@ -33,11 +33,6 @@ const vg = @import("virtio-gpu-protocol.zig");
|
||||
/// the compositor in the announce so it packs colours in the surface's byte order.
|
||||
const display_format_bgrx: u32 = 1;
|
||||
|
||||
/// The PCI vendor/device ids of a modern virtio-gpu (Red Hat / virtio; GPU is a
|
||||
/// virtio-1.0-only device, so the id is always the modern 0x1050 — no legacy variant).
|
||||
const virtio_vendor: u16 = 0x1AF4;
|
||||
const virtio_gpu_device: u16 = 0x1050;
|
||||
|
||||
/// The scanout resource + shared surface are sized to the *largest* mode we offer; a mode
|
||||
/// change (V5) re-points the scanout rectangle within it, so the resource, its backing, and
|
||||
/// the shared surface never churn — and the surface's row stride is always `max_width`, which
|
||||
@@ -210,19 +205,15 @@ fn initialise(endpoint: ipc.Handle) bool {
|
||||
return false;
|
||||
};
|
||||
|
||||
// Config space is resource 0. Confirm it really is a virtio-gpu, then enable memory-space
|
||||
// decode + bus mastering (the device DMAs the ring and backing out of RAM); pci-bus only
|
||||
// preserves whatever the firmware left, and a secondary display is often left disabled.
|
||||
// Config space is resource 0. The registry (/etc/devices.csv) bound this driver by the
|
||||
// exact virtio-gpu identity (vendor 0x1AF4 / device 0x1050), so there is no re-confirm to
|
||||
// do here any more — map config space and enable memory-space decode + bus mastering (the
|
||||
// device DMAs the ring and backing out of RAM; pci-bus only preserves whatever the firmware
|
||||
// left, and a secondary display is often left disabled).
|
||||
var function = pci.Function.map(device_id, descriptor) orelse {
|
||||
std.log.info("config-space map failed", .{});
|
||||
return false;
|
||||
};
|
||||
const vendor = function.vendorId();
|
||||
const dev = function.deviceId();
|
||||
if (vendor != virtio_vendor or dev != virtio_gpu_device) {
|
||||
std.log.info("not a virtio-gpu (vendor 0x{x} device 0x{x})", .{ vendor, dev });
|
||||
return false;
|
||||
}
|
||||
function.enableMemoryAndBusMaster();
|
||||
|
||||
// Walk the capability list for the virtio common-config and notify structures (V3 needs
|
||||
|
||||
@@ -220,7 +220,7 @@ fn onInit(endpoint: ipc.Handle) bool {
|
||||
else
|
||||
std.log.info("reported {s} (device {d}, {d} resources)", .{ hid, entry.device_id, entry.resource_count });
|
||||
if (manager) |h| {
|
||||
var report = device_manager_protocol.ChildAdded{ .parent = node_id, .bus_address = entry.device_id, .identity = 0, .device_id = entry.device_id };
|
||||
var report = device_manager_protocol.ChildAdded{ .bus = @intFromEnum(device_manager_protocol.BusKind.acpi), .parent = node_id, .bus_address = entry.device_id, .identity = 0, .device_id = entry.device_id };
|
||||
@memcpy(report.hid[0..entry.hid_len], entry.hid[0..entry.hid_len]);
|
||||
var reply: [device_manager_protocol.message_maximum]u8 = undefined;
|
||||
_ = ipc.call(h, std.mem.asBytes(&report), &reply) catch {};
|
||||
|
||||
@@ -23,86 +23,66 @@ const service = @import("service");
|
||||
const time = @import("time");
|
||||
const memory = @import("memory");
|
||||
const logging = @import("logging");
|
||||
const acpi_ids = @import("acpi-ids");
|
||||
const pci_class = @import("pci-class");
|
||||
const usb_ids = @import("usb-ids");
|
||||
const device_manager_protocol = @import("device-manager-protocol");
|
||||
const registry = @import("device-registry");
|
||||
const fs = @import("file-system");
|
||||
|
||||
/// The PCI class/subclass/prog-IF triple of an xHCI (USB 3) host controller —
|
||||
/// Serial Bus Controller / USB Controller / XHCI — named from pci-class.zig rather
|
||||
/// than written as the bare 0x0C0330 (docs/coding-standards.md, "Named values").
|
||||
const xhci_pci_class: u64 = pci_class.ClassCode.pack(.{
|
||||
.base = @intFromEnum(pci_class.BaseClass.serial_bus),
|
||||
.subclass = @intFromEnum(pci_class.serial_bus.SubClass.usb),
|
||||
.prog_if = @intFromEnum(pci_class.serial_bus.usb.ProgIf.xhci),
|
||||
});
|
||||
// --- the device registry ------------------------------------------------------
|
||||
// Driver matching is data-driven and authoritative: /etc/devices.csv (parsed by
|
||||
// the device-registry module) names, per bus, which driver binds a reported
|
||||
// device, the most-specific match winning. There is no compiled-in fallback — a
|
||||
// device no row matches goes unbound and is logged. This retired the hand-kept
|
||||
// pciDriverForIdentity / hidDriverFor / usbDriverForIdentity switch tables
|
||||
// (docs/device-manager.md: "matching stays code until the third bus").
|
||||
|
||||
/// The PCI class triple of a virtio-gpu — Display Controller / Other (0x80) / 0. The class
|
||||
/// alone cannot tell it from any other display/other function, so the driver re-confirms
|
||||
/// vendor 0x1AF4 / device 0x1050 from config space once spawned; this only gets it spawned.
|
||||
const virtio_gpu_pci_class: u64 = pci_class.ClassCode.pack(.{
|
||||
.base = @intFromEnum(pci_class.BaseClass.display),
|
||||
.subclass = 0x80, // "Other" — no named SubClass member (PCI convention)
|
||||
.prog_if = 0,
|
||||
});
|
||||
/// The CSV bytes, held for the life of the process because the parsed rules'
|
||||
/// string fields (hid, driver) slice into this buffer.
|
||||
var registry_source: [8192]u8 = undefined;
|
||||
var registry_rules: [64]registry.Rule = undefined;
|
||||
var registry_count: usize = 0;
|
||||
|
||||
|
||||
const vga_compatible_gpu_pci_class: u64 = pci_class.ClassCode.pack(.{
|
||||
.base = @intFromEnum(pci_class.BaseClass.display),
|
||||
.subclass = @intFromEnum(pci_class.display.SubClass.vga_compatible),
|
||||
.prog_if = 0,
|
||||
});
|
||||
|
||||
/// The driver that serves a *reported* PCI function (M19.3: matching moved
|
||||
/// from the boot snapshot to the bus reports), or null. A machine can carry
|
||||
/// several identical controllers — one driver instance per reported device,
|
||||
/// its registered id as argv[1].
|
||||
fn pciDriverForIdentity(identity: u64) ?[]const u8 {
|
||||
return switch (identity) {
|
||||
xhci_pci_class => "/system/drivers/usb-xhci-bus",
|
||||
vga_compatible_gpu_pci_class => "/system/drivers/display",
|
||||
virtio_gpu_pci_class => "/system/drivers/virtio-gpu",
|
||||
else => null,
|
||||
/// Read and parse /etc/devices.csv once at boot. The file lives in the initial
|
||||
/// ramdisk, which the kernel serves directly — no filesystem service need be up
|
||||
/// (fat is spawned after the manager), so this is a plain fs.open + read.
|
||||
fn loadRegistry() void {
|
||||
var file = fs.open("/etc/devices.csv", .{}) orelse {
|
||||
_ = logging.write("/system/services/device-manager: /etc/devices.csv missing — nothing will match\n");
|
||||
return;
|
||||
};
|
||||
defer file.close();
|
||||
var used: usize = 0;
|
||||
while (used < registry_source.len) {
|
||||
const n = file.read(registry_source[used..]) orelse break;
|
||||
if (n == 0) break;
|
||||
used += n;
|
||||
}
|
||||
const result = registry.parse(registry_source[0..used], ®istry_rules);
|
||||
registry_count = result.count;
|
||||
if (result.malformed != 0) std.log.info("/etc/devices.csv: {d} malformed line(s) skipped", .{result.malformed});
|
||||
if (result.truncated) _ = logging.write("/system/services/device-manager: /etc/devices.csv has more rules than the table holds\n");
|
||||
std.log.info("/etc/devices.csv: {d} rule(s) loaded", .{registry_count});
|
||||
}
|
||||
|
||||
/// The driver that serves a *reported* ACPI device by its `_HID` (M20.3:
|
||||
/// ps2-bus now binds the PS/2 nodes the acpi service reports, not boot-snapshot
|
||||
/// nodes the kernel used to build). ps2-bus is a singleton that finds both its
|
||||
/// devices by hid once spawned, so keyboard and mouse map to the same name.
|
||||
fn hidDriverFor(hid: []const u8) ?[]const u8 {
|
||||
if (std.mem.eql(u8, hid, "PNP0303")) return "/system/drivers/ps2-bus"; // PS/2 keyboard
|
||||
if (std.mem.eql(u8, hid, "PNP0F13")) return "/system/drivers/ps2-bus"; // PS/2 mouse
|
||||
return null;
|
||||
}
|
||||
|
||||
/// The driver that serves a *reported* USB interface by its (class, subclass,
|
||||
/// protocol) triple — the third bus after PCI and ACPI (docs/device-manager.md:
|
||||
/// matching stays code until the third bus). The xHCI bus driver reports each
|
||||
/// interface with this packed triple as its identity; the matched class driver is
|
||||
/// spawned with the interface's registered id as argv[1], which it presents to the
|
||||
/// bus driver to open the device.
|
||||
fn usbDriverForIdentity(identity: u64) ?[]const u8 {
|
||||
const keyboard = comptime usb_ids.packTriple(
|
||||
@intFromEnum(usb_ids.Class.hid),
|
||||
@intFromEnum(usb_ids.hid.SubClass.boot),
|
||||
@intFromEnum(usb_ids.hid.Protocol.keyboard),
|
||||
);
|
||||
const mouse = comptime usb_ids.packTriple(
|
||||
@intFromEnum(usb_ids.Class.hid),
|
||||
@intFromEnum(usb_ids.hid.SubClass.boot),
|
||||
@intFromEnum(usb_ids.hid.Protocol.mouse),
|
||||
);
|
||||
const storage = comptime usb_ids.packTriple(
|
||||
@intFromEnum(usb_ids.Class.mass_storage),
|
||||
@intFromEnum(usb_ids.mass_storage.SubClass.scsi),
|
||||
@intFromEnum(usb_ids.mass_storage.Protocol.bulk_only),
|
||||
);
|
||||
return switch (identity) {
|
||||
keyboard => "/system/drivers/usb-hid-keyboard",
|
||||
mouse => "/system/drivers/usb-hid-mouse",
|
||||
storage => "/system/drivers/usb-storage",
|
||||
else => null,
|
||||
/// Build a registry Identity from a bus driver's report: the bus it named, the
|
||||
/// class triple unpacked from `identity` (0xCCSSPP — the same packing for a PCI
|
||||
/// class code and a USB class triple), the widened numeric ids, and the ACPI hid.
|
||||
fn identityFromReport(report: device_manager_protocol.ChildAdded) registry.Identity {
|
||||
const bus: registry.Bus = switch (report.bus) {
|
||||
@intFromEnum(device_manager_protocol.BusKind.pci) => .pci,
|
||||
@intFromEnum(device_manager_protocol.BusKind.usb) => .usb,
|
||||
@intFromEnum(device_manager_protocol.BusKind.acpi) => .acpi,
|
||||
else => .unknown,
|
||||
};
|
||||
const hid_len = std.mem.indexOfScalar(u8, &report.hid, 0) orelse report.hid.len;
|
||||
return .{
|
||||
.bus = bus,
|
||||
.base = @truncate(report.identity >> 16),
|
||||
.subclass = @truncate(report.identity >> 8),
|
||||
.prog_if = @truncate(report.identity),
|
||||
.vendor = report.vendor,
|
||||
.device = report.device,
|
||||
.subsystem = report.subsystem,
|
||||
.hid = report.hid[0..hid_len],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -365,6 +345,10 @@ fn sweepDeadlines() void {
|
||||
fn initialise(endpoint: ipc.Handle) bool {
|
||||
manager_endpoint = endpoint;
|
||||
|
||||
// Load the authoritative driver-match registry before any bus driver can
|
||||
// report a device to match against it.
|
||||
loadRegistry();
|
||||
|
||||
// Enumerate into a heap buffer (too big for the one-page user stack).
|
||||
const buffer = memory.allocator().alloc(device.DeviceDescriptor, 64) catch {
|
||||
_ = logging.write("/system/services/device-manager: out of memory\n");
|
||||
@@ -459,24 +443,22 @@ fn onChildAdded(message: []const u8, reply: []u8, sender: u32) usize {
|
||||
if (!addChild(report.parent, report.bus_address, report.identity, report.device_id, sender)) status = -1;
|
||||
std.log.info("child added (device {d} port {d}, identity {d}) by {s}", .{ report.parent, report.bus_address, report.identity, driver.name() });
|
||||
if (status == 0) publishEvent(message[0..device_manager_protocol.child_added_size]);
|
||||
// Matching from reports (M19.3): a registered child whose identity
|
||||
// names a driver gets one, once — re-reports after a bus restart
|
||||
// dedupe on the registered id, exactly like the registrations do.
|
||||
// Matching from reports (M19.3), now data-driven via the /etc/devices.csv
|
||||
// registry: a registered child gets the most-specific driver its identity
|
||||
// matches, once — re-reports after a bus restart dedupe on the registered
|
||||
// id, exactly like the registrations do.
|
||||
if (status == 0 and report.device_id != device_manager_protocol.no_device) {
|
||||
if (pciDriverForIdentity(report.identity)) |child_driver| {
|
||||
if (!driverForDevice(report.device_id)) addDriver(child_driver, report.device_id, true);
|
||||
}
|
||||
// USB interface match: the reported identity is the packed class triple,
|
||||
// and the class driver is spawned with the interface's registered id.
|
||||
if (usbDriverForIdentity(report.identity)) |usb_driver| {
|
||||
if (!driverForDevice(report.device_id)) addDriver(usb_driver, report.device_id, true);
|
||||
}
|
||||
// ACPI _HID match (M20.3): ps2-bus is a singleton that finds its own
|
||||
// devices by hid, so spawn it once, without a device assignment.
|
||||
const hid_len = std.mem.indexOfScalar(u8, &report.hid, 0) orelse report.hid.len;
|
||||
if (hid_len != 0) {
|
||||
if (hidDriverFor(report.hid[0..hid_len])) |hid_driver| {
|
||||
if (!alreadySupervised(hid_driver)) addDriver(hid_driver, device_manager_protocol.no_device, false);
|
||||
const id = identityFromReport(report);
|
||||
if (registry.matchDriver(registry_rules[0..registry_count], id)) |match| {
|
||||
if (match.ambiguous)
|
||||
std.log.info("/etc/devices.csv: multiple equally-specific rules match the device {s} reported; binding {s}", .{ driver.name(), match.driver });
|
||||
if (id.bus == .acpi) {
|
||||
// An hid-matched driver (ps2-bus) is a singleton that finds its
|
||||
// own devices once spawned — spawn it once, no device assignment.
|
||||
if (!alreadySupervised(match.driver)) addDriver(match.driver, device_manager_protocol.no_device, false);
|
||||
} else {
|
||||
// A per-device driver: one instance, the registered id as argv[1].
|
||||
if (!driverForDevice(report.device_id)) addDriver(match.driver, report.device_id, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user