mirror of
https://github.com/LizardByte/libvirtualhid.git
synced 2026-10-31 00:56:42 +09:00
390 lines
12 KiB
PowerShell
390 lines
12 KiB
PowerShell
<#
|
|
.SYNOPSIS
|
|
Stages and installs the libvirtualhid Windows UMDF development driver.
|
|
#>
|
|
[CmdletBinding(SupportsShouldProcess)]
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string] $InfPath,
|
|
|
|
[string] $CertificatePath,
|
|
|
|
[string] $HardwareId = "ROOT\LIBVIRTUALHID",
|
|
|
|
[string] $BrokerPath,
|
|
|
|
[string] $SetupPath,
|
|
|
|
[string] $LogPath,
|
|
|
|
[switch] $StageOnly
|
|
)
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
$script:LibVirtualHidTranscriptStarted = $false
|
|
$script:LibVirtualHidBrokerServiceName = "libvirtualhid_broker"
|
|
$script:LibVirtualHidBrokerServiceDisplayName = "libvirtualhid Broker"
|
|
. (Join-Path $PSScriptRoot "libvirtualhid-driver-common.ps1")
|
|
|
|
function Invoke-CheckedCommand {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string] $FilePath,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string[]] $Arguments,
|
|
|
|
[int[]] $SuccessExitCodes = @(0)
|
|
)
|
|
|
|
& $FilePath @Arguments
|
|
if ($LASTEXITCODE -notin $SuccessExitCodes) {
|
|
throw "$FilePath exited with code $LASTEXITCODE"
|
|
}
|
|
}
|
|
|
|
function Resolve-LibVirtualHidBrokerPath {
|
|
param([string] $Path)
|
|
|
|
if ($Path) {
|
|
if (-not (Test-Path -LiteralPath $Path)) {
|
|
throw "The broker executable was not found at $Path"
|
|
}
|
|
return (Resolve-Path -LiteralPath $Path).Path
|
|
}
|
|
|
|
$packagedPath = Join-Path $PSScriptRoot "..\..\services\windows\libvirtualhid_broker.exe"
|
|
if (Test-Path -LiteralPath $packagedPath) {
|
|
return (Resolve-Path -LiteralPath $packagedPath).Path
|
|
}
|
|
|
|
return $null
|
|
}
|
|
|
|
function Resolve-LibVirtualHidDriverSetupPath {
|
|
param([string] $Path)
|
|
|
|
if ($Path) {
|
|
if (-not (Test-Path -LiteralPath $Path)) {
|
|
throw "The driver setup helper was not found at $Path"
|
|
}
|
|
return (Resolve-Path -LiteralPath $Path).Path
|
|
}
|
|
|
|
$packagedPath = Join-Path $PSScriptRoot "..\..\tools\windows\libvirtualhid_driver_setup.exe"
|
|
if (Test-Path -LiteralPath $packagedPath) {
|
|
return (Resolve-Path -LiteralPath $packagedPath).Path
|
|
}
|
|
|
|
throw "The libvirtualhid driver setup helper was not found. Pass its path with -SetupPath."
|
|
}
|
|
|
|
function Get-LibVirtualHidQuotedServiceBinaryPath {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string] $Path
|
|
)
|
|
|
|
if ($Path.Contains('"')) {
|
|
throw "The broker executable path must not contain quotation marks: $Path"
|
|
}
|
|
|
|
return "`"$Path`""
|
|
}
|
|
|
|
function Get-LibVirtualHidScBinaryPathArgument {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string] $Path
|
|
)
|
|
|
|
# Windows PowerShell 5.1 needs triple quotes in a native-command argument to pass one literal quote
|
|
# through to sc.exe while keeping a path containing spaces in a single argv element.
|
|
return (Get-LibVirtualHidQuotedServiceBinaryPath -Path $Path).Replace('"', '"""')
|
|
}
|
|
|
|
function Assert-LibVirtualHidBrokerServiceImagePath {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string] $Name,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string] $Path
|
|
)
|
|
|
|
$registryPath = "HKLM:\SYSTEM\CurrentControlSet\Services\$Name"
|
|
$imagePath = (Get-ItemProperty -LiteralPath $registryPath -Name "ImagePath").ImagePath
|
|
$expectedPath = Get-LibVirtualHidQuotedServiceBinaryPath -Path $Path
|
|
if ($imagePath -cne $expectedPath) {
|
|
throw "The $Name service ImagePath is not safely quoted. Expected $expectedPath but found $imagePath."
|
|
}
|
|
}
|
|
|
|
function Clear-LibVirtualHidBrokerServiceEnvironment {
|
|
[CmdletBinding(SupportsShouldProcess)]
|
|
param([string] $Name)
|
|
|
|
$registryPath = "HKLM:\SYSTEM\CurrentControlSet\Services\$Name"
|
|
if (-not (Test-Path -LiteralPath $registryPath)) {
|
|
return
|
|
}
|
|
|
|
if ($PSCmdlet.ShouldProcess($Name, "Clear legacy libvirtualhid broker service environment")) {
|
|
Remove-ItemProperty -LiteralPath $registryPath -Name "Environment" -ErrorAction SilentlyContinue
|
|
}
|
|
}
|
|
|
|
function Stop-LibVirtualHidBrokerService {
|
|
[CmdletBinding(SupportsShouldProcess)]
|
|
param([string] $Name)
|
|
|
|
$service = Get-Service -Name $Name -ErrorAction SilentlyContinue
|
|
if (-not $service -or $service.Status -eq "Stopped") {
|
|
return
|
|
}
|
|
|
|
if ($PSCmdlet.ShouldProcess($Name, "Stop libvirtualhid broker service")) {
|
|
Stop-Service -Name $Name -Force -ErrorAction Stop
|
|
$service.WaitForStatus("Stopped", [TimeSpan]::FromSeconds(15))
|
|
}
|
|
}
|
|
|
|
function Install-LibVirtualHidBrokerService {
|
|
[CmdletBinding(SupportsShouldProcess)]
|
|
param([string] $Path)
|
|
|
|
$resolvedBroker = Resolve-LibVirtualHidBrokerPath -Path $Path
|
|
if (-not $resolvedBroker) {
|
|
Write-Verbose "No libvirtualhid broker executable was found; skipping broker service registration."
|
|
return
|
|
}
|
|
|
|
$serviceBinaryPath = Get-LibVirtualHidScBinaryPathArgument -Path $resolvedBroker
|
|
$serviceRegistered = $false
|
|
$service = Get-Service -Name $script:LibVirtualHidBrokerServiceName -ErrorAction SilentlyContinue
|
|
if ($service) {
|
|
Stop-LibVirtualHidBrokerService -Name $script:LibVirtualHidBrokerServiceName
|
|
if ($PSCmdlet.ShouldProcess($script:LibVirtualHidBrokerServiceName, "Update libvirtualhid broker service")) {
|
|
Invoke-CheckedCommand -FilePath "sc.exe" -Arguments @(
|
|
"config",
|
|
$script:LibVirtualHidBrokerServiceName,
|
|
"binPath=",
|
|
$serviceBinaryPath,
|
|
"start=",
|
|
"auto",
|
|
"DisplayName=",
|
|
$script:LibVirtualHidBrokerServiceDisplayName
|
|
)
|
|
$serviceRegistered = $true
|
|
}
|
|
} else {
|
|
if ($PSCmdlet.ShouldProcess($script:LibVirtualHidBrokerServiceName, "Install libvirtualhid broker service")) {
|
|
$quotedServicePath = Get-LibVirtualHidQuotedServiceBinaryPath -Path $resolvedBroker
|
|
New-Service `
|
|
-Name $script:LibVirtualHidBrokerServiceName `
|
|
-BinaryPathName $quotedServicePath `
|
|
-DisplayName $script:LibVirtualHidBrokerServiceDisplayName `
|
|
-StartupType Automatic | Out-Null
|
|
$serviceRegistered = $true
|
|
}
|
|
}
|
|
|
|
if ($serviceRegistered) {
|
|
Assert-LibVirtualHidBrokerServiceImagePath -Name $script:LibVirtualHidBrokerServiceName -Path $resolvedBroker
|
|
}
|
|
|
|
Clear-LibVirtualHidBrokerServiceEnvironment -Name $script:LibVirtualHidBrokerServiceName
|
|
|
|
if ($PSCmdlet.ShouldProcess($script:LibVirtualHidBrokerServiceName, "Enable libvirtualhid broker service SID")) {
|
|
Invoke-CheckedCommand -FilePath "sc.exe" -Arguments @(
|
|
"sidtype",
|
|
$script:LibVirtualHidBrokerServiceName,
|
|
"unrestricted"
|
|
)
|
|
}
|
|
|
|
if ($PSCmdlet.ShouldProcess($script:LibVirtualHidBrokerServiceName, "Set libvirtualhid broker service description")) {
|
|
Invoke-CheckedCommand -FilePath "sc.exe" -Arguments @(
|
|
"description",
|
|
$script:LibVirtualHidBrokerServiceName,
|
|
"Authorizes libvirtualhid virtual gamepad creation and license state."
|
|
)
|
|
}
|
|
|
|
if ($PSCmdlet.ShouldProcess($script:LibVirtualHidBrokerServiceName, "Start libvirtualhid broker service")) {
|
|
Start-Service -Name $script:LibVirtualHidBrokerServiceName
|
|
}
|
|
}
|
|
|
|
function Import-DriverCertificate {
|
|
[CmdletBinding(SupportsShouldProcess)]
|
|
param([string] $Path)
|
|
|
|
if (-not $Path -or -not (Test-Path -LiteralPath $Path)) {
|
|
return
|
|
}
|
|
|
|
$resolvedCertificate = (Resolve-Path -LiteralPath $Path).Path
|
|
foreach ($store in @("Cert:\LocalMachine\Root", "Cert:\LocalMachine\TrustedPublisher")) {
|
|
if ($PSCmdlet.ShouldProcess($store, "Trust libvirtualhid driver certificate $resolvedCertificate")) {
|
|
Import-Certificate -FilePath $resolvedCertificate -CertStoreLocation $store | Out-Null
|
|
}
|
|
}
|
|
}
|
|
|
|
function Remove-DeviceInstance {
|
|
[CmdletBinding(SupportsShouldProcess)]
|
|
param([string] $InstanceId)
|
|
|
|
if ($PSCmdlet.ShouldProcess($InstanceId, "Remove stale libvirtualhid root device")) {
|
|
Invoke-CheckedCommand -FilePath "pnputil.exe" -Arguments @("/remove-device", $InstanceId)
|
|
}
|
|
}
|
|
|
|
function Set-RootDeviceVhfMode {
|
|
[CmdletBinding(SupportsShouldProcess)]
|
|
param([string] $InstanceId)
|
|
|
|
$deviceRegistryPath = "HKLM:\SYSTEM\CurrentControlSet\Enum\$InstanceId"
|
|
if (-not (Test-Path -LiteralPath $deviceRegistryPath)) {
|
|
Write-Verbose "Unable to set VhfMode because $deviceRegistryPath does not exist."
|
|
return
|
|
}
|
|
|
|
if ($PSCmdlet.ShouldProcess($InstanceId, "Set VhfMode=1 for UMDF VHF source device")) {
|
|
New-ItemProperty -LiteralPath $deviceRegistryPath -Name "VhfMode" -Value 1 -PropertyType DWord -Force | Out-Null
|
|
Write-Information "Set VhfMode=1 on $InstanceId." -InformationAction Continue
|
|
}
|
|
}
|
|
|
|
function Restart-RootDevice {
|
|
[CmdletBinding(SupportsShouldProcess)]
|
|
param([string] $InstanceId)
|
|
|
|
if (-not $InstanceId) {
|
|
return
|
|
}
|
|
|
|
if (-not $PSCmdlet.ShouldProcess($InstanceId, "Restart libvirtualhid development device")) {
|
|
return
|
|
}
|
|
|
|
$output = @(pnputil.exe /restart-device $InstanceId 2>&1)
|
|
$exitCode = $LASTEXITCODE
|
|
foreach ($line in $output) {
|
|
Write-Information $line -InformationAction Continue
|
|
}
|
|
|
|
if ($exitCode -ne 0) {
|
|
throw "pnputil.exe /restart-device $InstanceId exited with code $exitCode"
|
|
}
|
|
|
|
if ($output -match "reboot is needed") {
|
|
Write-Warning "Windows reported that a reboot is required to reload the libvirtualhid UMDF driver."
|
|
}
|
|
}
|
|
|
|
function Update-RootDeviceDriverWithSetupApi {
|
|
[CmdletBinding(SupportsShouldProcess)]
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string] $Path,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string] $TargetHardwareId,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string] $SetupHelperPath
|
|
)
|
|
|
|
if ($PSCmdlet.ShouldProcess($TargetHardwareId, "Update libvirtualhid development device driver")) {
|
|
Invoke-CheckedCommand `
|
|
-FilePath $SetupHelperPath `
|
|
-Arguments @("update", $Path, $TargetHardwareId) `
|
|
-SuccessExitCodes @(0, 3010)
|
|
if ($LASTEXITCODE -eq 3010) {
|
|
Write-Warning "Windows reported that a reboot is required to finish installing the libvirtualhid driver."
|
|
}
|
|
}
|
|
}
|
|
|
|
function Install-RootDeviceWithSetupApi {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string] $Path,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string] $TargetHardwareId,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string] $SetupHelperPath
|
|
)
|
|
|
|
Invoke-CheckedCommand -FilePath $SetupHelperPath -Arguments @("install", $Path, $TargetHardwareId)
|
|
}
|
|
|
|
if ($MyInvocation.InvocationName -eq ".") {
|
|
return
|
|
}
|
|
|
|
Start-LibVirtualHidTranscript -Path $LogPath
|
|
|
|
try {
|
|
$resolvedInf = (Resolve-Path -LiteralPath $InfPath).Path
|
|
Import-DriverCertificate -Path $CertificatePath
|
|
|
|
if ($PSCmdlet.ShouldProcess($resolvedInf, "Stage libvirtualhid driver package")) {
|
|
Invoke-CheckedCommand -FilePath "pnputil.exe" -Arguments @("/add-driver", $resolvedInf) -SuccessExitCodes @(0, 5)
|
|
}
|
|
|
|
if ($StageOnly) {
|
|
return
|
|
}
|
|
|
|
$resolvedSetup = Resolve-LibVirtualHidDriverSetupPath -Path $SetupPath
|
|
|
|
$registryRootDevices = @(Get-LibVirtualHidRegistryRootDevice -TargetHardwareId $HardwareId)
|
|
foreach ($device in ($registryRootDevices | Where-Object { $_.HasCorruptHardwareId -or $_.HasLegacyHidClass })) {
|
|
Remove-DeviceInstance -InstanceId $device.InstanceId
|
|
}
|
|
|
|
$rootDevices = @(Get-LibVirtualHidRootDeviceInstanceId -TargetHardwareId $HardwareId)
|
|
if ($rootDevices.Count -gt 0) {
|
|
Write-Information "Updating the existing $HardwareId device driver." -InformationAction Continue
|
|
foreach ($rootDevice in $rootDevices) {
|
|
Set-RootDeviceVhfMode -InstanceId $rootDevice
|
|
}
|
|
Update-RootDeviceDriverWithSetupApi `
|
|
-Path $resolvedInf `
|
|
-TargetHardwareId $HardwareId `
|
|
-SetupHelperPath $resolvedSetup
|
|
foreach ($rootDevice in $rootDevices) {
|
|
Restart-RootDevice -InstanceId $rootDevice
|
|
}
|
|
Install-LibVirtualHidBrokerService -Path $BrokerPath
|
|
return
|
|
}
|
|
|
|
if ($PSCmdlet.ShouldProcess($HardwareId, "Create libvirtualhid development device with SetupAPI")) {
|
|
Install-RootDeviceWithSetupApi `
|
|
-Path $resolvedInf `
|
|
-TargetHardwareId $HardwareId `
|
|
-SetupHelperPath $resolvedSetup
|
|
}
|
|
|
|
$rootDevices = @(Get-LibVirtualHidRootDeviceInstanceId -TargetHardwareId $HardwareId)
|
|
foreach ($rootDevice in $rootDevices) {
|
|
Set-RootDeviceVhfMode -InstanceId $rootDevice
|
|
}
|
|
Update-RootDeviceDriverWithSetupApi `
|
|
-Path $resolvedInf `
|
|
-TargetHardwareId $HardwareId `
|
|
-SetupHelperPath $resolvedSetup
|
|
foreach ($rootDevice in $rootDevices) {
|
|
Restart-RootDevice -InstanceId $rootDevice
|
|
}
|
|
Install-LibVirtualHidBrokerService -Path $BrokerPath
|
|
} finally {
|
|
Stop-LibVirtualHidTranscript
|
|
}
|