reorg: move mmio into library/device and spell out its API
mmio is device-driver code, so it joins the other domains under
library/device/mmio/ (module name "mmio" unchanged — a pure relocation, only
the build paths move). And its abbreviated function names are spelled out per
docs/coding-standards.md:
read -> readRegister mb -> memoryBarrier
write -> writeRegister rmb -> readMemoryBarrier
wmb -> writeMemoryBarrier
All call sites updated (virtio-gpu, usb-xhci-library, pci.Function); the two
display-driver placeholders import mmio but use nothing, so they're untouched.
Docs (driver-model graph, README layout, drivers.md, the FHS note) follow the
new path and names.
zig build + test green; virtio-gpu, display-native, display-reattach, usb-hid,
usb-hub, usb-storage, pci-scan pass.
This commit is contained in:
@@ -162,7 +162,7 @@ const ProducerRing = struct {
|
||||
// holds it) is written after `parameter`/`status`, with a barrier between.
|
||||
slot.parameter = trb.parameter;
|
||||
slot.status = trb.status;
|
||||
mmio.wmb();
|
||||
mmio.writeMemoryBarrier();
|
||||
slot.control = control;
|
||||
const physical = self.region.physical + index * @sizeOf(Trb);
|
||||
self.enqueue_index += 1;
|
||||
@@ -628,10 +628,10 @@ pub const Controller = struct {
|
||||
// interrupter is enabled, so a hot-plug port-change event is silently
|
||||
// dropped otherwise. Enabling it is harmless to a polling driver.
|
||||
write32(self.interrupter(interrupter_management), 1 << 1); // IE
|
||||
mmio.wmb();
|
||||
mmio.writeMemoryBarrier();
|
||||
|
||||
// Run.
|
||||
mmio.wmb();
|
||||
mmio.writeMemoryBarrier();
|
||||
write32(self.operational(op_usbcmd), read32(self.operational(op_usbcmd)) | usbcmd_run | usbcmd_interrupter_enable);
|
||||
if (!waitClear(self.operational(op_usbsts), usbsts_halted)) return null;
|
||||
|
||||
@@ -699,7 +699,7 @@ pub const Controller = struct {
|
||||
/// address of the enqueued TRB (which the Command Completion Event echoes).
|
||||
fn submitCommand(self: *Controller, trb: Trb) u64 {
|
||||
const physical = self.command_ring.push(trb);
|
||||
mmio.wmb();
|
||||
mmio.writeMemoryBarrier();
|
||||
self.ringDoorbell(0, 0); // doorbell 0, target 0 = command ring
|
||||
return physical;
|
||||
}
|
||||
@@ -711,7 +711,7 @@ pub const Controller = struct {
|
||||
const slot = &ring[self.event_ring.dequeue_index];
|
||||
const control = slot.control;
|
||||
if ((control & cycle_bit != 0) == self.event_ring.cycle) {
|
||||
mmio.rmb();
|
||||
mmio.readMemoryBarrier();
|
||||
const event = Trb{ .parameter = slot.parameter, .status = slot.status, .control = control };
|
||||
self.event_ring.dequeue_index += 1;
|
||||
if (self.event_ring.dequeue_index >= trbs_per_ring) {
|
||||
@@ -1216,7 +1216,7 @@ pub const Controller = struct {
|
||||
.control = trbControl(.status_stage, status_direction | (1 << 5)), // DIR | IOC
|
||||
});
|
||||
|
||||
mmio.wmb();
|
||||
mmio.writeMemoryBarrier();
|
||||
self.ringDoorbell(device.slot_id, 1); // DCI 1 = EP0
|
||||
const code = self.awaitTransfer(device.slot_id, 1, @intCast(data.len)) orelse return false;
|
||||
if (code != @intFromEnum(CompletionCode.success) and code != @intFromEnum(CompletionCode.short_packet)) return false;
|
||||
@@ -1457,7 +1457,7 @@ pub const Controller = struct {
|
||||
.status = length,
|
||||
.control = trbControl(.normal, (1 << 5)), // IOC
|
||||
});
|
||||
mmio.wmb();
|
||||
mmio.writeMemoryBarrier();
|
||||
const number: u8 = endpoint.address & 0x0F;
|
||||
const direction_in = endpoint.address & 0x80 != 0;
|
||||
const dci = doorbellContextIndex(number, direction_in);
|
||||
@@ -1507,7 +1507,7 @@ pub const Controller = struct {
|
||||
.status = subscription.max_length,
|
||||
.control = trbControl(.normal, (1 << 5)), // IOC
|
||||
});
|
||||
mmio.wmb();
|
||||
mmio.writeMemoryBarrier();
|
||||
self.ringDoorbell(subscription.slot_id, subscription.dci);
|
||||
}
|
||||
|
||||
|
||||
@@ -102,17 +102,17 @@ var used_shadow: u16 = 0;
|
||||
// --- common-config register access (little-endian MMIO at `common_base`) ---------------
|
||||
|
||||
fn cfgRead(comptime T: type, comptime field: []const u8) T {
|
||||
return mmio.read(T, common_base + @offsetOf(vp.CommonCfg, field));
|
||||
return mmio.readRegister(T, common_base + @offsetOf(vp.CommonCfg, field));
|
||||
}
|
||||
fn cfgWrite(comptime T: type, comptime field: []const u8, value: T) void {
|
||||
mmio.write(T, common_base + @offsetOf(vp.CommonCfg, field), value);
|
||||
mmio.writeRegister(T, common_base + @offsetOf(vp.CommonCfg, field), value);
|
||||
}
|
||||
/// Write a 64-bit common-config register as two 32-bit halves (low then high) — the widest
|
||||
/// access every virtio-pci host is required to accept for the queue-address registers.
|
||||
fn cfgWrite64(comptime field: []const u8, value: u64) void {
|
||||
const at = common_base + @offsetOf(vp.CommonCfg, field);
|
||||
mmio.write(u32, at, @truncate(value));
|
||||
mmio.write(u32, at + 4, @truncate(value >> 32));
|
||||
mmio.writeRegister(u32, at, @truncate(value));
|
||||
mmio.writeRegister(u32, at + 4, @truncate(value >> 32));
|
||||
}
|
||||
fn orStatus(bit: u8) void {
|
||||
cfgWrite(u8, "device_status", cfgRead(u8, "device_status") | bit);
|
||||
@@ -140,12 +140,12 @@ fn submit(request_len: usize, response_len: usize) bool {
|
||||
|
||||
const avail_ring: [*]u16 = @ptrFromInt(ring.virtual + avail_offset + 4);
|
||||
avail_ring[avail_shadow % queue_size] = 0; // head of the chain is descriptor 0
|
||||
mmio.wmb();
|
||||
mmio.writeMemoryBarrier();
|
||||
avail_shadow +%= 1;
|
||||
mmio.write(u16, ring.virtual + avail_offset + 2, avail_shadow); // avail.idx
|
||||
mmio.wmb();
|
||||
mmio.writeRegister(u16, ring.virtual + avail_offset + 2, avail_shadow); // avail.idx
|
||||
mmio.writeMemoryBarrier();
|
||||
|
||||
mmio.write(u16, notify_addr, 0); // ring the control queue's doorbell
|
||||
mmio.writeRegister(u16, notify_addr, 0); // ring the control queue's doorbell
|
||||
return waitUsed();
|
||||
}
|
||||
|
||||
@@ -155,8 +155,8 @@ fn submit(request_len: usize, response_len: usize) bool {
|
||||
fn waitUsed() bool {
|
||||
var tries: u32 = 0;
|
||||
while (tries < 2000) : (tries += 1) {
|
||||
mmio.rmb();
|
||||
const idx = mmio.read(u16, ring.virtual + used_offset + 2); // used.idx
|
||||
mmio.readMemoryBarrier();
|
||||
const idx = mmio.readRegister(u16, ring.virtual + used_offset + 2); // used.idx
|
||||
if (idx != used_shadow) {
|
||||
used_shadow = idx;
|
||||
return true;
|
||||
@@ -231,16 +231,16 @@ fn initialise(endpoint: ipc.Handle) bool {
|
||||
var caps = function.capabilities();
|
||||
while (caps.next()) |cap| {
|
||||
if (cap.id != vp.pci_cap_vendor) continue;
|
||||
const cfg_type = mmio.read(u8, cap.offset + 3);
|
||||
const cfg_type = mmio.readRegister(u8, cap.offset + 3);
|
||||
if (cfg_type != vp.cfg_common and cfg_type != vp.cfg_notify) continue;
|
||||
const bar = mmio.read(u8, cap.offset + 4);
|
||||
const offset = mmio.read(u32, cap.offset + 8);
|
||||
const bar = mmio.readRegister(u8, cap.offset + 4);
|
||||
const offset = mmio.readRegister(u32, cap.offset + 8);
|
||||
if (function.mapBar(bar)) |bar_base| {
|
||||
if (cfg_type == vp.cfg_common) {
|
||||
common_base = bar_base + offset;
|
||||
} else {
|
||||
notify_base = bar_base + offset;
|
||||
notify_multiplier = mmio.read(u32, cap.offset + 16); // virtio_pci_notify_cap tail
|
||||
notify_multiplier = mmio.readRegister(u32, cap.offset + 16); // virtio_pci_notify_cap tail
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -290,7 +290,7 @@ fn initialise(endpoint: ipc.Handle) bool {
|
||||
std.log.info("command-buffer allocation failed", .{});
|
||||
return false;
|
||||
};
|
||||
mmio.write(u16, ring.virtual + avail_offset, 1); // VIRTQ_AVAIL_F_NO_INTERRUPT: we poll
|
||||
mmio.writeRegister(u16, ring.virtual + avail_offset, 1); // VIRTQ_AVAIL_F_NO_INTERRUPT: we poll
|
||||
cfgWrite(u16, "queue_size", queue_size);
|
||||
cfgWrite64("queue_desc", ring.physical + desc_offset);
|
||||
cfgWrite64("queue_driver", ring.physical + avail_offset);
|
||||
@@ -372,7 +372,7 @@ fn initialise(endpoint: ipc.Handle) bool {
|
||||
}
|
||||
// The scanout surface is CPU-visible RAM: read the pattern back to prove the mapping,
|
||||
// which together with the flush ack above is the automated stand-in for "it's on screen".
|
||||
mmio.rmb();
|
||||
mmio.readMemoryBarrier();
|
||||
if (pixels[0] != testPixel(0) or pixels[pixel_count / 2] != testPixel(@intCast(pixel_count / 2))) {
|
||||
std.log.info("pixel read-back mismatch", .{});
|
||||
return false;
|
||||
@@ -435,7 +435,7 @@ fn readEdid() void {
|
||||
/// the panel. Reused by the V3 self-test and by every compositor present over `.scanout`. V4
|
||||
/// presents the full surface; the damage-rect fast path is a later refinement.
|
||||
fn presentFull() bool {
|
||||
mmio.wmb(); // the surface writes must be visible before the device transfers them
|
||||
mmio.writeMemoryBarrier(); // the surface writes must be visible before the device transfers them
|
||||
{
|
||||
// Transfer the current-mode rectangle from the guest backing to the host resource. The
|
||||
// device uses the resource's (max) width as the row stride, so the top-left rect at
|
||||
|
||||
Reference in New Issue
Block a user