/// <summary> /// Write the calibration blocks. /// </summary> private async Task <bool> Write(CancellationToken cancellationToken, byte[] image, WriteType writeType) { await this.vehicle.SendToolPresentNotification(); BlockType relevantBlocks; switch (writeType) { case WriteType.Compare: relevantBlocks = BlockType.All; break; case WriteType.TestWrite: relevantBlocks = BlockType.Calibration; break; case WriteType.Calibration: relevantBlocks = BlockType.Calibration; break; case WriteType.Parameters: relevantBlocks = BlockType.Parameter; break; case WriteType.OsAndCalibration: relevantBlocks = BlockType.Calibration | BlockType.OperatingSystem; break; case WriteType.Full: // Overwriting parameter blocks would break the EBCM pairing, // which is not what most users want. They just want a new OS // and the calibration to go along with it. // // The cast seems redundant, but C# converts the enum values // to ints when it does arithmetic. relevantBlocks = (BlockType)(BlockType.All - BlockType.Parameter); break; default: throw new InvalidDataException("Unsuppported operation type: " + writeType.ToString()); } // Which flash chip? await this.vehicle.SendToolPresentNotification(); await this.vehicle.SetDeviceTimeout(TimeoutScenario.ReadProperty); Query <UInt32> chipIdQuery = this.vehicle.CreateQuery <UInt32>( this.protocol.CreateFlashMemoryTypeQuery, this.protocol.ParseFlashMemoryType, cancellationToken); Response <UInt32> chipIdResponse = await chipIdQuery.Execute(); if (chipIdResponse.Status != ResponseStatus.Success) { logger.AddUserMessage("Unable to determine which flash chip is in this PCM"); return(false); } // TODO: Move the device types lookup to a function in Misc/FlashChips.cs // known chips in the P01 and P59 // http://ftp1.digi.com/support/documentation/jtag_v410_flashes.pdf string Amd = "AMD"; // 0001 string Intel = "Intel"; // 0089 string I4471 = "28F400B5-B 512Kb"; // 4471 string A2258 = "Am29F800B 1Mbyte"; // 2258 string I889D = "28F800B5-B 1Mbyte"; // 889D string unknown = "unknown"; // default case logger.AddUserMessage("Flash memory ID code: " + chipIdResponse.Value.ToString("X8")); switch ((chipIdResponse.Value >> 16)) { case 0x0001: logger.AddUserMessage("Flash memory manufactuer: " + Amd); break; case 0x0089: logger.AddUserMessage("Flash memory manufactuer: " + Intel); break; default: logger.AddUserMessage("Flash memory manufactuer: " + unknown); break; } switch (chipIdResponse.Value & 0xFFFF) { case 0x4471: logger.AddUserMessage("Flash memory type: " + I4471); break; case 0x2258: logger.AddUserMessage("Flash memory type: " + A2258); break; case 0x889D: logger.AddUserMessage("Flash memory type: " + I889D); break; default: logger.AddUserMessage("Flash memory type: " + unknown); break; } await this.vehicle.SendToolPresentNotification(); IList <MemoryRange> ranges = FlashChips.GetMemoryRanges(chipIdResponse.Value, this.logger); if (ranges == null) { return(false); } CKernelVerifier verifier = new CKernelVerifier( image, ranges, this.vehicle, this.protocol, this.logger); bool allRangesMatch = false; bool writeAttempted = false; for (int attempt = 1; attempt <= 5; attempt++) { if (await verifier.CompareRanges( ranges, image, relevantBlocks, cancellationToken)) { allRangesMatch = true; // Don't stop here if the user just wants to test their cable. if (writeType == WriteType.TestWrite) { if (attempt == 1) { this.logger.AddUserMessage("Beginning test."); } } else { this.logger.AddUserMessage("All ranges are identical."); return(true); } } if ((writeType == WriteType.TestWrite) && (attempt > 1)) { // TODO: the app should know if any errors were encountered. The user shouldn't need to check. this.logger.AddUserMessage("Test complete. Were any errors logged above?"); return(true); } // Stop now if the user only requested a comparison. if (writeType == WriteType.Compare) { this.logger.AddUserMessage("Note that mismatched Parameter blocks are to be expected."); this.logger.AddUserMessage("Parameter data can change every time the PCM is used."); return(true); } writeAttempted = true; // Erase and rewrite the required memory ranges. await this.vehicle.SetDeviceTimeout(TimeoutScenario.Maximum); foreach (MemoryRange range in ranges) { // We'll send a tool-present message during the erase request. if ((range.ActualCrc == range.DesiredCrc) && (writeType != WriteType.TestWrite)) { continue; } if ((range.Type & relevantBlocks) == 0) { continue; } this.logger.AddUserMessage( string.Format( "Processing range {0:X6}-{1:X6}", range.Address, range.Address + (range.Size - 1))); if (writeType == WriteType.TestWrite) { this.logger.AddUserMessage("Pretending to erase."); } else { if (!await this.EraseMemoryRange(range, cancellationToken)) { return(false); } } if (writeType == WriteType.TestWrite) { this.logger.AddUserMessage("Pretending to write..."); } else { this.logger.AddUserMessage("Writing..."); } await this.vehicle.SendToolPresentNotification(); await WriteMemoryRange( range, image, writeType == WriteType.TestWrite, cancellationToken); } } if (!writeAttempted) { this.logger.AddUserMessage("Assertion failed. WriteAttempted should be true."); } if (allRangesMatch) { this.logger.AddUserMessage("Flash successful!"); return(true); } this.logger.AddUserMessage("==============================================="); this.logger.AddUserMessage("THE CHANGES WERE -NOT- WRITTEN SUCCESSFULLY"); this.logger.AddUserMessage("==============================================="); if (writeType == WriteType.Calibration) { this.logger.AddUserMessage("Erasing Calibration to force recovery mode."); this.logger.AddUserMessage(""); foreach (MemoryRange range in ranges) { if (range.Type == BlockType.Calibration) { await this.EraseMemoryRange(range, cancellationToken); } } } if (cancellationToken.IsCancellationRequested) { this.logger.AddUserMessage(""); this.logger.AddUserMessage("The operation was cancelled."); this.logger.AddUserMessage("This PCM is probably not usable in its current state."); this.logger.AddUserMessage(""); } else { this.logger.AddUserMessage("This may indicate a hardware problem on the PCM."); this.logger.AddUserMessage("We tried, and re-tried, and it still didn't work."); this.logger.AddUserMessage(""); this.logger.AddUserMessage("Please start a new thread at pcmhacking.net, and"); this.logger.AddUserMessage("include the contents of the debug tab."); this.RequestDebugLogs(cancellationToken); } return(false); }
/// <summary> /// This was used to test the kernel's CRC code. /// </summary> private async Task InvestigateCrc(CancellationToken cancellationToken) { IList <MemoryRange> ranges = FlashChips.GetMemoryRanges(0x00894471, this.logger); logger.AddUserMessage("Requesting CRCs from PCM..."); foreach (MemoryRange range in ranges) { this.device.ClearMessageQueue(); bool success = false; UInt32 crc = 0; await this.device.SetTimeout(TimeoutScenario.ReadCrc); // You might think that a shorter retry delay would speed things up, // but 1500ms delay gets CRC results in about 3.5 seconds. // A 1000ms delay resulted in 4+ second CRC responses, and a 750ms // delay resulted in 5 second CRC responses. The PCM needs to spend // its time caculating CRCs rather than responding to messages. int retryDelay = 1500; Message query = this.protocol.CreateCrcQuery(range.Address, range.Size); for (int attempts = 0; attempts < 100; attempts++) { if (cancellationToken.IsCancellationRequested) { break; } if (!await this.device.SendMessage(query)) { await Task.Delay(retryDelay); continue; } Message response = await this.device.ReceiveMessage(); if (response == null) { await Task.Delay(retryDelay); continue; } Response <UInt32> crcResponse = this.protocol.ParseCrc(response, range.Address, range.Size); if (crcResponse.Status != ResponseStatus.Success) { await Task.Delay(retryDelay); continue; } success = true; crc = crcResponse.Value; break; } this.device.ClearMessageQueue(); if (!success) { this.logger.AddUserMessage("Unable to get CRC for memory range " + range.Address.ToString("X8") + " / " + range.Size.ToString("X8")); continue; } range.ActualCrc = crc; this.logger.AddUserMessage( string.Format( "Range {0:X6}-{1:X6} - Local: {2:X8} - PCM: {3:X8} - {4}", range.Address, range.Address + (range.Size - 1), range.DesiredCrc, range.ActualCrc, range.Type)); } }