//! device-list — the `ps` analog for the device tree (docs/device-manager.md //! M18.3): asks the device manager for the tree over IPC, prints it, then //! subscribes and prints every published add/remove event. The manager is the //! one answer to "what devices exist" for user space; nothing here touches a //! device_* system call. const std = @import("std"); const runtime = @import("runtime"); const protocol = runtime.device_manager_protocol; fn writeLine(comptime fmt: []const u8, arguments: anytype) void { var line: [96]u8 = undefined; _ = runtime.system.write(std.fmt.bufPrint(&line, fmt, arguments) catch return); } pub fn main() void { var manager: ?runtime.ipc.Handle = null; var tries: u32 = 0; while (manager == null and tries < 200) : (tries += 1) { manager = runtime.ipc.lookup(.device_manager); if (manager == null) runtime.system.sleep(20); } const h = manager orelse { _ = runtime.system.write("device-list: no device manager\n"); return; }; // The snapshot — polled briefly, because at boot the bus drivers may still // be scanning: an empty first answer usually just means "too early". var reply: [protocol.message_maximum]u8 = undefined; var count: u32 = 0; var length: usize = 0; tries = 0; while (tries < 20) : (tries += 1) { const request = protocol.Enumerate{}; length = runtime.ipc.call(h, std.mem.asBytes(&request), &reply) catch 0; if (length >= @sizeOf(protocol.EnumerateReply)) { count = std.mem.bytesToValue(protocol.EnumerateReply, reply[0..@sizeOf(protocol.EnumerateReply)]).count; if (count != 0) break; } runtime.system.sleep(100); } writeLine("device-list: {d} devices\n", .{count}); var offset: usize = @sizeOf(protocol.EnumerateReply); var index: u32 = 0; while (index < count and offset + @sizeOf(protocol.ChildEntry) <= length) : (index += 1) { const entry = std.mem.bytesToValue(protocol.ChildEntry, reply[offset..][0..@sizeOf(protocol.ChildEntry)]); writeLine("device-list: device {d} port {d} identity {d}\n", .{ entry.parent, entry.bus_address, entry.identity }); offset += @sizeOf(protocol.ChildEntry); } // The subscription: our endpoint rides as the call's capability; events // arrive as buffered messages carrying the same structs the bus sends. const endpoint = runtime.ipc.createIpcEndpoint() orelse { _ = runtime.system.write("device-list: no endpoint\n"); return; }; const subscribe = protocol.Subscribe{}; _ = runtime.ipc.callCap(h, std.mem.asBytes(&subscribe), &reply, endpoint) catch { _ = runtime.system.write("device-list: subscribe failed\n"); return; }; _ = runtime.system.write("device-list: subscribed\n"); var receive: [protocol.message_maximum]u8 = undefined; while (true) { const got = runtime.ipc.replyWait(endpoint, &.{}, &receive, null); if (!got.isMessage() or got.len < 1) continue; switch (receive[0]) { @intFromEnum(protocol.Operation.child_added) => { if (got.len < protocol.child_added_size) continue; const event = std.mem.bytesToValue(protocol.ChildAdded, receive[0..protocol.child_added_size]); writeLine("device-list: added (device {d} port {d})\n", .{ event.parent, event.bus_address }); }, @intFromEnum(protocol.Operation.child_removed) => { if (got.len < protocol.child_removed_size) continue; const event = std.mem.bytesToValue(protocol.ChildRemoved, receive[0..protocol.child_removed_size]); writeLine("device-list: removed (device {d} port {d})\n", .{ event.parent, event.bus_address }); }, else => {}, } } } pub const panic = runtime.panic; comptime { _ = &runtime.start._start; // pull the runtime entry shim into the image }