diff --git a/build.zig b/build.zig index c44c04b..a6f9b89 100644 --- a/build.zig +++ b/build.zig @@ -481,32 +481,31 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("library/kernel/start.zig"), .imports = &.{ .{ .name = "process", .module = process_module }, .{ .name = "logging", .module = logging_module } }, }); - const device_module = b.addModule("device", .{ - .root_source_file = b.path("library/kernel/device.zig"), + // The driver author's interface (library/device/driver): device access + the + // device-manager hello handshake, folded together. + const driver_module = b.addModule("driver", .{ + .root_source_file = b.path("library/device/driver/driver.zig"), .imports = &.{ .{ .name = "abi", .module = abi_module }, .{ .name = "device-abi", .module = device_abi_module }, .{ .name = "system-call", .module = system_call_module }, - }, - }); - const device_manager_client_module = b.addModule("device-manager", .{ - .root_source_file = b.path("library/kernel/device-manager.zig"), - .imports = &.{ .{ .name = "ipc", .module = ipc_module }, .{ .name = "time", .module = time_module }, .{ .name = "device-manager-protocol", .module = device_manager_protocol_module }, }, }); + // The block-device client — a device type, so library/device/block. const block_client_module = b.addModule("block", .{ - .root_source_file = b.path("library/kernel/block.zig"), + .root_source_file = b.path("library/device/block/block.zig"), .imports = &.{ .{ .name = "ipc", .module = ipc_module }, .{ .name = "time", .module = time_module }, .{ .name = "block-protocol", .module = block_protocol_module }, }, }); + // Userspace-service clients live in library/client (they talk to services, not the kernel). const display_client_module = b.addModule("display", .{ - .root_source_file = b.path("library/kernel/display.zig"), + .root_source_file = b.path("library/client/display/display.zig"), .imports = &.{ .{ .name = "ipc", .module = ipc_module }, .{ .name = "time", .module = time_module }, @@ -514,7 +513,7 @@ pub fn build(b: *std.Build) void { }, }); const input_client_module = b.addModule("input", .{ - .root_source_file = b.path("library/kernel/input.zig"), + .root_source_file = b.path("library/client/input/input.zig"), .imports = &.{ .{ .name = "ipc", .module = ipc_module }, .{ .name = "time", .module = time_module }, @@ -546,8 +545,7 @@ pub fn build(b: *std.Build) void { .{ .name = "logging", .module = logging_module }, .{ .name = "file-system", .module = file_system_module }, .{ .name = "start", .module = start_module }, - .{ .name = "device", .module = device_module }, - .{ .name = "device-manager", .module = device_manager_client_module }, + .{ .name = "driver", .module = driver_module }, .{ .name = "input", .module = input_client_module }, .{ .name = "block", .module = block_client_module }, .{ .name = "display", .module = display_client_module }, diff --git a/library/kernel/display.zig b/library/client/display/display.zig similarity index 100% rename from library/kernel/display.zig rename to library/client/display/display.zig diff --git a/library/kernel/input.zig b/library/client/input/input.zig similarity index 100% rename from library/kernel/input.zig rename to library/client/input/input.zig diff --git a/library/kernel/block.zig b/library/device/block/block.zig similarity index 100% rename from library/kernel/block.zig rename to library/device/block/block.zig diff --git a/library/kernel/device.zig b/library/device/driver/driver.zig similarity index 73% rename from library/kernel/device.zig rename to library/device/driver/driver.zig index b50703d..b6e89f2 100644 --- a/library/kernel/device.zig +++ b/library/device/driver/driver.zig @@ -1,12 +1,15 @@ -//! User-space device access: enumerate the kernel's device table, claim a device, -//! map its MMIO, and bind its interrupt. A driver uses these to find and take -//! ownership of its hardware; the claim is the capability the kernel checks before -//! mapping registers or routing an IRQ. +//! library/device/driver — the driver author's interface: enumerate the kernel's device +//! table, claim a device, map its MMIO, bind its interrupt (the claim is the capability the +//! kernel checks before mapping registers or routing an IRQ), and say `hello` to the device +//! manager at startup. The whole kernel + manager surface a driver needs, in one import. const std = @import("std"); const abi = @import("abi"); const device_abi = @import("device-abi"); const sc = @import("system-call"); +const ipc = @import("ipc"); +const time = @import("time"); +const device_manager_protocol = @import("device-manager-protocol"); pub const DeviceDescriptor = device_abi.DeviceDescriptor; pub const ResourceDescriptor = device_abi.ResourceDescriptor; @@ -129,3 +132,42 @@ pub fn findDeviceDescriptorByHid(buffer: []DeviceDescriptor, hid_needle: []const return null; } + +// --- device-manager handshake (folded in from the former device-manager.zig) --- + +/// What kind of driver is announcing itself (a bus that reports children, or a leaf +/// device). Re-exported so callers name it without importing the protocol. +pub const Role = device_manager_protocol.Role; + +const lookup_attempts: u32 = 100; +const lookup_pause_ms: u64 = 20; + +/// Say hello to the device manager and return its endpoint, or null if there is no manager +/// (best-effort standalone bring-up) or it refused the handshake. Bus drivers keep the handle +/// to report children through; a driver that runs fine unsupervised discards it with `_ =`, +/// and one that requires supervision bails on null. Logs the outcome itself. +pub fn hello(role: Role, device_id: u64) ?ipc.Handle { + var attempts: u32 = 0; + const manager = while (attempts < lookup_attempts) : (attempts += 1) { + if (ipc.lookup(.device_manager)) |handle| break handle; + time.sleepMillis(lookup_pause_ms); + } else { + std.log.info("no device manager to hello", .{}); + return null; + }; + + const message = device_manager_protocol.Hello{ .role = @intFromEnum(role), .device_id = device_id }; + var reply: [device_manager_protocol.reply_size]u8 = undefined; + const length = ipc.call(manager, std.mem.asBytes(&message), &reply) catch { + std.log.info("hello call failed", .{}); + return null; + }; + if (length < device_manager_protocol.reply_size or + std.mem.bytesToValue(device_manager_protocol.HelloReply, reply[0..device_manager_protocol.reply_size]).status != 0) + { + std.log.info("hello refused", .{}); + return null; + } + std.log.info("hello acknowledged", .{}); + return manager; +} diff --git a/library/kernel/device-manager.zig b/library/kernel/device-manager.zig deleted file mode 100644 index 10fd2d4..0000000 --- a/library/kernel/device-manager.zig +++ /dev/null @@ -1,59 +0,0 @@ -//! Client side of the device-manager protocol (docs/device-manager.md): the -//! calls a supervised driver makes *to* the manager, as opposed to -//! `device-manager-protocol.zig`, which is the wire contract both sides share. -//! -//! Today this is just the hello handshake every spawned driver owes. The -//! manager arms a hello deadline when it spawns a driver -//! (system/services/device-manager/device-manager.zig): a driver that stays -//! silent past it is assumed wedged before `main` and stopped, so every driver -//! calls `hello` early in its startup. The retry-lookup-call-check this used to -//! be — copied byte-for-byte into each bus and class driver — lives here once. - -const std = @import("std"); -const ipc = @import("ipc"); -const time = @import("time"); -const device_manager_protocol = @import("device-manager-protocol"); - -/// What kind of driver is announcing itself (a bus that reports children, or a -/// leaf device). Re-exported so callers name it without importing the protocol. -pub const Role = device_manager_protocol.Role; - -/// Endpoint lookups while the manager is still registering, and the pause -/// between them: 100 x 20 ms = ~2 s, comfortably inside the manager's 3 s hello -/// deadline (device-manager.zig `hello_deadline_ms`). -const lookup_attempts: u32 = 100; -const lookup_pause_ms: u64 = 20; - -/// Say hello to the device manager and return its endpoint, or null if there is -/// no manager (best-effort standalone bring-up) or it refused the handshake -/// (version mismatch, unknown sender). Bus drivers keep the returned handle to -/// report children through; a driver that runs fine unsupervised discards it -/// with `_ =`, and a driver that requires supervision bails on null. -/// -/// Logs the outcome itself (attribution is the kernel's, via the process's -/// binary path), so callers stay a single line. -pub fn hello(role: Role, device_id: u64) ?ipc.Handle { - var attempts: u32 = 0; - const manager = while (attempts < lookup_attempts) : (attempts += 1) { - if (ipc.lookup(.device_manager)) |handle| break handle; - time.sleepMillis(lookup_pause_ms); - } else { - std.log.info("no device manager to hello", .{}); - return null; - }; - - const message = device_manager_protocol.Hello{ .role = @intFromEnum(role), .device_id = device_id }; - var reply: [device_manager_protocol.reply_size]u8 = undefined; - const length = ipc.call(manager, std.mem.asBytes(&message), &reply) catch { - std.log.info("hello call failed", .{}); - return null; - }; - if (length < device_manager_protocol.reply_size or - std.mem.bytesToValue(device_manager_protocol.HelloReply, reply[0..device_manager_protocol.reply_size]).status != 0) - { - std.log.info("hello refused", .{}); - return null; - } - std.log.info("hello acknowledged", .{}); - return manager; -} diff --git a/library/kernel/runtime.zig b/library/kernel/runtime.zig index c6990bf..2637a14 100644 --- a/library/kernel/runtime.zig +++ b/library/kernel/runtime.zig @@ -20,9 +20,9 @@ pub const fs = @import("file-system"); pub const start = @import("start"); pub const panic = start.panic; -// Device / service clients (relocated to library/device and library/client in C3/C4). -pub const device = @import("device"); -pub const device_manager = @import("device-manager"); +// Device / service clients (now in library/device/driver, library/device/block, library/client). +pub const device = @import("driver"); +pub const device_manager = @import("driver"); // hello() folded into driver pub const input = @import("input"); pub const block = @import("block"); pub const display = @import("display");