QCT-Update-Management.psm1

<#
.SYNOPSIS
  <Overview of script>
.DESCRIPTION
  <Brief description of script>
.PARAMETER <Parameter_Name>
    <Brief description of parameter input required. Repeat this attribute if required>
.INPUTS
  <Inputs if any, otherwise state None>
.OUTPUTS
  <Outputs if any, otherwise state None - example: Log file stored in C:\Windows\Temp\<name>.log>
.NOTES
  Version: 1.2.23
  Author: Jim.Lin
  Creation Date: 2020.10.22
  Purpose/Change: Initial script development
   
.EXAMPLE
  <Example goes here. Repeat this attribute for more than one example>
#>


#---------------------------------------------------------[Initialisations]--------------------------------------------------------

#Set Error Action to Silently Continue
$ErrorActionPreference = "SilentlyContinue"

#----------------------------------------------------------[Declarations]----------------------------------------------------------


#-----------------------------------------------------------[Functions]------------------------------------------------------------

function callProcess($ProcessPath, $CmdArgs, $WaitSec)
{
    $pinfo = New-Object System.Diagnostics.ProcessStartInfo
    $pinfo.FileName = $ProcessPath
    $pinfo.RedirectStandardError = $true
    $pinfo.RedirectStandardOutput = $true
    $pinfo.UseShellExecute = $false
    $pinfo.Arguments = $CmdArgs
    $pinfo.CreateNoWindow = $false
    $p = New-Object System.Diagnostics.Process
    $p.StartInfo = $pinfo
    $p.Start() | Out-Null
    $p.WaitForExit($WaitSec*1000) | Out-Null
    $p.Kill()
    $stdout = $p.StandardOutput.ReadToEnd()
    $stderr = $p.StandardError.ReadToEnd()
    return $stdout
}

function callProcessStdin($ProcessPath, $CmdArgs, $StdinArray)
{
    $pinfo = New-Object System.Diagnostics.ProcessStartInfo
    $pinfo.FileName = $ProcessPath
    $pinfo.RedirectStandardError = $true
    $pinfo.RedirectStandardInput  = $true
    $pinfo.RedirectStandardOutput = $true
    $pinfo.UseShellExecute = $false
    $pinfo.Arguments = $CmdArgs
    $p = New-Object System.Diagnostics.Process
    $p.StartInfo = $pinfo
    $p.Start() | Out-Null
    $p.WaitForExit(3*1000) | Out-Null

    foreach ($command in $StdinArray)
    {
        $p.StandardInput.WriteLine($command)
        $p.WaitForExit(1*1000) | Out-Null
        if ($command.IndexOf("dl -f") -ge 0) {
            $p.WaitForExit(60*1000) | Out-Null
        }
    }

    $stdout = $p.StandardOutput.ReadToEnd()
    $stderr = $p.StandardError.ReadToEnd()
    return $stdout
}

function callProcess($ProcessPath, $CmdArgs)
{
    $pinfo = New-Object System.Diagnostics.ProcessStartInfo
    $pinfo.FileName = $ProcessPath
    $pinfo.RedirectStandardError = $true
    $pinfo.RedirectStandardOutput = $true
    $pinfo.UseShellExecute = $false
    $pinfo.Arguments = $CmdArgs
    $p = New-Object System.Diagnostics.Process
    $p.StartInfo = $pinfo
    $p.Start() | Out-Null
    $p.WaitForExit(3*1000) | Out-Null
    $stdout = $p.StandardOutput.ReadToEnd()
    $stderr = $p.StandardError.ReadToEnd()
    return $stdout
}

function callProcessWorkingDirectory($ProcessPath, $CmdArgs)
{
    $pinfo = New-Object System.Diagnostics.ProcessStartInfo
    $pinfo.FileName = $ProcessPath
    $pinfo.WorkingDirectory = Split-Path $ProcessPath
    $pinfo.RedirectStandardError = $true
    $pinfo.RedirectStandardOutput = $true
    $pinfo.UseShellExecute = $false
    $pinfo.Arguments = $CmdArgs
    $p = New-Object System.Diagnostics.Process
    $p.StartInfo = $pinfo
    $p.Start() | Out-Null
    $p.WaitForExit(3*1000) | Out-Null
    $stdout = $p.StandardOutput.ReadToEnd()
    $stderr = $p.StandardError.ReadToEnd()
    return $stdout
}

function callProcessNoWait($ProcessPath, $CmdArgs)
{
    $pinfo = New-Object System.Diagnostics.ProcessStartInfo
    $pinfo.FileName = $ProcessPath
    $pinfo.RedirectStandardError = $true
    $pinfo.RedirectStandardOutput = $true
    $pinfo.UseShellExecute = $false
    $pinfo.Arguments = $CmdArgs
    $p = New-Object System.Diagnostics.Process
    $p.StartInfo = $pinfo
    $p.Start(3*1000) | Out-Null
    return $stdout
}

function Get-Driver_FW()
{
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory=$false, Position=0)]
        [ValidateSet('BIOS','BMC','Disk','Expander','HBA','intelChipset','AMDChipset','NIC')]
        [string]$Device=''
    )

    $script:table2 = @()
    function addRow2($OEMDevice, $OEMName, $DriverFW, $OEMVersion, $OEMPS)
    {
        $row2 = New-Object -TypeName PSObject
        $row2 | Add-Member -Name 'Device' -MemberType Noteproperty  -Value $OEMDevice
        $row2 | Add-Member -Name 'Name' -MemberType Noteproperty  -Value $OEMName
        $row2 | Add-Member -Name 'DriverFW' -MemberType Noteproperty  -Value $DriverFW
        $row2 | Add-Member -Name 'Version' -MemberType Noteproperty  -Value $OEMVersion
        $row2 | Add-Member -Name 'PS' -MemberType Noteproperty  -Value $OEMPS
        $row2 | Add-Member -Name 'Location' -MemberType Noteproperty  -Value $hostname
        $row2 | Add-Member -Name 'Location2' -MemberType Noteproperty  -Value $hostname2
        $row2 | Add-Member -Name 'ServerModel' -MemberType Noteproperty  -Value $ServerModel

        $script:table2 += $row2
    }

    $ServerModel = (Get-WmiObject Win32_Computersystem | select Model).Model

    if ((Get-WmiObject -Class Win32_ComputerSystem).PartofDomain) {
        $hostname = $((Get-WmiObject win32_computersystem).DNSHostName+"."+(Get-WmiObject win32_computersystem).Domain)
    $hostname2 = hostname
    } else {
        $hostname = hostname
    $hostname2 = hostname
    }

    #BIOS
    if (($Device -eq 'BIOS') -or ($Device -eq ''))
    {
        $BIOSVersion = $(Get-WmiObject win32_bios).SMBIOSBIOSVersion
        #$PcsvDevice = Get-PcsvDevice | select CurrentBIOSVersionString,IPv4Address
        #$BIOSVersion = $PcsvDevice.CurrentBIOSVersionString
        #$BMCIP = $PcsvDevice.IPv4Address
        addRow2 "BIOS" "BIOS" "FW" $BIOSVersion ''
    }
    
    #BMC
    if (($Device -eq 'BMC') -or ($Device -eq ''))
    {
        [byte] $LUN = 0x00
        [byte] $rsSa = 0x20
        [byte] $NetFn = 0x06
        [byte] $Command = 0x01

        $CimSession = New-CimSession
        $ipmi = Get-CimInstance -Namespace root/wmi -CimSession $CimSession Microsoft_IPMI

        $requestData = @()
        $arguments = @{Command=$Command;LUN=$LUN;NetworkFunction=$NetFn;RequestData=$requestData;RequestDataSize=$requestData.Length;ResponderAddress=$rsSa}
        $result = Invoke-CimMethod -InputObject $ipmi -CimSession $CimSession RequestResponse -Arguments $arguments

        $BMCVersion = "{0:x}" -f $result.ResponseData[3] + '.' + "{0:x}" -f $result.ResponseData[4]
        $ipmi.Dispose()
        addRow2 "BMC" "BMC" "FW" $BMCVersion ''
    }

    if (($Device -eq 'intelChipset') -or ($Device -eq ''))
    {
        #Intel Chipset
        $intelChipset = Get-WmiObject Win32_PnPSignedDriver | where {$_.devicename -like "*Intel*C6*chip*SMB*"} | select devicename, driverversion -First 1
        addRow2 "intelChipset" "intelChipset" "Driver" $intelChipset[0].driverversion ''
    }
    
    if (($Device -eq 'AMDChipset') -or ($Device -eq ''))
    {
        #AMD Chipset
        $AMDChipset = Get-WmiObject Win32_PnPSignedDriver | where {$_.devicename -like "*AMD GPIO*"} | select devicename, driverversion -First 1
        addRow2 "AMDChipset" "AMD GPIO" "Driver" $AMDChipset[0].driverversion ''

        $AMDChipset = Get-WmiObject Win32_PnPSignedDriver | where {$_.devicename -like "*AMD PSP*"} | select devicename, driverversion -First 1
        addRow2 "AMDChipset" "AMD PSP" "Driver" $AMDChipset[0].driverversion ''

        $AMDChipset = Get-WmiObject Win32_PnPSignedDriver | where {$_.devicename -like "*AMD CCP*"} | select devicename, driverversion -First 1
        addRow2 "AMDChipset" "AMD CCP" "Driver" $AMDChipset[0].driverversion ''

    }

    if (($Device -eq 'NIC') -or ($Device -eq ''))
    {
        #Get-NetAdapter | fl name, InterfaceDescription, DriverFileName, DriverDate, DriverVersionString, NdisVersion
        $NetAdapters = Get-NetAdapter
        if ($($NetAdapters | where InterfaceDescription -like "*Mellanox*") -ne $null){
            $MellanoxFW = callProcess "$PSScriptRoot\mlxup.exe" "--query"
            $MellanoxFWName = $($MellanoxFW | findstr /c:'Device Type').Split(" ",[System.StringSplitOptions]::RemoveEmptyEntries)[2]
            $MellanoxFWVersion = $($MellanoxFW | findstr FW).Split(" ",[System.StringSplitOptions]::RemoveEmptyEntries)[1]
            if ($MellanoxFWName -ne $null)
            {
                addRow2 'NIC' $MellanoxFWName "FW" $MellanoxFWVersion ''
            }

            $Mellanoxs = Get-NetAdapter | where InterfaceDescription -Like *Mellanox* | sort InterfaceDescription | Select Name,InterfaceDescription,DriverVersionString
            foreach ($Mellanox in $Mellanoxs)
            {
                addRow2 'NIC' "$($Mellanox[0].InterfaceDescription)" "Driver" $Mellanox[0].DriverVersionString ''
                break;
            }
        }

        #Qlogic
        if ($($NetAdapters | where InterfaceDescription -like "*Qlogic*") -ne $null){
            $QlogicFW = callProcessStdin "$PSScriptRoot\winfwnx2.exe" "" @('q')
            $QlogicFW = $QlogicFW -split "`r`n"       
            for($i=0;$i -lt $QlogicFW.Length;$i++)
            {
                if ($QlogicFW[$i].ToUpper().IndexOf('QLOGIC') -ge 0) 
                {
                    if ($QlogicFW[$i].IndexOf(']') -ge 0) 
                    {
                        $QlogicFWName = $($QlogicFW[$i].Split("]")[1].trim() -replace '\s+ ','_').tostring().split('_')[0]
                        $QlogicFWVersion = $($QlogicFW[$i].Split("]")[1].trim() -replace '\s+ ','_').tostring().split('_')[1]
                        addRow2 'NIC' $QlogicFWName "FW" $QlogicFWVersion ''
                        # Write-Host $($QlogicFW[$i].Split("]")[1].trim() -replace '\s+ ','_').tostring().split('_')[0]
                    }
                }
            }
            $Qlogics = Get-NetAdapter | where InterfaceDescription -Like *QLogic* | sort InterfaceDescription | Select Name,InterfaceDescription,DriverVersionString
            foreach ($Qlogic in $Qlogics)
            {
                addRow2 'NIC' "$($Qlogic[0].InterfaceDescription)" "Driver" $Qlogic[0].DriverVersionString ''
                break;
            }
        }

        #Intel
        $NetAdapters = Get-NetAdapter
        if ($($NetAdapters | where InterfaceDescription -like "*Intel*") -ne $null){
            # $IntelNicFW
            # $IntelNicFWVersion
            # if ($IntelNicFWVersion -ne $null)
            # {
            # addRow2 'NIC' $IntelNicFW "FW" $IntelNicFWVersion ''
            # }

            $IntelNics = Get-NetAdapter | where InterfaceDescription -Like *Intel* | sort InterfaceDescription | Select Name,InterfaceDescription,DriverVersionString
            foreach ($IntelNic in $IntelNics)
            {
                addRow2 'NIC' "$($IntelNic[0].InterfaceDescription)" "Driver" $IntelNic[0].DriverVersionString ''
                break;
            }
        }
    }

    #Storage Controller
    if (($Device -eq 'HBA') -or ($Device -eq ''))
    {
        #Get-WmiObject -Class "Win32_SCSIController" | fl Drivername,Name
        $sasinfo1 = Get-WmiObject Win32_PnPSignedDriver| select devicename, driverversion | where {$_.devicename -like "*SAS3*"}
        if ($sasinfo1.count)
        {
            for ($i=0;$i -lt $sasinfo1.count;$i++)
            {
                $sasinfo = callProcess "$PSScriptRoot\sas3flash.exe" " -c $i -list"
                $sasController = $($sasinfo | findstr /c:"Controller ").Split(':',[System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim()
                $sasfwversion1 = $($sasinfo | findstr /c:"NVDATA Version (Default)").Split(':',[System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim()
                $sasfwversion2 = $($sasinfo | findstr /c:"Firmware Product ID").Split(':',[System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim()
                $sasfwversion3 = $($sasinfo | findstr /c:"Firmware Version").Split(':',[System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim()
                # addRow2 $sasController "Storage Controller" "FW" $sasfwversion1 ''
                addRow2 'Storage Controller' $sasController "FW" $($sasfwversion1 + "_" +$sasfwversion3) ''
                addRow2 'Storage Controller' $sasController "Driver" $($sasinfo1[$i].driverversion) ''
            }
        }
        else {
            $sasinfo = callProcess "$PSScriptRoot\sas3flash.exe" " -list"
            $sasController = $($sasinfo | findstr /c:"Controller ").Split(':',[System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim()
            $sasfwversion1 = $($sasinfo | findstr /c:"NVDATA Version (Default)").Split(':',[System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim()
            $sasfwversion2 = $($sasinfo | findstr /c:"Firmware Product ID").Split(':',[System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim()
            $sasfwversion3 = $($sasinfo | findstr /c:"Firmware Version").Split(':',[System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim()
            # addRow2 $sasController "Storage Controller" "FW" $sasfwversion1 ''
            addRow2 'Storage Controller' $sasController "FW" $($sasfwversion1 + "_" +$sasfwversion3) ''
            addRow2 'Storage Controller' $sasController "Driver" $($sasinfo1.driverversion) ''
        }
    }

    #Expander
    if (($Device -eq 'Expander') -or ($Device -eq ''))
    {
        $expanderinfo = $(callProcessStdin "$PSScriptRoot\g4Xflash_x64.exe" "" @('1','show','quit') | findstr /c:"Firmware Version").Split(':',[System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim()
        addRow2 'Expander' "Expander" "FW" $expanderinfo ''
    }

    #Disk
    if (($Device -eq 'Disk') -or ($Device -eq ''))
    {
        #
        $phds = $null

        $StorageNodes = Get-StorageNode | where Name -like "$(hostname)*"
        
        $disks = Get-Disk | where {($_.BootFromDisk -eq $true) -or ($_.IsBoot -eq $true)} | select UniqueId

        foreach($StorageNode in $StorageNodes)
        {
            $phds += Get-PhysicalDisk -StorageNode $StorageNode -PhysicallyConnected | where {$_.UniqueId -notin $disks.UniqueId}
        }
                
        $phds = $phds | where {$_.UniqueId -notin $disks.UniqueId} | select *
        
        #
        $SSDs = $phds | where {$_.Mediatype -eq "SSD"} #| Get-StorageFirmwareInformation | ft FirmwareVersionInSlot
        foreach ($SSD in $SSDs)
        {
            addRow2 'Disk' $SSD.FriendlyName "FW" $SSD.FirmwareVersion $SSD.SerialNumber
        }

        $HDDs = $phds | where {$_.Mediatype -eq "HDD"} #| Get-StorageFirmwareInformation | ft FirmwareVersionInSlot
        foreach ($HDD in $HDDs)
        {
            addRow2 'Disk' $HDD.FriendlyName "FW" $HDD.FirmwareVersion $HDD.SerialNumber
        }
    }

    $script:table2
}

function Get-ClusterDriver_FW()
{
    [CmdletBinding()]
    [OutputType([Bool])]
    Param
    (
        [Parameter(Mandatory=$false, Position=0)]
        [ValidateSet($false,$true)]
        $SkipInstallPackage=$false
    )

    $SkipInstallPackage = [System.Convert]::ToBoolean($SkipInstallPackage)

    $ClusterNodes = Get-ClusterNode

    if (-not $SkipInstallPackage)
    {
        foreach($ClusterNode in $ClusterNodes)
        {
            Invoke-Command -ComputerName $ClusterNode -ScriptBlock {
                Install-PackageProvider Nuget -Force
                Install-Module QCT-Update-Management -Force
                Import-Module QCT-Update-Management -Force
            } | out-null
        }
    }

    foreach ($CheckItem in $('BIOS','BMC','intelChipset','NIC','HBA','Expander','Disk'))
    {
        foreach($ClusterNode in $ClusterNodes)
        {    
            $Driver_FWs = Invoke-Command -ComputerName $ClusterNode -ScriptBlock {
                param($CheckItem)
                Get-Driver_FW -Device $CheckItem
            } -ArgumentList $CheckItem
            foreach($Driver_FW in $Driver_FWs)
            {
                Write-Host "$ClusterNode $CheckItem :" $Driver_FW.Version            
            }
        }
        Write-Host "----------------------------------------------------------------"
    }
}

function Enable-DiskLocate()
{
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory=$true, Position=0)]
        [string]$SerialNumber=''
    )
    $allController = callProcess "$PSScriptRoot\storcli.exe" "show ctrlcount"
    $ctrlCount = $($($allController[1] -split "`r`n" | Select-String -Pattern 'Controller') -split '=')[1].ToString().Trim()
    for ($i=0;$i -lt $ctrlCount;$i++)
    {
        $alldiskStr = callProcess "$PSScriptRoot\storcli.exe" "/c$i show all"
        $diskStr = $alldiskStr[1] -split "`r`n" | Select-String -Pattern $SerialNumber -Context 6
        if ($diskStr)
        {
            $disklocation = $($($diskStr.ToString() -split "`r`n" | Select-String "Drive").ToString().Trim() -split " ")[1]
            if ($disklocation)
            {      
                return callProcess "$PSScriptRoot\storcli.exe" "$disklocation start locate"
            }
        }
    }
    
    return "Failed."
}

function Disable-DiskLocate()
{
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory=$false, Position=0)]
        [string]$SerialNumber=''
    )
    $allController = callProcess "$PSScriptRoot\storcli.exe" "show ctrlcount"
    $ctrlCount = $($($allController[1] -split "`r`n" | Select-String -Pattern 'Controller') -split '=')[1].ToString().Trim()
    for ($i=0;$i -lt $ctrlCount;$i++)
    {
        $alldiskStr = callProcess "$PSScriptRoot\storcli.exe" "/c$i show all"
        $diskStr = $alldiskStr[1] -split "`r`n" | Select-String -Pattern $SerialNumber -Context 6
        if ($diskStr)
        {
            $disklocation = $($($diskStr.ToString() -split "`r`n" | Select-String "Drive").ToString().Trim() -split " ")[1]
            if ($disklocation)
            {      
                return callProcess "$PSScriptRoot\storcli.exe" "$disklocation stop locate"
            }
        }
    }
    
    return "Failed."
}

function checkModel($Model)
{
    $Brand = 'unknow'
    $Series = 'unknow'
    if ($Model -like '*INTEL*')
    {
        $Brand = 'INTEL'
    }
    elseif ($Model -like '*SAMSUNG*')
    {
        $Brand = 'SAMSUNG'
    }
    elseif ($Model -like '*TOSHIBA*')
    {
        $Brand = 'TOSHIBA'
    }
    else
    {
        $Brand = 'unknow'
    }

    if ($Brand -eq 'INTEL')
    {
        if ($Model -like '*SSDSC2BA*4*')
        {
            $Series = 'INTEL_S3710'
        }
        elseif ($Model -like '*SSDSC2BB*6*')
        {
            $Series = 'INTEL_S3510'
        }
        elseif (($Model -like '*SSDSC2BB*7*') -or ($Model -like '*SSDSCKJB*7*'))
        {
            $Series = 'INTEL_S3520'
        }
        elseif ($Model -like '*SSDSC2KB*7*')
        {
            $Series = 'INTEL_S4500'
        }
        elseif ($Model -like '*SSDSC2KB*8*')
        {
            $Series = 'INTEL_D3-S4510'
        }
        elseif ($Model -like '*SSDSC2KG*7*')
        {
            $Series = 'INTEL_S4600'
        }
        elseif ($Model -like '*SSDSC2KG*8*')
        {
            $Series = 'INTEL_D3-S4610'
        }        
        elseif ($Model -like '*SSDPEKKA512G7*')
        {
            $Series = 'INTEL_P3100'
        }
        elseif (($Model -like '*SSDPE2MD*4*') -or ($Model -like '*SSDPEDMD*4*'))
        {
            $Series = 'INTEL_P3700'
        }
        elseif (($Model -like '*SSDPEDKX*7*') -or ($Model -like '*SSDPE2KX*7*'))
        {
            $Series = 'INTEL_P4500'
        }
        elseif ($Model -like '*SSDPE7KX*7*')
        {
            $Series = 'INTEL_P4501'
        }
        elseif ($Model -like '*SSDPE2KX*8*')
        {
            $Series = 'INTEL_P4510'
        }
        elseif ($Model -like '*SSDPELKX*8*')
        {
            $Series = 'INTEL_P4511'
        }
        elseif (($Model -like '*SSDPE2KE*7*') -or ($Model -like '*SSDPEDKE*7*'))
        {
            $Series = 'INTEL_P4600'
        }
        elseif ($Model -like '*SSDPE7KE*7*')
        {
            $Series = 'INTEL_P4601'
        }
        elseif ($Model -like '*SSDPE2KE*8*')
        {
            $Series = 'INTEL_P4610'
        }
        elseif (($Model -like '*SSDPED1K*A*') -or ($Model -like '*SSDPE21K*A*'))
        {
            $Series = 'INTEL_P4800X'
        }
        else
        {
            $Series = 'unknow'
        }
    }
    elseif ($Brand -eq 'SAMSUNG')
    {
        if ($Model -like '*MZ7KM*HA*')
        {
            $Series = 'SAMSUNG_SM863'
        }
        elseif ($Model -like '*MZ7KM*HM*')
        {
            $Series = 'SAMSUNG_SM863a'
        }
        elseif ($Model -like '*MZ7KH*HA*')
        {
            $Series = 'SAMSUNG_SM883'
        }
        elseif ($Model -like '*MZQLB*H*')
        {
            $Series = 'SAMSUNG_PM983'
        }
        elseif ($Model -like '*MZQLW*H*')
        {
            $Series = 'SAMSUNG_PM963'
        }
        elseif ($Model -like '*MZ7LH*H*')
        {
            $Series = 'SAMSUNG_PM883'
        }
        elseif ($Model -like '*MZ7LM*H*')
        {
            $Series = 'SAMSUNG_PM863a'
        }
        elseif (($Model -like '*MZPLL*HMLA*') -or ($Model -like '*MZPLL*HAJQ*') -or ($Model -like '*MZWLL*HMLA*') -or ($Model -like '*MZWLL*HAJQ*'))
        {
            $Series = 'SAMSUNG_PM1725b'
        }
        elseif (($Model -like '*MZPLL*HMLS*') -or ($Model -like '*MZPLL*HEHP*') -or ($Model -like '*MZWLL*HEHP*') -or ($Model -like '*MZWLL*HMJP*') -or ($Model -like '*MZWLL*HMLS*'))
        {
            $Series = 'SAMSUNG_PM1725a'
        }
        elseif ($Model -like '*MZILT*H*')
        {
            $Series = 'SAMSUNG_PM1643'
        }
        elseif ($Model -like '*MZILS*H*')
        {
            $Series = 'SAMSUNG_PM1633a'
        }
        else
        {
            $Series = 'unknow'
        }
    }
    elseif ($Brand -eq 'TOSHIBA')
    {
        if ($Model -like '*THNSNJ800PCSZ*')
        {
            $Series = 'Toshiba_HK3E2'
        }
        else
        {
            $Series = 'unknow'
        }
    }
    else
    {
        if ($Model -like '*ST8000*')
        {
            $Series = 'SEAGATE_Archive_HDD_v2'
        }
        else
        {
            $Series = 'unknow'
        }
    }
    return $Series
}

function checkLifetime($smartInfo)
{
    $Wear_Leveling_Count = 0
    $Wear_Leveling_Count_Threshold = 0
    if ($($smartInfo -split "`r`n" | Select-String "Wear_Leveling_Count") -ne $null)
    {
        # Samsung SSD 99 to 0
        $Wear_Leveling_Count = [int] $($smartInfo -split "`r`n" | Select-String "Wear_Leveling_Count").ToString().Split(' ',[System.StringSplitOptions]::RemoveEmptyEntries)[3].Trim()
        $Wear_Leveling_Count_Threshold = [int] $($smartInfo -split "`r`n" | Select-String "Wear_Leveling_Count").ToString().Split(' ',[System.StringSplitOptions]::RemoveEmptyEntries)[5].Trim()
    }
    elseif ($($smartInfo -split "`r`n" | Select-String "Media_Wearout_Indicator") -ne $null)
    {
        # INTEL SSD 100 to 1
        $Wear_Leveling_Count = $($smartInfo -split "`r`n" | Select-String "Media_Wearout_Indicator").ToString().Split(' ',[System.StringSplitOptions]::RemoveEmptyEntries)[3].Trim()
        $Wear_Leveling_Count_Threshold = $($smartInfo -split "`r`n" | Select-String "Media_Wearout_Indicator").ToString().Split(' ',[System.StringSplitOptions]::RemoveEmptyEntries)[5].Trim()
    }
    elseif ($($smartInfo -split "`r`n" | Select-String "Available Spare:") -ne $null)
    {
        #INTEL NVME
        $Wear_Leveling_Count = $($smartInfo -split "`r`n" | Select-String "Available Spare:").ToString().Split(':',[System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim().Replace('%','')
        $Wear_Leveling_Count_Threshold = $($smartInfo -split "`r`n" | Select-String "Available Spare Threshold:").ToString().Split(':',[System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim().Replace('%','')
    }
    else 
    {
        $Wear_Leveling_Count = 'none'
        $Wear_Leveling_Count_Threshold = 'none'
    }

    $Wear_Leveling_Count
    $Wear_Leveling_Count_Threshold
}

function checkPoweronHour($smartInfo)
{
    if ($smartInfo -split "`r`n" | Select-String "Power_On_Hours")
    {
        $Power_On_Hours = $($smartInfo -split "`r`n" | Select-String "Power_On_Hours").ToString().Split(' ',[System.StringSplitOptions]::RemoveEmptyEntries)[9].Trim()
    }
    elseif ($smartInfo -split "`r`n" | Select-String "Power On Hours:")
    {
        $Power_On_Hours = $($smartInfo -split "`r`n" | Select-String "Power On Hours:").ToString().Split(':',[System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim()
    }
    else
    {
        $Power_On_Hours = "none"
    }

    if ($smartInfo -split "`r`n" | Select-String "Power_Cycle_Count")
    {
        $Power_Cycle_Count = $($smartInfo -split "`r`n" | Select-String "Power_Cycle_Count").ToString().Split(' ',[System.StringSplitOptions]::RemoveEmptyEntries)[9].Trim()
    }
    elseif ($smartInfo -split "`r`n" | Select-String "Power Cycles:")
    {
        $Power_Cycle_Count = $($smartInfo -split "`r`n" | Select-String "Power Cycles:").ToString().Split(':',[System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim()
    }
    else
    {
        $Power_Cycle_Count = "none"
    }
    $Power_On_Hours
    $Power_Cycle_Count
}

function checkUserCapacity($smartInfo)
{
    if ($smartInfo -split "`r`n" | Select-String "User Capacity")
    {
        $userCapacity = $($smartInfo -split "`r`n" | Select-String "User Capacity").ToString().Split('[',[System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim().Replace(']','').Replace(' ','_')
    }
    elseif ($smartInfo -split "`r`n" | Select-String "Namespace 1 Size/Capacity")
    {
        $userCapacity = $($smartInfo -split "`r`n" | Select-String "Namespace 1 Size/Capacity").ToString().Split('[',[System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim().Replace(']','').Replace(' ','_')
    }
    else
    {
        $userCapacity = 'none'
    }
    $userCapacity
}

function Write-HostColored() {
    [CmdletBinding(ConfirmImpact='None', SupportsShouldProcess=$false, SupportsTransactions=$false)]
    param(
        [parameter(Position=0, ValueFromPipeline=$true)]
        [string[]] $Text
        ,
        [switch] $NoNewline
        ,
        [ConsoleColor] $BackgroundColor = $host.UI.RawUI.BackgroundColor
        ,
        [ConsoleColor] $ForegroundColor = $host.UI.RawUI.ForegroundColor
    )

    begin {
        if ($Text -ne $null) {
            $Text = "$Text"
        }
    }

    process {
        if ($Text) {

            $curFgColor = $ForegroundColor
            $curBgColor = $BackgroundColor

            $tokens = $Text.split("#")

            $prevWasColorSpec = $false
            foreach($token in $tokens) {

                if (-not $prevWasColorSpec -and $token -match '^([a-z]+)(:([a-z]+))?$') { # a potential color spec.
                    try {
                        $curFgColor = [ConsoleColor]  $matches[1]
                        $prevWasColorSpec = $true
                    } catch {}
                    if ($matches[3]) {
                        try {
                            $curBgColor = [ConsoleColor]  $matches[3]
                            $prevWasColorSpec = $true
                        } catch {}
                    }
                    if ($prevWasColorSpec) {
                        continue                    
                    }
                }

                $prevWasColorSpec = $false

                if ($token) {
                    $argsHash = @{}
                    if ([int] $curFgColor -ne -1) { $argsHash += @{ 'ForegroundColor' = $curFgColor } }
                    if ([int] $curBgColor -ne -1) { $argsHash += @{ 'BackgroundColor' = $curBgColor } }
                    Write-Host -NoNewline @argsHash $token
                }

                $curFgColor = $ForegroundColor
                $curBgColor = $BackgroundColor

            }
        }
        if (-not $NoNewLine) { write-host }
    }
}

function Get-SmartInfo()
{
    [CmdletBinding()]
    [OutputType([Bool])]
    Param
    (
        [Parameter(Mandatory=$false, Position=0)]
        [ValidateSet($false,$true)]
        $Color=$false
    )

    $Color = [System.Convert]::ToBoolean($Color)
    $smartPath = "$PSScriptRoot\smartctl.exe"
    $disks = callProcess $smartPath "--scan" 1
    $diskArray = @()

    foreach($disk in $($disks -split "`r`n"))
    {
        if ($($disk | select-string '/dev/')) 
        {
            $disk = $($disk -split ' ' )[0]
            $smartInfo = callProcess $smartPath " -A -i -H $disk -s on" 0.2
            if (-not $($smartInfo -split "`r`n" | Select-String "Open failed"))
            {
                $smartResult = $($smartInfo -split "`r`n" | Select-String "SMART overall-health self-assessment test result").ToString().Split(':',[System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim()

                if ($smartInfo -split "`r`n" | Select-String "Sector Size")
                {
                    $sectorSize = [int] $($smartInfo -split "`r`n" | Select-String "Sector Size").ToString().Split(' ',[System.StringSplitOptions]::RemoveEmptyEntries)[2]
                }
                else
                {
                    $sectorSize = [int] 512
                }

                $Model = $($smartInfo -split "`r`n" | Select-String "Device Model").ToString().Split(':',[System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim().ToUpper().Replace(' ','_')
                $serialNumber = $($smartInfo -split "`r`n" | Select-String "Serial Number").ToString().Split(':',[System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim()
                                 
                $return = checkPoweronHour $smartInfo
                $Power_On_Hours = $return[0]
                $Power_Cycle_Count = $return[1]

                $userCapacity = checkUserCapacity $smartInfo                
                
                if ($smartInfo -split "`r`n" | Select-String "Rotation Rate")
                {
                    $RotationRate = $($smartInfo -split "`r`n" | Select-String "Rotation Rate").ToString().Split(':',[System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim().ToUpper()
                }
                else
                {
                    $RotationRate = 'none'
                }
            
                $Series = checkModel $Model

                if ($RotationRate.IndexOf('RPM') -ge 0)
                {
                    $diskType = 'HDD'
                }
                else
                {
                    $diskType = 'SSD'
                }
        
                $Reallocated_Sector_Ct = 0
                $Current_Pending_Sector = 0
                $Offline_Uncorrectable = 0
                if ($($smartInfo -split "`r`n" | Select-String "Reallocated_Sector_Ct") -ne $null)
                {
                    $Reallocated_Sector_Ct = [int] $($smartInfo -split "`r`n" | Select-String "Reallocated_Sector_Ct").ToString().Split(' ',[System.StringSplitOptions]::RemoveEmptyEntries)[9].Replace('[','')
                }
                if ($($smartInfo -split "`r`n" | Select-String "Current_Pending_Sector") -ne $null)
                {
                    $Current_Pending_Sector = [int] $($smartInfo -split "`r`n" | Select-String "Current_Pending_Sector").ToString().Split(' ',[System.StringSplitOptions]::RemoveEmptyEntries)[9].Replace('[','')
                }
                if ($($smartInfo -split "`r`n" | Select-String "Offline_Uncorrectable") -ne $null)
                {
                    $Offline_Uncorrectable = [int] $($smartInfo -split "`r`n" | Select-String "Offline_Uncorrectable").ToString().Split(' ',[System.StringSplitOptions]::RemoveEmptyEntries)[9].Replace('[','') 
                }
                
                if ($($smartInfo -split "`r`n" | Select-String "Command_Timeout") -ne $null)
                {
                    $Command_Timeout = [long] $($smartInfo -split "`r`n" | Select-String "Command_Timeout").ToString().Split(' ',[System.StringSplitOptions]::RemoveEmptyEntries)[9].Replace('[','') 
                }
                if ($($smartInfo -split "`r`n" | Select-String "Spin_Retry_Count") -ne $null)
                {
                    $Spin_Retry_Count = [int] $($smartInfo -split "`r`n" | Select-String "Spin_Retry_Count").ToString().Split(' ',[System.StringSplitOptions]::RemoveEmptyEntries)[9].Replace('[','') 
                }
                if ($($smartInfo -split "`r`n" | Select-String "End-to-End_Error") -ne $null)
                {
                    $End_to_End_Error = [int] $($smartInfo -split "`r`n" | Select-String "End-to-End_Error").ToString().Split(' ',[System.StringSplitOptions]::RemoveEmptyEntries)[9].Replace('[','') 
                }                

                $smartCaution = ''
                if ($Reallocated_Sector_Ct+$Current_Pending_Sector+$Offline_Uncorrectable+$Spin_Retry_Count+$End_to_End_Error -gt 0)
                {
                    $smartResult = 'Caution'
                    #$smartCaution = 'Reallocated Sector Ct:' + $Reallocated_Sector_Ct + "`n" + 'Current Pending Sector:' + $Current_Pending_Sector + "`n" +'Offline Uncorrectable:' + $Offline_Uncorrectable
                    if ($Reallocated_Sector_Ct -gt 0)
                    {
                        $smartCaution += 'Reallocated Sector Ct:' + $Reallocated_Sector_Ct + "`n"
                    }
                    if ($Current_Pending_Sector -gt 0)
                    {
                        $smartCaution += 'Current Pending Sector:' + $Current_Pending_Sector + "`n"
                    }
                    if ($Offline_Uncorrectable -gt 0)
                    {
                        $smartCaution += 'Offline Uncorrectable:' + $Offline_Uncorrectable + "`n"
                    }
                    if ($Spin_Retry_Count -gt 0)
                    {
                        $smartCaution += 'Spin Retry Count:' + $Spin_Retry_Count + "`n"
                    }
                    if ($End_to_End_Error -gt 0)
                    {
                        $smartCaution += 'End-to-End error:' + $End_to_End_Error + "`n"
                    }
                    if ($Command_Timeout -gt 0)
                    {
                        $smartCaution += 'Command Timeout:' + $Command_Timeout + "`n"
                    }
                }

                $Total_LBAs_Written = [long] $(($smartInfo -split "`r`n") -like "241 *").ToString().Split(' ',[System.StringSplitOptions]::RemoveEmptyEntries)[9]
                $Total_Host_Write = 0
                if (($Model.IndexOf('INTEL') -ge 0) -or ($Model.IndexOf('TOSHIBA') -ge 0))
                {
                    $Total_Host_Write = "{0:n2}" -f $($Total_LBAs_Written*32/1024)  #GB
                }
                else
                {
                    $Total_Host_Write = "{0:N2}" -f $($Total_LBAs_Written*$($sectorSize/1024)/1024/1024)  #GB
                }

                if ($Total_Host_Write -eq "0.00")
                {
                    $Total_Host_Write = "N/A"
                }
                else
                {
                    $Total_Host_Write = "$Total_Host_Write GB"
                }

                $return = checkLifetime $smartInfo
                $Wear_Leveling_Count = $return[0]
                $Wear_Leveling_Count_Threshold = $return[1]

                if ($Color -and ($smartResult -ne 'PASSED'))
                {
                    Write-HostColored "$disk $Series $userCapacity $Model #yellow#$smartResult# $serialNumber $Total_Host_Write $Wear_Leveling_Count $Wear_Leveling_Count_Threshold $Power_On_Hours $Power_Cycle_Count"
                }
                #else
                #{
                # Write-Host "$disk $Series $userCapacity $Model $smartResult $serialNumber $Total_Host_Write $Wear_Leveling_Count $Wear_Leveling_Count_Threshold $Power_On_Hours $Power_Cycle_Count"
                #}
                $diskObj = New-Object -TypeName PSObject
                $diskObj | Add-Member -Name 'Name' -MemberType Noteproperty  -Value $disk
                $diskObj | Add-Member -Name 'Series' -MemberType Noteproperty  -Value $Series
                $diskObj | Add-Member -Name 'userCapacity' -MemberType Noteproperty  -Value $disk
                $diskObj | Add-Member -Name 'Model' -MemberType Noteproperty  -Value $Model
                $diskObj | Add-Member -Name 'smartResult' -MemberType Noteproperty  -Value $smartResult

                $diskObj | Add-Member -Name 'serialNumber' -MemberType Noteproperty  -Value $serialNumber
                $diskObj | Add-Member -Name 'Total_Host_Write' -MemberType Noteproperty  -Value $Total_Host_Write
                $diskObj | Add-Member -Name 'Wear_Leveling_Count' -MemberType Noteproperty  -Value $Wear_Leveling_Count
                $diskObj | Add-Member -Name 'Wear_Leveling_Count_Threshold' -MemberType Noteproperty  -Value $Wear_Leveling_Count_Threshold
                $diskObj | Add-Member -Name 'Power_On_Hours' -MemberType Noteproperty  -Value $Power_On_Hours                             

                $diskObj | Add-Member -Name 'Power_Cycle_Count' -MemberType Noteproperty  -Value $Power_Cycle_Count
                $diskObj | Add-Member -Name 'SmartCaution' -MemberType Noteproperty  -Value $smartCaution   
                $diskArray += $diskObj
            }
        }
    }

    $disks = callProcess $smartPath "-d nvme --scan" 1

    foreach($disk in $($disks -split "`r`n"))
    {
        if ($($disk | select-string '/dev/')) 
        {
            $disk = $($disk -split ' ' )[0]
            $smartInfo = callProcess $smartPath " -A -i -H $disk -s on" 0.3

            $smartResult = $($smartInfo -split "`r`n" | Select-String "SMART overall-health self-assessment test result").ToString().Split(':',[System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim()

            $sectorSize = [int] 512 # $($smartInfo -split "`r`n" | Select-String "Sector Size").ToString().Split(' ',[System.StringSplitOptions]::RemoveEmptyEntries)[2]

            $Model = $($smartInfo -split "`r`n" | Select-String "Model").ToString().Split(':',[System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim().ToUpper().Replace(' ','_')
            $serialNumber = $($smartInfo -split "`r`n" | Select-String "Serial Number").ToString().Split(':',[System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim()
            
            $return = checkPoweronHour $smartInfo
            $Power_On_Hours = $return[0]
            $Power_Cycle_Count = $return[1]

            $userCapacity = checkUserCapacity $smartInfo

            $Series = checkModel $Model

            if ($($smartInfo -split "`r`n" | Select-String "Data Units Written").ToString().IndexOf('PB') -ge 0)
            {
                $Data_Units_Written = "{0:n2}" -f $(1024 * 1024 * [int] $($smartInfo -split "`r`n" | Select-String "Data Units Written").ToString().Split(' ',[System.StringSplitOptions]::RemoveEmptyEntries)[4].Replace('[',''))
            }
            elseif ($($smartInfo -split "`r`n" | Select-String "Data Units Written").ToString().IndexOf('TB') -ge 0)
            {
                $Data_Units_Written = "{0:n2}" -f $(1024 * [int] $($smartInfo -split "`r`n" | Select-String "Data Units Written").ToString().Split(' ',[System.StringSplitOptions]::RemoveEmptyEntries)[4].Replace('[',''))
            }
            elseif ($($smartInfo -split "`r`n" | Select-String "Data Units Written").ToString().IndexOf('GB') -ge 0)
            {
                $Data_Units_Written = "{0:n2}" -f $([int] $($smartInfo -split "`r`n" | Select-String "Data Units Written").ToString().Split(' ',[System.StringSplitOptions]::RemoveEmptyEntries)[4].Replace('[',''))
            }

            $smartCaution = ''
            $return = checkLifetime $smartInfo
            $Wear_Leveling_Count = $return[0]
            $Wear_Leveling_Count_Threshold = $return[1]
        
            if ($Color -and ($smartResult -ne 'PASSED'))
            {
                Write-HostColored "$disk $Series $userCapacity $Model #yellow#$smartResult# $serialNumber $Data_Units_Written $Wear_Leveling_Count $Wear_Leveling_Count_Threshold $Power_On_Hours $Power_Cycle_Count"
            }
            #else
            #{
            # Write-Host "$disk $Series $userCapacity $Model $smartResult $serialNumber $Data_Units_Written $Wear_Leveling_Count $Wear_Leveling_Count_Threshold $Power_On_Hours $Power_Cycle_Count"
            #}
            $diskObj = New-Object -TypeName PSObject
            $diskObj | Add-Member -Name 'Name' -MemberType Noteproperty  -Value $disk
            $diskObj | Add-Member -Name 'Series' -MemberType Noteproperty  -Value $Series
            $diskObj | Add-Member -Name 'userCapacity' -MemberType Noteproperty  -Value $userCapacity
            $diskObj | Add-Member -Name 'Model' -MemberType Noteproperty  -Value $Model
            $diskObj | Add-Member -Name 'smartResult' -MemberType Noteproperty  -Value $smartResult

            $diskObj | Add-Member -Name 'serialNumber' -MemberType Noteproperty  -Value $serialNumber
            $diskObj | Add-Member -Name 'Total_Host_Write' -MemberType Noteproperty  -Value "$Data_Units_Written GB"
            $diskObj | Add-Member -Name 'Wear_Leveling_Count' -MemberType Noteproperty  -Value $Wear_Leveling_Count
            $diskObj | Add-Member -Name 'Wear_Leveling_Count_Threshold' -MemberType Noteproperty  -Value $Wear_Leveling_Count_Threshold
            $diskObj | Add-Member -Name 'Power_On_Hours' -MemberType Noteproperty  -Value $Power_On_Hours
            
            $diskObj | Add-Member -Name 'Power_Cycle_Count' -MemberType Noteproperty  -Value $Power_Cycle_Count
            $diskObj | Add-Member -Name 'SmartCaution' -MemberType Noteproperty  -Value $smartCaution
        $diskArray += $diskObj
        }
    }
    $diskArray
}


function Update-OEMFirmware-LSI
{
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory=$true, Position=0)]
        [string]$FwPath='',
        [Parameter(Mandatory=$true, Position=1)]
        [string]$Mptsas='',
        [Parameter(Mandatory=$true, Position=2)]
        [string]$Mptx64=''
    )

    $script:tableFW = @()
    function addRowFW($FWType, $FWVersion, $FWVersionNew)
    {
        $row2 = New-Object -TypeName PSObject
        $row2 | Add-Member -Name 'Type' -MemberType Noteproperty  -Value $FWType
        $row2 | Add-Member -Name 'FWVersion' -MemberType Noteproperty  -Value $FWVersion
        $row2 | Add-Member -Name 'FWVersionNew' -MemberType Noteproperty  -Value $FWVersionNew

        $script:tableFW += $row2
    }

    $OEMStorageControllPath = $PSScriptRoot

    $sasinfo = callProcess "$OEMStorageControllPath\sas3flash.exe" "-list" + [System.Environment]::NewLine #Invoke-Expression -Command:"cmd.exe /C $OEMStorageControllPath\sas3flash.exe -list"
    $sasinfo1 = Get-WmiObject Win32_PnPSignedDriver| select devicename, driverversion | where {$_.devicename -like "*SAS3*"}
    $LsiFWVersion = $($sasinfo -split "`r`n" | Select-String "NVDATA Version" -CaseSensitive).line.Split(":",[System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim()
    $LsiBIOSVersion = $($sasinfo -split "`r`n" | Select-String "BIOS" -CaseSensitive).line.Split(":",[System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim()
    $LsiUEFIVersion = $($sasinfo -split "`r`n" | Select-String "UEFI" -CaseSensitive).line.Split(":",[System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim()
    if ($sasinfo1.count)
    {
        
        for ($i=0;$i -lt $sasinfo1.count;$i++)
        {
            #LSI Firmware
            $sasinfo += callProcess "$OEMStorageControllPath\sas3flash.exe" " -c $i -f $FwPath" + [System.Environment]::NewLine #Invoke-Expression -Command:"cmd.exe /C $OEMStorageControllPath\sas3flash.exe -f $FwPath" -Verbose
            Sleep 5
            #LSI Legacy Mode BIOS
            $sasinfo += callProcess "$OEMStorageControllPath\sas3flash.exe" " -c $i -b $Mptsas" + [System.Environment]::NewLine #Invoke-Expression -Command:"cmd.exe /C $OEMStorageControllPath\sas3flash.exe -b $Mptsas" -Verbose
            Sleep 5
            #LSI UEFI Mode BIOS
            $sasinfo += callProcess "$OEMStorageControllPath\sas3flash.exe" " -c $i -b $Mptx64" + [System.Environment]::NewLine #Invoke-Expression -Command:"cmd.exe /C $OEMStorageControllPath\sas3flash.exe -b $Mptx64" -Verbose
        }
    }
    else {
        #LSI Firmware
        $sasinfo += callProcess "$OEMStorageControllPath\sas3flash.exe" " -f $FwPath" + [System.Environment]::NewLine
        Sleep 5
        #LSI Legacy Mode BIOS
        $sasinfo += callProcess "$OEMStorageControllPath\sas3flash.exe" " -b $Mptsas" + [System.Environment]::NewLine
        Sleep 5
        #LSI UEFI Mode BIOS
        $sasinfo += callProcess "$OEMStorageControllPath\sas3flash.exe" " -b $Mptx64" + [System.Environment]::NewLine
    }
    $sasinfoNew = callProcess "$OEMStorageControllPath\sas3flash.exe" "-list" #Invoke-Expression -Command:"cmd.exe /C $OEMStorageControllPath\sas3flash.exe -list"
    $LsiFWVersionNew = $($sasinfoNew -split "`r`n" | Select-String "NVDATA Version" -CaseSensitive).line.Split(":",[System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim()
    $LsiBIOSVersionNew = $($sasinfoNew -split "`r`n" | Select-String "BIOS" -CaseSensitive).line.Split(":",[System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim()
    $LsiUEFIVersionNew = $($sasinfoNew -split "`r`n" | Select-String "UEFI" -CaseSensitive).line.Split(":",[System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim()

    addRowFW 'BIOS' $LsiBIOSVersion $LsiBIOSVersionNew
    addRowFW 'UEFI' $LsiUEFIVersion $LsiUEFIVersionNew
    addRowFW 'FW' $LsiFWVersion $LsiFWVersionNew

    $script:tableFW
    $script:rebootneed=1
}

function Update-OEMFirmware-Mellanox
{
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory=$true, Position=0)]
        [string]$FwPath=''
    )

    $script:tableFW = @()
    function addRowFW($FWType, $FWVersion, $FWVersionNew)
    {
        $row2 = New-Object -TypeName PSObject
        $row2 | Add-Member -Name 'Type' -MemberType Noteproperty  -Value $FWType
        $row2 | Add-Member -Name 'FWVersion' -MemberType Noteproperty  -Value $FWVersion
        $row2 | Add-Member -Name 'FWVersionNew' -MemberType Noteproperty  -Value $FWVersionNew

        $script:tableFW += $row2
    }

    #Mellanox Firmware Update
    $OEMMellanoxPath = $PSScriptRoot
    
    $nicinfo = callProcess "$OEMMellanoxPath\mlxup.exe" " -query --sfx-extract-dir C:\Windows\Temp"
    $MellanoxFWName = $($nicinfo -split "`r`n" | Select-String 'Device Type').line.Split(" ",[System.StringSplitOptions]::RemoveEmptyEntries)[2]
    $MellanoxFWVersion = $($nicinfo -split "`r`n" | Select-String "FW" -CaseSensitive).line.Split(" ",[System.StringSplitOptions]::RemoveEmptyEntries)[1]
    $MellanoxCLPVersion = $($nicinfo -split "`r`n" | Select-String "CLP" -CaseSensitive).line.Split(" ",[System.StringSplitOptions]::RemoveEmptyEntries)[1]
    $MellanoxPXEVersion = $($nicinfo -split "`r`n" | Select-String "PXE" -CaseSensitive).line.Split(" ",[System.StringSplitOptions]::RemoveEmptyEntries)[1]
    $MellanoxUEFIVersion = $($nicinfo -split "`r`n" | Select-String "UEFI" -CaseSensitive).line.Split(" ",[System.StringSplitOptions]::RemoveEmptyEntries)[1]

    $MellanoxFWPSID = $($nicinfo -split "`r`n" | Select-String "PSID" -CaseSensitive).line.Split(" ",[System.StringSplitOptions]::RemoveEmptyEntries)[1]
    $MellanoxPDName = $($nicinfo -split "`r`n" | Select-String "PCI Device Name" -CaseSensitive).line.Split(":",[System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim()
    
    if ($($nicinfo | Select-String "ConnectX3Pro").length){
        $result = callProcess "$OEMMellanoxPath\mlxup.exe" " -i $FwPath --sfx-extract-dir C:\Windows\Temp -u -f -y"
        if ($($result | Select-String "100%Done").length)
        {
            $resultNew = callProcess "$OEMMellanoxPath\mlxup.exe"
            $MellanoxFWVersionNew = $($resultNew -split "`r`n" | Select-String "FW" -CaseSensitive).line.Split(" ",[System.StringSplitOptions]::RemoveEmptyEntries)[1]
            $MellanoxCLPVersionNew = $($resultNew -split "`r`n" | Select-String "CLP" -CaseSensitive).line.Split(" ",[System.StringSplitOptions]::RemoveEmptyEntries)[1]
            $MellanoxPXEVersionNew = $($resultNew -split "`r`n" | Select-String "PXE" -CaseSensitive).line.Split(" ",[System.StringSplitOptions]::RemoveEmptyEntries)[1]
            $MellanoxUEFIVersionNew = $($resultNew -split "`r`n" | Select-String "UEFI" -CaseSensitive).line.Split(" ",[System.StringSplitOptions]::RemoveEmptyEntries)[1]

            addRowFW 'CLP' $MellanoxCLPVersion $MellanoxCLPVersionNew
            addRowFW 'PXE' $MellanoxPXEVersion $MellanoxPXEVersionNew
            addRowFW 'UEFI' $MellanoxUEFIVersion $MellanoxUEFIVersionNew
            addRowFW 'FW' $MellanoxFWVersion $MellanoxFWVersionNew
            
            $script:tableFW
        }
        $script:rebootneed=1
    }elseif ($($nicinfo | Select-String "ConnectX4").length){
        $result = callProcess "$OEMMellanoxPath\mlxup.exe" " -i $FwPath --sfx-extract-dir C:\Windows\Temp -u -f -y"
        if ($($result | Select-String "100%Done").length)
        {
            Reset-OEMFirmware-Mellanox -Verbose
            $resultNew = callProcess "$OEMMellanoxPath\mlxup.exe"
            $MellanoxFWVersionNew = $($resultNew -split "`r`n" | Select-String "FW" -CaseSensitive).line.Split(" ",[System.StringSplitOptions]::RemoveEmptyEntries)[1]
            $MellanoxCLPVersionNew = $($resultNew -split "`r`n" | Select-String "CLP" -CaseSensitive).line.Split(" ",[System.StringSplitOptions]::RemoveEmptyEntries)[1]
            $MellanoxPXEVersionNew = $($resultNew -split "`r`n" | Select-String "PXE" -CaseSensitive).line.Split(" ",[System.StringSplitOptions]::RemoveEmptyEntries)[1]
            $MellanoxUEFIVersionNew = $($resultNew -split "`r`n" | Select-String "UEFI" -CaseSensitive).line.Split(" ",[System.StringSplitOptions]::RemoveEmptyEntries)[1]

            addRowFW 'CLP' $MellanoxCLPVersion $MellanoxCLPVersionNew
            addRowFW 'PXE' $MellanoxPXEVersion $MellanoxPXEVersionNew
            addRowFW 'UEFI' $MellanoxUEFIVersion $MellanoxUEFIVersionNew
            addRowFW 'FW' $MellanoxFWVersion $MellanoxFWVersionNew
            
            $script:tableFW
        }
        $script:rebootneed=1
    }
}

function Reset-OEMFirmware-Mellanox
{
    function addRowFW($FWType, $FWVersion, $FWVersionNew)
    {
        $row2 = New-Object -TypeName PSObject
        $row2 | Add-Member -Name 'Type' -MemberType Noteproperty  -Value $FWType
        $row2 | Add-Member -Name 'FWVersion' -MemberType Noteproperty  -Value $FWVersion
        $row2 | Add-Member -Name 'FWVersionNew' -MemberType Noteproperty  -Value $FWVersionNew

        $script:tableFW += $row2
    }
    #Mellanox Firmware Update
    $OEMMellanoxPath = $PSScriptRoot
    
    $nicinfo = callProcess "$OEMMellanoxPath\mlxup.exe" " -query --sfx-extract-dir C:\Windows\Temp"
    $MellanoxFWName = $($nicinfo -split "`r`n" | Select-String 'Device Type').line.Split(" ",[System.StringSplitOptions]::RemoveEmptyEntries)[2]
    $MellanoxFWVersion = $($nicinfo -split "`r`n" | Select-String "FW" -CaseSensitive).line.Split(" ",[System.StringSplitOptions]::RemoveEmptyEntries)[1]
    $MellanoxFWPSID = $($nicinfo -split "`r`n" | Select-String "PSID" -CaseSensitive).line.Split(" ",[System.StringSplitOptions]::RemoveEmptyEntries)[1]
    $MellanoxPDName = $($nicinfo -split "`r`n" | Select-String "PCI Device Name" -CaseSensitive).line.Split(":",[System.StringSplitOptions]::RemoveEmptyEntries)[1].Trim()
    # $nicinfo
    if ($($nicinfo | Select-String "ConnectX4").length){
        $result = callProcessWorkingDirectory "$OEMMellanoxPath\mlxfwreset.exe" " -d mt4117_pciconf0 -l 3 reset -y"

    $resultNew = callProcess "$OEMMellanoxPath\mlxup.exe"
    $MellanoxFWVersionNew = $($resultNew -split "`r`n" | Select-String "FW" -CaseSensitive).line.Split(" ",[System.StringSplitOptions]::RemoveEmptyEntries)[1]
    $MellanoxCLPVersionNew = $($resultNew -split "`r`n" | Select-String "CLP" -CaseSensitive).line.Split(" ",[System.StringSplitOptions]::RemoveEmptyEntries)[1]
    $MellanoxPXEVersionNew = $($resultNew -split "`r`n" | Select-String "PXE" -CaseSensitive).line.Split(" ",[System.StringSplitOptions]::RemoveEmptyEntries)[1]
    $MellanoxUEFIVersionNew = $($resultNew -split "`r`n" | Select-String "UEFI" -CaseSensitive).line.Split(" ",[System.StringSplitOptions]::RemoveEmptyEntries)[1]

    addRowFW 'CLP' $MellanoxCLPVersion $MellanoxCLPVersionNew
    addRowFW 'PXE' $MellanoxPXEVersion $MellanoxPXEVersionNew
    addRowFW 'UEFI' $MellanoxUEFIVersion $MellanoxUEFIVersionNew
    addRowFW 'FW' $MellanoxFWVersion $MellanoxFWVersionNew
            
    $script:tableFW
    $script:rebootneed=1
    }
}

function Query-OEMFirmware-Mellanox
{
    #Mellanox Firmware Update
    $OEMMellanoxPath = $PSScriptRoot
    
    $nicinfo = callProcess "$OEMMellanoxPath\mlxup.exe" " -query --sfx-extract-dir C:\Windows\Temp"
    $nicinfo
}

function Update-OEMFirmware-QLogic
{
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory=$true, Position=0)]
        [string]$FwPath=''
    )

    #QLogic Firmware Update
    $OEMQLogicPath = $PSScriptRoot
    
    $nicinfo = callProcessStdin "$OEMQLogicPath\winfwnx2.exe" "" @('q')
    $FwPath2 = $FwPath.Replace("\","/")
    $nicinfo = callProcess "$OEMQLogicPath\winfwnx2.exe" " -all upgrade -f -mbi $FwPath2 qlgcrestore"
    $nicinfo
    $script:rebootneed=1
}

function Update-OEMFirmware-Disk
{
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory=$true, Position=0)]
        [string]$FwPath='',
        [Parameter(Mandatory=$true, Position=1)]
        [string]$SerialNumber=''
    )

    $pd = Get-PhysicalDisk | Where-Object SerialNumber -eq $SerialNumber
    $SupportsUpdate = ($pd | Get-StorageFirmwareInformation).SupportsUpdate
    if ($SupportsUpdate)
    {
        $pd | Update-StorageFirmware -ImagePath $FwPath -SlotNumber 0
        Get-PhysicalDisk | Where-Object SerialNumber -eq $SerialNumber | Get-StorageFirmwareInformation | select FirmwareVersionInSlot | out-string
    }
    else 
    {
        $SupportsUpdate | out-string
    }
}

Export-ModuleMember Get-Driver_FW
Export-ModuleMember Enable-DiskLocate
Export-ModuleMember Disable-DiskLocate
Export-ModuleMember Get-SmartInfo
Export-ModuleMember Get-ClusterDriver_FW
Export-ModuleMember Update-OEMFirmware-LSI
Export-ModuleMember Update-OEMFirmware-Mellanox
Export-ModuleMember Update-OEMFirmware-QLogic
Export-ModuleMember Update-OEMFirmware-Disk
Export-ModuleMember Reset-OEMFirmware-Mellanox
Export-ModuleMember Query-OEMFirmware-Mellanox