Classes/PsGadgetI2CDevice.ps1
|
#Requires -Version 5.1 # Classes/PsGadgetI2CDevice.ps1 # Base class for I2C device drivers backed by FTDI hardware. # # Provides the five shared fields, the I2CWrite transport method, # the no-arg Initialize() overload (polymorphically dispatched to the # derived class), and BeginInitialize() which encapsulates the guard # that every device's Initialize([bool]$force) must run first. # # PowerShell 5.1 inheritance notes: # - The parameterless base constructor runs automatically before every # derived constructor body. Derived constructors must NOT re-create Logger. # - There is no abstract keyword; direct instantiation is possible but # Initialize() will throw at runtime since Initialize([bool]) is not # defined on this class - it is defined on each derived device class. # - $this.Initialize($false) in Initialize() resolves to the derived # class override at runtime (dynamic dispatch). class PsGadgetI2CDevice { [PsGadgetLogger]$Logger [System.Object]$FtdiDevice [System.Object]$I2cDevice # I2C device instance when available (IoT path) [byte]$I2CAddress [bool]$IsInitialized # Parameterless constructor - called automatically before every derived # constructor body. Sets Logger so derived constructors can call # $this.Logger.WriteInfo() on their first line. PsGadgetI2CDevice() { $this.Logger = [PsGadgetLogger]::new() $this.IsInitialized = $false } # I2CWrite: send bytes to the device via IoT or raw MPSSE. # All internal write operations in derived classes go through this method. [bool] I2CWrite([byte[]]$data) { try { if ($null -ne $this.I2cDevice) { $this.I2cDevice.Write($this.I2CAddress, $data) return $true } else { return (Send-MpsseI2CWrite -DeviceHandle $this.FtdiDevice -Address $this.I2CAddress -Data $data) } } catch { $this.Logger.WriteError("I2CWrite failed: $_") return $false } } # Initialize() no-arg overload - defined once here for all derived classes. # Dispatches to the derived class Initialize([bool]$force) at runtime. [bool] Initialize() { return $this.Initialize($false) } # BeginInitialize - shared guard called at the top of every derived # Initialize([bool]$force) implementation. # # Returns $true = transport is ready; proceed with device-specific init. # Returns $false = bail; caller must: return $this.IsInitialized # "already initialized" -> $this.IsInitialized is $true -> caller returns $true # "no device / failed" -> $this.IsInitialized is $false -> caller returns $false [bool] BeginInitialize([bool]$force) { if ($this.IsInitialized -and -not $force) { $this.Logger.WriteInfo("Device already initialized") return $false } if (-not $this.FtdiDevice) { $this.Logger.WriteError("No FTDI device assigned") return $false } try { # Initialize MPSSE I2C only when using raw D2XX bit-bang path. # .NET IoT backend manages its own I2C init; D2XX uses raw MPSSE. # Skip if Set-PsGadgetFtdiMode already ran Initialize-MpsseI2C this session. if ($null -eq $this.I2cDevice) { if ($this.FtdiDevice.GpioMethod -ne 'MpsseI2c') { if (-not (Initialize-MpsseI2C -DeviceHandle $this.FtdiDevice -ClockFrequency 100000)) { $this.Logger.WriteError("MPSSE I2C initialization failed") return $false } } } return $true } catch { $this.Logger.WriteError("MPSSE I2C initialization error: $_") return $false } } } |