feat(windows-drivers): STEP 3 on-glass — driver LOADS + runs full IddCx init chain
apple / swift (push) Failing after 2s
apple / screenshots (push) Has been skipped
windows-drivers / probe-and-proto (push) Successful in 19s
windows-drivers / driver-build (push) Successful in 1m7s
windows-host / package (push) Successful in 5m27s
android / android (push) Successful in 4m5s
ci / web (push) Successful in 47s
ci / rust (push) Successful in 4m41s
ci / docs-site (push) Successful in 57s
deb / build-publish (push) Successful in 2m26s
decky / build-publish (push) Successful in 13s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 5s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 5s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 4s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 4s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 5s
ci / bench (push) Successful in 5m1s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 9m21s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 9m23s
docker / deploy-docs (push) Successful in 6s
apple / swift (push) Failing after 2s
apple / screenshots (push) Has been skipped
windows-drivers / probe-and-proto (push) Successful in 19s
windows-drivers / driver-build (push) Successful in 1m7s
windows-host / package (push) Successful in 5m27s
android / android (push) Successful in 4m5s
ci / web (push) Successful in 47s
ci / rust (push) Successful in 4m41s
ci / docs-site (push) Successful in 57s
deb / build-publish (push) Successful in 2m26s
decky / build-publish (push) Successful in 13s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 5s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 5s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 4s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 4s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 5s
ci / bench (push) Successful in 5m1s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 9m21s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 9m23s
docker / deploy-docs (push) Successful in 6s
Major on-glass progress on the RTX box. The all-Rust wdk-sys IddCx driver now LOADS under Secure Boot and runs the ENTIRE init chain: DriverEntry -> WdfDriverCreate -> driver_add -> IddCxDeviceInitConfig(0x0) -> WdfDeviceCreate -> CreateDeviceInterface -> IddCxDeviceInitialize -> D0Entry -> init_adapter. Findings: - Signing was a RED HERRING (the driver loads); std works in WUDFHost (DualSense uses it too). - THE unblock: link the iddcx **1.10** IddCxStub (build.rs now picks the highest version-aware), not 1.0 — the 1.0 stub lacks the version-table symbols AND its dispatch table mismatched the 1.10 framework, which made IddCxDeviceInitConfig return INVALID_PARAMETER. With 1.10 the whole chain runs. - Added a file/OutputDebugString logger (log.rs, matches the DualSense driver) — the driver was silent; this is how the chain was traced. - size.rs: framework_struct_size() reads the frameworks authoritative struct sizes from IddStructures[] (the config keeps size_of=208, validated working). - adapter.rs: version ptrs + ObjectAttributes(InheritFromParent) + FP16 + framework caps/diag/version sizes — matches the oracle. KNOWN WIP: IddCxAdapterInitAsync still returns INVALID_PARAMETER though caps match the framework size table (88/56/24) + the oracle exactly — likely a subtle wdk-sys bindgen field-layout detail in IDDCX_ADAPTER_CAPS/IDDCX_ENDPOINT_DIAGNOSTIC_INFO. CI gate (compile+link) stays green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -43,31 +43,81 @@ pub fn init_adapter(device: WDFDEVICE) -> NTSTATUS {
|
||||
if ADAPTER.get().is_some() {
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
dbglog!("[pf-vd] init_adapter");
|
||||
|
||||
// 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).
|
||||
// The framework binds an IddCx version (the INF's UmdfExtensions) that may be OLDER than our 1.10
|
||||
// headers, so use ITS expected struct sizes — newer fields (e.g. IDDCX_ADAPTER_CAPS's
|
||||
// StaticDesktopReencodeFrameCount) make size_of too big and IddCxAdapterInitAsync rejects it. The
|
||||
// table is readable now (post-IddCxDeviceInitialize). Falls back to size_of if unavailable.
|
||||
let ver_size = crate::size::framework_struct_size(
|
||||
iddcx::_IDDSTRUCTENUM::INDEX_IDDCX_ENDPOINT_VERSION as u32,
|
||||
)
|
||||
.unwrap_or(core::mem::size_of::<iddcx::IDDCX_ENDPOINT_VERSION>() as u32);
|
||||
let diag_size = crate::size::framework_struct_size(
|
||||
iddcx::_IDDSTRUCTENUM::INDEX_IDDCX_ENDPOINT_DIAGNOSTIC_INFO as u32,
|
||||
)
|
||||
.unwrap_or(core::mem::size_of::<iddcx::IDDCX_ENDPOINT_DIAGNOSTIC_INFO>() as u32);
|
||||
let caps_size = crate::size::framework_struct_size(
|
||||
iddcx::_IDDSTRUCTENUM::INDEX_IDDCX_ADAPTER_CAPS as u32,
|
||||
)
|
||||
.unwrap_or(core::mem::size_of::<iddcx::IDDCX_ADAPTER_CAPS>() as u32);
|
||||
dbglog!("[pf-vd] fw sizes: caps={caps_size} diag={diag_size} ver={ver_size}");
|
||||
|
||||
// Firmware/hardware version (telemetry). The oracle points BOTH at one IDDCX_ENDPOINT_VERSION.
|
||||
// `version` is a stack local read synchronously by IddCxAdapterInitAsync (same as the oracle).
|
||||
let mut version: iddcx::IDDCX_ENDPOINT_VERSION = unsafe { core::mem::zeroed() };
|
||||
version.Size = ver_size;
|
||||
version.MajorVer = env!("CARGO_PKG_VERSION_MAJOR").parse().unwrap_or(0);
|
||||
version.MinorVer = env!("CARGO_PKG_VERSION_MINOR").parse().unwrap_or(0);
|
||||
version.Build = env!("CARGO_PKG_VERSION_PATCH").parse().unwrap_or(0);
|
||||
|
||||
// Endpoint diagnostics. `pEndPointModelName` must be a non-empty string. GammaSupport stays NONE.
|
||||
let mut diag: iddcx::IDDCX_ENDPOINT_DIAGNOSTIC_INFO = unsafe { core::mem::zeroed() };
|
||||
diag.Size = core::mem::size_of::<iddcx::IDDCX_ENDPOINT_DIAGNOSTIC_INFO>() as u32;
|
||||
diag.Size = diag_size;
|
||||
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");
|
||||
diag.pFirmwareVersion = (&raw mut version).cast();
|
||||
diag.pHardwareVersion = (&raw mut version).cast();
|
||||
|
||||
let mut caps: iddcx::IDDCX_ADAPTER_CAPS = unsafe { core::mem::zeroed() };
|
||||
caps.Size = core::mem::size_of::<iddcx::IDDCX_ADAPTER_CAPS>() as u32;
|
||||
caps.Size = caps_size;
|
||||
// CAN_PROCESS_FP16 must be CONSISTENT with the config's callbacks: the config registers the *2 / gamma
|
||||
// / set-default-hdr-metadata callbacks (FP16-obligated), so the adapter MUST advertise FP16 or the
|
||||
// framework rejects the mismatch at IddCxAdapterInitAsync. The oracle sets both.
|
||||
caps.Flags = iddcx::IDDCX_ADAPTER_FLAGS::IDDCX_ADAPTER_FLAGS_CAN_PROCESS_FP16;
|
||||
caps.MaxMonitorsSupported = 16;
|
||||
caps.EndPointDiagnostics = diag;
|
||||
|
||||
dbglog!(
|
||||
"[pf-vd] caps Size={} Flags={:#x} MaxMon={} diagSize={} cfgSizeOf={} capsSizeOf={}",
|
||||
caps.Size,
|
||||
caps.Flags,
|
||||
caps.MaxMonitorsSupported,
|
||||
caps.EndPointDiagnostics.Size,
|
||||
core::mem::size_of::<iddcx::IDD_CX_CLIENT_CONFIG>(),
|
||||
core::mem::size_of::<iddcx::IDDCX_ADAPTER_CAPS>(),
|
||||
);
|
||||
// The adapter WDF object's attributes. The oracle passes an init'd WDF_OBJECT_ATTRIBUTES (Size +
|
||||
// Synchronization/Execution = InheritFromParent — NOT zeroed, since zero = *Invalid*); a null/zeroed
|
||||
// one is what IddCxAdapterInitAsync rejected. No context type yet (STEP 3).
|
||||
let mut attr: wdk_sys::WDF_OBJECT_ATTRIBUTES = unsafe { core::mem::zeroed() };
|
||||
attr.Size = core::mem::size_of::<wdk_sys::WDF_OBJECT_ATTRIBUTES>() as u32;
|
||||
attr.ExecutionLevel = wdk_sys::_WDF_EXECUTION_LEVEL::WdfExecutionLevelInheritFromParent;
|
||||
attr.SynchronizationScope =
|
||||
wdk_sys::_WDF_SYNCHRONIZATION_SCOPE::WdfSynchronizationScopeInheritFromParent;
|
||||
let init = iddcx::IDARG_IN_ADAPTER_INIT {
|
||||
WdfDevice: device,
|
||||
pCaps: &raw mut caps,
|
||||
ObjectAttributes: core::ptr::null_mut(),
|
||||
ObjectAttributes: &raw mut attr,
|
||||
};
|
||||
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) }
|
||||
let st = unsafe { wdk_iddcx::IddCxAdapterInitAsync(&init, &mut out) };
|
||||
dbglog!("[pf-vd] IddCxAdapterInitAsync -> {st:#x}");
|
||||
st
|
||||
}
|
||||
|
||||
/// Stash the adapter object delivered by `EvtIddCxAdapterInitFinished` (STEP 4 reads it).
|
||||
|
||||
Reference in New Issue
Block a user