149 lines
5.7 KiB
Zig
149 lines
5.7 KiB
Zig
//! The device-manager protocol (docs/device-manager.md): what drivers and
|
|
//! applications say to the device manager over its well-known endpoint. The
|
|
//! vfs-protocol pattern — extern-struct messages, a version in the handshake,
|
|
//! reserved fields — so both sides depend on the contract by name. Deliberately
|
|
//! contains nothing lifecycle-shaped: stopping, liveness (the zero-length ping),
|
|
//! and exit reasons are the universal vocabulary of
|
|
//! docs/process-lifecycle.md, not this protocol.
|
|
|
|
/// The protocol version a driver states in its hello. A manager that cannot
|
|
/// serve a driver's version refuses the hello, and the mismatch is loud at
|
|
/// startup instead of quiet corruption later.
|
|
pub const version: u16 = 1;
|
|
|
|
/// What kind of driver is talking (docs/driver-model.md's shapes).
|
|
pub const Role = enum(u8) {
|
|
/// Owns a controller and reports the devices behind it (`child_added`).
|
|
bus = 1,
|
|
/// Serves one device, reached through a bus's transfer protocol.
|
|
device = 2,
|
|
};
|
|
|
|
/// The message kinds.
|
|
pub const Operation = enum(u8) {
|
|
hello = 1,
|
|
child_added = 2,
|
|
child_removed = 3,
|
|
enumerate = 4,
|
|
subscribe = 5,
|
|
};
|
|
|
|
/// `Hello.device_id` for a driver that serves no enumerated device (a test
|
|
/// fixture, a synthetic source).
|
|
pub const no_device: u64 = ~@as(u64, 0);
|
|
|
|
/// The handshake, sent once by every driver the manager spawns — the manager's
|
|
/// one self-enforced deadline: spawned and silent past it means wrong binary,
|
|
/// wrong version, or wedged before main, and the stop sequence follows.
|
|
pub const Hello = extern struct {
|
|
operation: u8 = @intFromEnum(Operation.hello),
|
|
/// A Role value.
|
|
role: u8,
|
|
/// The protocol version this driver was built against (`version`).
|
|
version: u16 = version,
|
|
reserved: u32 = 0,
|
|
/// The device this driver was assigned (its argv[1]), or `no_device`.
|
|
device_id: u64,
|
|
};
|
|
|
|
pub const hello_size = @sizeOf(Hello);
|
|
|
|
/// The manager's answer to a hello. Nonzero status = refused (version mismatch,
|
|
/// unknown sender); a refused driver should exit cleanly.
|
|
pub const HelloReply = extern struct {
|
|
status: i32,
|
|
reserved: u32 = 0,
|
|
};
|
|
|
|
pub const reply_size = @sizeOf(HelloReply);
|
|
|
|
/// A bus driver reporting one device it discovered behind its controller
|
|
/// (docs/device-manager.md "the tree"). Identity is the bus's native language —
|
|
/// for USB a port-speed class; the (class, subclass, protocol) triple joins it
|
|
/// once control transfers exist (the USB track). The manager mirrors the child
|
|
/// into its tree; when the reporting driver dies, the manager prunes everything
|
|
/// it reported (the children describe protocol state that died with it) and the
|
|
/// restarted instance rediscovers and re-reports.
|
|
pub const ChildAdded = extern struct {
|
|
operation: u8 = @intFromEnum(Operation.child_added),
|
|
reserved0: u8 = 0,
|
|
reserved1: u16 = 0,
|
|
reserved2: u32 = 0,
|
|
/// The reporting driver's own device (the controller) — the child's parent.
|
|
parent: u64,
|
|
/// Where on the bus (for USB: the root port number, 1-based).
|
|
bus_address: u64,
|
|
/// Bus-specific identity (for USB: the PORTSC port-speed class; for PCI:
|
|
/// the class triple; for ACPI devices, 0 — identity is the hid below).
|
|
identity: u64,
|
|
/// The kernel device id this child was `device_register`ed as — what the
|
|
/// manager hands a matched driver as its argv assignment — or `no_device`
|
|
/// for an unregistered leaf (a USB port before the descriptor track).
|
|
device_id: u64 = no_device,
|
|
/// The ACPI hardware id (`_HID`), EISA-decoded (e.g. "PNP0303"), for devices
|
|
/// discovered by firmware string rather than a numeric bus identity. Empty
|
|
/// (all zero) otherwise. Widens for FDT `compatible` strings later.
|
|
hid: [8]u8 = .{0} ** 8,
|
|
};
|
|
|
|
pub const child_added_size = @sizeOf(ChildAdded);
|
|
|
|
/// A bus driver reporting a device gone (hot-unplug). Not yet sent by any
|
|
/// driver — the port scan has no unplug interrupt — but the manager handles it;
|
|
/// death-pruning covers removal until hotplug lands.
|
|
pub const ChildRemoved = extern struct {
|
|
operation: u8 = @intFromEnum(Operation.child_removed),
|
|
reserved0: u8 = 0,
|
|
reserved1: u16 = 0,
|
|
reserved2: u32 = 0,
|
|
parent: u64,
|
|
bus_address: u64,
|
|
};
|
|
|
|
pub const child_removed_size = @sizeOf(ChildRemoved);
|
|
|
|
/// The manager's answer to a tree report.
|
|
pub const ReportReply = extern struct {
|
|
status: i32,
|
|
reserved: u32 = 0,
|
|
};
|
|
|
|
/// An application asking for the tree (M18.3): the reply is an EnumerateReply
|
|
/// header followed by `count` ChildEntry records.
|
|
pub const Enumerate = extern struct {
|
|
operation: u8 = @intFromEnum(Operation.enumerate),
|
|
reserved0: u8 = 0,
|
|
reserved1: u16 = 0,
|
|
reserved2: u32 = 0,
|
|
};
|
|
|
|
pub const EnumerateReply = extern struct {
|
|
status: i32,
|
|
/// ChildEntry records following this header.
|
|
count: u32,
|
|
};
|
|
|
|
pub const ChildEntry = extern struct {
|
|
parent: u64,
|
|
bus_address: u64,
|
|
identity: u64,
|
|
};
|
|
|
|
/// An application subscribing to published add/remove events (the input-service
|
|
/// pattern): the subscriber's endpoint rides as the call's **capability**, and
|
|
/// events arrive on it as buffered messages whose payload is the same
|
|
/// ChildAdded / ChildRemoved struct the bus drivers send — one encoding, both
|
|
/// directions.
|
|
pub const Subscribe = extern struct {
|
|
operation: u8 = @intFromEnum(Operation.subscribe),
|
|
reserved0: u8 = 0,
|
|
reserved1: u16 = 0,
|
|
reserved2: u32 = 0,
|
|
};
|
|
|
|
/// Upper bound on any message in this protocol — sizes the endpoint buffers.
|
|
/// Capped by the kernel's IPC MESSAGE_MAXIMUM (256): an EnumerateReply carries
|
|
/// up to ten ChildEntry records per call, plenty for the mirror's current
|
|
/// bounds; paging joins the protocol if a tree ever outgrows one message.
|
|
pub const message_maximum = 256;
|