/// <summary> /// Downloads the firmware file to the meter and activates it. /// The activation will cause the meter to drop the psem task so meter log off must /// follow this function call on success. This method supports resuming /// a previous failed FWDL. /// </summary> /// <param name="path">Complete file path of the firmware file</param> /// <param name="usBlockIndex">Dual purpose parameter. The passed in value indicates /// which block to begin downloading. The passed out parameter indicates which block to /// resume downloading in case there was a failure. This can then passed in again to /// restart the download at the point where it left off.</param> /// <param name="blnRetry">Whether or not to leave the FWDL in a state /// to permit subsequent retries at point of faliure. If false the pending table /// will be cleared on failure.</param> /// <param name="blnActivate">Whether or not to activate the firmware.</param> /// <returns>Itron.Metering.Device.FWDownloadResult</returns> // Revision History // MM/DD/YY who Version ID Number Description // -------- --- ------- -- ------ --------------------------------------- // 08/28/06 AF 7.35.00 N/A Created // 09/15/06 AF 7.35.00 N/A Added Catch for TimeOutException // 10/18/06 AF 7.40.00 N/A Removed wait within the main loop // 05/13/08 AF 1.50.24 Removed IFirmwareDownload from the method name // 04/19/10 AF 2.40.39 Added M2 Gateway support // 08/18/11 AF 2.52.05 Added support for authentication using a hash code // 08/26/11 AF 2.52.08 Added support for Cisco f/w // 09/22/11 AF 2.52.21 N/A Added support for Cisco config file f/w d/l - TODO remove when no longer needed // 10/12/11 AF 2.53.00 Changed the Cisco Comm fw enum name // 03/22/12 JJJ 2.60.xx Added support for ChoiceConnect FW // 05/10/12 JJJ 2.60.xx Tweaked FW Type passed to AuthenticateFWDL if ChoiceConnect, make RFLAN // 04/19/13 jrf 2.80.21 TQ 7639 Adding support for ICS comm module FWDL. // 07/08/13 jrf 2.80.51 TC 13201 Created to support retries of FWDL. // 07/15/13 jrf 2.80.?? TC 15062 Added parameter to control activation. // 08/22/13 jrf 2.85.26 WR 420902 Decrementing the block index on a failed block write, so // on a retry we will start back on the correct block. // 11/07/13 AF 3.50.02 TQ9508,9514 Have to activate the pending table using mfg proc 69 rather than std proc 13 // for I-210 and kV2c ICM firmware // 11/15/13 AF 3.50.03 Class re-architecture - removed code specific to ICS_Gateway // 12/02/13 jrf 3.50.10 Refactored code used to modify the FWType byte to it's own method. // 02/07/14 jrf 3.50.32 WR 419257 Modified to use a dynamic FWDL block size based on the negotiated // PSEM packet size. // 08/24/16 PGH 4.70.15 701952 Added HAN OTA Firmware // 02/06/17 AF 4.71.07 743128 Added supported for ICM modem firmware // 03/10/17 AF 4.71.09 749833 Renamed the firmware type because 37 is for Verizon LTE only // 12/05/17 AF 4.73.00 Task 469253 Added back the verizon modem fwdl // 12/06/17 AF 4.73.00 Task 469254 Added ATT/Rogers modem fwdl // public FWDownloadResult DownloadFW(string path, ref ushort usBlockIndex, bool blnRetry = false, bool blnActivate = true) { FWDownloadResult Result = FWDownloadResult.UNKNOWN_DRIVER_ERROR; ProcedureResultCodes ProcResult = ProcedureResultCodes.INVALID_PARAM; PSEMResponse ProtocolResponse = PSEMResponse.Ok; byte byEventNumber = 0; ushort idTable = (ushort)PendingTableIds.RegisterFWTbl | PENDING_BIT; ushort usNumberChunks = 0; //A non-zero starting block means we are need to pick up were we left off. bool blnResumeFWDL = (0 != usBlockIndex); System.IO.FileStream streamFile; System.IO.MemoryStream streamHeader = new System.IO.MemoryStream(); System.IO.MemoryStream streamPSEM = new System.IO.MemoryStream(); try { if (true == blnResumeFWDL) { Result = FWDownloadResult.SUCCESS; } else { // Tell the meter to enter firmware download mode Result = EnterFirmwareDownloadMode(path); } if (FWDownloadResult.SUCCESS != Result) { m_Logger.WriteLine(Logger.LoggingLevel.Detailed, "Initiate F/W Download procedure failed with result = " + Result); } else { // Meter is ready to receive the firmware file streamFile = new System.IO.FileStream(path, System.IO.FileMode.Open, FileAccess.Read); byte[] bybuffer = new byte[streamFile.Length]; streamFile.Read(bybuffer, 0, (int)streamFile.Length); streamFile.Position = 0; switch (bybuffer[9]) { case (byte)FirmwareType.RegisterFW: case (byte)FirmwareType.M2GTWY: case (byte)FirmwareType.DisplayFW: { byEventNumber = REGISTER_EVENT_NUMBER; idTable = (ushort)PendingTableIds.RegisterFWTbl | PENDING_BIT; break; } case (byte)FirmwareType.ZigbeeFW: { byEventNumber = ZIGBEE_EVENT_NUMBER; idTable = (ushort)PendingTableIds.HANModuleFWTbl | PENDING_BIT; break; } case (byte)FirmwareType.RFLANFW: case (byte)FirmwareType.PLANFW: case (byte)FirmwareType.CiscoCommFW: case (byte)FirmwareType.CiscoCfgFW: case (byte)FirmwareType.ChoiceConnectFW: case (byte)FirmwareType.ICSFW: case (byte)FirmwareType.ICS_MODEM_FW_Sierra_Verizon_LTE: case (byte)FirmwareType.ICS_MODEM_FW_Sierra_ATT_Rogers_Bell_LTE: { byEventNumber = COMM_EVENT_NUMBER; idTable = (ushort)PendingTableIds.CommModuleFWTbl | PENDING_BIT; break; } case (byte)FirmwareType.HAN_OTA_FW: case (byte)FirmwareType.HANDevFW: { byEventNumber = HAN_DEV_EVENT_NUMBER; idTable = (ushort)PendingTableIds.HANDeviceFWTbl | PENDING_BIT; break; } default: { throw new NotImplementedException("Table not supported"); } } BuildPendingHeader(ref streamHeader, false, false, byEventNumber, PendingEventRecord.PendingEventCode.NonTimeTrigger); usNumberChunks = (ushort)(streamFile.Length / FWDLBlockSize); if (streamFile.Length != FWDLBlockSize * usNumberChunks) { usNumberChunks++; } OnShowProgress(new ShowProgressEventArgs(1, usNumberChunks + 1, "Firmware Download", "Downloading...")); ushort usSendSize = FWDLBlockSize; for (; (usBlockIndex < usNumberChunks) && (PSEMResponse.Ok == ProtocolResponse); usBlockIndex++) { // The last chunk could be smaller if (usNumberChunks - 1 == usBlockIndex) { usSendSize = (ushort)(streamFile.Length % FWDLBlockSize); // If no remainder then it is a full packet if (0 == usSendSize) { usSendSize = FWDLBlockSize; } } streamHeader.Position = 0; streamPSEM.Position = 0; streamPSEM.SetLength(0); streamHeader.WriteTo(streamPSEM); streamPSEM.Write(bybuffer, usBlockIndex * FWDLBlockSize, usSendSize); ProtocolResponse = m_PSEM.OffsetWrite((ushort)idTable, usBlockIndex * FWDLBlockSize, streamPSEM.ToArray()); OnStepProgress(new ProgressEventArgs()); } // Translate Protocol result Result = TranslateProtocolResult(ProtocolResponse); streamFile.Close(); //Check on success and then activate the table if (PSEMResponse.Ok == ProtocolResponse) { if (FWDLLogSupported) { //Construct the hash code and call the procedure to authenticate CENTRON_AMI_FW_File FWFile = new CENTRON_AMI_FW_File(path); byte[] FWHashCode = FWFile.HashCode; if ((bybuffer[9] != (byte)FirmwareType.ICSFW) && (bybuffer[9] != (byte)FirmwareType.ICS_MODEM_FW_Sierra_Verizon_LTE)) { //Some devices may require this byte to be adjusted. bybuffer[9] = SelectFWTypeByte(bybuffer[9]); ProcResult = AuthenticateFWDL(idTable, bybuffer[9], FWHashCode); } else //Skip authenticate for ICS comm module and modem FWDL { ProcResult = ProcedureResultCodes.COMPLETED; } } else { ProcResult = ProcedureResultCodes.COMPLETED; } if (ProcResult == ProcedureResultCodes.COMPLETED) { if (true == blnActivate) { // Activate the pending table using std proc 13 ProcResult = ActivatePendingTable(false, false, byEventNumber, PendingEventRecord.PendingEventCode.NonTimeTrigger); Result = TranslateProcedureResult(ProcResult); } } else { if (false == blnRetry) { //We couldn't authenticate using the hash code so activation will fail ProcResult = ClearPendingTable(false, false, byEventNumber, PendingEventRecord.PendingEventCode.NonTimeTrigger); } //TODO - not sure this is the correct error Result = FWDownloadResult.SECURITY_ERROR; } OnStepProgress(new ProgressEventArgs()); } else //PSEMResponse.Ok != ProtocolResponse { //Decrement the block index so we make sure we restart on the block that we failed on. usBlockIndex--; if (false == blnRetry) { // Write failed, so clear the pending table ProcResult = ClearPendingTable(false, false, byEventNumber, PendingEventRecord.PendingEventCode.NonTimeTrigger); } Result = FWDownloadResult.WRITE_ERROR; } OnHideProgress(new EventArgs()); } } catch (Exception e) { if (false == blnRetry) { // Log it and pass it up OnHideProgress(new EventArgs()); m_Logger.WriteException(this, e); throw e; } else { Result = FWDownloadResult.WRITE_ERROR; } } return(Result); }
/// <summary> /// Downloads the firmware file to the meter and activates it. /// On download failure, the pending table is cleared. The activation /// will cause the meter to drop the psem task so meter log off must /// follow this function call /// </summary> /// <param name="path">Complete file path of the firmware file</param> /// <returns>Itron.Metering.Device.FWDownloadResult</returns> // Revision History // MM/DD/YY who Version Issue# Description // -------- --- ------- ------ --------------------------------------- // 08/28/06 AF 7.35.00 N/A Created // 09/15/06 AF 7.35.00 N/A Added Catch for TimeOutException // 10/18/06 AF 7.40.00 N/A Removed wait within the main loop // 05/13/08 AF 1.50.24 Removed IFirmwareDownload from the method name // 04/19/10 AF 2.40.39 Added M2 Gateway support // 08/18/11 AF 2.52.05 Added support for authentication using a hash code // 08/26/11 AF 2.52.08 Added support for Cisco f/w // 09/22/11 AF 2.52.21 N/A Added support for Cisco config file f/w d/l - TODO remove when no longer needed // 10/12/11 AF 2.53.00 Changed the Cisco Comm fw enum name // 03/22/12 JJJ 2.60.xx Added support for ChoiceConnect FW // 05/10/12 JJJ 2.60.xx Tweaked FW Type passed to AuthenticateFWDL if ChoiceConnect, make RFLAN // public FWDownloadResult DownloadFW(string path) { FWDownloadResult Result = FWDownloadResult.UNKNOWN_DRIVER_ERROR; ProcedureResultCodes ProcResult = ProcedureResultCodes.INVALID_PARAM; PSEMResponse ProtocolResponse = PSEMResponse.Ok; byte byEventNumber = 0; ushort idTable = (ushort)PendingTableIds.RegisterFWTbl | PENDING_BIT; ushort usNumberChunks = 0; ushort intIndex; System.IO.FileStream streamFile; System.IO.MemoryStream streamHeader = new System.IO.MemoryStream(); System.IO.MemoryStream streamPSEM = new System.IO.MemoryStream(); try { // Tell the meter to enter firmware download mode Result = EnterFirmwareDownloadMode(path); if (FWDownloadResult.SUCCESS != Result) { m_Logger.WriteLine(Logger.LoggingLevel.Detailed, "Initiate F/W Download procedure failed with result = " + Result); } else { // Meter is ready to receive the firmware file streamFile = new System.IO.FileStream(path, System.IO.FileMode.Open, FileAccess.Read); byte[] bybuffer = new byte[streamFile.Length]; streamFile.Read(bybuffer, 0, (int)streamFile.Length); streamFile.Position = 0; switch (bybuffer[9]) { case (byte)FirmwareType.RegisterFW: case (byte)FirmwareType.M2GTWY: case (byte)FirmwareType.DisplayFW: { byEventNumber = REGISTER_EVENT_NUMBER; idTable = (ushort)PendingTableIds.RegisterFWTbl | PENDING_BIT; break; } case (byte)FirmwareType.ZigbeeFW: { byEventNumber = ZIGBEE_EVENT_NUMBER; idTable = (ushort)PendingTableIds.HANModuleFWTbl | PENDING_BIT; break; } case (byte)FirmwareType.RFLANFW: case (byte)FirmwareType.PLANFW: case (byte)FirmwareType.CiscoCommFW: case (byte)FirmwareType.CiscoCfgFW: case (byte)FirmwareType.ChoiceConnectFW: { byEventNumber = COMM_EVENT_NUMBER; idTable = (ushort)PendingTableIds.CommModuleFWTbl | PENDING_BIT; break; } case (byte)FirmwareType.HANDevFW: { byEventNumber = HAN_DEV_EVENT_NUMBER; idTable = (ushort)PendingTableIds.HANDeviceFWTbl | PENDING_BIT; break; } default: { throw new NotImplementedException("Table not supported"); } } BuildPendingHeader(ref streamHeader, false, false, byEventNumber, PendingEventRecord.PendingEventCode.NonTimeTrigger); usNumberChunks = (ushort)(streamFile.Length / BLOCK_SIZE); if (streamFile.Length != BLOCK_SIZE * usNumberChunks) { usNumberChunks++; } OnShowProgress(new ShowProgressEventArgs(1, usNumberChunks + 1, "Firmware Download", "Downloading...")); ushort usSendSize = BLOCK_SIZE; for (intIndex = 0; (intIndex < usNumberChunks) && (PSEMResponse.Ok == ProtocolResponse); intIndex++) { // The last chunk could be smaller if (usNumberChunks - 1 == intIndex) { usSendSize = (ushort)(streamFile.Length % BLOCK_SIZE); // If no remainder then it is a full packet if (0 == usSendSize) { usSendSize = BLOCK_SIZE; } } streamHeader.Position = 0; streamPSEM.Position = 0; streamPSEM.SetLength(0); streamHeader.WriteTo(streamPSEM); streamPSEM.Write(bybuffer, intIndex * BLOCK_SIZE, usSendSize); ProtocolResponse = m_PSEM.OffsetWrite((ushort)idTable, intIndex * BLOCK_SIZE, streamPSEM.ToArray()); OnStepProgress(new ProgressEventArgs()); } // Translate Protocol result Result = TranslateProtocolResult(ProtocolResponse); streamFile.Close(); //Check on success and then activate the table if (PSEMResponse.Ok == ProtocolResponse) { if (FWDLLogSupported) { //Construct the hash code and call the procedure to authenticate CENTRON_AMI_FW_File FWFile = new CENTRON_AMI_FW_File(path); byte[] FWHashCode = FWFile.HashCode; // if MSM ChoiceConnect meter and ChoiceConnect FWDL request, spoof RFLAN FWDL CENTRON_AMI AmiDevice = this as CENTRON_AMI; if (AmiDevice != null && bybuffer[9] == (byte)FirmwareType.ChoiceConnectFW && AmiDevice.IsChoiceConnectMsmMeter) { bybuffer[9] = (byte)FirmwareType.RFLANFW; } ProcResult = AuthenticateFWDL(idTable, bybuffer[9], FWHashCode); } else { ProcResult = ProcedureResultCodes.COMPLETED; } if (ProcResult == ProcedureResultCodes.COMPLETED) { // Activate the pending table ProcResult = ActivatePendingTable(false, false, byEventNumber, PendingEventRecord.PendingEventCode.NonTimeTrigger); Result = TranslateProcedureResult(ProcResult); } else { //We couldn't authenticate using the hash code so activation will fail ProcResult = ClearPendingTable(false, false, byEventNumber, PendingEventRecord.PendingEventCode.NonTimeTrigger); //TODO - not sure this is the correct error Result = FWDownloadResult.SECURITY_ERROR; } OnStepProgress(new ProgressEventArgs()); } else { // Write failed, so clear the pending table ProcResult = ClearPendingTable(false, false, byEventNumber, PendingEventRecord.PendingEventCode.NonTimeTrigger); Result = FWDownloadResult.WRITE_ERROR; } OnHideProgress(new EventArgs()); } } catch (Exception e) { // Log it and pass it up OnHideProgress(new EventArgs()); m_Logger.WriteException(this, e); throw e; } return(Result); }