feat(vdisplay/driver): DDC/CI against the virtual monitor fails fast

In exclusive topology the virtual display is the ONLY monitor on the desktop,
so monitor-control software (the Twinkle Tray / PowerToys PowerDisplay /
Monitorian class) aims its entire DDC traffic — brightness polls,
capabilities-string requests — at OUR monitor. There is no bus and no sink;
the only wrong answer is a slow one (a timeout-shaped failure occupies
win32k's physical-monitor path, serialized per monitor, for its full
duration). Register EvtIddCxMonitorI2CTransmit/Receive and answer every probe
with an immediate STATUS_NOT_SUPPORTED, making the virtual monitor the
cheapest thing on the "bus" to poll. (The receive DDI has no out-arg at the
callback — refusing synchronously refuses the whole transaction.) EDID needs
no equivalent: the OS serves descriptor queries from the blob supplied at
monitor creation without calling the driver.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-27 15:37:06 +02:00
co-authored by Claude Fable 5
parent 1945052e66
commit ab58fd2f0e
3 changed files with 33 additions and 0 deletions
@@ -375,3 +375,30 @@ pub unsafe extern "C" fn device_io_control(
// SAFETY: `request` is the framework-provided WDFREQUEST; `control::dispatch` completes it exactly once.
unsafe { crate::control::dispatch(request, ioctl_code) };
}
/// DDC/CI transmit toward the virtual monitor — refuse INSTANTLY (stall-immunity: DDC fail-fast).
///
/// In exclusive topology the virtual display is the ONLY monitor on the desktop, so
/// monitor-control software (the Twinkle Tray / PowerToys PowerDisplay / Monitorian class) aims
/// its entire DDC traffic — brightness polls, capabilities-string requests — at THIS monitor.
/// There is no bus and no sink here; the only wrong answer is a slow one (a timeout-shaped
/// failure occupies win32k's physical-monitor path, serialized per monitor, for its full
/// duration). Registering the pair pins every probe's cost to one immediate
/// `STATUS_NOT_SUPPORTED`. EDID needs no equivalent: the OS serves descriptor queries from the
/// blob supplied at monitor creation without calling the driver.
pub unsafe extern "C" fn monitor_i2c_transmit(
_monitor: iddcx::IDDCX_MONITOR,
_p_in: *const iddcx::IDARG_IN_I2C_TRANSMIT,
) -> NTSTATUS {
crate::STATUS_NOT_SUPPORTED
}
/// DDC/CI receive from the virtual monitor — same contract as [`monitor_i2c_transmit`]. (The
/// receive DDI has no out-arg at the callback — data would flow back through an async completion;
/// a synchronous failure return refuses the whole transaction, which is exactly the point.)
pub unsafe extern "C" fn monitor_i2c_receive(
_monitor: iddcx::IDDCX_MONITOR,
_p_in: *const iddcx::IDARG_IN_I2C_RECEIVE,
) -> NTSTATUS {
crate::STATUS_NOT_SUPPORTED
}
@@ -89,6 +89,11 @@ extern "C" fn driver_add(_driver: WDFDRIVER, mut init: PWDFDEVICE_INIT) -> NTSTA
cfg.EvtIddCxMonitorSetGammaRamp = Some(callbacks::set_gamma_ramp);
cfg.EvtIddCxMonitorAssignSwapChain = Some(callbacks::assign_swap_chain);
cfg.EvtIddCxMonitorUnassignSwapChain = Some(callbacks::unassign_swap_chain);
// DDC fail-fast (stall-immunity): monitor-control software polls the virtual monitor's DDC —
// in exclusive topology it is the ONLY monitor. These answer every probe with an immediate
// STATUS_NOT_SUPPORTED instead of whatever slow path an unregistered interface takes.
cfg.EvtIddCxMonitorI2CTransmit = Some(callbacks::monitor_i2c_transmit);
cfg.EvtIddCxMonitorI2CReceive = Some(callbacks::monitor_i2c_receive);
cfg.EvtIddCxDeviceIoControl = Some(callbacks::device_io_control);
// SAFETY: init is the framework device-init; cfg is fully populated + sized. (Links IddCxStub.)
@@ -35,6 +35,7 @@ use wdk_sys::NTSTATUS;
// NTSTATUS codes the driver returns (wdk-sys doesn't surface all of these as constants).
pub(crate) const STATUS_SUCCESS: NTSTATUS = 0;
pub(crate) const STATUS_NOT_IMPLEMENTED: NTSTATUS = 0xC000_0002u32 as NTSTATUS;
pub(crate) const STATUS_NOT_SUPPORTED: NTSTATUS = 0xC000_00BBu32 as NTSTATUS;
pub(crate) const STATUS_NOT_FOUND: NTSTATUS = 0xC000_0225u32 as NTSTATUS;
pub(crate) const STATUS_INVALID_PARAMETER: NTSTATUS = 0xC000_000Du32 as NTSTATUS;
pub(crate) const STATUS_BUFFER_TOO_SMALL: NTSTATUS = 0xC000_0023u32 as NTSTATUS;