diff --git a/packaging/windows/build-gamepad-drivers.ps1 b/packaging/windows/build-gamepad-drivers.ps1 new file mode 100644 index 0000000..5317cba --- /dev/null +++ b/packaging/windows/build-gamepad-drivers.ps1 @@ -0,0 +1,130 @@ +<# +.SYNOPSIS + Build + sign the punktfunk virtual-gamepad UMDF drivers (pf-dualsense = DualSense/DualShock 4, pf-xusb = + Xbox 360 / XInput) FROM SOURCE, in CI, and stage them for the host installer. The gamepad analogue of + build-pf-vdisplay.ps1 - replaces the checked-in prebuilt binaries (packaging/windows/gamepad-drivers/) + so the .dll/.inf/.cat stay in lockstep with the source and never go stale. + +.DESCRIPTION + Both drivers are members of the in-tree drivers workspace (packaging/windows/drivers/), so one + `cargo build --release` builds the whole workspace (this shares wdk-sys/wdk-build + the bindgen pin with + pf-vdisplay). Then, per driver: CLEAR the FORCE_INTEGRITY PE bit, sign the .dll, stampinf a DriverVer + into the INF; then Inf2Cat both catalogs and sign them. Both drivers share ONE self-signed cert (or a + supplied DRIVER_CERT secret) + ONE exported .cer, matching the vendored gamepad-drivers/ layout that + install-gamepad-drivers.ps1 expects. + + Output (-Out): pf_dualsense.{dll,inf,cat} + pf_xusb.{dll,inf,cat} + punktfunk-driver.cer. + +.EXAMPLE + pwsh -File build-gamepad-drivers.ps1 -Out C:\t\gamepad +#> +[CmdletBinding()] +param( + [string]$DriversDir = (Join-Path $PSScriptRoot 'drivers'), + [Parameter(Mandatory = $true)][string]$Out, + [string]$DriverVer, + [string]$CertPfxB64 = $env:DRIVER_CERT_PFX_B64, + [string]$CertPassword = $env:DRIVER_CERT_PASSWORD, + [switch]$SkipBuild +) +$ErrorActionPreference = 'Stop' +$ProgressPreference = 'SilentlyContinue' +$PSNativeCommandUseErrorActionPreference = $false + +$DriversDir = (Resolve-Path $DriversDir).Path +$clear = Join-Path $PSScriptRoot 'clear-force-integrity.ps1' + +$drivers = @( + @{ crate = 'pf-dualsense'; dll = 'pf_dualsense.dll'; inx = 'pf-dualsense\pf_dualsense.inx'; inf = 'pf_dualsense.inf'; cat = 'pf_dualsense.cat' } + @{ crate = 'pf-xusb'; dll = 'pf_xusb.dll'; inx = 'pf-xusb\pf_xusb.inx'; inf = 'pf_xusb.inf'; cat = 'pf_xusb.cat' } +) +foreach ($d in $drivers) { + if (-not (Test-Path (Join-Path $DriversDir $d.inx))) { throw "no $($d.inx) under $DriversDir" } +} + +# --- WDK build env ---------------------------------------------------------------------------- +if (-not $env:Version_Number) { $env:Version_Number = '10.0.26100.0' } +if (-not $env:LIBCLANG_PATH -and (Test-Path 'C:\Program Files\LLVM\bin\libclang.dll')) { + $env:LIBCLANG_PATH = 'C:\Program Files\LLVM\bin' +} +# Build into the DEFAULT workspace target dir (not an external CARGO_TARGET_DIR) - wdk-build walks up +# from OUT_DIR for a Cargo.lock and doesn't support out-of-tree target dirs. See build-pf-vdisplay.ps1. +$rel = Join-Path $DriversDir 'target\x86_64-pc-windows-msvc\release' + +# --- 1. build (release) - one build covers the whole workspace -------------------------------- +if (-not $SkipBuild) { + Write-Host "==> cargo build --release (drivers workspace) in $DriversDir" + $prevTarget = $env:CARGO_TARGET_DIR + Remove-Item Env:\CARGO_TARGET_DIR -ErrorAction SilentlyContinue + Push-Location $DriversDir + & cargo build --release + $rc = $LASTEXITCODE + Pop-Location + if ($prevTarget) { $env:CARGO_TARGET_DIR = $prevTarget } else { Remove-Item Env:\CARGO_TARGET_DIR -ErrorAction SilentlyContinue } + if ($rc -ne 0) { throw "gamepad drivers cargo build failed ($rc)" } +} +foreach ($d in $drivers) { + if (-not (Test-Path (Join-Path $rel $d.dll))) { throw "driver not built: $(Join-Path $rel $d.dll)" } +} + +# --- 2. WDK sign tools ------------------------------------------------------------------------ +$kits = 'C:\Program Files (x86)\Windows Kits\10\bin' +function Find-Tool([string]$name, [string]$arch) { + (Get-ChildItem "$kits\*\$arch\$name" -ErrorAction SilentlyContinue | Sort-Object FullName | Select-Object -Last 1).FullName +} +$signtool = Find-Tool 'signtool.exe' 'x64' +$stampinf = Find-Tool 'stampinf.exe' 'x64' +$inf2cat = Find-Tool 'Inf2Cat.exe' 'x86' +foreach ($t in @($signtool, $stampinf, $inf2cat)) { + if (-not $t) { throw 'a WDK tool (signtool/stampinf/Inf2Cat) was not found - install the Windows 10/11 WDK.' } +} + +# --- 3. signing cert (supplied stable pfx OR fresh self-signed; shared by both drivers) ------- +$cleanupCert = $null +if ($CertPfxB64) { + Write-Host '==> signing with supplied driver cert (DRIVER_CERT_PFX_B64)' + $pfx = Join-Path (Split-Path -Parent $Out) 'driver-signing.pfx' + [IO.File]::WriteAllBytes($pfx, [Convert]::FromBase64String($CertPfxB64)) + $sec = if ($CertPassword) { ConvertTo-SecureString $CertPassword -AsPlainText -Force } else { $null } + $signArgs = @('/f', $pfx); if ($CertPassword) { $signArgs += @('/p', $CertPassword) } + $pubForCer = if ($sec) { Get-PfxCertificate -FilePath $pfx -Password $sec } else { Get-PfxCertificate -FilePath $pfx } +} +else { + Write-Host '==> no DRIVER_CERT_PFX_B64 -> generating a fresh self-signed driver cert (the installer trusts the bundled .cer at install time)' + $cleanupCert = New-SelfSignedCertificate -Type CodeSigningCert -Subject 'CN=punktfunk-driver' ` + -CertStoreLocation Cert:\CurrentUser\My -KeyExportPolicy Exportable -NotAfter (Get-Date).AddYears(10) + $signArgs = @('/sha1', $cleanupCert.Thumbprint) + $pubForCer = $cleanupCert +} + +# --- 4. stage + clear FORCE_INTEGRITY + sign dlls + stampinf infs ------------------------------ +if (Test-Path $Out) { Remove-Item $Out -Recurse -Force } +New-Item -ItemType Directory -Force -Path $Out | Out-Null +if (-not $DriverVer) { $now = Get-Date; $DriverVer = '9.9.{0}.{1}' -f $now.ToString('MMdd'), $now.ToString('HHmm') } + +foreach ($d in $drivers) { + $sDll = Join-Path $Out $d.dll + $sInf = Join-Path $Out $d.inf + Copy-Item (Join-Path $rel $d.dll) $sDll -Force + Copy-Item (Join-Path $DriversDir $d.inx) $sInf -Force # stampinf rewrites this copy in place + & powershell -NoProfile -ExecutionPolicy Bypass -File $clear -Path $sDll | Out-Null + & $signtool sign /fd SHA256 @signArgs $sDll | Out-Null + if ($LASTEXITCODE -ne 0) { throw "signtool sign ($($d.dll)) failed ($LASTEXITCODE)" } + & $stampinf -f $sInf -d '*' -a 'amd64' -u '2.15.0' -v $DriverVer | Out-Null +} + +# --- 5. Inf2Cat both catalogs (one pass over -Out), then sign each ----------------------------- +& $inf2cat /driver:$Out /os:10_X64 /uselocaltime | Out-Null +foreach ($d in $drivers) { + $sCat = Join-Path $Out $d.cat + if (-not (Test-Path $sCat)) { throw "Inf2Cat did not produce $sCat" } + & $signtool sign /fd SHA256 @signArgs $sCat | Out-Null + if ($LASTEXITCODE -ne 0) { throw "signtool sign ($($d.cat)) failed ($LASTEXITCODE)" } +} + +# --- 6. one shared public .cer ---------------------------------------------------------------- +Export-Certificate -Cert $pubForCer -FilePath (Join-Path $Out 'punktfunk-driver.cer') | Out-Null +if ($cleanupCert) { Remove-Item "Cert:\CurrentUser\My\$($cleanupCert.Thumbprint)" -Force -ErrorAction SilentlyContinue } + +Write-Host "==> built + signed gamepad drivers DriverVer=$DriverVer -> $Out" +Get-ChildItem $Out -File | ForEach-Object { " $($_.Name) ($($_.Length) bytes)" } diff --git a/packaging/windows/drivers/Cargo.lock b/packaging/windows/drivers/Cargo.lock index 1525c2c..f127f58 100644 --- a/packaging/windows/drivers/Cargo.lock +++ b/packaging/windows/drivers/Cargo.lock @@ -63,9 +63,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.102" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" [[package]] name = "bindgen" @@ -401,6 +401,15 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "pf-dualsense" +version = "0.0.1" +dependencies = [ + "wdk", + "wdk-build", + "wdk-sys", +] + [[package]] name = "pf-vdisplay" version = "0.0.1" @@ -414,6 +423,15 @@ dependencies = [ "windows", ] +[[package]] +name = "pf-xusb" +version = "0.0.1" +dependencies = [ + "wdk", + "wdk-build", + "wdk-sys", +] + [[package]] name = "pin-project-lite" version = "0.2.17" diff --git a/packaging/windows/drivers/Cargo.toml b/packaging/windows/drivers/Cargo.toml index f7dc964..2e39492 100644 --- a/packaging/windows/drivers/Cargo.toml +++ b/packaging/windows/drivers/Cargo.toml @@ -7,7 +7,7 @@ # crates/pf-driver-proto from the main tree. [workspace] resolver = "2" -members = ["wdk-probe", "wdk-iddcx", "pf-vdisplay"] +members = ["wdk-probe", "wdk-iddcx", "pf-vdisplay", "pf-dualsense", "pf-xusb"] [workspace.package] edition = "2024" diff --git a/packaging/windows/drivers/pf-dualsense/Cargo.toml b/packaging/windows/drivers/pf-dualsense/Cargo.toml new file mode 100644 index 0000000..6ff4161 --- /dev/null +++ b/packaging/windows/drivers/pf-dualsense/Cargo.toml @@ -0,0 +1,30 @@ +# pf-dualsense - punktfunk virtual DualSense (DS5) / DualShock 4 (DS4) UMDF2 HID minidriver. +# A member of the in-tree drivers workspace (shares the vendored wdk-sys/wdk-build with the bindgen pin +# + the crt-static .cargo/config), built from source per release like pf-vdisplay. +[package] +name = "pf-dualsense" +edition.workspace = true +version.workspace = true +license.workspace = true +publish = false +description = "punktfunk virtual DualSense / DualShock 4 UMDF2 HID minidriver" + +[package.metadata.wdk.driver-model] +driver-type = "UMDF" +umdf-version-major = 2 +target-umdf-version-minor = 31 + +[lib] +crate-type = ["cdylib"] + +[build-dependencies] +wdk-build.workspace = true + +[dependencies] +wdk.workspace = true +wdk-sys.workspace = true + +[features] +default = ["hid"] +hid = ["wdk-sys/hid"] +nightly = ["wdk-sys/nightly", "wdk/nightly"] diff --git a/packaging/windows/dualsense-driver/README.md b/packaging/windows/drivers/pf-dualsense/README.md similarity index 100% rename from packaging/windows/dualsense-driver/README.md rename to packaging/windows/drivers/pf-dualsense/README.md diff --git a/packaging/windows/dualsense-driver/build.rs b/packaging/windows/drivers/pf-dualsense/build.rs similarity index 100% rename from packaging/windows/dualsense-driver/build.rs rename to packaging/windows/drivers/pf-dualsense/build.rs diff --git a/packaging/windows/dualsense-driver/pf_dualsense.inx b/packaging/windows/drivers/pf-dualsense/pf_dualsense.inx similarity index 100% rename from packaging/windows/dualsense-driver/pf_dualsense.inx rename to packaging/windows/drivers/pf-dualsense/pf_dualsense.inx diff --git a/packaging/windows/dualsense-driver/src/lib.rs b/packaging/windows/drivers/pf-dualsense/src/lib.rs similarity index 100% rename from packaging/windows/dualsense-driver/src/lib.rs rename to packaging/windows/drivers/pf-dualsense/src/lib.rs diff --git a/packaging/windows/drivers/pf-xusb/Cargo.toml b/packaging/windows/drivers/pf-xusb/Cargo.toml new file mode 100644 index 0000000..a0e9e04 --- /dev/null +++ b/packaging/windows/drivers/pf-xusb/Cargo.toml @@ -0,0 +1,29 @@ +# pf-xusb - punktfunk virtual Xbox 360 XUSB companion (UMDF2, classic XInput). +# A member of the in-tree drivers workspace (shares the vendored wdk-sys/wdk-build with the bindgen pin +# + the crt-static .cargo/config), built from source per release like pf-vdisplay. +[package] +name = "pf-xusb" +edition.workspace = true +version.workspace = true +license.workspace = true +publish = false +description = "punktfunk virtual Xbox 360 XUSB companion (UMDF2 - classic XInput)" + +[package.metadata.wdk.driver-model] +driver-type = "UMDF" +umdf-version-major = 2 +target-umdf-version-minor = 31 + +[lib] +crate-type = ["cdylib"] + +[build-dependencies] +wdk-build.workspace = true + +[dependencies] +wdk.workspace = true +wdk-sys.workspace = true + +[features] +default = [] +nightly = ["wdk-sys/nightly", "wdk/nightly"] diff --git a/packaging/windows/xusb-driver/README.md b/packaging/windows/drivers/pf-xusb/README.md similarity index 100% rename from packaging/windows/xusb-driver/README.md rename to packaging/windows/drivers/pf-xusb/README.md diff --git a/packaging/windows/xusb-driver/build.rs b/packaging/windows/drivers/pf-xusb/build.rs similarity index 100% rename from packaging/windows/xusb-driver/build.rs rename to packaging/windows/drivers/pf-xusb/build.rs diff --git a/packaging/windows/xusb-driver/pf_xusb.inx b/packaging/windows/drivers/pf-xusb/pf_xusb.inx similarity index 100% rename from packaging/windows/xusb-driver/pf_xusb.inx rename to packaging/windows/drivers/pf-xusb/pf_xusb.inx diff --git a/packaging/windows/xusb-driver/src/lib.rs b/packaging/windows/drivers/pf-xusb/src/lib.rs similarity index 100% rename from packaging/windows/xusb-driver/src/lib.rs rename to packaging/windows/drivers/pf-xusb/src/lib.rs diff --git a/packaging/windows/dualsense-driver/Cargo.toml b/packaging/windows/dualsense-driver/Cargo.toml deleted file mode 100644 index c30e8d1..0000000 --- a/packaging/windows/dualsense-driver/Cargo.toml +++ /dev/null @@ -1,36 +0,0 @@ -[package] -edition = "2024" -name = "pf-dualsense" -version = "0.1.0" -publish = false -license = "MIT OR Apache-2.0" -description = "punktfunk virtual DualSense UMDF2 HID minidriver (M0 spike)" - -[package.metadata.wdk.driver-model] -driver-type = "UMDF" -target-umdf-version-minor = 31 -umdf-version-major = 2 - -[lib] -crate-type = ["cdylib"] - -[build-dependencies] -wdk-build.path = "../../crates/wdk-build" - -[dependencies] -wdk.path = "../../crates/wdk" -wdk-sys.path = "../../crates/wdk-sys" - -[features] -default = ["hid"] -hid = ["wdk-sys/hid"] -nightly = ["wdk-sys/nightly", "wdk/nightly"] - -[profile.dev] -lto = true - -[profile.release] -lto = true - -# Standalone package (not part of the windows-drivers-rs root workspace). -[workspace] diff --git a/packaging/windows/dualsense-driver/Makefile.toml b/packaging/windows/dualsense-driver/Makefile.toml deleted file mode 100644 index df1e841..0000000 --- a/packaging/windows/dualsense-driver/Makefile.toml +++ /dev/null @@ -1,4 +0,0 @@ -extend = [ - { path = "../../crates/wdk-build/rust-driver-makefile.toml" }, - { path = "../../crates/wdk-build/rust-driver-sample-makefile.toml" }, -] diff --git a/packaging/windows/pack-host-installer.ps1 b/packaging/windows/pack-host-installer.ps1 index 59e936c..c3e809a 100644 --- a/packaging/windows/pack-host-installer.ps1 +++ b/packaging/windows/pack-host-installer.ps1 @@ -157,21 +157,21 @@ if (-not $NoDriver) { } else { Write-Host "-NoDriver: building installer WITHOUT the bundled pf-vdisplay driver" } -# --- stage the punktfunk virtual-gamepad UMDF drivers (DualSense/DS4 + Xbox 360 XUSB) ---------- -# Vendored, pre-signed under packaging/windows/gamepad-drivers/ (like pf-vdisplay). Rebuild + re-vendor -# from packaging/windows/{dualsense,xusb}-driver/ when the driver source changes (see their READMEs). +# --- build (from source) + stage the punktfunk virtual-gamepad UMDF drivers -------------------- +# pf-dualsense (DualSense / DualShock 4) + pf-xusb (Xbox 360 / XInput) are members of the same drivers +# workspace as pf-vdisplay, built from source per release (build-gamepad-drivers.ps1) - same anti-stale +# reasoning as pf-vdisplay; the prior checked-in binaries under gamepad-drivers/ are retired. install- +# gamepad-drivers.ps1 adds each to the store (the host SwDeviceCreate's the per-session devnodes). if (-not $NoDriver) { - $gpVendor = Join-Path $here 'gamepad-drivers' - if (Test-Path (Join-Path $gpVendor 'pf_dualsense.inf')) { - $gpStage = Join-Path $OutDir 'gamepad' - if (Test-Path $gpStage) { Remove-Item -Recurse -Force $gpStage } - New-Item -ItemType Directory -Force -Path $gpStage | Out-Null - Copy-Item (Join-Path $gpVendor '*') $gpStage -Force - Copy-Item (Join-Path $here 'install-gamepad-drivers.ps1') (Join-Path $gpStage 'install-gamepad-drivers.ps1') -Force - $defines += "/DGamepadStageDir=$gpStage" - Write-Host "==> staged vendored gamepad UMDF drivers from $gpVendor" - } - else { Write-Warning "no vendored gamepad drivers under $gpVendor - installer built WITHOUT them" } + $gpBuilt = Join-Path $OutDir 'gamepad-built' + & (Join-Path $here 'build-gamepad-drivers.ps1') -Out $gpBuilt + $gpStage = Join-Path $OutDir 'gamepad' + if (Test-Path $gpStage) { Remove-Item -Recurse -Force $gpStage } + New-Item -ItemType Directory -Force -Path $gpStage | Out-Null + Copy-Item (Join-Path $gpBuilt '*') $gpStage -Force + Copy-Item (Join-Path $here 'install-gamepad-drivers.ps1') (Join-Path $gpStage 'install-gamepad-drivers.ps1') -Force + $defines += "/DGamepadStageDir=$gpStage" + Write-Host "==> built + staged gamepad UMDF drivers -> $gpStage" } # --- stage the FFmpeg shared DLLs (AMD/Intel AMF/QSV build) ------------------------------------ diff --git a/packaging/windows/xusb-driver/Cargo.toml b/packaging/windows/xusb-driver/Cargo.toml deleted file mode 100644 index 9ff4ee1..0000000 --- a/packaging/windows/xusb-driver/Cargo.toml +++ /dev/null @@ -1,35 +0,0 @@ -[package] -edition = "2024" -name = "pf-xusb" -version = "0.1.0" -publish = false -license = "MIT OR Apache-2.0" -description = "punktfunk virtual Xbox 360 XUSB companion (UMDF2 — classic XInput)" - -[package.metadata.wdk.driver-model] -driver-type = "UMDF" -target-umdf-version-minor = 31 -umdf-version-major = 2 - -[lib] -crate-type = ["cdylib"] - -[build-dependencies] -wdk-build.path = "../../crates/wdk-build" - -[dependencies] -wdk.path = "../../crates/wdk" -wdk-sys.path = "../../crates/wdk-sys" - -[features] -default = [] -nightly = ["wdk-sys/nightly", "wdk/nightly"] - -[profile.dev] -lto = true - -[profile.release] -lto = true - -# Standalone package (not part of the windows-drivers-rs root workspace). -[workspace] diff --git a/packaging/windows/xusb-driver/Makefile.toml b/packaging/windows/xusb-driver/Makefile.toml deleted file mode 100644 index be118bb..0000000 --- a/packaging/windows/xusb-driver/Makefile.toml +++ /dev/null @@ -1,4 +0,0 @@ -extend = [ - { path = "../../crates/wdk-build/rust-driver-makefile.toml" }, - { path = "../../crates/wdk-build/rust-driver-sample-makefile.toml" }, -]