diff --git a/packaging/windows/drivers/wdk-iddcx/src/lib.rs b/packaging/windows/drivers/wdk-iddcx/src/lib.rs index 00f9eb0..0c2cff4 100644 --- a/packaging/windows/drivers/wdk-iddcx/src/lib.rs +++ b/packaging/windows/drivers/wdk-iddcx/src/lib.rs @@ -1,11 +1,152 @@ //! Safe-ish typed wrappers over the wdk-sys IddCx DDIs, dispatched through the `IddFunctions` table //! (indexed by `_IDDFUNCENUM::TableIndex`, with `IddDriverGlobals` as the implicit first arg) — //! the same model wdk-sys uses for WDF. Graduates the proven dispatch from `wdk-probe/src/iddcx_rt.rs` -//! (M1 step 1) into the crate the pf-vdisplay driver builds on. +//! (M1 step 1) and adds the full DDI set the pf-vdisplay driver needs (M1 step 2 / §14). //! -//! STEP 0: re-export the wdk-sys `iddcx` module so the crate + the `iddcx` feature resolve in the -//! workspace. STEP 1 adds the 11 typed DDI wrappers (one fixed `(_IDDFUNCENUM index, PFN_*)` pair per -//! site) + `is_nt_error`/`other_is_error` + the inbound `PFN_IDD_CX_*` callback-type re-exports. +//! Each DDI pins its `(_IDDFUNCENUM index, PFN_* type)` pair in exactly ONE place (the macro invocation) +//! — a wrong pairing is the only way the table dispatch can be UB, so it must never be expressed twice +//! (port-plan unsafe_hotspot #1). All wrappers return the raw `NTSTATUS`; the caller classifies it with +//! [`nt_success`] (or, for the HRESULT-shaped SwapChain DDIs, treats a nonzero as the documented retry +//! code — handled at the call site in STEP 5). #![no_std] +#![allow(non_snake_case, clippy::missing_safety_doc)] pub use wdk_sys::iddcx; + +use wdk_sys::{NTSTATUS, PWDFDEVICE_INIT, WDFDEVICE}; + +/// `NT_SUCCESS` — IddCx DDIs that return a true NTSTATUS are errors iff negative. +#[inline] +#[must_use] +pub const fn nt_success(status: NTSTATUS) -> bool { + status >= 0 +} + +/// Read the IddCx DDI at `index` from the stub-provided `IddFunctions` table and reinterpret it as the +/// concrete `PFN_*` type. `IddFunctions` is a flexible array (`[PFN_IDD_CX; 0]`) populated by IddCxStub +/// once the driver is loaded. +/// +/// # Safety +/// `index`/`T` must be the matching `_IDDFUNCENUM` index and `PFN_*` type for the same DDI, and the +/// table must be populated (true after the driver is loaded by the IddCx runtime). +#[inline] +unsafe fn ddi(index: i32) -> T { + let table = (&raw const iddcx::IddFunctions).cast::(); + // SAFETY: `index` is a valid IddCx table slot; the slot holds a `PFN_*` whose layout is `T`. + let slot = unsafe { table.add(index as usize) }; + unsafe { slot.cast::().read() } +} + +/// The IddCx driver globals (set by `IddCxStub`), passed as the implicit first arg to every DDI. +/// +/// # Safety +/// Only valid once the driver is loaded by the IddCx runtime. +#[inline] +unsafe fn globals() -> iddcx::PIDD_DRIVER_GLOBALS { + // SAFETY: we only read the pointer value of the stub-provided global. + unsafe { (&raw const iddcx::IddDriverGlobals).read() } +} + +/// Generate a typed outbound-DDI wrapper. Body is identical for every DDI (table lookup → call with the +/// globals); only the `(index, PFN_*, args)` triple varies, and it appears here exactly once. +macro_rules! iddcx_ddi { + ( + $(#[$meta:meta])* + $name:ident ( $( $arg:ident : $aty:ty ),* $(,)? ) @ $idx:ident as $pfn:ident + ) => { + $(#[$meta])* + /// + /// # Safety + /// Call only after the driver is loaded by IddCx; pointers must satisfy the IddCx contract. + #[inline] + pub unsafe fn $name( $( $arg: $aty ),* ) -> NTSTATUS { + let f: iddcx::$pfn = unsafe { ddi(iddcx::_IDDFUNCENUM::$idx) }; + let g = unsafe { globals() }; + // SAFETY: dispatching a populated DDI with the stub globals and caller-valid args. + unsafe { (f.unwrap())(g, $( $arg ),* ) } + } + }; + // void-returning variant (e.g. IddCxAdapterSetRenderAdapter): the DDI's PFN returns `()`. + ( + $(#[$meta:meta])* + $name:ident ( $( $arg:ident : $aty:ty ),* $(,)? ) @ $idx:ident as $pfn:ident -> () + ) => { + $(#[$meta])* + /// + /// # Safety + /// Call only after the driver is loaded by IddCx; pointers must satisfy the IddCx contract. + #[inline] + pub unsafe fn $name( $( $arg: $aty ),* ) { + let f: iddcx::$pfn = unsafe { ddi(iddcx::_IDDFUNCENUM::$idx) }; + let g = unsafe { globals() }; + // SAFETY: dispatching a populated DDI with the stub globals and caller-valid args. + unsafe { (f.unwrap())(g, $( $arg ),* ) } + } + }; +} + +iddcx_ddi!( + /// Configure a `WDFDEVICE_INIT` for IddCx (call before `WdfDeviceCreate`). + IddCxDeviceInitConfig(device_init: PWDFDEVICE_INIT, config: *const iddcx::IDD_CX_CLIENT_CONFIG) + @ IddCxDeviceInitConfigTableIndex as PFN_IDDCXDEVICEINITCONFIG +); +iddcx_ddi!( + /// Finish IddCx device init (call after `WdfDeviceCreate`). + IddCxDeviceInitialize(device: WDFDEVICE) + @ IddCxDeviceInitializeTableIndex as PFN_IDDCXDEVICEINITIALIZE +); +iddcx_ddi!( + /// Create the WDDM adapter asynchronously; `out.AdapterObject` is the `IDDCX_ADAPTER`. + IddCxAdapterInitAsync( + in_args: *const iddcx::IDARG_IN_ADAPTER_INIT, + out_args: *mut iddcx::IDARG_OUT_ADAPTER_INIT, + ) @ IddCxAdapterInitAsyncTableIndex as PFN_IDDCXADAPTERINITASYNC +); +iddcx_ddi!( + /// Create a monitor on the adapter; `out.MonitorObject` is the `IDDCX_MONITOR`. + IddCxMonitorCreate( + adapter: iddcx::IDDCX_ADAPTER, + in_args: *const iddcx::IDARG_IN_MONITORCREATE, + out_args: *mut iddcx::IDARG_OUT_MONITORCREATE, + ) @ IddCxMonitorCreateTableIndex as PFN_IDDCXMONITORCREATE +); +iddcx_ddi!( + /// Announce monitor arrival; `out.OsTargetId`/`out.OsAdapterLuid` come back. + IddCxMonitorArrival( + monitor: iddcx::IDDCX_MONITOR, + out_args: *mut iddcx::IDARG_OUT_MONITORARRIVAL, + ) @ IddCxMonitorArrivalTableIndex as PFN_IDDCXMONITORARRIVAL +); +iddcx_ddi!( + /// Depart a monitor (host disconnect / session teardown). + IddCxMonitorDeparture(monitor: iddcx::IDDCX_MONITOR) + @ IddCxMonitorDepartureTableIndex as PFN_IDDCXMONITORDEPARTURE +); +iddcx_ddi!( + /// Set the preferred render adapter (LUID) for the virtual adapter. + IddCxAdapterSetRenderAdapter( + adapter: iddcx::IDDCX_ADAPTER, + in_args: *const iddcx::IDARG_IN_ADAPTERSETRENDERADAPTER, + ) @ IddCxAdapterSetRenderAdapterTableIndex as PFN_IDDCXADAPTERSETRENDERADAPTER -> () +); +iddcx_ddi!( + /// Bind a D3D device to an assigned swap-chain. HRESULT-shaped (0x887A0026 → retry on monitor flap). + IddCxSwapChainSetDevice( + swap_chain: iddcx::IDDCX_SWAPCHAIN, + in_args: *const iddcx::IDARG_IN_SWAPCHAINSETDEVICE, + ) @ IddCxSwapChainSetDeviceTableIndex as PFN_IDDCXSWAPCHAINSETDEVICE +); +iddcx_ddi!( + /// Release the previous frame and acquire the next (HDR/FP16 variant). HRESULT-shaped; E_PENDING + /// (0x8000000A) means wait on the surface-available event. + IddCxSwapChainReleaseAndAcquireBuffer2( + swap_chain: iddcx::IDDCX_SWAPCHAIN, + in_args: *mut iddcx::IDARG_IN_RELEASEANDACQUIREBUFFER2, + out_args: *mut iddcx::IDARG_OUT_RELEASEANDACQUIREBUFFER2, + ) @ IddCxSwapChainReleaseAndAcquireBuffer2TableIndex as PFN_IDDCXSWAPCHAINRELEASEANDACQUIREBUFFER2 +); +iddcx_ddi!( + /// Signal that the acquired frame has been processed. HRESULT-shaped. + IddCxSwapChainFinishedProcessingFrame(swap_chain: iddcx::IDDCX_SWAPCHAIN) + @ IddCxSwapChainFinishedProcessingFrameTableIndex as PFN_IDDCXSWAPCHAINFINISHEDPROCESSINGFRAME +);