establishment: usb-transfer stops being a name
P1 of docs/establishment-planes-plan.md. The bus no longer binds
/protocol/usb-transfer — the bind race made whichever instance came second
unreachable, which on a real three-controller Ryzen meant a mouse no class
driver could reach ("could not open device 50"). Each instance hands its
serving endpoint up in the hello that already delegates its controller, and
class drivers receive their OWN controller's channel from helloForChannel —
routed by the manager's lineage, retried while a provider is mid-restart,
on one manager handle so retries never spend handle-table slots.
usb.open(bus, id) now takes the channel it used to look up; the name rows
leave protocol.csv with the code (bind 67, opens 119/120/122); the
conformance fixture's prose stops claiming the bus binds; and the stale
input-client import leaves the bus with the channel one.
Gate: 19 QEMU cases green (usb family, hubs, both IOMMU variants, fat chain,
boot-from-USB, orderly shutdown, conformance).
This commit is contained in:
@@ -10,10 +10,10 @@ pub fn build(b: *std.Build) void {
|
||||
.name = "usb-xhci-bus",
|
||||
.root_source_file = b.path("usb-xhci-bus.zig"),
|
||||
.imports = &.{
|
||||
"channel", "device-manager-protocol", "driver", "envelope",
|
||||
"input-client", "ipc", "logging", "memory",
|
||||
"mmio", "pci", "process", "service",
|
||||
"time", "usb-abi", "usb-ids", "usb-transfer-protocol",
|
||||
"device-manager-protocol", "driver", "envelope", "ipc",
|
||||
"logging", "memory", "mmio", "pci",
|
||||
"process", "service", "time", "usb-abi",
|
||||
"usb-ids", "usb-transfer-protocol",
|
||||
},
|
||||
});
|
||||
b.installArtifact(exe);
|
||||
|
||||
@@ -15,12 +15,10 @@
|
||||
|
||||
const std = @import("std");
|
||||
const device = @import("driver");
|
||||
const channel = @import("channel");
|
||||
const ipc = @import("ipc");
|
||||
const process = @import("process");
|
||||
const service = @import("service");
|
||||
const time = @import("time");
|
||||
const input = @import("input-client");
|
||||
const device_manager = @import("driver");
|
||||
const memory = @import("memory");
|
||||
const logging = @import("logging");
|
||||
@@ -158,37 +156,27 @@ fn initialise(endpoint: ipc.Handle) bool {
|
||||
// its device open, or its restarted instance could never claim it back.
|
||||
_ = process.subscribeExits(endpoint);
|
||||
|
||||
// The transfer contract, bound by hand rather than through the harness's
|
||||
// `.service`, because **losing it is not fatal here**. One machine can carry
|
||||
// several xHCI controllers and the driver model spawns one process per
|
||||
// controller, so several processes provide the same contract for different
|
||||
// hardware — and `/protocol` holds exactly one name, deliberately (addressing
|
||||
// lives inside the protocol, never in the path). Whoever binds first is the
|
||||
// one clients reach by name; a later instance still owns its controller,
|
||||
// enumerates its bus, and reports its children to the device manager, so it
|
||||
// keeps running. **Known gap:** a class driver behind a second controller
|
||||
// cannot reach it — the transfer protocol has no controller field for
|
||||
// `target`, and the fix is either one process multiplexing every controller
|
||||
// or the spawner wiring the child's channel (P5), not a second name.
|
||||
if (!channel.bindPatiently("usb-transfer", endpoint))
|
||||
_ = logging.write("/system/drivers/usb-xhci-bus: /protocol/usb-transfer is another controller's; serving mine unnamed\n");
|
||||
|
||||
// **The handshake comes first, because it is where the device arrives.** This
|
||||
// driver used to claim `controller_id` here — first-come-first-served, so the
|
||||
// manager's matching was advisory and any process could have claimed it by
|
||||
// passing the same integer. Now the manager holds the controller and transfers
|
||||
// it in `onHello`, so by the time this call returns the device is ours and
|
||||
// nothing else could have taken it (docs/os-development/device-authority.md).
|
||||
// **The handshake comes first, because it is where everything moves.** The
|
||||
// manager holds the controller and transfers it in `onHello`, so by the time
|
||||
// this call returns the device is ours and nothing else could have taken it
|
||||
// (docs/os-development/device-authority.md). And the serving endpoint rides
|
||||
// up with the same call: one machine can carry several xHCI controllers, one
|
||||
// process per controller, so the transfer contract is never a registry name
|
||||
// — `/protocol` holds no instances, deliberately. Class drivers reach *this*
|
||||
// controller because the manager answers their hellos with this endpoint,
|
||||
// routed by lineage (communication.md "Establishment: two planes"). The bind
|
||||
// race this replaces left every device behind a losing controller
|
||||
// unreachable — a real machine's mouse, "could not open device 50".
|
||||
//
|
||||
// `hello` is synchronous, so the transfer has completed before the reply lands —
|
||||
// there is no window between being told yes and holding the thing.
|
||||
//
|
||||
// Keep the handle: the tick's hot-plug dispatch reports through it.
|
||||
const handle = device_manager.hello(.bus, controller_id) orelse {
|
||||
const exchanged = device_manager.helloExchange(.bus, controller_id, endpoint, false) orelse {
|
||||
std.log.warn("no hello with the device manager; controller {d} not delegated", .{controller_id});
|
||||
return false;
|
||||
};
|
||||
manager_handle = handle;
|
||||
manager_handle = exchanged.manager;
|
||||
|
||||
// Fetch our own descriptor back for the controller's resources.
|
||||
const buffer = memory.allocator().alloc(device.DeviceDescriptor, 64) catch {
|
||||
@@ -259,7 +247,7 @@ fn initialise(endpoint: ipc.Handle) bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
scanPorts(handle);
|
||||
scanPorts(exchanged.manager);
|
||||
|
||||
// Arm the timer: in polling mode it drains the event ring; in MSI mode it is the
|
||||
// slower port-reconcile/safety-net tick. Re-armed on each tick in onNotification.
|
||||
|
||||
Reference in New Issue
Block a user