//! 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 channel = @import("channel"); const ipc = @import("ipc"); const time = @import("time"); const logging = @import("logging"); const device_manager_protocol = @import("device-manager-protocol"); fn writeLine(comptime fmt: []const u8, arguments: anytype) void { var line: [96]u8 = undefined; _ = logging.write(std.fmt.bufPrint(&line, fmt, arguments) catch return); } pub fn main() void { var manager: ?ipc.Handle = null; var tries: u32 = 0; while (manager == null and tries < 200) : (tries += 1) { manager = channel.openEndpoint("device-manager"); if (manager == null) time.sleepMillis(20); } const h = manager orelse { _ = logging.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: [device_manager_protocol.message_maximum]u8 = undefined; var count: u32 = 0; var length: usize = 0; tries = 0; while (tries < 20) : (tries += 1) { const request = device_manager_protocol.Enumerate{}; length = ipc.call(h, std.mem.asBytes(&request), &reply) catch 0; if (length >= @sizeOf(device_manager_protocol.EnumerateReply)) { count = std.mem.bytesToValue(device_manager_protocol.EnumerateReply, reply[0..@sizeOf(device_manager_protocol.EnumerateReply)]).count; if (count != 0) break; } time.sleepMillis(100); } writeLine("device-list: {d} devices\n", .{count}); var offset: usize = @sizeOf(device_manager_protocol.EnumerateReply); var index: u32 = 0; while (index < count and offset + @sizeOf(device_manager_protocol.ChildEntry) <= length) : (index += 1) { const entry = std.mem.bytesToValue(device_manager_protocol.ChildEntry, reply[offset..][0..@sizeOf(device_manager_protocol.ChildEntry)]); writeLine("device-list: device {d} port {d} identity {d}\n", .{ entry.parent, entry.bus_address, entry.identity }); offset += @sizeOf(device_manager_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 = ipc.createIpcEndpoint() orelse { _ = logging.write("device-list: no endpoint\n"); return; }; const subscribe = device_manager_protocol.Subscribe{}; _ = ipc.callCap(h, std.mem.asBytes(&subscribe), &reply, endpoint) catch { _ = logging.write("device-list: subscribe failed\n"); return; }; _ = logging.write("device-list: subscribed\n"); var receive: [device_manager_protocol.message_maximum]u8 = undefined; while (true) { const got = ipc.replyWait(endpoint, &.{}, &receive, null); if (!got.isMessage() or got.len < 1) continue; switch (receive[0]) { @intFromEnum(device_manager_protocol.Operation.child_added) => { if (got.len < device_manager_protocol.child_added_size) continue; const event = std.mem.bytesToValue(device_manager_protocol.ChildAdded, receive[0..device_manager_protocol.child_added_size]); writeLine("device-list: added (device {d} port {d})\n", .{ event.parent, event.bus_address }); }, @intFromEnum(device_manager_protocol.Operation.child_removed) => { if (got.len < device_manager_protocol.child_removed_size) continue; const event = std.mem.bytesToValue(device_manager_protocol.ChildRemoved, receive[0..device_manager_protocol.child_removed_size]); writeLine("device-list: removed (device {d} port {d})\n", .{ event.parent, event.bus_address }); }, else => {}, } } }