The application surface: enumerate, subscribe, and device-list (M18.3)
Applications ask the device manager for the tree (enumerate: a header plus ChildEntry records) and subscribe to published add/remove events by handing their endpoint over as the call's capability — the input-service pattern; events are the same ChildAdded/ChildRemoved structs the bus drivers send, one encoding in both directions. device-list is the first client: it prints the tree, subscribes, and narrates the events through a driver restart. The protocol's message maximum is capped at the kernel's IPC MESSAGE_MAXIMUM (256 bytes, ten entries per reply; paging joins the protocol when a tree outgrows one message). The startUserTask debug print is gone: it wrote to serial unserialized against user-space lines and sheared concurrent log markers in half — the root cause of the scenario flakes.
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
//! 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
|
||||
}
|
||||
Reference in New Issue
Block a user