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(@). // //*************************************************************************************** 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(@). // //*************************************************************************************** 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(@). // //*************************************************************************************** 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(@). // 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(@). // 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(@). // 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(@). // 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 ****************************