From cc1edfc89d88272b48fb4120421e14cf600069ea Mon Sep 17 00:00:00 2001 From: jim800121chen Date: Mon, 9 Mar 2026 22:50:57 +0800 Subject: [PATCH] fix: use DiInstallDriver API for WinUSB driver installation pnputil rejects unsigned INF, installer_x64.exe is not a CLI tool. Switch to calling Windows DiInstallDriverW from newdev.dll directly, which can install unsigned drivers with a user confirmation dialog. Uses DIIRFLAG_FORCE_INF flag to force INF installation. Co-Authored-By: Claude Opus 4.6 --- .gitea/workflows/build-installer.yaml | 3 +- .github/workflows/build-installer.yaml | 3 +- edge-ai-platform/Makefile | 5 +- .../installer/platform_windows.go | 85 ++++++++++++------- edge-ai-platform/scripts/build-installer.ps1 | 15 ++-- 5 files changed, 67 insertions(+), 44 deletions(-) diff --git a/.gitea/workflows/build-installer.yaml b/.gitea/workflows/build-installer.yaml index 0c82834..2edbb56 100644 --- a/.gitea/workflows/build-installer.yaml +++ b/.gitea/workflows/build-installer.yaml @@ -118,8 +118,7 @@ jobs: $kpWheel = Get-ChildItem "local_service_win" -Filter "KneronPLUS*.whl" -ErrorAction SilentlyContinue | Select-Object -First 1 if ($kpWheel) { Copy-Item $kpWheel.FullName "$base\installer\payload\scripts\" } New-Item -ItemType Directory -Force -Path "$base\installer\payload\drivers\amd64" - Copy-Item "local_service_win\LocalAPI\win_driver\installer_x64.exe" "$base\installer\payload\drivers\" - Copy-Item "local_service_win\LocalAPI\win_driver\kneron_kl520.inf" "$base\installer\payload\drivers\" + Copy-Item "$base\server\scripts\drivers\kneron_winusb.inf" "$base\installer\payload\drivers\" Copy-Item "local_service_win\LocalAPI\win_driver\amd64\WdfCoInstaller01011.dll" "$base\installer\payload\drivers\amd64\" Copy-Item "local_service_win\LocalAPI\win_driver\amd64\winusbcoinstaller2.dll" "$base\installer\payload\drivers\amd64\" diff --git a/.github/workflows/build-installer.yaml b/.github/workflows/build-installer.yaml index 0c82834..2edbb56 100644 --- a/.github/workflows/build-installer.yaml +++ b/.github/workflows/build-installer.yaml @@ -118,8 +118,7 @@ jobs: $kpWheel = Get-ChildItem "local_service_win" -Filter "KneronPLUS*.whl" -ErrorAction SilentlyContinue | Select-Object -First 1 if ($kpWheel) { Copy-Item $kpWheel.FullName "$base\installer\payload\scripts\" } New-Item -ItemType Directory -Force -Path "$base\installer\payload\drivers\amd64" - Copy-Item "local_service_win\LocalAPI\win_driver\installer_x64.exe" "$base\installer\payload\drivers\" - Copy-Item "local_service_win\LocalAPI\win_driver\kneron_kl520.inf" "$base\installer\payload\drivers\" + Copy-Item "$base\server\scripts\drivers\kneron_winusb.inf" "$base\installer\payload\drivers\" Copy-Item "local_service_win\LocalAPI\win_driver\amd64\WdfCoInstaller01011.dll" "$base\installer\payload\drivers\amd64\" Copy-Item "local_service_win\LocalAPI\win_driver\amd64\winusbcoinstaller2.dll" "$base\installer\payload\drivers\amd64\" diff --git a/edge-ai-platform/Makefile b/edge-ai-platform/Makefile index 5d9e148..9b8cde3 100644 --- a/edge-ai-platform/Makefile +++ b/edge-ai-platform/Makefile @@ -137,9 +137,8 @@ installer-payload: build-server-tray ## Stage payload files for GUI installer fi @# Copy WinUSB driver files (for Windows installer) @mkdir -p installer/payload/drivers/amd64 - @if [ -d "../local_service_win/LocalAPI/win_driver" ]; then \ - cp ../local_service_win/LocalAPI/win_driver/installer_x64.exe installer/payload/drivers/; \ - cp ../local_service_win/LocalAPI/win_driver/kneron_kl520.inf installer/payload/drivers/; \ + cp server/scripts/drivers/kneron_winusb.inf installer/payload/drivers/ + @if [ -d "../local_service_win/LocalAPI/win_driver/amd64" ]; then \ cp ../local_service_win/LocalAPI/win_driver/amd64/WdfCoInstaller01011.dll installer/payload/drivers/amd64/; \ cp ../local_service_win/LocalAPI/win_driver/amd64/winusbcoinstaller2.dll installer/payload/drivers/amd64/; \ echo " WinUSB driver files bundled."; \ diff --git a/edge-ai-platform/installer/platform_windows.go b/edge-ai-platform/installer/platform_windows.go index dbe7dbe..06469cf 100644 --- a/edge-ai-platform/installer/platform_windows.go +++ b/edge-ai-platform/installer/platform_windows.go @@ -8,6 +8,8 @@ import ( "os/exec" "path/filepath" "strings" + "syscall" + "unsafe" ) func platformDefaultDir() string { @@ -86,42 +88,67 @@ func installLibusb(installDir string) error { return nil } -// installWinUSBDriver extracts the Kneron driver installer from the payload -// and runs it to install the WinUSB driver for Kneron USB devices. -// The installer_x64.exe is a libwdi-based tool that handles driver signing -// automatically (self-signed certificate), which pnputil cannot do with -// unsigned INF files. +// installWinUSBDriver extracts the WinUSB driver INF and co-installer files +// from the payload, then uses the Windows SetupAPI (DiInstallDriver) to install +// the driver. DiInstallDriver can install unsigned drivers — Windows will show +// a security prompt asking the user to confirm. func installWinUSBDriver(installDir string) { driverDir := filepath.Join(installDir, "drivers") - os.MkdirAll(driverDir, 0755) - - // Extract the Kneron driver installer from payload - installerPath := filepath.Join(driverDir, "installer_x64.exe") - data, err := payloadFS.ReadFile("payload/drivers/installer_x64.exe") - if err != nil { - return // not bundled, skip - } - if err := os.WriteFile(installerPath, data, 0755); err != nil { - return - } - - // Also extract the INF + co-installer files (installer_x64.exe needs them in the same directory) - driverFiles := map[string]string{ - "payload/drivers/kneron_kl520.inf": filepath.Join(driverDir, "kneron_kl520.inf"), - "payload/drivers/amd64/WdfCoInstaller01011.dll": filepath.Join(driverDir, "amd64", "WdfCoInstaller01011.dll"), - "payload/drivers/amd64/winusbcoinstaller2.dll": filepath.Join(driverDir, "amd64", "winusbcoinstaller2.dll"), - } os.MkdirAll(filepath.Join(driverDir, "amd64"), 0755) + + // Extract driver files from embedded payload + driverFiles := map[string]string{ + "payload/drivers/kneron_winusb.inf": filepath.Join(driverDir, "kneron_winusb.inf"), + "payload/drivers/amd64/WdfCoInstaller01011.dll": filepath.Join(driverDir, "amd64", "WdfCoInstaller01011.dll"), + "payload/drivers/amd64/winusbcoinstaller2.dll": filepath.Join(driverDir, "amd64", "winusbcoinstaller2.dll"), + } + for src, dst := range driverFiles { - if d, e := payloadFS.ReadFile(src); e == nil { - os.WriteFile(dst, d, 0644) + data, err := payloadFS.ReadFile(src) + if err != nil { + return // driver files not bundled, skip + } + if err := os.WriteFile(dst, data, 0644); err != nil { + return } } - // Run the Kneron driver installer (libwdi-based, handles signing automatically) - cmd := exec.Command(installerPath) - cmd.Dir = driverDir - cmd.CombinedOutput() // best effort — non-fatal, may trigger UAC prompt + infPath := filepath.Join(driverDir, "kneron_winusb.inf") + + // Use DiInstallDriver from newdev.dll to pre-install the driver. + // This works with unsigned INF files — Windows shows a trust dialog. + diInstallDriver(infPath) +} + +// diInstallDriver calls Windows DiInstallDriver API to install a driver INF. +// Flags: DIIRFLAG_FORCE_INF (0x00000002) forces the specified INF to be installed +// even if it's unsigned, showing a Windows Security prompt to the user. +func diInstallDriver(infPath string) { + newdev, err := syscall.LoadDLL("newdev.dll") + if err != nil { + return + } + defer newdev.Release() + + proc, err := newdev.FindProc("DiInstallDriverW") + if err != nil { + return + } + + infPathUTF16, err := syscall.UTF16PtrFromString(infPath) + if err != nil { + return + } + + const DIIRFLAG_FORCE_INF = 0x00000002 + // DiInstallDriverW(hwndParent, InfPath, Flags, NeedReboot) + proc.Call( + 0, // hwndParent = NULL (no parent window) + uintptr(unsafe.Pointer(infPathUTF16)), + DIIRFLAG_FORCE_INF, + 0, // NeedReboot = NULL (don't care) + ) + // Best effort — ignore errors. The user may decline the security prompt. } func checkLibusbInstalled() bool { diff --git a/edge-ai-platform/scripts/build-installer.ps1 b/edge-ai-platform/scripts/build-installer.ps1 index d4c3f8b..876eaef 100644 --- a/edge-ai-platform/scripts/build-installer.ps1 +++ b/edge-ai-platform/scripts/build-installer.ps1 @@ -94,17 +94,16 @@ if ($kpWheel) { Write-Host " KneronPLUS wheel not found, skipping." -ForegroundColor Yellow } -# Copy WinUSB driver files (Kneron official installer + INF + co-installers) +# Copy WinUSB driver files (INF + co-installers) New-Item -ItemType Directory -Force -Path installer\payload\drivers\amd64 | Out-Null -$winDriverBase = Join-Path $repoRoot "local_service_win\LocalAPI\win_driver" -if (Test-Path $winDriverBase) { - Copy-Item (Join-Path $winDriverBase "installer_x64.exe") installer\payload\drivers\ - Copy-Item (Join-Path $winDriverBase "kneron_kl520.inf") installer\payload\drivers\ - Copy-Item (Join-Path $winDriverBase "amd64\WdfCoInstaller01011.dll") installer\payload\drivers\amd64\ - Copy-Item (Join-Path $winDriverBase "amd64\winusbcoinstaller2.dll") installer\payload\drivers\amd64\ +Copy-Item server\scripts\drivers\kneron_winusb.inf installer\payload\drivers\ +$winDriverDir = Join-Path $repoRoot "local_service_win\LocalAPI\win_driver\amd64" +if (Test-Path $winDriverDir) { + Copy-Item (Join-Path $winDriverDir "WdfCoInstaller01011.dll") installer\payload\drivers\amd64\ + Copy-Item (Join-Path $winDriverDir "winusbcoinstaller2.dll") installer\payload\drivers\amd64\ Write-Host " WinUSB driver files bundled." -ForegroundColor Green } else { - Write-Host " WinUSB driver files not found, skipping." -ForegroundColor Yellow + Write-Host " WinUSB co-installer DLLs not found, skipping." -ForegroundColor Yellow } $fileCount = (Get-ChildItem -Recurse installer\payload -File).Count