diff --git a/edge-ai-platform/installer/app.go b/edge-ai-platform/installer/app.go index 7beeb12..6a5a7ac 100644 --- a/edge-ai-platform/installer/app.go +++ b/edge-ai-platform/installer/app.go @@ -230,8 +230,9 @@ func (inst *Installer) runInstall(config InstallConfig) { {"Extracting models and firmware", 30, true, inst.stepExtractData}, {"Extracting scripts", 48, true, inst.stepExtractScripts}, {"Configuring system", 55, false, inst.stepConfigureSystem}, - {"Setting up USB driver", 62, false, inst.stepSetupLibusb}, + {"Setting up USB library", 62, false, inst.stepSetupLibusb}, {"Setting up Python environment", 70, false, inst.stepSetupPython}, + {"Installing USB device driver", 76, false, inst.stepInstallUSBDriver}, {"Installing media tools", 78, false, inst.stepInstallFfmpeg}, {"Writing configuration", 85, true, inst.stepWriteConfig}, {"Verifying installation", 90, false, inst.stepVerify}, @@ -325,6 +326,10 @@ func (inst *Installer) stepSetupPython(config InstallConfig) error { return inst.setupPythonVenv(config.InstallDir) } +func (inst *Installer) stepInstallUSBDriver(config InstallConfig) error { + return installUSBDriver(config.InstallDir) +} + func (inst *Installer) stepVerify(config InstallConfig) error { binName := "edge-ai-server" if runtime.GOOS == "windows" { diff --git a/edge-ai-platform/installer/platform_darwin.go b/edge-ai-platform/installer/platform_darwin.go index 22beeb1..24d8982 100644 --- a/edge-ai-platform/installer/platform_darwin.go +++ b/edge-ai-platform/installer/platform_darwin.go @@ -102,6 +102,11 @@ func checkLibusbInstalled() bool { return exec.Command("brew", "list", "libusb").Run() == nil } +// installUSBDriver is a no-op on macOS (libusb handles USB access). +func installUSBDriver(installDir string) error { + return nil +} + func removeQuarantine(installDir string) { binPath := filepath.Join(installDir, "edge-ai-server") exec.Command("xattr", "-dr", "com.apple.quarantine", binPath).Run() diff --git a/edge-ai-platform/installer/platform_linux.go b/edge-ai-platform/installer/platform_linux.go index 095f234..3b7af66 100644 --- a/edge-ai-platform/installer/platform_linux.go +++ b/edge-ai-platform/installer/platform_linux.go @@ -92,6 +92,11 @@ func checkLibusbInstalled() bool { return exec.Command("dpkg", "-s", "libusb-1.0-0-dev").Run() == nil } +// installUSBDriver is a no-op on Linux (udev rules handle USB access). +func installUSBDriver(installDir string) error { + return nil +} + func removeQuarantine(installDir string) { // No-op on Linux } diff --git a/edge-ai-platform/installer/platform_windows.go b/edge-ai-platform/installer/platform_windows.go index 1067b6c..18b7a48 100644 --- a/edge-ai-platform/installer/platform_windows.go +++ b/edge-ai-platform/installer/platform_windows.go @@ -129,9 +129,86 @@ func installLibusb(installDir string) error { } } - // 2. Extract and install WinUSB driver for Kneron devices - if err := installWinUSBDriver(installDir); err != nil { - return err + // 2. Extract WinUSB driver files (for manual fallback) + extractWinUSBDriverFiles(installDir) + + // 3. Install WinUSB driver via KneronPLUS SDK (uses libwdi internally) + // This is called after Python venv + kp wheel are installed (step 7), + // so we defer the actual driver install to postInstallDriver(). + // Here we just extract the files. + + return nil +} + +// extractWinUSBDriverFiles extracts INF and co-installer DLLs from payload +// for manual fallback (Zadig). The actual driver install happens via kp SDK. +func extractWinUSBDriverFiles(installDir string) { + driverDir := filepath.Join(installDir, "drivers") + os.MkdirAll(filepath.Join(driverDir, "amd64"), 0755) + + 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 { + data, err := payloadFS.ReadFile(src) + if err != nil { + continue + } + os.WriteFile(dst, data, 0644) + } +} + +// installUSBDriver installs WinUSB driver via KneronPLUS SDK (libwdi). +func installUSBDriver(installDir string) error { + return installKneronDriverViaSDK(installDir) +} + +// installKneronDriverViaSDK uses the KneronPLUS SDK's built-in libwdi to +// install WinUSB driver for all known Kneron devices. Must be called after +// the Python venv with kp module is set up. +func installKneronDriverViaSDK(installDir string) error { + venvPython := filepath.Join(installDir, "venv", "Scripts", "python.exe") + if _, err := os.Stat(venvPython); err != nil { + return fmt.Errorf("Python venv not found, cannot install driver") + } + + // Use kp.core.install_driver_for_windows() for each known product ID + // KL520=0x100, KL720=0x720, KL720_LEGACY=0x200 + script := ` +import sys +try: + import kp + for pid in [kp.ProductId.KP_DEVICE_KL520, kp.ProductId.KP_DEVICE_KL720, kp.ProductId.KP_DEVICE_KL720_LEGACY]: + try: + kp.core.install_driver_for_windows(pid) + print(f"OK: driver installed for {pid.name}") + except Exception as e: + print(f"SKIP: {pid.name}: {e}") + print("DONE") +except ImportError: + print("ERROR: kp module not available") + sys.exit(1) +` + cmd := exec.Command(venvPython, "-c", script) + cmd.Dir = installDir + // Ensure kp can find its DLLs + cmd.Env = append(os.Environ(), + fmt.Sprintf("PATH=%s;%s;%s", + filepath.Join(installDir, "venv", "Lib", "site-packages", "kp", "lib"), + installDir, + os.Getenv("PATH"))) + + out, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("WinUSB driver install via KneronPLUS SDK failed: %s — please install manually using Zadig (https://zadig.akeo.ie/)", strings.TrimSpace(string(out))) + } + + output := string(out) + if strings.Contains(output, "ERROR:") { + return fmt.Errorf("WinUSB driver install failed: %s — please install manually using Zadig (https://zadig.akeo.ie/)", strings.TrimSpace(output)) } return nil