openblt/Host/Source/MicroBoot/firmwareupdate.pas

1208 lines
49 KiB
ObjectPascal

unit FirmwareUpdate;
//***************************************************************************************
// Description: Contains the classes for handling firmwware updates through LibOpenBLT.
// File Name: firmwareupdate.pas
//
//---------------------------------------------------------------------------------------
// C O P Y R I G H T
//---------------------------------------------------------------------------------------
// Copyright (c) 2018 by Feaser http://www.feaser.com All rights reserved
//
// This software has been carefully tested, but is not guaranteed for any particular
// purpose. The author does not offer any warranties and does not guarantee the accuracy,
// adequacy, or completeness of the software and is not responsible for any errors or
// omissions or the results obtained from use of the software.
//
//---------------------------------------------------------------------------------------
// L I C E N S E
//---------------------------------------------------------------------------------------
// This file is part of OpenBLT. OpenBLT is free software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option) any later
// version.
//
// OpenBLT is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the GNU General Public License for more details.
//
// You have received a copy of the GNU General Public License along with OpenBLT. It
// should be located in ".\Doc\license.html". If not, contact Feaser to obtain a copy.
//
//***************************************************************************************
{$IFDEF FPC}
{$MODE objfpc}{$H+}
{$ENDIF}
interface
//***************************************************************************************
// Includes
//***************************************************************************************
uses
{$IFDEF UNIX}{$IFDEF UseCThreads}
cthreads,
cmem, // the c memory manager is on some systems much faster for multi-threading
{$ENDIF}{$ENDIF}
Classes, SysUtils, CurrentConfig, ConfigGroups, OpenBlt;
//***************************************************************************************
// Type Definitions
//***************************************************************************************
type
// Forward declarations
TFirmwareUpdate = class;
//------------------------------ TFirmwareUpdateStartedEvent --------------------------
TFirmwareUpdateStartedEvent = procedure(Sender: TObject) of object;
//------------------------------ TFirmwareUpdateStoppedEvent --------------------------
TFirmwareUpdateStoppedEvent = procedure(Sender: TObject) of object;
//------------------------------ TFirmwareUpdateDoneEvent -----------------------------
TFirmwareUpdateDoneEvent = procedure(Sender: TObject) of object;
//------------------------------ TFirmwareUpdateInfoEvent -----------------------------
TFirmwareUpdateInfoEvent = procedure(Sender: TObject; InfoString: String) of object;
//------------------------------ TFirmwareUpdateLogEvent ------------------------------
TFirmwareUpdateLogEvent = procedure(Sender: TObject; LogString: String) of object;
//------------------------------ TFirmwareUpdateProgressEvent -------------------------
TFirmwareUpdateProgressEvent = procedure(Sender: TObject; Percentage: Integer) of object;
//------------------------------ TFirmwareUpdateErrorEvent ----------------------------
TFirmwareUpdateErrorEvent = procedure(Sender: TObject; ErrorString: String) of object;
//------------------------------ TFirmwareUpdateState ---------------------------------
TFirmwareUpdateState = ( FUS_IDLE = 0,
FUS_INITIALIZING,
FUS_CONNECTING,
FUS_LOADING_FIRMWARE,
FUS_ERASING_MEMORY,
FUS_PROGRAMMING_MEMORY,
FUS_FINISHING_UP );
//------------------------------ TFirmwareUpdateThread --------------------------------
TFirmwareUpdateThread = class(TThread)
private
FFirmwareUpdate: TFirmwareUpdate;
FFirmwareFile: String;
FState: TFirmwareUpdateState;
FInfoString: String;
FLogString: String;
FErrorString: String;
FPercentage: Integer;
procedure Initialize;
procedure Cleanup;
function GetSessionProtocolName: String;
procedure LogSessionProtocolSettings;
function GetTransportLayerName: String;
procedure LogTransportLayerSettings;
procedure SynchronizeStartedEvent;
procedure SynchronizeStoppedEvent;
procedure SynchronizeDoneEvent;
procedure SynchronizeInfoEvent;
procedure SynchronizeLogEvent;
procedure SynchronizeProgressEvent;
procedure SynchronizeErrorEvent;
protected
procedure Execute; override;
public
constructor Create(CreateSuspended : Boolean; FirmwareUpdate: TFirmwareUpdate); reintroduce;
property FirmwareFile: String read FFirmwareFile write FFirmwareFile;
property State: TFirmwareUpdateState read FState write FState;
end;
//------------------------------ TFirmwareUpdate --------------------------------------
TFirmwareUpdate = class (TObject)
private
FCurrentConfig: TCurrentConfig;
FWorkerThread: TFirmwareUpdateThread;
FStartedEvent: TFirmwareUpdateStartedEvent;
FStoppedEvent: TFirmwareUpdateStoppedEvent;
FDoneEvent: TFirmwareUpdateDoneEvent;
FInfoEvent: TFirmwareUpdateInfoEvent;
FLogEvent: TFirmwareUpdateLogEvent;
FProgressEvent: TFirmwareUpdateProgressEvent;
FErrorEvent: TFirmwareUpdateErrorEvent;
public
constructor Create(CurrentConfig: TCurrentConfig); reintroduce;
destructor Destroy; override;
function Start(FirmwareFile: String): Boolean;
procedure Stop;
property OnStarted: TFirmwareUpdateStartedEvent read FStartedEvent write FStartedEvent;
property OnStopped: TFirmwareUpdateStoppedEvent read FStoppedEvent write FStoppedEvent;
property OnDone: TFirmwareUpdateDoneEvent read FDoneEvent write FDoneEvent;
property OnInfo: TFirmwareUpdateInfoEvent read FInfoEvent write FInfoEvent;
property OnLog: TFirmwareUpdateLogEvent read FLogEvent write FLogEvent;
property OnProgress: TFirmwareUpdateProgressEvent read FProgressEvent write FProgressEvent;
property OnError: TFirmwareUpdateErrorEvent read FErrorEvent write FErrorEvent;
end;
implementation
//---------------------------------------------------------------------------------------
//-------------------------------- TFirmwareUpdate --------------------------------------
//---------------------------------------------------------------------------------------
//***************************************************************************************
// NAME: Create
// PARAMETER: CurrentConfig Current configuration instance.
// RETURN VALUE: none
// DESCRIPTION: Class constructor.
//
//***************************************************************************************
constructor TFirmwareUpdate.Create(CurrentConfig: TCurrentConfig);
begin
// Call the inherited constructor.
inherited Create;
// Check parameters.
Assert(CurrentConfig <> nil, 'Current configuration instance cannot be null');
// Store the configuration instance.
FCurrentConfig := CurrentConfig;
// Initialize fields.
FStartedEvent := nil;
FStoppedEvent := nil;
FDoneEvent := nil;
FInfoEvent := nil;
FLogEvent := nil;
FProgressEvent := nil;
FErrorEvent := nil;
FWorkerThread := nil;
end; //*** end of Create ***
//***************************************************************************************
// NAME: Destroy
// PARAMETER: none
// RETURN VALUE: none
// DESCRIPTION: Class destructor.
//
//***************************************************************************************
destructor TFirmwareUpdate.Destroy;
begin
// Check if the worker thread is instanced.
if Assigned(FWorkerThread) then
begin
// Set termination request for the worker thread.
FWorkerThread.Terminate;
// Wait for thread termination to complete.
FWorkerThread.WaitFor;
// Release the thread instance.
FWorkerThread.Free;
end;
// call inherited destructor
inherited Destroy;
end; //*** end of Destroy ***
//***************************************************************************************
// NAME: Start
// PARAMETER: FirmwareFile Filename and path of the firmware file with program data
// that is to be programmed on the target using the bootloader.
// RETURN VALUE: True if successful, False otherwise.
// DESCRIPTION: Starts the firmware update procedure.
//
//***************************************************************************************
function TFirmwareUpdate.Start(FirmwareFile: String): Boolean;
begin
// Initialize the result.
Result := False;
// Check if the worker thread is terminated but not yet freed from a previous update.
if Assigned(FWorkerThread) then
begin
if FWorkerThread.Finished then
begin
// Free it.
FreeAndNil(FWorkerThread);
end;
end;
// Only start a firmware update if another one is not already in progress.
if not Assigned(FWorkerThread) then
begin
// Only start the firmware update if the specified file exists.
if FileExists(FirmwareFile) then
begin
// Create the worker thread in a suspended state.
FWorkerThread := TFirmwareUpdateThread.Create(True, Self);
// Only continue if the worker thread could be instanced.
if Assigned(FWorkerThread) then
begin
// Pass the firmware file on to the worker thread.
FWorkerThread.FirmwareFile := FirmwareFile;
// Set the initial state for the worker thread so it knows where to start.
FWorkerThread.State := FUS_INITIALIZING;
// Start the worker thread, which handles the actual firmware update.
FWorkerThread.Start;
// Update the result.
Result := True;
end;
end;
end;
end; //*** end of Start ***
//***************************************************************************************
// NAME: Stop
// PARAMETER: none
// RETURN VALUE: none
// DESCRIPTION: Cancel an active firmware update procedure, if any.
//
//***************************************************************************************
procedure TFirmwareUpdate.Stop;
begin
// No need to stop the worker thread if it is not instanced.
if Assigned(FWorkerThread) then
begin
// Set worker thread state to idle.
FWorkerThread.State := FUS_IDLE;
// Set termination request for the worker thread.
FWorkerThread.Terminate;
// Wait for thread termination to complete.
FWorkerThread.WaitFor;
// Release the thread instance.
FreeAndNil(FWorkerThread);
end;
end; //*** end of Stop ***
//---------------------------------------------------------------------------------------
//-------------------------------- TFirmwareUpdateThread --------------------------------
//---------------------------------------------------------------------------------------
//***************************************************************************************
// NAME: Create
// PARAMETER: CreateSuspended True to suspend the thread after creation.
// FirmwareUpdate Instance of the TFirmwareUpdate class, needed to
// trigger its events.
// RETURN VALUE: none
// DESCRIPTION: Thread constructor.
//
//***************************************************************************************
constructor TFirmwareUpdateThread.Create(CreateSuspended : Boolean; FirmwareUpdate: TFirmwareUpdate);
begin
// Call inherited constructor.
inherited Create(CreateSuspended);
// Configure the thread to not automatically free itself upon termination.
FreeOnTerminate := False;
// Initialize fields.
FFirmwareUpdate := FirmwareUpdate;
FFirmwareFile := '';
FState := FUS_IDLE;
FInfoString := '';
FLogString := '';
FErrorString := '';
FPercentage := 0;
end; //*** end of Create ***
//***************************************************************************************
// NAME: Execute
// PARAMETER: none
// RETURN VALUE: none
// DESCRIPTION: Thread execution function.
//
//***************************************************************************************
procedure TFirmwareUpdateThread.Execute;
const
ERASE_SIZE_MAX = 32768;
PROGRAM_SIZE_MAX = 256;
var
initialized: Boolean;
errorDetected: Boolean;
firmwareDataTotalSize: LongWord;
firmwareDataTotalSegments: LongWord;
firmwareDataBaseAddress: LongWord;
segmentIdx: LongWord;
segmentLen: LongWord;
segmentBase: LongWord;
segmentData: PByte;
eraseCurrentLen: LongWord;
eraseCurrentBase: LongWord;
eraseStillLeft: LongWord;
eraseProgressPct: Integer;
eraseProgressLen: LongWord;
programCurrentLen: LongWord;
programCurrentBase: LongWord;
programCurrentDataPtr: PByte;
programStillLeft: LongWord;
programProgressPct: Integer;
programProgressLen: LongWord;
begin
// Initialize locals.
initialized := False;
// Trigger the started event.
Synchronize(@SynchronizeStartedEvent);
// Enter thread's execution loop.
while not Terminated do
begin
// --------------------------- Initializing -----------------------------------------
if FState = FUS_INITIALIZING then
begin
// Initialize error flag.
errorDetected := False;
// Update the info.
FInfoString := 'Starting firmware update';
Synchronize(@SynchronizeInfoEvent);
// Update the log.
FLogString := FInfoString;
Synchronize(@SynchronizeLogEvent);
FLogString := 'Specified firmware file: ' + FFirmwareFile;
Synchronize(@SynchronizeLogEvent);
FLogString := 'Using LibOpenBLT version ' + BltVersionGetString;
Synchronize(@SynchronizeLogEvent);
FLogString := 'Detected session protocol: ' + GetSessionProtocolName;
Synchronize(@SynchronizeLogEvent);
FLogString := 'Using session protocol settings:';
Synchronize(@SynchronizeLogEvent);
LogSessionProtocolSettings;
FLogString := 'Detected transport layer: ' + GetTransportLayerName;
Synchronize(@SynchronizeLogEvent);
FLogString := 'Using transport layer settings:';
Synchronize(@SynchronizeLogEvent);
LogTransportLayerSettings;
FLogString := 'Initializing firmware update engine';
Synchronize(@SynchronizeLogEvent);
// Initialize LibOpenBLT modules.
Initialize;
initialized := True;
// Transition to the next state if all is okay.
if not errorDetected then
begin
FState := FUS_LOADING_FIRMWARE;
end;
end
// --------------------------- Loading firmware data --------------------------------
else if FState = FUS_LOADING_FIRMWARE then
begin
// Initialize error flag.
errorDetected := False;
// Update the info.
FInfoString := 'Loading firmware data from file';
Synchronize(@SynchronizeInfoEvent);
// Update the log.
FLogString := FInfoString;
Synchronize(@SynchronizeLogEvent);
// Load firmware data from the file.
if BltFirmwareLoadFromFile(PAnsiChar(AnsiString(FFirmwareFile)), 0) <> BLT_RESULT_OK then
begin
// Set error flag.
errorDetected := True;
// Cancel firmware update procedure by transitioning to the idle state.
FState := FUS_IDLE;
// Update the log.
FLogString := 'Error occured while loading firmware data from the file';
Synchronize(@SynchronizeLogEvent);
// Trigger error.
FErrorString := FLogString;
Synchronize(@SynchronizeErrorEvent);
end
// Display information regarding the loaded firmware data.
else
begin
// Store the number of segments.
firmwareDataTotalSegments := BltFirmwareGetSegmentCount();
// Initialize locals.
firmwareDataTotalSize := 0;
segmentBase := 0;
segmentLen := 0;
// Loop through all segments.
for segmentIdx := 0 to (firmwareDataTotalSegments - 1) do
begin
// Extract segment info.
segmentData := BltFirmwareGetSegment(segmentIdx, segmentBase, segmentLen);
// Validate the segment info
if (segmentData = nil) or (segmentLen = 0) then
begin
// Set error flag.
errorDetected := True;
// Cancel firmware update procedure by transitioning to the idle state.
FState := FUS_IDLE;
// Update the log.
FLogString := 'Invalid segment encountered in the firmware data';
Synchronize(@SynchronizeLogEvent);
// Trigger error.
FErrorString := FLogString;
Synchronize(@SynchronizeErrorEvent);
// No need to continue looping through segments.
Break;
end
// Segment is valid.
else
begin
// Update total size.
firmwareDataTotalSize := firmwareDataTotalSize + segmentLen;
// If it is the first segment, then store the base address.
if segmentIdx = 0 then
begin
firmwareDataBaseAddress := segmentBase;
end;
end;
end;
// Sanity check to make sure there was actually firmware data present.
if not errorDetected then
begin
if firmwareDataTotalSize = 0 then
begin
// Set error flag.
errorDetected := True;
// Cancel firmware update procedure by transitioning to the idle state.
FState := FUS_IDLE;
// Update the log.
FLogString := 'Firmware data is empty. Cannot continue with firmware update';
Synchronize(@SynchronizeLogEvent);
// Trigger error.
FErrorString := FLogString;
Synchronize(@SynchronizeErrorEvent);
end;
end;
end;
// Display information about the loaded firmware data
if not errorDetected then
begin
// Update the log.
FLogString := ' -> Number of segments: ' + IntToStr(firmwareDataTotalSegments);
Synchronize(@SynchronizeLogEvent);
FLogString := ' -> Base memory address: ' + Format('%.8xh', [firmwareDataBaseAddress]);
Synchronize(@SynchronizeLogEvent);
FLogString := ' -> Total data size: ' + IntToStr(firmwareDataTotalSize);
Synchronize(@SynchronizeLogEvent);
end;
// Transition to the next state if all is okay.
if not errorDetected then
begin
FState := FUS_CONNECTING;
end;
end
// --------------------------- Connecting to target ---------------------------------
else if FState = FUS_CONNECTING then
begin
// Initialize error flag.
errorDetected := False;
// Update the info.
FInfoString := 'Connecting to the target';
Synchronize(@SynchronizeInfoEvent);
// Update the log.
FLogString := FInfoString;
Synchronize(@SynchronizeLogEvent);
// Attempt connection with the target.
if BltSessionStart() <> BLT_RESULT_OK then
begin
// Not yet successful. Request the user to reset the system if it takes too long.
FInfoString := 'Connecting to the target (reset your target if this takes a long time)';
Synchronize(@SynchronizeInfoEvent);
// Update the log.
FLogString := 'First connection attempt failed';
Synchronize(@SynchronizeLogEvent);
FLogString := 'Switching to backdoor entry mode';
Synchronize(@SynchronizeLogEvent);
// Now keep retrying until successful
while BltSessionStart() <> BLT_RESULT_OK do
begin
// Check for thread termination request
if Terminated then
begin
// Set error flag to force idle mode after breaking this loop.
errorDetected := True;
// Update the log.
FLogString := 'Cancellation request detected, so stopping firmware update';
Synchronize(@SynchronizeLogEvent);
// Trigger the stopped event.
Synchronize(@SynchronizeStoppedEvent);
// Cancel firmware update procedure by transitioning to the idle state.
FState := FUS_IDLE;
// Stop looping.
Break;
end;
// Delay a bit to not starve the CPU.
Sleep(20);
end;
end;
// Transition to the next state if all is okay.
if not errorDetected then
begin
FState := FUS_ERASING_MEMORY;
end;
end
// --------------------------- Erasing memory ---------------------------------------
else if FState = FUS_ERASING_MEMORY then
begin
// Initialize error flag.
errorDetected := False;
// Reset progress variables
eraseProgressPct := 0;
eraseProgressLen := 0;
// Loop through all segments.
for segmentIdx := 0 to (firmwareDataTotalSegments - 1) do
begin
// Don't bother looping if an error was detected.
if errorDetected then
begin
Break;
end;
// Extract segment info.
eraseCurrentBase := 0;
eraseStillLeft := 0;
segmentData := BltFirmwareGetSegment(segmentIdx, eraseCurrentBase, eraseStillLeft);
// Perform erase in chunks of maximum ERASE_SIZE_MAX. Otherwise the erase
// operation can take a long time, which would lead to a non-responsive user
// interface.
while eraseStillLeft > 0 do
begin
// Check for cancellation request.
if Terminated then
begin
// Set error flag to force idle mode after breaking this loop.
errorDetected := True;
// Update the log.
FLogString := 'Cancellation request detected, so stopping firmware update';
Synchronize(@SynchronizeLogEvent);
// Trigger the stopped event.
Synchronize(@SynchronizeStoppedEvent);
// Cancel firmware update procedure by transitioning to the idle state.
FState := FUS_IDLE;
// Stop looping.
Break;
end;
// Determine chunk size.
eraseCurrentLen := ERASE_SIZE_MAX;
if eraseCurrentLen > eraseStillLeft then
begin
eraseCurrentLen := eraseStillLeft;
end;
// Update the info.
FInfoString := Format('Erasing %u bytes starting at %.8xh', [eraseCurrentLen, eraseCurrentBase]);
Synchronize(@SynchronizeInfoEvent);
// Update the log.
FLogString := FInfoString;
Synchronize(@SynchronizeLogEvent);
// Perform the erase operation.
if BltSessionClearMemory(eraseCurrentBase, eraseCurrentLen) <> BLT_RESULT_OK then
begin
// Set error flag.
errorDetected := True;
// Cancel firmware update procedure by transitioning to the idle state.
FState := FUS_IDLE;
// Update the log.
FLogString := Format('Could not erase memory at %.8xh', [eraseCurrentBase]);
Synchronize(@SynchronizeLogEvent);
// Trigger error.
FErrorString := FLogString;
Synchronize(@SynchronizeErrorEvent);
// Stop looping
Break;
end
// Erase operation was successful. Update loop variables for the next chunk.
else
begin
eraseStillLeft := eraseStillLeft - eraseCurrentLen;
eraseCurrentBase := eraseCurrentBase + eraseCurrentLen;
// Update erase progress
eraseProgressLen := eraseProgressLen + eraseCurrentLen;
eraseProgressPct := (Int64(eraseProgressLen) * 100) div firmwareDataTotalSize;
// Dedicate the first 20% of the total firmware update progress to the
// erase operation.
FPercentage := (eraseProgressPct * 20) div 100;
Synchronize(@SynchronizeProgressEvent);
end;
end;
end;
// Transition to the next state if all is okay.
if not errorDetected then
begin
FState := FUS_PROGRAMMING_MEMORY;
end;
end
// --------------------------- Programming memory -----------------------------------
else if FState = FUS_PROGRAMMING_MEMORY then
begin
// Initialize error flag.
errorDetected := False;
// Reset progress variables
programProgressPct := 0;
programProgressLen := 0;
// Loop through all segments.
for segmentIdx := 0 to (firmwareDataTotalSegments - 1) do
begin
// Don't bother looping if an error was detected.
if errorDetected then
begin
Break;
end;
// Extract segment info.
programCurrentBase := 0;
programStillLeft := 0;
programCurrentDataPtr := BltFirmwareGetSegment(segmentIdx, programCurrentBase, programStillLeft);
// Perform programming in chunks of maximum PROGRAM_SIZE_MAX. Otherwise the
// programming operation can take a long time, which would lead to a non-
// responsive user interface.
while programStillLeft > 0 do
begin
// Check for cancellation request.
if Terminated then
begin
// Set error flag to force idle mode after breaking this loop.
errorDetected := True;
// Update the log.
FLogString := 'Cancellation request detected, so stopping firmware update';
Synchronize(@SynchronizeLogEvent);
// Trigger the stopped event.
Synchronize(@SynchronizeStoppedEvent);
// Cancel firmware update procedure by transitioning to the idle state.
FState := FUS_IDLE;
// Stop looping.
Break;
end;
// Determine chunk size.
programCurrentLen := PROGRAM_SIZE_MAX;
if programCurrentLen > programStillLeft then
begin
programCurrentLen := programStillLeft;
end;
// Update the info.
FInfoString := Format('Programming %u bytes starting at %.8xh', [programCurrentLen, programCurrentBase]);
Synchronize(@SynchronizeInfoEvent);
// Update the log.
FLogString := FInfoString;
Synchronize(@SynchronizeLogEvent);
// Perform the programming operation.
if BltSessionWriteData(programCurrentBase, programCurrentLen, programCurrentDataPtr) <> BLT_RESULT_OK then
begin
// Set error flag.
errorDetected := True;
// Cancel firmware update procedure by transitioning to the idle state.
FState := FUS_IDLE;
// Update the log.
FLogString := Format('Could not program memory at %.8xh', [programCurrentBase]);
Synchronize(@SynchronizeLogEvent);
// Trigger error.
FErrorString := FLogString;
Synchronize(@SynchronizeErrorEvent);
// Stop looping
Break;
end
// Program operation was successful. Update loop variables for the next chunk.
else
begin
programStillLeft := programStillLeft - programCurrentLen;
programCurrentBase := programCurrentBase + programCurrentLen;
programCurrentDataPtr := programCurrentDataPtr + programCurrentLen;
// Update programming progress
programProgressLen := programProgressLen + programCurrentLen;
programProgressPct := (Int64(programProgressLen) * 100) div firmwareDataTotalSize;
// Dedicate the remaining 80% of the total firmware update progress to the
// programing operation.
FPercentage := 20 + ((programProgressPct * 80) div 100);
Synchronize(@SynchronizeProgressEvent);
end;
end;
end;
// Transition to the next state if all is okay.
if not errorDetected then
begin
FState := FUS_FINISHING_UP;
end;
end
// --------------------------- Finishing up -----------------------------------------
else if FState = FUS_FINISHING_UP then
begin
// Initialize error flag.
errorDetected := False;
// Update the info.
FInfoString := 'Finishing programming session';
Synchronize(@SynchronizeInfoEvent);
// Update the log.
FLogString := FInfoString;
Synchronize(@SynchronizeLogEvent);
// Stop the session
BltSessionStop();
// Update the info.
FInfoString := 'Firmware update completed successfully';
Synchronize(@SynchronizeInfoEvent);
// Update the log.
FLogString := FInfoString;
Synchronize(@SynchronizeLogEvent);
// Trigger the OnDone event
Synchronize(@SynchronizeDoneEvent);
// Transition back to the idle state.
FState := FUS_IDLE;
end
// --------------------------- Idle -------------------------------------------------
else
begin
// Idle mode means that the worker thread is all done and can be exited.
Break;
end;
end;
// Cleanup LibOpenBLT modules if initialized.
if initialized then
begin
FLogString := 'Cleaning up firmware update engine';
Synchronize(@SynchronizeLogEvent);
initialized := False;
Cleanup;
end;
end; //*** end of Execute ***
//***************************************************************************************
// NAME: Initialize
// PARAMETER: none
// RETURN VALUE: none
// DESCRIPTION: Initializes the firmware update process.
//
//***************************************************************************************
procedure TFirmwareUpdateThread.Initialize;
var
sessionConfig: TSessionConfig;
sessionXcpConfig: TSessionXcpConfig;
transportConfig: TTransportConfig;
transportXcpRs232Config: TTransportXcpRs232Config;
transportXcpCanConfig: TTransportXcpCanConfig;
transportXcpTcpIpConfig: TTransportXcpTcpIpConfig;
sessionType: LongWord;
transportType: LongWord;
sessionSettingsXcp: tBltSessionSettingsXcpV10;
transportSettingsXcpRs232: tBltTransportSettingsXcpV10Rs232;
transportSettingsXcpCan: tBltTransportSettingsXcpV10Can;
transportSettingsXcpNet: tBltTransportSettingsXcpV10Net;
sessionSettingsPtr: Pointer;
transportSettingsPtr: Pointer;
begin
// Initialize locals.
sessionSettingsPtr := nil;
transportSettingsPtr := nil;
// Initialize the firmware data module using the S-record parser.
BltFirmwareInit(BLT_FIRMWARE_PARSER_SRECORD);
// Determine the session protocol to use and set its settings.
sessionConfig := FFirmwareUpdate.FCurrentConfig.Groups[TSessionConfig.GROUP_NAME]
as TSessionConfig;
// ------------------------------------ XCP version 1.0 -------------------------------
if sessionConfig.Session = 'xcp' then
begin
// Store the session protocol type.
sessionType := BLT_SESSION_XCP_V10;
// Obtain access to the related configuration group.
sessionXcpConfig := FFirmwareUpdate.FCurrentConfig.Groups[TSessionXcpConfig.GROUP_NAME]
as TSessionXcpConfig;
// Copy over the settings.
sessionSettingsXcp.timeoutT1 := sessionXcpConfig.TimeoutT1;
sessionSettingsXcp.timeoutT3 := sessionXcpConfig.TimeoutT3;
sessionSettingsXcp.timeoutT4 := sessionXcpConfig.TimeoutT4;
sessionSettingsXcp.timeoutT5 := sessionXcpConfig.TimeoutT5;
sessionSettingsXcp.timeoutT7 := sessionXcpConfig.TimeoutT7;
sessionSettingsXcp.connectMode := sessionXcpConfig.ConnectMode;
sessionSettingsXcp.seedKeyFile := PAnsiChar(AnsiString(sessionXcpConfig.SeedKey));
// Point the session settings pointer to this one.
sessionSettingsPtr := @sessionSettingsXcp;
// Determine the transport layer and its settings.
transportConfig := FFirmwareUpdate.FCurrentConfig.Groups[TTransportConfig.GROUP_NAME]
as TTransportConfig;
// ---------------------------------- XCP on RS232 ----------------------------------
if transportConfig.Transport = 'xcp_rs232' then
begin
// Store the transport layer type.
transportType := BLT_TRANSPORT_XCP_V10_RS232;
// Obtain access to the related configuration group.
transportXcpRs232Config := FFirmwareUpdate.FCurrentConfig.Groups[TTransportXcpRs232Config.GROUP_NAME]
as TTransportXcpRs232Config;
// Copy over the settings.
transportSettingsXcpRs232.portName := PAnsiChar(AnsiString(transportXcpRs232Config.Device));
transportSettingsXcpRs232.baudrate := transportXcpRs232Config.Baudrate;
// Point the transport settings pointer to this one.
transportSettingsPtr := @transportSettingsXcpRs232;
end
// ---------------------------------- XCP on CAN ------------------------------------
else if transportConfig.Transport = 'xcp_can' then
begin
// Store the transport layer type.
transportType := BLT_TRANSPORT_XCP_V10_CAN;
// Obtain access to the related configuration group.
transportXcpCanConfig := FFirmwareUpdate.FCurrentConfig.Groups[TTransportXcpCanConfig.GROUP_NAME]
as TTransportXcpCanConfig;
// Copy over the settings.
transportSettingsXcpCan.deviceName := PAnsiChar(AnsiString(transportXcpCanConfig.Device));
transportSettingsXcpCan.deviceChannel := transportXcpCanConfig.Channel;
transportSettingsXcpCan.baudrate := transportXcpCanConfig.Baudrate;
transportSettingsXcpCan.transmitId := transportXcpCanConfig.TransmitId;
transportSettingsXcpCan.receiveId := transportXcpCanConfig.ReceiveId;
transportSettingsXcpCan.useExtended := transportXcpCanConfig.ExtendedId;
// Point the transport settings pointer to this one.
transportSettingsPtr := @transportSettingsXcpCan;
end
// ---------------------------------- XCP on USB ------------------------------------
else if transportConfig.Transport = 'xcp_usb' then
begin
// Store the transport layer type.
transportType := BLT_TRANSPORT_XCP_V10_USB;
// No settings to copy over for USB.
end
// ---------------------------------- XCP on TCP/IP ---------------------------------
else if transportConfig.Transport = 'xcp_net' then
begin
// Store the transport layer type.
transportType := BLT_TRANSPORT_XCP_V10_NET;
// Obtain access to the related configuration group.
transportXcpTcpIpConfig := FFirmwareUpdate.FCurrentConfig.Groups[TTransportXcpTcpIpConfig.GROUP_NAME]
as TTransportXcpTcpIpConfig;
// Copy over the settings.
transportSettingsXcpNet.address := PAnsiChar(AnsiString(transportXcpTcpIpConfig.Address));
transportSettingsXcpNet.port := transportXcpTcpIpConfig.Port;
// Point the transport settings pointer to this one.
transportSettingsPtr := @transportSettingsXcpNet;
end;
end;
// Initialize the session module using the detected settings.
BltSessionInit(sessionType, sessionSettingsPtr, transportType, transportSettingsPtr);
end; //*** end of Initialize ***
//***************************************************************************************
// NAME: Cleanup
// PARAMETER: none
// RETURN VALUE: none
// DESCRIPTION: Cleans up the firmware update process.
//
//***************************************************************************************
procedure TFirmwareUpdateThread.Cleanup;
begin
// Terminate the session.
BltSessionTerminate();
// Terminate the firmware data module.
BltFirmwareTerminate();
end; //*** end of Cleanup ***
//***************************************************************************************
// NAME: GetSessionProtocolName
// PARAMETER: none
// RETURN VALUE: Name of the configured session protocol.
// DESCRIPTION: Obtains the name of the session protocol that will be used for the
// firmware update.
//
//***************************************************************************************
function TFirmwareUpdateThread.GetSessionProtocolName: String;
var
sessionConfig: TSessionConfig;
begin
// Initialize the result.
Result := 'Unknown session protocol';
// Obtain access to the related configuration group.
sessionConfig := FFirmwareUpdate.FCurrentConfig.Groups[TSessionConfig.GROUP_NAME]
as TSessionConfig;
// Filter on the configured session protocol.
if sessionConfig.Session = 'xcp' then
begin
Result := 'XCP version 1.0';
end;
end; //*** end of GetSessionProtocolName ***
//***************************************************************************************
// NAME: LogSessionProtocolSettings
// PARAMETER: none
// RETURN VALUE: none
// DESCRIPTION: Logs the settings of the session protocol that will be used for the
// firmware update.
//
//***************************************************************************************
procedure TFirmwareUpdateThread.LogSessionProtocolSettings;
var
sessionConfig: TSessionConfig;
sessionXcpConfig: TSessionXcpConfig;
begin
// Obtain access to the related configuration group.
sessionConfig := FFirmwareUpdate.FCurrentConfig.Groups[TSessionConfig.GROUP_NAME]
as TSessionConfig;
// Filter on the configured session protocol.
if sessionConfig.Session = 'xcp' then
begin
// Obtain access to the related configuration group.
sessionXcpConfig := FFirmwareUpdate.FCurrentConfig.Groups[TSessionXcpConfig.GROUP_NAME]
as TSessionXcpConfig;
FLogString := ' -> Timeout T1: ' + IntToStr(sessionXcpConfig.TimeoutT1) + ' ms';
Synchronize(@SynchronizeLogEvent);
FLogString := ' -> Timeout T3: ' + IntToStr(sessionXcpConfig.TimeoutT3) + ' ms';
Synchronize(@SynchronizeLogEvent);
FLogString := ' -> Timeout T4: ' + IntToStr(sessionXcpConfig.TimeoutT4) + ' ms';
Synchronize(@SynchronizeLogEvent);
FLogString := ' -> Timeout T5: ' + IntToStr(sessionXcpConfig.TimeoutT5) + ' ms';
Synchronize(@SynchronizeLogEvent);
FLogString := ' -> Timeout T7: ' + IntToStr(sessionXcpConfig.TimeoutT7) + ' ms';
Synchronize(@SynchronizeLogEvent);
if sessionXcpConfig.SeedKey <> '' then
FLogString := ' -> Seed/Key file: ' + sessionXcpConfig.SeedKey
else
FLogString := ' -> Seed/Key file: ' + 'None';
Synchronize(@SynchronizeLogEvent);
FLogString := ' -> Connection mode: ' + IntToStr(sessionXcpConfig.ConnectMode);
Synchronize(@SynchronizeLogEvent);
end
else
begin
FLogString := ' -> Unknown session protocol settings';
Synchronize(@SynchronizeLogEvent);
end;
end; //*** end of LogSessionProtocolSettings ***
//***************************************************************************************
// NAME: GetTransportLayerName
// PARAMETER: none
// RETURN VALUE: Name of the configured transport layer.
// DESCRIPTION: Obtains the name of the tansport layer that will be used for the
// firmware update.
//
//***************************************************************************************
function TFirmwareUpdateThread.GetTransportLayerName: String;
var
transportConfig: TTransportConfig;
begin
// Initialize the result.
Result := 'Unknown transport layer';
// Obtain access to the related configuration group.
transportConfig := FFirmwareUpdate.FCurrentConfig.Groups[TTransportConfig.GROUP_NAME]
as TTransportConfig;
// Filter on the configured transport layer.
if transportConfig.Transport = 'xcp_rs232' then
begin
Result := 'XCP on RS232';
end
else if transportConfig.Transport = 'xcp_can' then
begin
Result := 'XCP on CAN';
end
else if transportConfig.Transport = 'xcp_usb' then
begin
Result := 'XCP on USB';
end
else if transportConfig.Transport = 'xcp_net' then
begin
Result := 'XCP on TCP/IP';
end;
end; //*** end of GetTransportLayerName ***
//***************************************************************************************
// NAME: LogTransportLayerSettings
// PARAMETER: none
// RETURN VALUE: none
// DESCRIPTION: Logs the settings of the transport layer that will be used for the
// firmware update.
//
//***************************************************************************************
procedure TFirmwareUpdateThread.LogTransportLayerSettings;
var
transportConfig: TTransportConfig;
transportXcpRs232Config: TTransportXcpRs232Config;
transportXcpCanConfig: TTransportXcpCanConfig;
transportXcpTcpIpConfig: TTransportXcpTcpIpConfig;
begin
// Obtain access to the related configuration group.
transportConfig := FFirmwareUpdate.FCurrentConfig.Groups[TTransportConfig.GROUP_NAME]
as TTransportConfig;
// Filter on the configured transport layer.
// ------------------------------------ XCP on RS232 ----------------------------------
if transportConfig.Transport = 'xcp_rs232' then
begin
// Obtain access to the related configuration group.
transportXcpRs232Config := FFirmwareUpdate.FCurrentConfig.Groups[TTransportXcpRs232Config.GROUP_NAME]
as TTransportXcpRs232Config;
FLogString := ' -> Device: ' + transportXcpRs232Config.Device;
Synchronize(@SynchronizeLogEvent);
FLogString := ' -> Baudrate: ' + IntToStr(transportXcpRs232Config.Baudrate) + ' bit/sec';
Synchronize(@SynchronizeLogEvent);
end
// ------------------------------------ XCP on CAN ------------------------------------
else if transportConfig.Transport = 'xcp_can' then
begin
// Obtain access to the related configuration group.
transportXcpCanConfig := FFirmwareUpdate.FCurrentConfig.Groups[TTransportXcpCanConfig.GROUP_NAME]
as TTransportXcpCanConfig;
FLogString := ' -> Device: ' + transportXcpCanConfig.Device + ' (channel ' +
IntToStr(transportXcpCanConfig.Channel) + ' )';
Synchronize(@SynchronizeLogEvent);
FLogString := ' -> Baudrate: ' + IntToStr(transportXcpCanConfig.Baudrate) + ' bit/sec';
Synchronize(@SynchronizeLogEvent);
FLogString := ' -> Transmit CAN identifer: ' + Format('%.xh', [transportXcpCanConfig.TransmitId]);
Synchronize(@SynchronizeLogEvent);
FLogString := ' -> Receive CAN identifer: ' + Format('%.xh', [transportXcpCanConfig.ReceiveId]);
Synchronize(@SynchronizeLogEvent);
FLogString := ' -> Use 29-bit CAN identifiers: ';
if transportXcpCanConfig.ExtendedId > 0 then
FLogString := FLogString + 'Yes'
else
FLogString := FLogString + 'No';
Synchronize(@SynchronizeLogEvent);
end
// ------------------------------------ XCP on USB ------------------------------------
else if transportConfig.Transport = 'xcp_usb' then
begin
FLogString := ' -> No additional settings required';
Synchronize(@SynchronizeLogEvent);
end
// ------------------------------------ XCP on TCP/IP ---------------------------------
else if transportConfig.Transport = 'xcp_net' then
begin
// Obtain access to the related configuration group.
transportXcpTcpIpConfig := FFirmwareUpdate.FCurrentConfig.Groups[TTransportXcpTcpIpConfig.GROUP_NAME]
as TTransportXcpTcpIpConfig;
FLogString := ' -> Address: ' + transportXcpTcpIpConfig.Address;
Synchronize(@SynchronizeLogEvent);
FLogString := ' -> Port: ' + IntToStr(transportXcpTcpIpConfig.Port);
Synchronize(@SynchronizeLogEvent);
end
else
begin
FLogString := ' -> Unknown transport layer settings';
Synchronize(@SynchronizeLogEvent);
end;
end; //*** end of LogTransportLayerSettings ***
//***************************************************************************************
// NAME: SynchronizeStartedEvent
// PARAMETER: none
// RETURN VALUE: none
// DESCRIPTION: Synchronizes to the main thread to execute the code inside this
// procedure. This function should only be called from thread level,
// so from Execute-method in the following manner: Synchronize(@<name>).
//
//***************************************************************************************
procedure TFirmwareUpdateThread.SynchronizeStartedEvent;
begin
// Only continue if the event is set.
if Assigned(FFirmwareUpdate.FStartedEvent) then
begin
// Trigger the event.
FFirmwareUpdate.FStartedEvent(FFirmwareUpdate);
end;
end; //*** end of SynchronizeStartedEvent ***
//***************************************************************************************
// NAME: SynchronizeStoppedEvent
// PARAMETER: none
// RETURN VALUE: none
// DESCRIPTION: Synchronizes to the main thread to execute the code inside this
// procedure. This function should only be called from thread level,
// so from Execute-method in the following manner: Synchronize(@<name>).
//
//***************************************************************************************
procedure TFirmwareUpdateThread.SynchronizeStoppedEvent;
begin
// Only continue if the event is set.
if Assigned(FFirmwareUpdate.FStoppedEvent) then
begin
// Trigger the event.
FFirmwareUpdate.FStoppedEvent(FFirmwareUpdate);
end;
end; //*** end of SynchronizeStoppedEvent ***
//***************************************************************************************
// NAME: SynchronizeDoneEvent
// PARAMETER: none
// RETURN VALUE: none
// DESCRIPTION: Synchronizes to the main thread to execute the code inside this
// procedure. This function should only be called from thread level,
// so from Execute-method in the following manner: Synchronize(@<name>).
//
//***************************************************************************************
procedure TFirmwareUpdateThread.SynchronizeDoneEvent;
begin
// Only continue if the event is set.
if Assigned(FFirmwareUpdate.FDoneEvent) then
begin
// Trigger the event.
FFirmwareUpdate.FDoneEvent(FFirmwareUpdate);
end;
end; //*** end of SynchronizeDoneEvent ***
//***************************************************************************************
// NAME: SynchronizeInfoEvent
// PARAMETER: none
// RETURN VALUE: none
// DESCRIPTION: Synchronizes to the main thread to execute the code inside this
// procedure. This function should only be called from thread level,
// so from Execute-method in the following manner: Synchronize(@<name>).
// Make sure field FInfoString is set to the desired value.
//
//***************************************************************************************
procedure TFirmwareUpdateThread.SynchronizeInfoEvent;
begin
// Only continue if the event is set.
if Assigned(FFirmwareUpdate.FInfoEvent) then
begin
// Trigger the event.
FFirmwareUpdate.FInfoEvent(FFirmwareUpdate, FInfoString);
end;
end; //*** end of SynchronizeInfoEvent ***
//***************************************************************************************
// NAME: SynchronizeLogEvent
// PARAMETER: none
// RETURN VALUE: none
// DESCRIPTION: Synchronizes to the main thread to execute the code inside this
// procedure. This function should only be called from thread level,
// so from Execute-method in the following manner: Synchronize(@<name>).
// Make sure field FLogString is set to the desired value.
//
//***************************************************************************************
procedure TFirmwareUpdateThread.SynchronizeLogEvent;
begin
// Only continue if the event is set.
if Assigned(FFirmwareUpdate.FLogEvent) then
begin
// Trigger the event.
FFirmwareUpdate.FLogEvent(FFirmwareUpdate, FLogString);
end;
end; //*** end of SynchronizeLogEvent ***
//***************************************************************************************
// NAME: SynchronizeProgressEvent
// PARAMETER: none
// RETURN VALUE: none
// DESCRIPTION: Synchronizes to the main thread to execute the code inside this
// procedure. This function should only be called from thread level,
// so from Execute-method in the following manner: Synchronize(@<name>).
// Make sure field FPercentage is set to the desired value.
//
//***************************************************************************************
procedure TFirmwareUpdateThread.SynchronizeProgressEvent;
begin
// Only continue if the event is set.
if Assigned(FFirmwareUpdate.FProgressEvent) then
begin
// Trigger the event.
FFirmwareUpdate.FProgressEvent(FFirmwareUpdate, FPercentage);
end;
end; //*** end of SynchronizeProgressEvent ***
//***************************************************************************************
// NAME: SynchronizeErrorEvent
// PARAMETER: none
// RETURN VALUE: none
// DESCRIPTION: Synchronizes to the main thread to execute the code inside this
// procedure. This function should only be called from thread level,
// so from Execute-method in the following manner: Synchronize(@<name>).
// Make sure field FLogString is set to the desired value.
//
//***************************************************************************************
procedure TFirmwareUpdateThread.SynchronizeErrorEvent;
begin
// Only continue if the event is set.
if Assigned(FFirmwareUpdate.FErrorEvent) then
begin
// Trigger the event.
FFirmwareUpdate.FErrorEvent(FFirmwareUpdate, FErrorString);
end;
end; //*** end of SynchronizeErrorEvent ***
end.
//******************************** end of firmwareupdate.pas ****************************