/// <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); }
/// <summary> /// Performs a manufacturer procedure 37 to tell the meter to get ready for /// firmware download /// </summary> /// <param name="strFilePath">path to the f/w file to download</param> /// <returns>ProcedureResultCodes</returns> // Revision History // MM/DD/YY who Version Issue# Description // -------- --- ------- ------ --------------------------------------- // 08/21/06 AF 7.35.00 N/A Created // 09/05/06 AF 7.35.00 Corrected the order of the data in the // procedure parameters // 10/04/06 AF 7.40.00 N/A Moved from CENTRON_AMI.cs // 07/31/07 KRC 8.10.16 3058 Adding more detailed errors; // 01/23/12 RCG 2.53.33 191589 Adding the Device Class to the parameter list in order to support Gas Ranger Extended FW DL // 05/10/12 JJJ 2.60.xx Tweaking FW Type so Register sees ChoiceConnect FW as RFLAN FW private FWDownloadResult EnterFirmwareDownloadMode(string strFilePath) { //Construct the parameters for mfg proc 37 and execute the procedure FileInfo fi = new FileInfo(strFilePath); bool bIsThirdPartyFWDownload = this is M2_Gateway == false && VersionChecker.CompareTo(FWRevision, CENTRON_AMI.VERSION_LITHIUM_3_12) >= 0; byte[] bybuf = new byte[FW_HEADER_LENGTH]; byte[] byParameters = null; byte[] byImageSize = new byte[IMAGE_SIZE_FIELD_LEN]; byte[] byChunkSize = new byte[CHUNK_SIZE_FIELD_LEN]; byte[] ProcResponse; ProcedureResultCodes ProcResult = ProcedureResultCodes.INVALID_PARAM; FWDownloadResult FWResult = FWDownloadResult.UNKNOWN_DRIVER_ERROR; try { m_Logger.WriteLine(Logger.LoggingLevel.Functional, "Initiating Firmware Download"); // Most of the procedure parameters are in the f/w file header FileStream fs = new FileStream(strFilePath, FileMode.Open, FileAccess.Read, FileShare.Read); fs.Read(bybuf, 0, FW_HEADER_LENGTH); fs.Close(); if (bIsThirdPartyFWDownload == true) { byParameters = new byte[INIT_FW_DOWNLOAD_THIRD_PARTY_LEN]; } else { byParameters = new byte[INIT_FW_DOWNLOAD_PARAM_LEN]; } byParameters.Initialize(); // CRC LSB first byParameters[0] = bybuf[1]; byParameters[1] = bybuf[0]; // Retrieve the parameters out of the header Array.Copy(bybuf, 5, byParameters, 2, 9); // if MSM ChoiceConnect meter and ChoiceConnect FWDL request, spoof RFLAN FWDL CENTRON_AMI AmiDevice = this as CENTRON_AMI; if (AmiDevice != null && byParameters[6] == (byte)FirmwareType.ChoiceConnectFW && AmiDevice.IsChoiceConnectMsmMeter) { byParameters[6] = (byte)FirmwareType.RFLANFW; } // image size byImageSize = BitConverter.GetBytes((int)fi.Length); Array.Copy(byImageSize, 0, byParameters, 11, IMAGE_SIZE_FIELD_LEN); // chunk size -- 64 or 128 bytes; hard coded here to 128 byChunkSize = BitConverter.GetBytes(BLOCK_SIZE); Array.Copy(byChunkSize, 0, byParameters, 15, CHUNK_SIZE_FIELD_LEN); if (bIsThirdPartyFWDownload == true) { // The Device Class is a required parameter for 3rd Party Firmware Downloads // The meter needs this in the reverse order that it is stored in the firmware file byParameters[17] = bybuf[17]; byParameters[18] = bybuf[16]; byParameters[19] = bybuf[15]; byParameters[20] = bybuf[14]; } ProcResult = ExecuteProcedure(Procedures.INITIATE_FW_LOADER_SETUP, byParameters, out ProcResponse); if (ProcedureResultCodes.INVALID_PARAM == ProcResult) { // The Firmware load did not work. At some point during development they added // more detail in the Response, so we can read the byte and see what the error is switch (ProcResponse[0]) { case 1: FWResult = FWDownloadResult.FW_IMAGE_TOO_BIG; break; case 2: FWResult = FWDownloadResult.HW_REVISION_OUTSIDE_RANGE; break; case 3: FWResult = FWDownloadResult.HW_VERSION_OUTSIDE_RANGE; break; case 10: FWResult = FWDownloadResult.FW_TYPE_IS_INVALID; break; case 11: FWResult = FWDownloadResult.ZIGBEE_FW_TYPE_INVALID; break; default: FWResult = FWDownloadResult.INVALID_CONFIG; break; } } else { FWResult = TranslateProcedureResult(ProcResult); } } catch (Exception e) { // Log it and pass it up m_Logger.WriteException(this, e); throw (e); } return(FWResult); }