//! The **device ABI**: the flat, `extern` device types that cross the system_call
//! boundary — what `device_enumerate` hands a user-space driver, what
//! `device_register` takes back. This is the devices sub-project's *public
//! interface*, exposed as its own `device-abi` module the same way the VFS server
//! exposes `vfs-protocol` — so both the kernel and user space depend on the contract
//! by name, and neither reaches into the other's files.
//!
//! It is also the **single source of truth** for `DeviceClass` and `ResourceKind`:
//! the kernel's rich, pointer-based device tree (system/devices/device-model.zig,
//! which user space must never import) re-exports these, so the enum that a driver
//! matches on and the enum the kernel classifies with are the *same* type — no
//! hand-kept "mirror in order" to drift. The core kernel↔user ABI is [[abi]]; the
//! loader↔kernel handoff is [[boot-handoff]].

/// A coarse classification of a device, independent of the describing firmware.
/// Kept small on purpose; refine as real drivers arrive. `enum(u32)` because the
/// `@intFromEnum` value crosses the system_call boundary in `DeviceDescriptor.class`.
pub const DeviceClass = enum(u32) {
    /// The synthetic root every discovered device hangs beneath.
    root,
    processor,
    interrupt_controller,
    timer,
    /// A PCI(e) host bridge — the root of a PCI segment (owns an ECAM window).
    pci_host_bridge,
    /// A single PCI function.
    pci_device,
    /// A device named in the ACPI namespace (from the DSDT/SSDT), carrying a
    /// hardware ID (`_HID`) and, where static, current resource settings (`_CRS`).
    acpi_device,
    /// The ACPI tables themselves, published as one node for the user-space acpi
    /// service (docs/discovery.md): memory resources over the AML blobs,
    /// a broad io_port grant for OperationRegion access, and the SCI interrupt.
    /// The one node whose claimant is trusted to run firmware bytecode.
    acpi_tables,
    /// One interface of a USB device, registered by the xHCI bus driver. It owns
    /// no MMIO — it is reached through its controller — so it carries no
    /// resources; the (class, subclass, protocol) triple that says what it is
    /// travels in the bus report's identity, not here.
    usb_device,
    /// A scanout framebuffer: a linear region of pixel memory the display service
    /// claims and maps. Unlike the other classes this one is not firmware-discovered
    /// — the kernel seeds it from the loader's [[boot-handoff]] framebuffer
    /// (`devices_broker.seedDisplay`). Its one `memory` resource is the framebuffer,
    /// flagged write-combining; the geometry to interpret it travels in
    /// `DeviceDescriptor.display`.
    display,
    unknown,
};

/// The kind of hardware resource a device occupies. `enum(u32)` for the same
/// boundary-crossing reason as `DeviceClass` (see `ResourceDescriptor.kind`).
pub const ResourceKind = enum(u32) {
    /// A memory-mapped I/O window: `start` is the physical base, `len` its size.
    memory,
    /// A legacy I/O-port range: `start` is the first port, `len` the count.
    io_port,
    /// An interrupt: `start` is the global system interrupt (GSI), `len` is 1.
    irq,
    /// A range of bus numbers owned by a bridge: `start`..`start+len`.
    bus_range,
};

/// One device resource, as handed to a user-space driver (flat, extern).
pub const ResourceDescriptor = extern struct {
    kind: u64, // a ResourceKind value
    start: u64,
    len: u64,
    /// A bitmask of `resource_flag_*` hints. Zero for a plain register/RAM window;
    /// the kernel reads it when it maps the resource. Defaulted so every existing
    /// literal (which never set flags) keeps compiling and lays out identically.
    flags: u64 = 0,
};

/// `ResourceDescriptor.flags`: map this `memory` resource **write-combining** rather
/// than strong-uncacheable — for a framebuffer, where batched bursts to pixel memory
/// are the whole point (an uncacheable framebuffer blit is glacial). See
/// `mmio_map` (system/kernel/process.zig) and `setupPat` (…/x86_64/paging.zig).
pub const resource_flag_write_combining: u64 = 1 << 0;

pub const maximum_device_resources = 8;

/// The byte order of a display's pixels — mirrors the loader's `PixelFormat`
/// ([[boot-handoff]]) with the same numeric values, but lives here so user space
/// (which must never import the loader↔kernel handoff) can name it. Only the two
/// linear 32-bpp layouts a console can paint into exist; see docs/gop.md.
pub const DisplayFormat = enum(u32) {
    rgbx = 0, // byte 0 = Red,  1 = Green, 2 = Blue,  3 = reserved
    bgrx = 1, // byte 0 = Blue, 1 = Green, 2 = Red,   3 = reserved
};

/// The geometry of a `display` device's framebuffer, carried in its descriptor so a
/// claiming driver knows how to interpret the pixel bytes its `memory` resource maps.
/// `pitch` is bytes per row (may exceed `width * 4`; see docs/framebuffer.md).
pub const DisplayInfo = extern struct {
    width: u32 = 0, // visible pixels per row
    height: u32 = 0, // visible rows
    pitch: u32 = 0, // bytes from one row's start to the next
    format: u32 = 0, // a DisplayFormat value
    refresh_hz: u32 = 0, // panel refresh rate from EDID (0 = unknown); see boot-handoff
};

/// `DeviceDescriptor.parent` for a device with no parent — a root of the device tree.
pub const no_parent: u64 = ~@as(u64, 0);

/// `DeviceDescriptor.pci_class` for a device that is not a PCI function. (Zero would be
/// ambiguous: 0x000000 is a real class code, "unclassified device".)
pub const no_pci_class: u64 = ~@as(u64, 0);

/// A device, as snapshotted for user space by `device_enumerate`. A driver scans
/// these to find the hardware it owns, claims it, and maps its MMIO.
///
/// `parent` makes the table a tree rather than a list, which is what a **bus driver**
/// needs: it claims the bus, finds the devices below it, and publishes any it
/// discovers itself with `device_register`. A registered child's resources must lie
/// within its parent's (the kernel enforces this) — that containment is what makes
/// delegation safe, since a device descriptor is otherwise a licence to map physical
/// memory.
pub const DeviceDescriptor = extern struct {
    id: u64,
    parent: u64, // a device id, or `no_parent`
    class: u64, // a DeviceClass value
    // The PCI class/subclass/prog-IF triple packed as 0xCCSSPP when this device is a PCI
    // function, or `no_pci_class` otherwise. This is how a manager tells *what* a
    // `pci_device` is (an xHCI controller, an AHCI controller) — decode the triple into
    // names with the pci-class module.
    pci_class: u64,
    hid_len: u64,
    resource_count: u64,
    hid: [8]u8,
    resources: [maximum_device_resources]ResourceDescriptor,
    // Framebuffer geometry, meaningful only when `class` is `DeviceClass.display`
    // (zeroed otherwise). Kept here — a class-specific field on the shared descriptor —
    // the same way `pci_class` is meaningful only for `pci_device` and `hid` only for
    // `acpi_device`.
    display: DisplayInfo = .{},
};
