C2: migrate consumers off the runtime shim to direct concern-module imports
Every user binary and the two device-logic library modules (pci, usb) now
`@import` the concern modules directly instead of aliasing through `runtime`:
runtime.ipc/process/time/service/input/block/display -> @import("<module>")
runtime.device / runtime.device_manager -> @import("driver")
runtime.fs -> @import("file-system")
runtime.Thread -> @import("thread").Thread
runtime.system.{write,writeRecord,klog*} -> logging.*
runtime.system.{sleep,timerOnce,wallClock,clock} -> time.*
runtime.system.{spawn*,kill,exit,yield,processes,...}-> process.*
runtime.system.{mmap,munmap,PROT_*} -> memory.*
runtime.dma.* / runtime.shared_memory.* / runtime.allocator -> memory.*
Each consumer keeps its own alias name (e.g. `const device = @import("driver")`),
so call sites are unchanged and there are no collisions with local `driver`
variables. build.zig now injects the concern modules into every user binary via
`default_imports`; pci/usb module import lists were updated to match.
The `runtime` and `system` shims remain for one more step (root.zig still uses
runtime); they are deleted in C5. Nothing but root.zig imports `runtime` now.
Verified: zig build, zig build test, and 17 QEMU cases (smoke, device-manager,
logger, fat-mount, fat-mutations, usb-storage, usb-hid, display-native,
virtio-gpu, input, thread-spawn, thread-mutex, process-kill, shared-memory,
driver-restart, acpi-ps2, pci-scan).
This commit is contained in:
@@ -12,13 +12,13 @@
|
||||
//!
|
||||
//! It takes over the framebuffer feature that was setup during system boot.
|
||||
const std = @import("std");
|
||||
const runtime = @import("runtime");
|
||||
const device = @import("driver");
|
||||
const ipc = @import("ipc");
|
||||
const process = @import("process");
|
||||
const service = @import("service");
|
||||
const device_manager = @import("driver");
|
||||
const logging = @import("logging");
|
||||
const mmio = @import("mmio");
|
||||
const device = runtime.device;
|
||||
const dma = runtime.dma;
|
||||
const shared_memory = runtime.shared_memory;
|
||||
const system = runtime.system;
|
||||
const ipc = runtime.ipc;
|
||||
const display_protocol = @import("display-protocol");
|
||||
const scanout_protocol = @import("scanout-protocol");
|
||||
var device_id: u64 = 0;
|
||||
@@ -29,7 +29,7 @@ fn initialise(endpoint: ipc.Handle) bool {
|
||||
// and serve its display engine; we report no children). Best-effort: without a
|
||||
// manager the driver still runs standalone; when present, the manager marks us
|
||||
// up before the hello deadline and restarts us if we die.
|
||||
_ = runtime.device_manager.hello(.device, device_id);
|
||||
_ = device_manager.hello(.device, device_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -42,16 +42,16 @@ fn onMessage(message: []const u8, reply: []u8, sender: u32, capability: ?ipc.Han
|
||||
return 0;
|
||||
}
|
||||
|
||||
pub fn main(init: runtime.process.Init) void {
|
||||
pub fn main(init: process.Init) void {
|
||||
const argument = init.arguments.get(1) orelse {
|
||||
_ = system.write("display: missing device id (argv[1])\n");
|
||||
_ = logging.write("display: missing device id (argv[1])\n");
|
||||
return;
|
||||
};
|
||||
device_id = std.fmt.parseInt(u64, argument, 10) catch {
|
||||
std.log.info("malformed device id '{s}'", .{argument});
|
||||
return;
|
||||
};
|
||||
runtime.service.run(256, .{
|
||||
service.run(256, .{
|
||||
.service = .scanout,
|
||||
.init = initialise,
|
||||
.on_message = onMessage,
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
//! /system/drivers/display/intel-integrated - the intel 985 family display engine driver.
|
||||
const std = @import("std");
|
||||
const runtime = @import("runtime");
|
||||
const device = @import("driver");
|
||||
const ipc = @import("ipc");
|
||||
const process = @import("process");
|
||||
const service = @import("service");
|
||||
const logging = @import("logging");
|
||||
const mmio = @import("mmio");
|
||||
const device = runtime.device;
|
||||
const dma = runtime.dma;
|
||||
const shared_memory = runtime.shared_memory;
|
||||
const system = runtime.system;
|
||||
const ipc = runtime.ipc;
|
||||
const display_protocol = @import("display-protocol");
|
||||
const scanout_protocol = @import("scanout-protocol");
|
||||
const device_manager_protocol = @import("device-manager-protocol");
|
||||
@@ -53,16 +52,16 @@ fn onMessage(message: []const u8, reply: []u8, sender: u32, capability: ?ipc.Han
|
||||
return 0;
|
||||
}
|
||||
|
||||
pub fn main(init: runtime.process.Init) void {
|
||||
pub fn main(init: process.Init) void {
|
||||
const argument = init.arguments.get(1) orelse {
|
||||
_ = system.write("display/intel-985: missing device id (argv[1])\n");
|
||||
_ = logging.write("display/intel-985: missing device id (argv[1])\n");
|
||||
return;
|
||||
};
|
||||
device_id = std.fmt.parseInt(u64, argument, 10) catch {
|
||||
std.log.info("malformed device id '{s}'", .{argument});
|
||||
return;
|
||||
};
|
||||
runtime.service.run(256, .{
|
||||
service.run(256, .{
|
||||
.service = .scanout,
|
||||
.init = initialise,
|
||||
.on_message = onMessage,
|
||||
|
||||
@@ -11,9 +11,14 @@
|
||||
//! the kernel walk's retirement (M19.3), build on this proven-equivalent scan.
|
||||
|
||||
const std = @import("std");
|
||||
const runtime = @import("runtime");
|
||||
const device = @import("driver");
|
||||
const ipc = @import("ipc");
|
||||
const process = @import("process");
|
||||
const service = @import("service");
|
||||
const device_manager = @import("driver");
|
||||
const memory = @import("memory");
|
||||
const logging = @import("logging");
|
||||
const device_manager_protocol = @import("device-manager-protocol");
|
||||
const device = runtime.device;
|
||||
const pci_class = @import("pci-class");
|
||||
|
||||
/// Log a discovered function with its (class / subclass / prog-IF) triple decoded
|
||||
@@ -29,7 +34,7 @@ fn logFunction(bus: u64, dev: u64, function: u64, class_triple: u32) void {
|
||||
std.fmt.bufPrint(&line, "/system/drivers/pci-bus: {d}:{d}.{d} class 0x{x:0>2} ({s}) subclass 0x{x:0>2} ({s}) progif 0x{x:0>2} ({s})\n", .{ bus, dev, function, cc.base, pci_class.className(cc.base), cc.subclass, pci_class.subclassName(cc.base, cc.subclass), cc.prog_if, pif }) catch return
|
||||
else
|
||||
std.fmt.bufPrint(&line, "/system/drivers/pci-bus: {d}:{d}.{d} class 0x{x:0>2} ({s}) subclass 0x{x:0>2} ({s}) progif 0x{x:0>2}\n", .{ bus, dev, function, cc.base, pci_class.className(cc.base), cc.subclass, pci_class.subclassName(cc.base, cc.subclass), cc.prog_if }) catch return;
|
||||
_ = runtime.system.write(text);
|
||||
_ = logging.write(text);
|
||||
}
|
||||
|
||||
var bridge_id: u64 = device_manager_protocol.no_device;
|
||||
@@ -37,7 +42,7 @@ var ecam_base: usize = 0;
|
||||
var ecam_physical: u64 = 0;
|
||||
var start_bus: u64 = 0;
|
||||
var bus_count: u64 = 0;
|
||||
var manager_handle: runtime.ipc.Handle = 0;
|
||||
var manager_handle: ipc.Handle = 0;
|
||||
|
||||
/// One aligned 32-bit read from a function's configuration space.
|
||||
fn configRead(bus: u64, dev: u64, function: u64, offset: u64) u32 {
|
||||
@@ -66,14 +71,14 @@ fn configWrite16(bus: u64, dev: u64, function: u64, offset: u64, value: u16) voi
|
||||
}
|
||||
|
||||
/// Claim the bridge, map the ECAM, hello the manager, then scan.
|
||||
fn initialise(endpoint: runtime.ipc.Handle) bool {
|
||||
fn initialise(endpoint: ipc.Handle) bool {
|
||||
_ = endpoint;
|
||||
if (!device.claim(bridge_id)) {
|
||||
std.log.info("unable to claim bridge device {d}", .{bridge_id});
|
||||
return false;
|
||||
}
|
||||
const buffer = runtime.allocator().alloc(device.DeviceDescriptor, 64) catch {
|
||||
_ = runtime.system.write("/system/drivers/pci-bus: out of memory\n");
|
||||
const buffer = memory.allocator().alloc(device.DeviceDescriptor, 64) catch {
|
||||
_ = logging.write("/system/drivers/pci-bus: out of memory\n");
|
||||
return false;
|
||||
};
|
||||
const total = device.enumerate(buffer);
|
||||
@@ -86,27 +91,27 @@ fn initialise(endpoint: runtime.ipc.Handle) bool {
|
||||
// Resource 0 is the ECAM window (1 MiB of config space per bus); the bus
|
||||
// range rides beside it. The MMIO apertures (M19.0) come after both.
|
||||
if (descriptor.resource_count < 2 or descriptor.resources[0].kind != @intFromEnum(device.ResourceKind.memory)) {
|
||||
_ = runtime.system.write("/system/drivers/pci-bus: bridge has no ECAM window\n");
|
||||
_ = logging.write("/system/drivers/pci-bus: bridge has no ECAM window\n");
|
||||
return false;
|
||||
}
|
||||
const bus_range = for (descriptor.resources[0..@intCast(descriptor.resource_count)]) |resource| {
|
||||
if (resource.kind == @intFromEnum(device.ResourceKind.bus_range)) break resource;
|
||||
} else {
|
||||
_ = runtime.system.write("/system/drivers/pci-bus: bridge has no bus range\n");
|
||||
_ = logging.write("/system/drivers/pci-bus: bridge has no bus range\n");
|
||||
return false;
|
||||
};
|
||||
start_bus = bus_range.start;
|
||||
bus_count = bus_range.len;
|
||||
ecam_physical = descriptor.resources[0].start;
|
||||
ecam_base = device.mmioMap(bridge_id, 0) orelse {
|
||||
_ = runtime.system.write("/system/drivers/pci-bus: ECAM mmio_map failed\n");
|
||||
_ = logging.write("/system/drivers/pci-bus: ECAM mmio_map failed\n");
|
||||
return false;
|
||||
};
|
||||
|
||||
// The handshake (role: bus — we enumerate PCI and report the functions we
|
||||
// find), then the scan. Keep the manager handle to report children through;
|
||||
// a supervised bus that cannot reach its manager has nothing to serve.
|
||||
manager_handle = runtime.device_manager.hello(.bus, bridge_id) orelse return false;
|
||||
manager_handle = device_manager.hello(.bus, bridge_id) orelse return false;
|
||||
|
||||
scan();
|
||||
return true;
|
||||
@@ -216,12 +221,12 @@ fn registerAndReport(bus: u64, dev: u64, function: u64, class_triple: u32) void
|
||||
.device_id = registered,
|
||||
};
|
||||
var reply: [device_manager_protocol.message_maximum]u8 = undefined;
|
||||
_ = runtime.ipc.call(manager_handle, std.mem.asBytes(&report), &reply) catch {
|
||||
_ = ipc.call(manager_handle, std.mem.asBytes(&report), &reply) catch {
|
||||
std.log.info("child report for {d}:{d}.{d} failed", .{ bus, dev, function });
|
||||
};
|
||||
}
|
||||
|
||||
fn onMessage(message: []const u8, reply: []u8, sender: u32, capability: ?runtime.ipc.Handle) usize {
|
||||
fn onMessage(message: []const u8, reply: []u8, sender: u32, capability: ?ipc.Handle) usize {
|
||||
_ = message;
|
||||
_ = reply;
|
||||
_ = sender;
|
||||
@@ -229,13 +234,13 @@ fn onMessage(message: []const u8, reply: []u8, sender: u32, capability: ?runtime
|
||||
return 0;
|
||||
}
|
||||
|
||||
pub fn main(init: runtime.process.Init) void {
|
||||
pub fn main(init: process.Init) void {
|
||||
const argument = init.arguments.get(1) orelse return; // bare (ramdisk sweep): stay silent
|
||||
bridge_id = std.fmt.parseInt(u64, argument, 10) catch {
|
||||
std.log.info("malformed bridge device id '{s}'", .{argument});
|
||||
return;
|
||||
};
|
||||
runtime.service.run(device_manager_protocol.message_maximum, .{
|
||||
service.run(device_manager_protocol.message_maximum, .{
|
||||
.init = initialise,
|
||||
.on_message = onMessage,
|
||||
});
|
||||
|
||||
@@ -15,12 +15,16 @@
|
||||
//! -> xkeyboard-config -> character -> key_press
|
||||
|
||||
const std = @import("std");
|
||||
const runtime = @import("runtime");
|
||||
const device = @import("driver");
|
||||
const ipc = @import("ipc");
|
||||
const process = @import("process");
|
||||
const time = @import("time");
|
||||
const input = @import("input");
|
||||
const memory = @import("memory");
|
||||
const logging = @import("logging");
|
||||
const xkb = @import("xkeyboard-config");
|
||||
const ps2 = @import("ps2-library.zig");
|
||||
const scancode = @import("scancode.zig");
|
||||
const device = runtime.device;
|
||||
const ipc = runtime.ipc;
|
||||
const input_protocol = @import("input-protocol");
|
||||
|
||||
/// Look up the ps2-bus service, retrying while the bus (which spawned us before
|
||||
@@ -29,7 +33,7 @@ fn lookupBus() ?ipc.Handle {
|
||||
var attempts: usize = 0;
|
||||
while (attempts < 100) : (attempts += 1) {
|
||||
if (ipc.lookup(.ps2_bus)) |handle| return handle;
|
||||
runtime.system.sleep(50);
|
||||
time.sleepMillis(50);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -64,16 +68,16 @@ fn modifierWord(modifiers: scancode.ModifierSnapshot) u32 {
|
||||
return word;
|
||||
}
|
||||
|
||||
pub fn main(init: runtime.process.Init) void {
|
||||
pub fn main(init: process.Init) void {
|
||||
const hid = init.arguments.get(1).?;
|
||||
if (hid.len == 0) {
|
||||
_ = runtime.system.write("/system/drivers/ps2-bus/keyboard: no HID argument\n");
|
||||
_ = logging.write("/system/drivers/ps2-bus/keyboard: no HID argument\n");
|
||||
return;
|
||||
}
|
||||
std.log.info("starting for hid {s}", .{hid});
|
||||
|
||||
const buffer = runtime.allocator().alloc(device.DeviceDescriptor, 64) catch {
|
||||
_ = runtime.system.write("/system/drivers/ps2-bus/keyboard: out of memory\n");
|
||||
const buffer = memory.allocator().alloc(device.DeviceDescriptor, 64) catch {
|
||||
_ = logging.write("/system/drivers/ps2-bus/keyboard: out of memory\n");
|
||||
return;
|
||||
};
|
||||
if (device.findDeviceDescriptorByHid(buffer, hid) == null) {
|
||||
@@ -90,33 +94,33 @@ pub fn main(init: runtime.process.Init) void {
|
||||
// Attach to the bus: hand it our endpoint, and it forwards every byte the
|
||||
// keyboard sends (it owns the controller; we own the decoding).
|
||||
const bus = lookupBus() orelse {
|
||||
_ = runtime.system.write("/system/drivers/ps2-bus/keyboard: ps2-bus service unavailable\n");
|
||||
_ = logging.write("/system/drivers/ps2-bus/keyboard: ps2-bus service unavailable\n");
|
||||
return;
|
||||
};
|
||||
const endpoint = ipc.createIpcEndpoint() orelse {
|
||||
_ = runtime.system.write("/system/drivers/ps2-bus/keyboard: no endpoint\n");
|
||||
_ = logging.write("/system/drivers/ps2-bus/keyboard: no endpoint\n");
|
||||
return;
|
||||
};
|
||||
var attach = ps2.AttachRequest{ .device_type = @intFromEnum(ps2.DeviceType.keyboard) };
|
||||
var attach_reply: [@sizeOf(ps2.AttachReply)]u8 = undefined;
|
||||
const attached = ipc.callCap(bus, std.mem.asBytes(&attach), &attach_reply, endpoint) catch {
|
||||
_ = runtime.system.write("/system/drivers/ps2-bus/keyboard: attach call failed\n");
|
||||
_ = logging.write("/system/drivers/ps2-bus/keyboard: attach call failed\n");
|
||||
return;
|
||||
};
|
||||
if (attached.len < @sizeOf(ps2.AttachReply) or
|
||||
std.mem.bytesToValue(ps2.AttachReply, attach_reply[0..@sizeOf(ps2.AttachReply)]).status != @intFromEnum(ps2.AttachStatus.ok))
|
||||
{
|
||||
_ = runtime.system.write("/system/drivers/ps2-bus/keyboard: attach refused\n");
|
||||
_ = logging.write("/system/drivers/ps2-bus/keyboard: attach refused\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Broadcast keyboard events through the input service so programs can listen
|
||||
// for them (docs/input.md).
|
||||
var source = runtime.input.connectSource() orelse {
|
||||
_ = runtime.system.write("/system/drivers/ps2-bus/keyboard: input service unavailable\n");
|
||||
var source = input.connectSource() orelse {
|
||||
_ = logging.write("/system/drivers/ps2-bus/keyboard: input service unavailable\n");
|
||||
return;
|
||||
};
|
||||
_ = runtime.system.write("/system/drivers/ps2-bus/keyboard: ok\n");
|
||||
_ = logging.write("/system/drivers/ps2-bus/keyboard: ok\n");
|
||||
|
||||
var decoder = scancode.Decoder{};
|
||||
var state = scancode.KeyboardState{};
|
||||
|
||||
@@ -15,11 +15,15 @@
|
||||
//! -> movement -> motion (dx/dy, screen convention)
|
||||
|
||||
const std = @import("std");
|
||||
const runtime = @import("runtime");
|
||||
const device = @import("driver");
|
||||
const ipc = @import("ipc");
|
||||
const process = @import("process");
|
||||
const time = @import("time");
|
||||
const input = @import("input");
|
||||
const memory = @import("memory");
|
||||
const logging = @import("logging");
|
||||
const ps2 = @import("ps2-library.zig");
|
||||
const mouse_packet = @import("mouse-packet.zig");
|
||||
const device = runtime.device;
|
||||
const ipc = runtime.ipc;
|
||||
const input_protocol = @import("input-protocol");
|
||||
|
||||
/// Look up the ps2-bus service, retrying while the bus (which spawned us before
|
||||
@@ -28,7 +32,7 @@ fn lookupBus() ?ipc.Handle {
|
||||
var attempts: usize = 0;
|
||||
while (attempts < 100) : (attempts += 1) {
|
||||
if (ipc.lookup(.ps2_bus)) |handle| return handle;
|
||||
runtime.system.sleep(50);
|
||||
time.sleepMillis(50);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -42,17 +46,17 @@ fn buttonMask(packet: mouse_packet.Packet) u32 {
|
||||
return mask;
|
||||
}
|
||||
|
||||
pub fn main(init: runtime.process.Init) void {
|
||||
pub fn main(init: process.Init) void {
|
||||
const hid = init.arguments.get(1).?;
|
||||
|
||||
if (hid.len == 0) {
|
||||
_ = runtime.system.write("/system/drivers/ps2-bus/mouse: no HID argument\n");
|
||||
_ = logging.write("/system/drivers/ps2-bus/mouse: no HID argument\n");
|
||||
return;
|
||||
}
|
||||
std.log.info("starting for hid {s}", .{hid});
|
||||
|
||||
const buffer = runtime.allocator().alloc(device.DeviceDescriptor, 64) catch {
|
||||
_ = runtime.system.write("/system/drivers/ps2-bus/mouse: out of memory\n");
|
||||
const buffer = memory.allocator().alloc(device.DeviceDescriptor, 64) catch {
|
||||
_ = logging.write("/system/drivers/ps2-bus/mouse: out of memory\n");
|
||||
return;
|
||||
};
|
||||
if (ps2.findMouseDescriptor(buffer) == null) {
|
||||
@@ -63,33 +67,33 @@ pub fn main(init: runtime.process.Init) void {
|
||||
// Attach to the bus: hand it our endpoint, and it forwards every byte the
|
||||
// mouse sends (it owns the controller; we own the decoding).
|
||||
const bus = lookupBus() orelse {
|
||||
_ = runtime.system.write("/system/drivers/ps2-bus/mouse: ps2-bus service unavailable\n");
|
||||
_ = logging.write("/system/drivers/ps2-bus/mouse: ps2-bus service unavailable\n");
|
||||
return;
|
||||
};
|
||||
const endpoint = ipc.createIpcEndpoint() orelse {
|
||||
_ = runtime.system.write("/system/drivers/ps2-bus/mouse: no endpoint\n");
|
||||
_ = logging.write("/system/drivers/ps2-bus/mouse: no endpoint\n");
|
||||
return;
|
||||
};
|
||||
var attach = ps2.AttachRequest{ .device_type = @intFromEnum(ps2.DeviceType.mouse) };
|
||||
var attach_reply: [@sizeOf(ps2.AttachReply)]u8 = undefined;
|
||||
const attached = ipc.callCap(bus, std.mem.asBytes(&attach), &attach_reply, endpoint) catch {
|
||||
_ = runtime.system.write("/system/drivers/ps2-bus/mouse: attach call failed\n");
|
||||
_ = logging.write("/system/drivers/ps2-bus/mouse: attach call failed\n");
|
||||
return;
|
||||
};
|
||||
if (attached.len < @sizeOf(ps2.AttachReply) or
|
||||
std.mem.bytesToValue(ps2.AttachReply, attach_reply[0..@sizeOf(ps2.AttachReply)]).status != @intFromEnum(ps2.AttachStatus.ok))
|
||||
{
|
||||
_ = runtime.system.write("/system/drivers/ps2-bus/mouse: attach refused\n");
|
||||
_ = logging.write("/system/drivers/ps2-bus/mouse: attach refused\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Broadcast mouse events through the input service so programs can listen
|
||||
// for them (docs/input.md).
|
||||
var source = runtime.input.connectSource() orelse {
|
||||
_ = runtime.system.write("/system/drivers/ps2-bus/mouse: input service unavailable\n");
|
||||
var source = input.connectSource() orelse {
|
||||
_ = logging.write("/system/drivers/ps2-bus/mouse: input service unavailable\n");
|
||||
return;
|
||||
};
|
||||
_ = runtime.system.write("/system/drivers/ps2-bus/mouse: ok\n");
|
||||
_ = logging.write("/system/drivers/ps2-bus/mouse: ok\n");
|
||||
|
||||
var assembler = mouse_packet.Assembler{};
|
||||
var buttons: u32 = 0;
|
||||
|
||||
@@ -10,11 +10,14 @@
|
||||
//! MOU_ [acpi_device] hid=PNP0F13 (PS/2 Mouse)
|
||||
//! - irq 0xc len 0x1
|
||||
const std = @import("std");
|
||||
const runtime = @import("runtime");
|
||||
const device = @import("driver");
|
||||
const ipc = @import("ipc");
|
||||
const process = @import("process");
|
||||
const service = @import("service");
|
||||
const memory = @import("memory");
|
||||
const logging = @import("logging");
|
||||
const acpi_ids = @import("acpi-ids");
|
||||
const ps2 = @import("ps2-library.zig");
|
||||
const device = runtime.device;
|
||||
const ipc = runtime.ipc;
|
||||
|
||||
/// Ask the device on `port` what it is, then spawn the matching driver from the
|
||||
/// initial-ramdisk, handing it the device's HID as argv[1]. The driver is chosen
|
||||
@@ -31,7 +34,7 @@ fn spawnIdentifiedDriver(controller: ps2.Controller, port: ps2.Port) ?ps2.Device
|
||||
return null;
|
||||
};
|
||||
const hid = device_type.hid() orelse "";
|
||||
if (runtime.system.spawnWithArguments(driver_name, &.{hid}) != null) {
|
||||
if (process.spawnWithArguments(driver_name, &.{hid}) != null) {
|
||||
std.log.info("port {s} is a {s}, spawned {s}", .{ @tagName(port), hid, driver_name });
|
||||
return device_type;
|
||||
}
|
||||
@@ -83,8 +86,8 @@ fn handleAttach(message: []const u8, got: ipc.Received, out: []u8) usize {
|
||||
}
|
||||
|
||||
pub fn main() void {
|
||||
const buffer = runtime.allocator().alloc(device.DeviceDescriptor, 64) catch {
|
||||
_ = runtime.system.write("/system/drivers/ps2-bus: out of memory\n");
|
||||
const buffer = memory.allocator().alloc(device.DeviceDescriptor, 64) catch {
|
||||
_ = logging.write("/system/drivers/ps2-bus: out of memory\n");
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -96,16 +99,16 @@ pub fn main() void {
|
||||
// is on which port is decided later by identify, not by this HID.
|
||||
const maybe_controller_device_descriptor = device.findDeviceDescriptorByHid(buffer, acpi_ids.HardwareId.ps2_keyboard.hid());
|
||||
if (maybe_controller_device_descriptor) |controller_device_descriptor| {
|
||||
_ = runtime.system.write("/system/drivers/ps2-bus: found PS/2 controller\n");
|
||||
_ = runtime.system.write("/system/drivers/ps2-bus: initializing controller\n");
|
||||
_ = logging.write("/system/drivers/ps2-bus: found PS/2 controller\n");
|
||||
_ = logging.write("/system/drivers/ps2-bus: initializing controller\n");
|
||||
|
||||
if (!device.claim(controller_device_descriptor.id)) {
|
||||
_ = runtime.system.write("/system/drivers/ps2-bus: unable to claim controller \n");
|
||||
_ = logging.write("/system/drivers/ps2-bus: unable to claim controller \n");
|
||||
return;
|
||||
}
|
||||
|
||||
const controller = ps2.Controller.init(controller_device_descriptor) orelse {
|
||||
_ = runtime.system.write("/system/drivers/ps2-bus: controller is missing its IO ports\n");
|
||||
_ = logging.write("/system/drivers/ps2-bus: controller is missing its IO ports\n");
|
||||
return;
|
||||
};
|
||||
maybe_controller = controller;
|
||||
@@ -116,7 +119,7 @@ pub fn main() void {
|
||||
controller.flushOutputBuffer();
|
||||
|
||||
const current = controller.readConfigurationByte() orelse {
|
||||
_ = runtime.system.write("/system/drivers/ps2-bus: controller configuration timed out\n");
|
||||
_ = logging.write("/system/drivers/ps2-bus: controller configuration timed out\n");
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -125,49 +128,49 @@ pub fn main() void {
|
||||
ps2.configuration_first_port_translation);
|
||||
|
||||
if (controller.writeConfigurationByte(update) == null) {
|
||||
_ = runtime.system.write("/system/drivers/ps2-bus: controller configuration timed out\n");
|
||||
_ = logging.write("/system/drivers/ps2-bus: controller configuration timed out\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (controller.performSelfTest()) |reply| {
|
||||
if (reply != ps2.response_controller_test_passed) {
|
||||
_ = runtime.system.write("/system/drivers/ps2-bus: perform controller self test failed\n");
|
||||
_ = logging.write("/system/drivers/ps2-bus: perform controller self test failed\n");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
_ = runtime.system.write("/system/drivers/ps2-bus: controller self test timed out\n");
|
||||
_ = logging.write("/system/drivers/ps2-bus: controller self test timed out\n");
|
||||
return;
|
||||
}
|
||||
|
||||
has_two_channels = controller.hasTwoChannels() orelse {
|
||||
_ = runtime.system.write("/system/drivers/ps2-bus: controller channels timed out\n");
|
||||
_ = logging.write("/system/drivers/ps2-bus: controller channels timed out\n");
|
||||
return;
|
||||
};
|
||||
|
||||
if (has_two_channels) {
|
||||
_ = runtime.system.write("/system/drivers/ps2-bus: has two channels\n");
|
||||
_ = logging.write("/system/drivers/ps2-bus: has two channels\n");
|
||||
// keep the bus quiet until we have tested the ports and are ready to use them
|
||||
controller.disablePort(.two);
|
||||
} else {
|
||||
_ = runtime.system.write("/system/drivers/ps2-bus: has one channel\n");
|
||||
_ = logging.write("/system/drivers/ps2-bus: has one channel\n");
|
||||
}
|
||||
|
||||
// interface tests: always test port 1, test port 2 only if it exists
|
||||
const port_one_works = (controller.testPort(.one) orelse {
|
||||
_ = runtime.system.write("/system/drivers/ps2-bus: port 1 test timed out\n");
|
||||
_ = logging.write("/system/drivers/ps2-bus: port 1 test timed out\n");
|
||||
return;
|
||||
}) == ps2.response_port_test_passed;
|
||||
|
||||
var port_two_works = false;
|
||||
if (has_two_channels) {
|
||||
port_two_works = (controller.testPort(.two) orelse {
|
||||
_ = runtime.system.write("/system/drivers/ps2-bus: port 2 test timed out\n");
|
||||
_ = logging.write("/system/drivers/ps2-bus: port 2 test timed out\n");
|
||||
return;
|
||||
}) == ps2.response_port_test_passed;
|
||||
}
|
||||
|
||||
if (!port_one_works and !port_two_works) {
|
||||
_ = runtime.system.write("/system/drivers/ps2-bus: no usable ports\n");
|
||||
_ = logging.write("/system/drivers/ps2-bus: no usable ports\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -181,16 +184,16 @@ pub fn main() void {
|
||||
// abort bring-up of the other one
|
||||
if (port_one_works) {
|
||||
if (controller.resetDevice(.one)) |passed| {
|
||||
if (!passed) _ = runtime.system.write("/system/drivers/ps2-bus: port 1 device reset failed\n");
|
||||
if (!passed) _ = logging.write("/system/drivers/ps2-bus: port 1 device reset failed\n");
|
||||
} else {
|
||||
_ = runtime.system.write("/system/drivers/ps2-bus: port 1 device reset timed out\n");
|
||||
_ = logging.write("/system/drivers/ps2-bus: port 1 device reset timed out\n");
|
||||
}
|
||||
}
|
||||
if (port_two_works) {
|
||||
if (controller.resetDevice(.two)) |passed| {
|
||||
if (!passed) _ = runtime.system.write("/system/drivers/ps2-bus: port 2 device reset failed\n");
|
||||
if (!passed) _ = logging.write("/system/drivers/ps2-bus: port 2 device reset failed\n");
|
||||
} else {
|
||||
_ = runtime.system.write("/system/drivers/ps2-bus: port 2 device reset timed out\n");
|
||||
_ = logging.write("/system/drivers/ps2-bus: port 2 device reset timed out\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,13 +203,13 @@ pub fn main() void {
|
||||
if (port_one_works) port_device_types[@intFromEnum(ps2.Port.one)] = spawnIdentifiedDriver(controller, .one);
|
||||
if (port_two_works) port_device_types[@intFromEnum(ps2.Port.two)] = spawnIdentifiedDriver(controller, .two);
|
||||
} else {
|
||||
_ = runtime.system.write("/system/drivers/ps2-bus: no PS/2 controller found\n");
|
||||
_ = logging.write("/system/drivers/ps2-bus: no PS/2 controller found\n");
|
||||
return;
|
||||
}
|
||||
|
||||
const controller = maybe_controller.?;
|
||||
const interrupt_index = maybe_interrupt_index orelse {
|
||||
_ = runtime.system.write("/system/drivers/ps2-bus: controller is missing its IRQ\n");
|
||||
_ = logging.write("/system/drivers/ps2-bus: controller is missing its IRQ\n");
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -214,11 +217,11 @@ pub fn main() void {
|
||||
// well-known id so the children can find it, the way input subscribers find
|
||||
// the input service.
|
||||
const endpoint = ipc.createIpcEndpoint() orelse {
|
||||
_ = runtime.system.write("/system/drivers/ps2-bus: no endpoint\n");
|
||||
_ = logging.write("/system/drivers/ps2-bus: no endpoint\n");
|
||||
return;
|
||||
};
|
||||
if (!ipc.register(.ps2_bus, endpoint)) {
|
||||
_ = runtime.system.write("/system/drivers/ps2-bus: register failed\n");
|
||||
_ = logging.write("/system/drivers/ps2-bus: register failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -227,7 +230,7 @@ pub fn main() void {
|
||||
// let the controller raise them — an interrupt with nobody bound is lost.
|
||||
controller.drainOutputBuffer();
|
||||
if (!device.irqBind(controller.device_id, interrupt_index, endpoint)) {
|
||||
_ = runtime.system.write("/system/drivers/ps2-bus: irq_bind failed\n");
|
||||
_ = logging.write("/system/drivers/ps2-bus: irq_bind failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -246,21 +249,21 @@ pub fn main() void {
|
||||
.gsi = descriptor.resources[auxiliary_index].start,
|
||||
};
|
||||
} else {
|
||||
_ = runtime.system.write("/system/drivers/ps2-bus: auxiliary irq_bind failed\n");
|
||||
_ = logging.write("/system/drivers/ps2-bus: auxiliary irq_bind failed\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var configuration = controller.readConfigurationByte() orelse {
|
||||
_ = runtime.system.write("/system/drivers/ps2-bus: controller configuration timed out\n");
|
||||
_ = logging.write("/system/drivers/ps2-bus: controller configuration timed out\n");
|
||||
return;
|
||||
};
|
||||
if (port_device_types[@intFromEnum(ps2.Port.one)] != null) configuration |= ps2.Port.one.interruptBit();
|
||||
if (maybe_auxiliary_interrupt != null) configuration |= ps2.Port.two.interruptBit();
|
||||
_ = controller.writeConfigurationByte(configuration);
|
||||
|
||||
_ = runtime.system.write("/system/drivers/ps2-bus: ok\n");
|
||||
_ = logging.write("/system/drivers/ps2-bus: ok\n");
|
||||
|
||||
// The forwarding loop: an IRQ1 notification drains the output buffer, routing
|
||||
// each byte to the attached driver of the port it came from; a client message
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//! shared definitions between the different PS/2 drivers
|
||||
const std = @import("std");
|
||||
const runtime = @import("runtime");
|
||||
const device = @import("driver");
|
||||
const ipc = @import("ipc");
|
||||
const time = @import("time");
|
||||
const acpi_ids = @import("acpi-ids");
|
||||
const device = runtime.device;
|
||||
const system = runtime.system;
|
||||
|
||||
/// PS-2 io ports:
|
||||
/// The PS/2 Controller itself uses 2 IO ports (usually, IO ports 0x60 and 0x64). Like many IO
|
||||
@@ -114,16 +114,16 @@ pub const identify_mouse_scroll: u8 = 0x03; // mouse with scroll wheel
|
||||
pub const identify_mouse_five_button: u8 = 0x04; // 5-button mouse
|
||||
|
||||
fn waitReadable(id: u64, cmd_index: u64, wait_timeout_nanoseconds: u64) bool {
|
||||
const deadline = system.clock() + wait_timeout_nanoseconds;
|
||||
while (system.clock() < deadline) {
|
||||
const deadline = time.clock() + wait_timeout_nanoseconds;
|
||||
while (time.clock() < deadline) {
|
||||
if (status(id, cmd_index) & status_output_buffer_full != 0) return true; // OBF set -> data ready
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fn waitWritable(id: u64, cmd_index: u64, wait_timeout_nanoseconds: u64) bool {
|
||||
const deadline = system.clock() + wait_timeout_nanoseconds;
|
||||
while (system.clock() < deadline) {
|
||||
const deadline = time.clock() + wait_timeout_nanoseconds;
|
||||
while (time.clock() < deadline) {
|
||||
if (status(id, cmd_index) & status_input_buffer_full == 0) return true; // IBF clear -> ok to write
|
||||
}
|
||||
return false; // timed out
|
||||
|
||||
@@ -15,13 +15,16 @@
|
||||
//! HID keyboard page 0x07), the decode is nearly 1:1 — no scancode translation.
|
||||
|
||||
const std = @import("std");
|
||||
const runtime = @import("runtime");
|
||||
const ipc = @import("ipc");
|
||||
const process = @import("process");
|
||||
const service = @import("service");
|
||||
const input = @import("input");
|
||||
const device_manager = @import("driver");
|
||||
const logging = @import("logging");
|
||||
const usb = @import("usb");
|
||||
const usb_abi = @import("usb-abi");
|
||||
const xkb = @import("xkeyboard-config");
|
||||
const hid = @import("hid-report.zig");
|
||||
const ipc = runtime.ipc;
|
||||
const process = runtime.process;
|
||||
const input_protocol = @import("input-protocol");
|
||||
|
||||
// The modifier state a character lookup needs — derived from the report's
|
||||
@@ -61,9 +64,9 @@ fn modifierWord(modifiers: u8) u32 {
|
||||
return word;
|
||||
}
|
||||
|
||||
pub fn main(init: runtime.process.Init) void {
|
||||
pub fn main(init: process.Init) void {
|
||||
const argument = init.arguments.get(1) orelse {
|
||||
_ = runtime.system.write("/system/drivers/usb-hid/keyboard: missing device id (argv[1])\n");
|
||||
_ = logging.write("/system/drivers/usb-hid/keyboard: missing device id (argv[1])\n");
|
||||
return;
|
||||
};
|
||||
const device_id = std.fmt.parseInt(u64, argument, 10) catch {
|
||||
@@ -73,13 +76,13 @@ pub fn main(init: runtime.process.Init) void {
|
||||
const layout = xkb.byName(init.arguments.get(2) orelse "us") orelse xkb.us;
|
||||
|
||||
// Hello the manager first (meet the spawn deadline), then open the device.
|
||||
if (runtime.device_manager.hello(.device, device_id) == null) return;
|
||||
if (device_manager.hello(.device, device_id) == null) return;
|
||||
var device = usb.open(device_id) orelse {
|
||||
std.log.info("could not open device {d}", .{device_id});
|
||||
return;
|
||||
};
|
||||
const endpoint = device.findEndpoint(usb.transfer_type_interrupt, true) orelse {
|
||||
_ = runtime.system.write("/system/drivers/usb-hid/keyboard: no interrupt-IN endpoint\n");
|
||||
_ = logging.write("/system/drivers/usb-hid/keyboard: no interrupt-IN endpoint\n");
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -88,12 +91,12 @@ pub fn main(init: runtime.process.Init) void {
|
||||
_ = device.controlOut(@bitCast(usb_abi.setIdle(@enumFromInt(device.interface_number), 0, 0)));
|
||||
|
||||
if (!device.subscribeInterrupt(endpoint.address, endpoint.max_packet_size)) {
|
||||
_ = runtime.system.write("/system/drivers/usb-hid/keyboard: interrupt subscribe failed\n");
|
||||
_ = logging.write("/system/drivers/usb-hid/keyboard: interrupt subscribe failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
var source = runtime.input.connectSource() orelse {
|
||||
_ = runtime.system.write("/system/drivers/usb-hid/keyboard: input service unavailable\n");
|
||||
var source = input.connectSource() orelse {
|
||||
_ = logging.write("/system/drivers/usb-hid/keyboard: input service unavailable\n");
|
||||
return;
|
||||
};
|
||||
_ = process.bindSignals(device.endpoint);
|
||||
@@ -153,7 +156,7 @@ pub fn main(init: runtime.process.Init) void {
|
||||
// the kernel console is a log sink). Printable ASCII and
|
||||
// newline only; other keys are left to the input service.
|
||||
if (character == '\n' or (character >= 0x20 and character < 0x7F)) {
|
||||
_ = runtime.system.write(&[1]u8{@intCast(character)});
|
||||
_ = logging.write(&[1]u8{@intCast(character)});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -11,12 +11,15 @@
|
||||
//! is passed straight through (the decode in hid-report.zig does not negate it).
|
||||
|
||||
const std = @import("std");
|
||||
const runtime = @import("runtime");
|
||||
const ipc = @import("ipc");
|
||||
const process = @import("process");
|
||||
const service = @import("service");
|
||||
const input = @import("input");
|
||||
const device_manager = @import("driver");
|
||||
const logging = @import("logging");
|
||||
const usb = @import("usb");
|
||||
const usb_abi = @import("usb-abi");
|
||||
const hid = @import("hid-report.zig");
|
||||
const ipc = runtime.ipc;
|
||||
const process = runtime.process;
|
||||
const input_protocol = @import("input-protocol");
|
||||
|
||||
// The current pressed-button bitmask in input-protocol terms.
|
||||
@@ -28,9 +31,9 @@ fn buttonMask(buttons: u8) u32 {
|
||||
return mask;
|
||||
}
|
||||
|
||||
pub fn main(init: runtime.process.Init) void {
|
||||
pub fn main(init: process.Init) void {
|
||||
const argument = init.arguments.get(1) orelse {
|
||||
_ = runtime.system.write("/system/drivers/usb-hid/mouse: missing device id (argv[1])\n");
|
||||
_ = logging.write("/system/drivers/usb-hid/mouse: missing device id (argv[1])\n");
|
||||
return;
|
||||
};
|
||||
const device_id = std.fmt.parseInt(u64, argument, 10) catch {
|
||||
@@ -38,25 +41,25 @@ pub fn main(init: runtime.process.Init) void {
|
||||
return;
|
||||
};
|
||||
|
||||
if (runtime.device_manager.hello(.device, device_id) == null) return;
|
||||
if (device_manager.hello(.device, device_id) == null) return;
|
||||
var device = usb.open(device_id) orelse {
|
||||
std.log.info("could not open device {d}", .{device_id});
|
||||
return;
|
||||
};
|
||||
const endpoint = device.findEndpoint(usb.transfer_type_interrupt, true) orelse {
|
||||
_ = runtime.system.write("/system/drivers/usb-hid/mouse: no interrupt-IN endpoint\n");
|
||||
_ = logging.write("/system/drivers/usb-hid/mouse: no interrupt-IN endpoint\n");
|
||||
return;
|
||||
};
|
||||
|
||||
_ = device.controlOut(@bitCast(usb_abi.setProtocol(@enumFromInt(device.interface_number), .boot)));
|
||||
|
||||
if (!device.subscribeInterrupt(endpoint.address, endpoint.max_packet_size)) {
|
||||
_ = runtime.system.write("/system/drivers/usb-hid/mouse: interrupt subscribe failed\n");
|
||||
_ = logging.write("/system/drivers/usb-hid/mouse: interrupt subscribe failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
var source = runtime.input.connectSource() orelse {
|
||||
_ = runtime.system.write("/system/drivers/usb-hid/mouse: input service unavailable\n");
|
||||
var source = input.connectSource() orelse {
|
||||
_ = logging.write("/system/drivers/usb-hid/mouse: input service unavailable\n");
|
||||
return;
|
||||
};
|
||||
_ = process.bindSignals(device.endpoint);
|
||||
|
||||
@@ -12,12 +12,17 @@
|
||||
//! physical address, which the data stage DMAs straight to/from.
|
||||
|
||||
const std = @import("std");
|
||||
const runtime = @import("runtime");
|
||||
const ipc = @import("ipc");
|
||||
const process = @import("process");
|
||||
const service = @import("service");
|
||||
const time = @import("time");
|
||||
const device_manager = @import("driver");
|
||||
const memory = @import("memory");
|
||||
const logging = @import("logging");
|
||||
const usb = @import("usb");
|
||||
const scsi = @import("scsi.zig");
|
||||
const bot = @import("bulk-only-transport.zig");
|
||||
const block_protocol = @import("block-protocol");
|
||||
const dma = runtime.dma;
|
||||
|
||||
var device_id: u64 = 0;
|
||||
var device: usb.Device = undefined;
|
||||
@@ -26,9 +31,9 @@ var bulk_out: usb.Endpoint = undefined;
|
||||
|
||||
// DMA buffers for the transport: the 31-byte CBW, the 13-byte CSW, and a page
|
||||
// for the small command data (INQUIRY / READ CAPACITY / the self-check sector).
|
||||
var command_wrapper: dma.Region = undefined;
|
||||
var status_wrapper: dma.Region = undefined;
|
||||
var command_data: dma.Region = undefined;
|
||||
var command_wrapper: memory.DmaRegion = undefined;
|
||||
var status_wrapper: memory.DmaRegion = undefined;
|
||||
var command_data: memory.DmaRegion = undefined;
|
||||
|
||||
var next_tag: u32 = 1;
|
||||
var block_size: u32 = 512;
|
||||
@@ -68,26 +73,26 @@ fn transact(cdb: []const u8, direction_in: bool, data_physical: u64, data_length
|
||||
/// Device-absent paths stay clean exits: nothing to serve, nothing to retry.
|
||||
var bring_up_failed = false;
|
||||
|
||||
fn initialise(endpoint: runtime.ipc.Handle) bool {
|
||||
fn initialise(endpoint: ipc.Handle) bool {
|
||||
_ = endpoint;
|
||||
if (runtime.device_manager.hello(.device, device_id) == null) return false;
|
||||
if (device_manager.hello(.device, device_id) == null) return false;
|
||||
device = usb.open(device_id) orelse {
|
||||
std.log.info("could not open device {d}", .{device_id});
|
||||
return false;
|
||||
};
|
||||
bulk_in = device.findEndpoint(usb.transfer_type_bulk, true) orelse {
|
||||
_ = runtime.system.write("/system/drivers/usb-storage: no bulk-IN endpoint\n");
|
||||
_ = logging.write("/system/drivers/usb-storage: no bulk-IN endpoint\n");
|
||||
bring_up_failed = true;
|
||||
return false;
|
||||
};
|
||||
bulk_out = device.findEndpoint(usb.transfer_type_bulk, false) orelse {
|
||||
_ = runtime.system.write("/system/drivers/usb-storage: no bulk-OUT endpoint\n");
|
||||
_ = logging.write("/system/drivers/usb-storage: no bulk-OUT endpoint\n");
|
||||
bring_up_failed = true;
|
||||
return false;
|
||||
};
|
||||
command_wrapper = dma.alloc(4096, dma.coherent) orelse return false;
|
||||
status_wrapper = dma.alloc(4096, dma.coherent) orelse return false;
|
||||
command_data = dma.alloc(4096, dma.coherent) orelse return false;
|
||||
command_wrapper = memory.dmaAlloc(4096, memory.dma_coherent) orelse return false;
|
||||
status_wrapper = memory.dmaAlloc(4096, memory.dma_coherent) orelse return false;
|
||||
command_data = memory.dmaAlloc(4096, memory.dma_coherent) orelse return false;
|
||||
|
||||
// Bring the LUN up: wait for it to be ready (clearing the initial unit-attention
|
||||
// with REQUEST SENSE), identify it, and read its capacity.
|
||||
@@ -97,14 +102,14 @@ fn initialise(endpoint: runtime.ipc.Handle) bool {
|
||||
if (transact(&ready, false, 0, 0)) break;
|
||||
const sense = scsi.requestSense(18);
|
||||
_ = transact(&sense, true, command_data.physical, 18);
|
||||
runtime.system.sleep(50);
|
||||
time.sleepMillis(50);
|
||||
}
|
||||
const inquiry = scsi.inquiry(36);
|
||||
_ = transact(&inquiry, true, command_data.physical, 36);
|
||||
|
||||
const capacity_command = scsi.readCapacity10();
|
||||
if (!transact(&capacity_command, true, command_data.physical, 8)) {
|
||||
_ = runtime.system.write("/system/drivers/usb-storage: READ CAPACITY failed\n");
|
||||
_ = logging.write("/system/drivers/usb-storage: READ CAPACITY failed\n");
|
||||
bring_up_failed = true;
|
||||
return false;
|
||||
}
|
||||
@@ -128,7 +133,7 @@ fn initialise(endpoint: runtime.ipc.Handle) bool {
|
||||
|
||||
/// Serve the block protocol: geometry, and whole-block read/write to/from the
|
||||
/// caller's DMA buffer (named by physical address).
|
||||
fn onMessage(message: []const u8, reply: []u8, sender: u32, capability: ?runtime.ipc.Handle) usize {
|
||||
fn onMessage(message: []const u8, reply: []u8, sender: u32, capability: ?ipc.Handle) usize {
|
||||
_ = sender;
|
||||
_ = capability;
|
||||
if (message.len < block_protocol.request_size) return 0;
|
||||
@@ -167,21 +172,21 @@ fn writeReply(reply: []u8, value: block_protocol.Reply) usize {
|
||||
return bytes.len;
|
||||
}
|
||||
|
||||
pub fn main(init: runtime.process.Init) void {
|
||||
pub fn main(init: process.Init) void {
|
||||
const argument = init.arguments.get(1) orelse {
|
||||
_ = runtime.system.write("/system/drivers/usb-storage: missing device id (argv[1])\n");
|
||||
_ = logging.write("/system/drivers/usb-storage: missing device id (argv[1])\n");
|
||||
return;
|
||||
};
|
||||
device_id = std.fmt.parseInt(u64, argument, 10) catch {
|
||||
std.log.info("malformed device id '{s}'", .{argument});
|
||||
return;
|
||||
};
|
||||
runtime.service.run(block_protocol.message_maximum, .{
|
||||
service.run(block_protocol.message_maximum, .{
|
||||
.service = .block,
|
||||
.init = initialise,
|
||||
.on_message = onMessage,
|
||||
});
|
||||
// A failure exit (nonzero -> .aborted) tells the device manager to restart
|
||||
// us with backoff; a clean return means there was nothing to serve.
|
||||
if (bring_up_failed) runtime.system.exit(1);
|
||||
if (bring_up_failed) process.exit(1);
|
||||
}
|
||||
|
||||
@@ -14,9 +14,16 @@
|
||||
//! not the controller is running.
|
||||
|
||||
const std = @import("std");
|
||||
const runtime = @import("runtime");
|
||||
const device = @import("driver");
|
||||
const ipc = @import("ipc");
|
||||
const process = @import("process");
|
||||
const service = @import("service");
|
||||
const time = @import("time");
|
||||
const input = @import("input");
|
||||
const device_manager = @import("driver");
|
||||
const memory = @import("memory");
|
||||
const logging = @import("logging");
|
||||
const device_manager_protocol = @import("device-manager-protocol");
|
||||
const device = runtime.device;
|
||||
const usb_ids = @import("usb-ids");
|
||||
const usb_abi = @import("usb-abi");
|
||||
const usb_transfer_protocol = @import("usb-transfer-protocol");
|
||||
@@ -27,7 +34,7 @@ var controller: ?library.Controller = null;
|
||||
|
||||
/// This driver's service endpoint (registered as `.usb_bus`), where class-driver
|
||||
/// requests, signals, and the interrupt-poll timer all arrive.
|
||||
var service_endpoint: runtime.ipc.Handle = 0;
|
||||
var service_endpoint: ipc.Handle = 0;
|
||||
|
||||
/// How often the driver drains the event ring for interrupt reports (~125 Hz),
|
||||
/// re-armed each tick. Frequent enough for responsive input.
|
||||
@@ -69,7 +76,7 @@ var controller_id: u64 = device_manager_protocol.no_device;
|
||||
/// Claim the assigned controller, find its register window, and hello the
|
||||
/// manager. Any failure returns false: the process exits cleanly, which the
|
||||
/// manager reads as "meant to stop" — a missing assignment is not a crash loop.
|
||||
fn initialise(endpoint: runtime.ipc.Handle) bool {
|
||||
fn initialise(endpoint: ipc.Handle) bool {
|
||||
service_endpoint = endpoint;
|
||||
if (!device.claim(controller_id)) {
|
||||
std.log.info("unable to claim controller device {d}", .{controller_id});
|
||||
@@ -77,8 +84,8 @@ fn initialise(endpoint: runtime.ipc.Handle) bool {
|
||||
}
|
||||
|
||||
// Fetch our own descriptor back for the controller's resources.
|
||||
const buffer = runtime.allocator().alloc(device.DeviceDescriptor, 64) catch {
|
||||
_ = runtime.system.write("/system/drivers/usb-xhci-bus: out of memory\n");
|
||||
const buffer = memory.allocator().alloc(device.DeviceDescriptor, 64) catch {
|
||||
_ = logging.write("/system/drivers/usb-xhci-bus: out of memory\n");
|
||||
return false;
|
||||
};
|
||||
const total = device.enumerate(buffer);
|
||||
@@ -107,14 +114,14 @@ fn initialise(endpoint: runtime.ipc.Handle) bool {
|
||||
register_window.len,
|
||||
});
|
||||
register_base = device.mmioMap(controller_id, register_index) orelse {
|
||||
_ = runtime.system.write("/system/drivers/usb-xhci-bus: mmio_map failed\n");
|
||||
_ = logging.write("/system/drivers/usb-xhci-bus: mmio_map failed\n");
|
||||
return false;
|
||||
};
|
||||
|
||||
// Bring the controller up: reset it, stand up the command and event rings,
|
||||
// and start it running (the hardware half lives in usb-xhci-library.zig).
|
||||
controller = library.Controller.init(register_base) orelse {
|
||||
_ = runtime.system.write("/system/drivers/usb-xhci-bus: controller reset/bring-up failed\n");
|
||||
_ = logging.write("/system/drivers/usb-xhci-bus: controller reset/bring-up failed\n");
|
||||
return false;
|
||||
};
|
||||
std.log.info("controller running ({d} slots, {d}-byte contexts)", .{
|
||||
@@ -125,23 +132,23 @@ fn initialise(endpoint: runtime.ipc.Handle) bool {
|
||||
// ring, the doorbell, and the cycle-bit bookkeeping. If this completes, the
|
||||
// engine is sound; transfers build on exactly this machinery.
|
||||
if (controller.?.noOpCommand()) {
|
||||
_ = runtime.system.write("/system/drivers/usb-xhci-bus: command ring running (no-op ok)\n");
|
||||
_ = logging.write("/system/drivers/usb-xhci-bus: command ring running (no-op ok)\n");
|
||||
} else {
|
||||
_ = runtime.system.write("/system/drivers/usb-xhci-bus: no-op command did not complete\n");
|
||||
_ = logging.write("/system/drivers/usb-xhci-bus: no-op command did not complete\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// The handshake (role: bus — we enumerate USB ports and report the devices
|
||||
// behind them), inside the manager's hello deadline. Keep the handle: the
|
||||
// tick's hot-plug dispatch reports through it.
|
||||
const handle = runtime.device_manager.hello(.bus, controller_id) orelse return false;
|
||||
const handle = device_manager.hello(.bus, controller_id) orelse return false;
|
||||
manager_handle = handle;
|
||||
|
||||
scanPorts(handle);
|
||||
|
||||
// Arm the poll timer that drains interrupt reports from the event ring. It is
|
||||
// re-armed on each tick in onNotification; class drivers subscribe later.
|
||||
_ = runtime.system.timerOnce(service_endpoint, poll_interval_ms);
|
||||
_ = time.timerOnce(service_endpoint, poll_interval_ms);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -168,15 +175,15 @@ fn speedName(speed: u32) []const u8 {
|
||||
/// report one child per interface — carrying the interface's (class, subclass,
|
||||
/// protocol) triple as identity, which is what the device manager matches a
|
||||
/// class driver against.
|
||||
var manager_handle: ?runtime.ipc.Handle = null;
|
||||
var manager_handle: ?ipc.Handle = null;
|
||||
|
||||
// Per-root-port connected state from the previous tick, so the poll acts on
|
||||
// empty->connected transitions (edge), never re-attempting a level every tick.
|
||||
var prev_connected: [64]bool = [_]bool{false} ** 64;
|
||||
|
||||
fn scanPorts(manager: runtime.ipc.Handle) void {
|
||||
fn scanPorts(manager: ipc.Handle) void {
|
||||
const engine = if (controller) |*c| c else {
|
||||
_ = runtime.system.write("/system/drivers/usb-xhci-bus: controller not initialised\n");
|
||||
_ = logging.write("/system/drivers/usb-xhci-bus: controller not initialised\n");
|
||||
return;
|
||||
};
|
||||
std.log.info("{d} root-hub ports", .{engine.max_ports});
|
||||
@@ -190,7 +197,7 @@ fn scanPorts(manager: runtime.ipc.Handle) void {
|
||||
bringUpPort(manager, engine, port);
|
||||
}
|
||||
if (connected == 0) {
|
||||
_ = runtime.system.write("/system/drivers/usb-xhci-bus: no devices connected\n");
|
||||
_ = logging.write("/system/drivers/usb-xhci-bus: no devices connected\n");
|
||||
engine.dumpPortTopology(); // help diagnose an empty scan: the xECP map + raw PORTSC
|
||||
}
|
||||
}
|
||||
@@ -198,7 +205,7 @@ fn scanPorts(manager: runtime.ipc.Handle) void {
|
||||
/// Bring up whatever is on `port`: setup + enumerate + register/report one child
|
||||
/// per interface. Shared by the boot scan and hot-plug (a port-change event with
|
||||
/// the port now connected).
|
||||
fn bringUpPort(manager: runtime.ipc.Handle, engine: *library.Controller, port: u32) void {
|
||||
fn bringUpPort(manager: ipc.Handle, engine: *library.Controller, port: u32) void {
|
||||
const speed = (engine.portStatus(port) >> 10) & 0xF; // the PORTSC port-speed class
|
||||
std.log.info("port {d} connected — {s} (speed class {d})", .{ port, speedName(speed), speed });
|
||||
|
||||
@@ -249,7 +256,7 @@ fn hubPortKey(hub_slot: u8, port: u16) u32 {
|
||||
/// new device) or a disconnect (tear the old one down). Recurses for a hub
|
||||
/// behind a hub — a nested hub is set up on connect and its downstream devices
|
||||
/// torn down first on disconnect.
|
||||
fn bringUpBehindHub(manager: runtime.ipc.Handle, engine: *library.Controller, hub: *library.Device, port: u16) void {
|
||||
fn bringUpBehindHub(manager: ipc.Handle, engine: *library.Controller, hub: *library.Device, port: u16) void {
|
||||
const status = engine.hubPortStatusAck(hub, port) orelse return;
|
||||
const connected = library.Controller.hubPortConnected(status);
|
||||
const existing = engine.deviceOnHubPort(hub, port);
|
||||
@@ -291,7 +298,7 @@ fn bringUpBehindHub(manager: runtime.ipc.Handle, engine: *library.Controller, hu
|
||||
/// Tear down a device that disconnected from a hub: recursively tear down its
|
||||
/// own downstream devices first if it is a hub, report each interface removed,
|
||||
/// then Disable Slot. Mirrors tearDownPort for a hub-attached device.
|
||||
fn tearDownHubDevice(manager: runtime.ipc.Handle, engine: *library.Controller, dev: *library.Device) void {
|
||||
fn tearDownHubDevice(manager: ipc.Handle, engine: *library.Controller, dev: *library.Device) void {
|
||||
// A hub that left takes its whole subtree with it — tear children down first.
|
||||
if (dev.is_hub) {
|
||||
while (engine.nextChildOf(dev.slot_id, 0)) |child| tearDownHubDevice(manager, engine, child);
|
||||
@@ -305,7 +312,7 @@ fn tearDownHubDevice(manager: runtime.ipc.Handle, engine: *library.Controller, d
|
||||
.bus_address = (@as(u64, key) << 8) | interface.number,
|
||||
};
|
||||
var reply: [device_manager_protocol.message_maximum]u8 = undefined;
|
||||
_ = runtime.ipc.call(manager, std.mem.asBytes(&event), &reply) catch {};
|
||||
_ = ipc.call(manager, std.mem.asBytes(&event), &reply) catch {};
|
||||
interface.registered_device_id = 0;
|
||||
}
|
||||
engine.tearDownDevice(dev);
|
||||
@@ -325,7 +332,7 @@ fn deviceIsHub(usb_device: *const library.Device) bool {
|
||||
/// interface as removed (the manager prunes the node, notifies watchers, and
|
||||
/// stops the class driver's world honestly), then release the controller-side
|
||||
/// device state (Disable Slot).
|
||||
fn tearDownPort(manager: runtime.ipc.Handle, engine: *library.Controller, port: u32) void {
|
||||
fn tearDownPort(manager: ipc.Handle, engine: *library.Controller, port: u32) void {
|
||||
const usb_device = engine.deviceOnPort(port) orelse return;
|
||||
std.log.info("port {d} disconnected", .{port});
|
||||
for (usb_device.interfaces[0..usb_device.interface_count]) |*interface| {
|
||||
@@ -335,7 +342,7 @@ fn tearDownPort(manager: runtime.ipc.Handle, engine: *library.Controller, port:
|
||||
.bus_address = (@as(u64, port) << 8) | interface.number,
|
||||
};
|
||||
var reply: [device_manager_protocol.message_maximum]u8 = undefined;
|
||||
_ = runtime.ipc.call(manager, std.mem.asBytes(&event), &reply) catch {
|
||||
_ = ipc.call(manager, std.mem.asBytes(&event), &reply) catch {
|
||||
std.log.info("child-removed report for port {d} interface {d} failed", .{ port, interface.number });
|
||||
};
|
||||
interface.registered_device_id = 0;
|
||||
@@ -348,7 +355,7 @@ fn tearDownPort(manager: runtime.ipc.Handle, engine: *library.Controller, port:
|
||||
/// manager can match a class driver (HID keyboard, mouse, mass storage); the
|
||||
/// registered device id becomes that driver's argv[1] assignment. Returns the
|
||||
/// registered device id, or null if registration or the report failed.
|
||||
fn reportInterface(manager: runtime.ipc.Handle, port: u32, interface: library.InterfaceInfo) ?u64 {
|
||||
fn reportInterface(manager: ipc.Handle, port: u32, interface: library.InterfaceInfo) ?u64 {
|
||||
const identity = usb_ids.packTriple(interface.class, interface.subclass, interface.protocol);
|
||||
|
||||
// A USB device is reached through its controller, not by MMIO, so the child
|
||||
@@ -378,7 +385,7 @@ fn reportInterface(manager: runtime.ipc.Handle, port: u32, interface: library.In
|
||||
.device_id = registered,
|
||||
};
|
||||
var reply: [device_manager_protocol.message_maximum]u8 = undefined;
|
||||
_ = runtime.ipc.call(manager, std.mem.asBytes(&report), &reply) catch {
|
||||
_ = ipc.call(manager, std.mem.asBytes(&report), &reply) catch {
|
||||
std.log.info("child report for port {d} interface {d} failed", .{ port, interface.number });
|
||||
return null;
|
||||
};
|
||||
@@ -396,7 +403,7 @@ fn reportInterface(manager: runtime.ipc.Handle, port: u32, interface: library.In
|
||||
|
||||
/// Serve the USB transfer protocol: a class driver opens its device, then issues
|
||||
/// control / interrupt-subscribe / bulk requests against it.
|
||||
fn onMessage(message: []const u8, reply: []u8, sender: u32, capability: ?runtime.ipc.Handle) usize {
|
||||
fn onMessage(message: []const u8, reply: []u8, sender: u32, capability: ?ipc.Handle) usize {
|
||||
_ = sender;
|
||||
if (message.len < 4) return 0;
|
||||
const operation = std.mem.readInt(u32, message[0..4], .little);
|
||||
@@ -418,7 +425,7 @@ fn writeReply(reply: []u8, value: anytype) usize {
|
||||
/// open: resolve the assigned device id to an interface, remember the caller's
|
||||
/// endpoint (for interrupt reports), and answer with a device token + the
|
||||
/// interface's endpoints so the class driver need not re-read the config.
|
||||
fn handleOpen(message: []const u8, reply: []u8, capability: ?runtime.ipc.Handle) usize {
|
||||
fn handleOpen(message: []const u8, reply: []u8, capability: ?ipc.Handle) usize {
|
||||
if (message.len < @sizeOf(usb_transfer_protocol.OpenRequest)) return writeReply(reply, usb_transfer_protocol.OpenReply{ .status = -1, .endpoint_count = 0, .device_token = 0, .interface_class = 0, .interface_subclass = 0, .interface_protocol = 0, .interface_number = 0 });
|
||||
const request = std.mem.bytesToValue(usb_transfer_protocol.OpenRequest, message[0..@sizeOf(usb_transfer_protocol.OpenRequest)]);
|
||||
const engine = if (controller) |*c| c else return writeReply(reply, usb_transfer_protocol.OpenReply{ .status = -1, .endpoint_count = 0, .device_token = 0, .interface_class = 0, .interface_subclass = 0, .interface_protocol = 0, .interface_number = 0 });
|
||||
@@ -493,7 +500,7 @@ fn handleBulk(message: []const u8, reply: []u8) usize {
|
||||
/// The poll timer landed: drain any interrupt reports off the event ring and push
|
||||
/// each to the class driver that subscribed, then re-arm the timer.
|
||||
fn onNotification(badge: u64) void {
|
||||
if (badge & runtime.ipc.notify_timer_bit == 0) return;
|
||||
if (badge & ipc.notify_timer_bit == 0) return;
|
||||
if (controller) |*engine| {
|
||||
engine.pump();
|
||||
// Poll every root port and reconcile — a device present but not yet
|
||||
@@ -550,22 +557,22 @@ fn onNotification(badge: u64) void {
|
||||
};
|
||||
const n = @min(report.length, usb_transfer_protocol.max_report_data);
|
||||
@memcpy(message.data[0..n], report.data[0..n]);
|
||||
_ = runtime.ipc.send(report.report_endpoint, std.mem.asBytes(&message));
|
||||
_ = ipc.send(report.report_endpoint, std.mem.asBytes(&message));
|
||||
}
|
||||
}
|
||||
_ = runtime.system.timerOnce(service_endpoint, poll_interval_ms);
|
||||
_ = time.timerOnce(service_endpoint, poll_interval_ms);
|
||||
}
|
||||
|
||||
pub fn main(init: runtime.process.Init) void {
|
||||
pub fn main(init: process.Init) void {
|
||||
const argument = init.arguments.get(1) orelse {
|
||||
_ = runtime.system.write("/system/drivers/usb-xhci-bus: missing controller device id (argv[1])\n");
|
||||
_ = logging.write("/system/drivers/usb-xhci-bus: missing controller device id (argv[1])\n");
|
||||
return;
|
||||
};
|
||||
controller_id = std.fmt.parseInt(u64, argument, 10) catch {
|
||||
std.log.info("malformed controller device id '{s}'", .{argument});
|
||||
return;
|
||||
};
|
||||
runtime.service.run(usb_transfer_protocol.message_maximum, .{
|
||||
service.run(usb_transfer_protocol.message_maximum, .{
|
||||
.service = .usb_bus,
|
||||
.init = initialise,
|
||||
.on_message = onMessage,
|
||||
|
||||
@@ -19,12 +19,11 @@
|
||||
//! proven.
|
||||
|
||||
const std = @import("std");
|
||||
const runtime = @import("runtime");
|
||||
const time = @import("time");
|
||||
const memory = @import("memory");
|
||||
const mmio = @import("mmio");
|
||||
const usb_abi = @import("usb-abi");
|
||||
const usb_ids = @import("usb-ids");
|
||||
const dma = runtime.dma;
|
||||
const system = runtime.system;
|
||||
|
||||
// --- register offsets -------------------------------------------------------
|
||||
|
||||
@@ -134,7 +133,7 @@ const trbs_per_ring = page_size / @sizeOf(Trb); // 256
|
||||
// A producer ring (command ring, or a transfer ring): a page of TRBs whose last
|
||||
// entry is a Link TRB back to the start. `cycle` is the producer cycle state.
|
||||
const ProducerRing = struct {
|
||||
region: dma.Region,
|
||||
region: memory.DmaRegion,
|
||||
enqueue_index: usize = 0,
|
||||
cycle: bool = true,
|
||||
|
||||
@@ -182,8 +181,8 @@ const ProducerRing = struct {
|
||||
// The event ring: a single segment the controller fills and the driver drains.
|
||||
// `cycle` is the consumer cycle state, flipped each time the dequeue wraps.
|
||||
const EventRing = struct {
|
||||
segment: dma.Region,
|
||||
table: dma.Region,
|
||||
segment: memory.DmaRegion,
|
||||
table: memory.DmaRegion,
|
||||
dequeue_index: usize = 0,
|
||||
cycle: bool = true,
|
||||
|
||||
@@ -269,12 +268,12 @@ pub const Device = struct {
|
||||
port: u32 = 0,
|
||||
speed: u32 = 0,
|
||||
max_packet_size_0: u32 = 8,
|
||||
input_context: dma.Region = .{ .virtual = 0, .physical = 0 },
|
||||
device_context: dma.Region = .{ .virtual = 0, .physical = 0 },
|
||||
input_context: memory.DmaRegion = .{ .virtual = 0, .physical = 0 },
|
||||
device_context: memory.DmaRegion = .{ .virtual = 0, .physical = 0 },
|
||||
ep0_ring: ProducerRing = .{ .region = .{ .virtual = 0, .physical = 0 } },
|
||||
// A page-sized bounce buffer for control-transfer data (descriptors are read
|
||||
// here, then copied out to the caller).
|
||||
control_buffer: dma.Region = .{ .virtual = 0, .physical = 0 },
|
||||
control_buffer: memory.DmaRegion = .{ .virtual = 0, .physical = 0 },
|
||||
device_descriptor: usb_abi.DeviceDescriptor = std.mem.zeroes(usb_abi.DeviceDescriptor),
|
||||
configuration_value: u8 = 0,
|
||||
interface_count: u8 = 0,
|
||||
@@ -308,7 +307,7 @@ const Subscription = struct {
|
||||
dci: u32 = 0,
|
||||
endpoint_address: u8 = 0,
|
||||
ring: *ProducerRing = undefined,
|
||||
buffer: dma.Region = .{ .virtual = 0, .physical = 0 },
|
||||
buffer: memory.DmaRegion = .{ .virtual = 0, .physical = 0 },
|
||||
max_length: u16 = 0,
|
||||
armed_trb_physical: u64 = 0,
|
||||
// The bus layer's per-subscription IPC state (opaque here): the class driver's
|
||||
@@ -447,7 +446,7 @@ pub const Controller = struct {
|
||||
max_ports: u32,
|
||||
context_size: usize, // 32 or 64 (CSZ)
|
||||
|
||||
device_context_array: dma.Region,
|
||||
device_context_array: memory.DmaRegion,
|
||||
command_ring: ProducerRing,
|
||||
event_ring: EventRing,
|
||||
devices: [max_devices]Device = [_]Device{.{}} ** max_devices,
|
||||
@@ -601,19 +600,19 @@ pub const Controller = struct {
|
||||
write32(self.operational(op_config), self.max_slots);
|
||||
|
||||
// The Device Context Base Address Array (entry 0 = scratchpad array).
|
||||
self.device_context_array = dma.alloc(page_size, dma.coherent) orelse return null;
|
||||
self.device_context_array = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return null;
|
||||
self.setupScratchpad(register_base);
|
||||
write64(self.operational(op_dcbaap), self.device_context_array.physical);
|
||||
|
||||
// The command ring: a page of TRBs, last entry a Link back to the start.
|
||||
self.command_ring = .{ .region = dma.alloc(page_size, dma.coherent) orelse return null };
|
||||
self.command_ring = .{ .region = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return null };
|
||||
self.command_ring.installLink();
|
||||
write64(self.operational(op_crcr), self.command_ring.region.physical | cycle_bit);
|
||||
|
||||
// The event ring: one segment + a one-entry segment table.
|
||||
self.event_ring = .{
|
||||
.segment = dma.alloc(page_size, dma.coherent) orelse return null,
|
||||
.table = dma.alloc(page_size, dma.coherent) orelse return null,
|
||||
.segment = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return null,
|
||||
.table = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return null,
|
||||
};
|
||||
const table: *volatile ErstEntry = @ptrFromInt(self.event_ring.table.virtual);
|
||||
table.ring_segment_base = self.event_ring.segment.physical;
|
||||
@@ -663,11 +662,11 @@ pub const Controller = struct {
|
||||
return;
|
||||
}
|
||||
// One page per scratchpad buffer, plus a page holding their address array.
|
||||
const pointers = dma.alloc(page_size, dma.coherent) orelse return;
|
||||
const pointers = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return;
|
||||
const pointer_array: [*]volatile u64 = @ptrFromInt(pointers.virtual);
|
||||
var index: u32 = 0;
|
||||
while (index < count) : (index += 1) {
|
||||
const buffer = dma.alloc(page_size, dma.coherent) orelse return;
|
||||
const buffer = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return;
|
||||
pointer_array[index] = buffer.physical;
|
||||
}
|
||||
array[0] = pointers.physical;
|
||||
@@ -675,16 +674,16 @@ pub const Controller = struct {
|
||||
|
||||
// Spin (with a deadline) until every bit in `mask` reads back as zero / one.
|
||||
fn waitClear(address: usize, mask: u32) bool {
|
||||
const deadline = system.clock() + 1_000_000_000; // 1 s
|
||||
const deadline = time.clock() + 1_000_000_000; // 1 s
|
||||
while (read32(address) & mask != 0) {
|
||||
if (system.clock() >= deadline) return false;
|
||||
if (time.clock() >= deadline) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
fn waitSet(address: usize, mask: u32) bool {
|
||||
const deadline = system.clock() + 1_000_000_000;
|
||||
const deadline = time.clock() + 1_000_000_000;
|
||||
while (read32(address) & mask == 0) {
|
||||
if (system.clock() >= deadline) return false;
|
||||
if (time.clock() >= deadline) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -723,7 +722,7 @@ pub const Controller = struct {
|
||||
write64(self.interrupter(event_ring_dequeue_pointer), dequeue | (1 << 3));
|
||||
return event;
|
||||
}
|
||||
if (system.clock() >= deadline_ns) return null;
|
||||
if (time.clock() >= deadline_ns) return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -732,7 +731,7 @@ pub const Controller = struct {
|
||||
/// serviced even during a command); other events are ignored. Returns the
|
||||
/// completion code, or null on timeout.
|
||||
fn awaitCommand(self: *Controller, command_physical: u64) ?u8 {
|
||||
const deadline = system.clock() + 1_000_000_000;
|
||||
const deadline = time.clock() + 1_000_000_000;
|
||||
while (true) {
|
||||
const event = self.nextEvent(deadline) orelse return null;
|
||||
const kind = trbType(event.control);
|
||||
@@ -764,9 +763,9 @@ pub const Controller = struct {
|
||||
// bits and PED are untouched) and preserving PP.
|
||||
const before = self.portStatus(port);
|
||||
self.writePortStatus(port, (before & ~portsc_write_1_to_clear) | portsc_reset);
|
||||
const deadline = system.clock() + 500_000_000;
|
||||
const deadline = time.clock() + 500_000_000;
|
||||
while (self.portStatus(port) & portsc_reset_change == 0) {
|
||||
if (system.clock() >= deadline) return false;
|
||||
if (time.clock() >= deadline) return false;
|
||||
}
|
||||
// Clear the Port Reset Change bit (write 1 to PRC, 0 to the rest).
|
||||
const after = self.portStatus(port);
|
||||
@@ -778,7 +777,7 @@ pub const Controller = struct {
|
||||
/// assigned (carried in bits 31:24 of the completion event's control field).
|
||||
fn enableSlot(self: *Controller) ?u8 {
|
||||
const physical = self.submitCommand(.{ .control = trbControl(.enable_slot, 0) });
|
||||
const deadline = system.clock() + 1_000_000_000;
|
||||
const deadline = time.clock() + 1_000_000_000;
|
||||
while (true) {
|
||||
const event = self.nextEvent(deadline) orelse return null;
|
||||
if (trbType(event.control) == @intFromEnum(TrbType.command_completion_event) and
|
||||
@@ -858,7 +857,7 @@ pub const Controller = struct {
|
||||
// retried here; its port was reset in serviceHubPort.)
|
||||
if (device.parent_slot == 0) {
|
||||
if (!self.resetPort(device.port)) return false;
|
||||
system.sleep(10);
|
||||
time.sleepMillis(10);
|
||||
} else return false;
|
||||
}
|
||||
return false;
|
||||
@@ -891,7 +890,7 @@ pub const Controller = struct {
|
||||
// (TRSTRCY, 10 ms) after reset before it answers SET_ADDRESS.
|
||||
// Addressing immediately gives a USB Transaction Error (code 4) on
|
||||
// real full-speed devices; QEMU tolerates the omission.
|
||||
system.sleep(10);
|
||||
time.sleepMillis(10);
|
||||
}
|
||||
const slot_id = self.enableSlot() orelse {
|
||||
std.log.info("port {d} setup: Enable Slot failed", .{port});
|
||||
@@ -909,11 +908,11 @@ pub const Controller = struct {
|
||||
.max_packet_size_0 = defaultMaxPacketSize0(effective_speed),
|
||||
.root_port = port, // a root-port device: the chain root IS this port
|
||||
};
|
||||
device.input_context = dma.alloc(page_size, dma.coherent) orelse return self.abandon(device);
|
||||
device.device_context = dma.alloc(page_size, dma.coherent) orelse return self.abandon(device);
|
||||
device.ep0_ring = .{ .region = dma.alloc(page_size, dma.coherent) orelse return self.abandon(device) };
|
||||
device.input_context = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return self.abandon(device);
|
||||
device.device_context = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return self.abandon(device);
|
||||
device.ep0_ring = .{ .region = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return self.abandon(device) };
|
||||
device.ep0_ring.installLink();
|
||||
device.control_buffer = dma.alloc(page_size, dma.coherent) orelse return self.abandon(device);
|
||||
device.control_buffer = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return self.abandon(device);
|
||||
|
||||
self.buildAddressInputContext(device);
|
||||
const array: [*]volatile u64 = @ptrFromInt(self.device_context_array.virtual);
|
||||
@@ -991,7 +990,7 @@ pub const Controller = struct {
|
||||
if (!is_interrupt or !is_in) continue;
|
||||
const ring = self.getOrConfigureEndpoint(device, endpoint) orelse return;
|
||||
const subscription = self.allocateSubscription() orelse return;
|
||||
const buffer = dma.alloc(page_size, dma.coherent) orelse return;
|
||||
const buffer = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return;
|
||||
const number: u8 = endpoint.address & 0x0F;
|
||||
subscription.* = .{
|
||||
.active = true,
|
||||
@@ -1068,7 +1067,7 @@ pub const Controller = struct {
|
||||
_ = self.controlTransfer(hub, hubreq.setPortFeature(hubreq.feature_port_reset, port), &.{}, false);
|
||||
var tries: u32 = 0;
|
||||
while (tries < 200) : (tries += 1) {
|
||||
system.sleep(5);
|
||||
time.sleepMillis(5);
|
||||
const s = self.readHubPortStatus(hub, port) orelse return null;
|
||||
if (s & hubreq.status_enable != 0) break;
|
||||
}
|
||||
@@ -1113,11 +1112,11 @@ pub const Controller = struct {
|
||||
.parent_slot = hub.slot_id,
|
||||
.parent_port = @intCast(port),
|
||||
};
|
||||
device.input_context = dma.alloc(page_size, dma.coherent) orelse return self.abandon(device);
|
||||
device.device_context = dma.alloc(page_size, dma.coherent) orelse return self.abandon(device);
|
||||
device.ep0_ring = .{ .region = dma.alloc(page_size, dma.coherent) orelse return self.abandon(device) };
|
||||
device.input_context = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return self.abandon(device);
|
||||
device.device_context = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return self.abandon(device);
|
||||
device.ep0_ring = .{ .region = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return self.abandon(device) };
|
||||
device.ep0_ring.installLink();
|
||||
device.control_buffer = dma.alloc(page_size, dma.coherent) orelse return self.abandon(device);
|
||||
device.control_buffer = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return self.abandon(device);
|
||||
|
||||
self.buildAddressInputContext(device);
|
||||
const array: [*]volatile u64 = @ptrFromInt(self.device_context_array.virtual);
|
||||
@@ -1164,7 +1163,7 @@ pub const Controller = struct {
|
||||
/// a way that survived every driver restart (the 1-in-3 READ CAPACITY
|
||||
/// failure at boot, with a USB keyboard and mouse polling concurrently).
|
||||
fn awaitTransfer(self: *Controller, slot_id: u8, dci: u32, requested_length: u32) ?u8 {
|
||||
const deadline = system.clock() + 1_000_000_000;
|
||||
const deadline = time.clock() + 1_000_000_000;
|
||||
while (true) {
|
||||
const event = self.nextEvent(deadline) orelse return null;
|
||||
if (trbType(event.control) != @intFromEnum(TrbType.transfer_event)) continue;
|
||||
@@ -1401,7 +1400,7 @@ pub const Controller = struct {
|
||||
|
||||
const configured = &device.endpoint_rings[device.endpoint_ring_count];
|
||||
configured.dci = dci;
|
||||
configured.ring = .{ .region = dma.alloc(page_size, dma.coherent) orelse return null };
|
||||
configured.ring = .{ .region = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return null };
|
||||
configured.ring.installLink();
|
||||
self.buildConfigureEndpointInputContext(device, endpoint, dci, &configured.ring);
|
||||
if (!self.configureEndpointCommand(device)) return null;
|
||||
@@ -1481,7 +1480,7 @@ pub const Controller = struct {
|
||||
pub fn subscribeInterrupt(self: *Controller, device: *Device, endpoint: EndpointInfo, device_token: u64, report_endpoint: usize) bool {
|
||||
const ring = self.getOrConfigureEndpoint(device, endpoint) orelse return false;
|
||||
const subscription = self.allocateSubscription() orelse return false;
|
||||
const buffer = dma.alloc(page_size, dma.coherent) orelse return false;
|
||||
const buffer = memory.dmaAlloc(page_size, memory.dma_coherent) orelse return false;
|
||||
const number: u8 = endpoint.address & 0x0F;
|
||||
const direction_in = endpoint.address & 0x80 != 0;
|
||||
subscription.* = .{
|
||||
@@ -1570,7 +1569,7 @@ pub const Controller = struct {
|
||||
/// driver's timer tick.
|
||||
pub fn pump(self: *Controller) void {
|
||||
while (true) {
|
||||
const event = self.nextEvent(system.clock()) orelse return; // deadline=now: null when empty
|
||||
const event = self.nextEvent(time.clock()) orelse return; // deadline=now: null when empty
|
||||
const kind = trbType(event.control);
|
||||
if (kind == @intFromEnum(TrbType.transfer_event)) {
|
||||
_ = self.serviceInterruptEvent(event);
|
||||
|
||||
@@ -14,14 +14,16 @@
|
||||
//! restart/re-attach are V4–V6. See docs/display-v2.md.
|
||||
|
||||
const std = @import("std");
|
||||
const runtime = @import("runtime");
|
||||
const device = @import("driver");
|
||||
const ipc = @import("ipc");
|
||||
const process = @import("process");
|
||||
const service = @import("service");
|
||||
const time = @import("time");
|
||||
const device_manager = @import("driver");
|
||||
const memory = @import("memory");
|
||||
const logging = @import("logging");
|
||||
const mmio = @import("mmio");
|
||||
const pci = @import("pci");
|
||||
const device = runtime.device;
|
||||
const dma = runtime.dma;
|
||||
const shared_memory = runtime.shared_memory;
|
||||
const system = runtime.system;
|
||||
const ipc = runtime.ipc;
|
||||
const display_protocol = @import("display-protocol");
|
||||
const scanout_protocol = @import("scanout-protocol");
|
||||
const vp = @import("virtio-pci.zig");
|
||||
@@ -86,14 +88,14 @@ var notify_multiplier: u32 = 0;
|
||||
var notify_addr: usize = 0;
|
||||
|
||||
// DMA memory: the virtqueue rings and the command scratch.
|
||||
var ring: dma.Region = undefined;
|
||||
var command: dma.Region = undefined;
|
||||
var ring: memory.DmaRegion = undefined;
|
||||
var command: memory.DmaRegion = undefined;
|
||||
|
||||
// The scanout backing is a **shared** (shared-memory) region, not DMA: cacheable so the compositor
|
||||
// composites into it cheaply (x86 DMA is coherent, so the device still sees the writes), and
|
||||
// shareable so the same physical pages the device scans out of are the ones the compositor
|
||||
// paints. The driver keeps the capability to hand to the compositor in the announce.
|
||||
var surface: shared_memory.Region = undefined;
|
||||
var surface: memory.SharedRegion = undefined;
|
||||
|
||||
// Split-virtqueue producer/consumer shadows.
|
||||
var avail_shadow: u16 = 0;
|
||||
@@ -161,7 +163,7 @@ fn waitUsed() bool {
|
||||
used_shadow = idx;
|
||||
return true;
|
||||
}
|
||||
if (tries > 8) system.sleep(1);
|
||||
if (tries > 8) time.sleepMillis(1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -282,11 +284,11 @@ fn initialise(endpoint: ipc.Handle) bool {
|
||||
std.log.info("control queue too small ({d})", .{device_qsize});
|
||||
return false;
|
||||
}
|
||||
ring = dma.alloc(4096, dma.coherent) orelse {
|
||||
ring = memory.dmaAlloc(4096, memory.dma_coherent) orelse {
|
||||
std.log.info("virtqueue allocation failed", .{});
|
||||
return false;
|
||||
};
|
||||
command = dma.alloc(4096, dma.coherent) orelse {
|
||||
command = memory.dmaAlloc(4096, memory.dma_coherent) orelse {
|
||||
std.log.info("command-buffer allocation failed", .{});
|
||||
return false;
|
||||
};
|
||||
@@ -322,11 +324,11 @@ fn initialise(endpoint: ipc.Handle) bool {
|
||||
|
||||
// Back the resource with a shared (shared-memory) surface, so the compositor and the device work
|
||||
// the same physical pages. The device needs the guest-physical base for attach_backing.
|
||||
surface = shared_memory.create(scanout_bytes) orelse {
|
||||
surface = memory.sharedCreate(scanout_bytes) orelse {
|
||||
std.log.info("scanout surface allocation failed", .{});
|
||||
return false;
|
||||
};
|
||||
const surface_physical = shared_memory.physical(surface.handle) orelse {
|
||||
const surface_physical = memory.sharedPhysical(surface.handle) orelse {
|
||||
std.log.info("could not resolve the scanout surface physical address", .{});
|
||||
return false;
|
||||
};
|
||||
@@ -354,7 +356,7 @@ fn initialise(endpoint: ipc.Handle) bool {
|
||||
// deadline). Role: device — we claim one PCI function and serve its scanout; we report
|
||||
// no children. A restarted instance re-hellos here and re-announces below — the compositor
|
||||
// re-attaches to the fresh scanout (V6). Best-effort: standalone bring-up has no manager.
|
||||
_ = runtime.device_manager.hello(.device, device_id);
|
||||
_ = device_manager.hello(.device, device_id);
|
||||
|
||||
// Read the monitor's EDID (best-effort, when the device offers it) — the mode list a real
|
||||
// driver derives from it; we log the preferred mode and keep our fixed offered list.
|
||||
@@ -474,7 +476,7 @@ fn announce() void {
|
||||
var tries: u32 = 0;
|
||||
const display = while (tries < 50) : (tries += 1) {
|
||||
if (ipc.lookup(.display)) |h| break h;
|
||||
system.sleep(20);
|
||||
time.sleepMillis(20);
|
||||
} else {
|
||||
std.log.info("no display service to announce to (scanout-only)", .{});
|
||||
return;
|
||||
@@ -535,16 +537,16 @@ fn onMessage(message: []const u8, reply: []u8, sender: u32, capability: ?ipc.Han
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main(init: runtime.process.Init) void {
|
||||
pub fn main(init: process.Init) void {
|
||||
const argument = init.arguments.get(1) orelse {
|
||||
_ = system.write("virtio-gpu: missing device id (argv[1])\n");
|
||||
_ = logging.write("virtio-gpu: missing device id (argv[1])\n");
|
||||
return;
|
||||
};
|
||||
device_id = std.fmt.parseInt(u64, argument, 10) catch {
|
||||
std.log.info("malformed device id '{s}'", .{argument});
|
||||
return;
|
||||
};
|
||||
runtime.service.run(256, .{
|
||||
service.run(256, .{
|
||||
.service = .scanout,
|
||||
.init = initialise,
|
||||
.on_message = onMessage,
|
||||
|
||||
Reference in New Issue
Block a user