From ad2717402763ade7ac457d18d3b19955aa12c29a Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Wed, 24 Jun 2026 16:28:10 +0000 Subject: [PATCH] =?UTF-8?q?feat(windows-drivers):=20STEP=203=20=E2=80=94?= =?UTF-8?q?=20IddCx=20adapter=20init=20(deferred=20D0,=20FP16=20caps)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit adapter.rs: init_adapter(device) builds IDDCX_ADAPTER_CAPS (CAN_PROCESS_FP16, MaxMonitorsSupported=16, endpoint diagnostics with wstr! PCWSTR names) + IDARG_IN_ADAPTER_INIT and calls IddCxAdapterInitAsync; EvtDeviceD0Entry triggers it (idempotent), EvtIddCxAdapterInitFinished stashes the adapter in a OnceLock for later DDIs. zeroed()+named-field construction dodges the Default-derive + field-order questions. Compiles + links clean on the box (pf_vdisplay.dll 268KB). CI gate = compile+link; the on-glass load/enumerate gate needs the box + an INF + SwDeviceCreate (next). Co-Authored-By: Claude Opus 4.8 (1M context) --- .../drivers/pf-vdisplay/src/adapter.rs | 76 +++++++++++++++++++ .../drivers/pf-vdisplay/src/callbacks.rs | 14 ++-- .../windows/drivers/pf-vdisplay/src/lib.rs | 1 + 3 files changed, 85 insertions(+), 6 deletions(-) create mode 100644 packaging/windows/drivers/pf-vdisplay/src/adapter.rs diff --git a/packaging/windows/drivers/pf-vdisplay/src/adapter.rs b/packaging/windows/drivers/pf-vdisplay/src/adapter.rs new file mode 100644 index 0000000..58e7285 --- /dev/null +++ b/packaging/windows/drivers/pf-vdisplay/src/adapter.rs @@ -0,0 +1,76 @@ +//! IddCx adapter bring-up. Adapter creation is DEFERRED to the first `EvtDeviceD0Entry` (the adapter +//! object is only valid after D0), and is ASYNC: `init_adapter` builds the caps and calls +//! `IddCxAdapterInitAsync`; the adapter object arrives later via `EvtIddCxAdapterInitFinished` +//! (`adapter_init_finished` → [`set_adapter`]). FP16 caps + the obligated `*2`/gamma/hdr callbacks (in +//! `callbacks.rs`) together enable HDR. STEP 3. + +use std::sync::OnceLock; + +use wdk_sys::{iddcx, NTSTATUS, WDFDEVICE}; + +use crate::STATUS_SUCCESS; + +/// A static, null-terminated UTF-16 string pointer (ASCII only) — wdk-sys has no `windows` `w!`. +macro_rules! wstr { + ($s:literal) => {{ + const N: usize = $s.len() + 1; + const W: [u16; N] = { + let b = $s.as_bytes(); + let mut w = [0u16; N]; + let mut i = 0; + while i < b.len() { + w[i] = b[i] as u16; + i += 1; + } + w + }; + W.as_ptr() + }}; +} + +/// The IddCx adapter handle, stashed for later DDIs (e.g. `SET_RENDER_ADAPTER`, STEP 4). +struct SendAdapter(iddcx::IDDCX_ADAPTER); +// SAFETY: an opaque IddCx handle, used only as an argument to IddCx DDIs (themselves the synchronisation +// point) — never dereferenced in Rust. Storing it across threads in a OnceLock is sound. +unsafe impl Send for SendAdapter {} +unsafe impl Sync for SendAdapter {} + +static ADAPTER: OnceLock = OnceLock::new(); + +/// Build the adapter caps (FP16/HDR-capable) and kick off the async adapter creation. Called from +/// `EvtDeviceD0Entry`; idempotent across re-entrant D0 transitions. +pub fn init_adapter(device: WDFDEVICE) -> NTSTATUS { + if ADAPTER.get().is_some() { + return STATUS_SUCCESS; + } + + // Endpoint diagnostics (telemetry only — not used for OS runtime decisions). `pEndPointModelName` + // must be a non-empty string; the rest are optional. GammaSupport stays NONE (zeroed). + let mut diag: iddcx::IDDCX_ENDPOINT_DIAGNOSTIC_INFO = unsafe { core::mem::zeroed() }; + diag.Size = core::mem::size_of::() as u32; + diag.TransmissionType = iddcx::IDDCX_TRANSMISSION_TYPE::IDDCX_TRANSMISSION_TYPE_WIRED_OTHER; + diag.pEndPointFriendlyName = wstr!("punktfunk Virtual Display Adapter"); + diag.pEndPointManufacturerName = wstr!("punktfunk"); + diag.pEndPointModelName = wstr!("Virtual Display"); + + let mut caps: iddcx::IDDCX_ADAPTER_CAPS = unsafe { core::mem::zeroed() }; + caps.Size = core::mem::size_of::() as u32; + caps.Flags = iddcx::IDDCX_ADAPTER_FLAGS::IDDCX_ADAPTER_FLAGS_CAN_PROCESS_FP16; + caps.MaxMonitorsSupported = 16; + caps.EndPointDiagnostics = diag; + + let init = iddcx::IDARG_IN_ADAPTER_INIT { + WdfDevice: device, + pCaps: &raw mut caps, + ObjectAttributes: core::ptr::null_mut(), + }; + let mut out: iddcx::IDARG_OUT_ADAPTER_INIT = unsafe { core::mem::zeroed() }; + // SAFETY: `init`/`out` are valid local storage; IddCxAdapterInitAsync reads the caps synchronously + // (the adapter object itself is delivered later via adapter_init_finished). Called once per device. + unsafe { wdk_iddcx::IddCxAdapterInitAsync(&init, &mut out) } +} + +/// Stash the adapter object delivered by `EvtIddCxAdapterInitFinished` (STEP 4 reads it). +pub fn set_adapter(adapter: iddcx::IDDCX_ADAPTER) { + let _ = ADAPTER.set(SendAdapter(adapter)); +} diff --git a/packaging/windows/drivers/pf-vdisplay/src/callbacks.rs b/packaging/windows/drivers/pf-vdisplay/src/callbacks.rs index 655de42..1f76f6f 100644 --- a/packaging/windows/drivers/pf-vdisplay/src/callbacks.rs +++ b/packaging/windows/drivers/pf-vdisplay/src/callbacks.rs @@ -11,20 +11,22 @@ use wdk_sys::{call_unsafe_wdf_function_binding, NTSTATUS, WDFDEVICE, WDFREQUEST} use crate::{STATUS_NOT_IMPLEMENTED, STATUS_SUCCESS}; -/// PnP `EvtDeviceD0Entry` (not an IddCx config callback). STEP 3 calls `DeviceContext::init_adapter` -/// here (adapter creation is deferred to first D0, not driver_add). +/// PnP `EvtDeviceD0Entry` (not an IddCx config callback). Adapter creation is deferred to the first D0 +/// (the adapter object is only valid after D0), not driver_add. pub unsafe extern "C" fn device_d0_entry( - _device: WDFDEVICE, + device: WDFDEVICE, _previous_state: wdk_sys::WDF_POWER_DEVICE_STATE, ) -> NTSTATUS { - STATUS_SUCCESS + crate::adapter::init_adapter(device) } -/// Async completion of `IddCxAdapterInitAsync`. STEP 3: stash the adapter + start the watchdog. +/// Async completion of `IddCxAdapterInitAsync`: stash the adapter for later DDIs. STEP 4 also starts the +/// watchdog here. pub unsafe extern "C" fn adapter_init_finished( - _adapter: iddcx::IDDCX_ADAPTER, + adapter: iddcx::IDDCX_ADAPTER, _p_in: *const iddcx::IDARG_IN_ADAPTER_INIT_FINISHED, ) -> NTSTATUS { + crate::adapter::set_adapter(adapter); STATUS_SUCCESS } diff --git a/packaging/windows/drivers/pf-vdisplay/src/lib.rs b/packaging/windows/drivers/pf-vdisplay/src/lib.rs index b0e1e6a..e2d2c38 100644 --- a/packaging/windows/drivers/pf-vdisplay/src/lib.rs +++ b/packaging/windows/drivers/pf-vdisplay/src/lib.rs @@ -10,6 +10,7 @@ #![allow(non_snake_case, clippy::missing_safety_doc)] +mod adapter; mod callbacks; #[allow(dead_code)] // salvaged verbatim; wired into the mode callbacks in STEP 4 mod edid;