Example #1
0
        /// <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);
        }
Example #2
0
        /// <summary>
        /// Write the calibration blocks.
        /// </summary>
        private async Task <bool> Write(CancellationToken cancellationToken, byte[] image, WriteType writeType, ToolPresentNotifier notifier)
        {
            await notifier.Notify();

            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:
                relevantBlocks = BlockType.All;
                break;

            default:
                throw new InvalidDataException("Unsuppported operation type: " + writeType.ToString());
            }

            // Which flash chip?
            await notifier.Notify();

            await this.device.SetTimeout(TimeoutScenario.ReadProperty);

            Query <UInt32> chipIdQuery = new Query <uint>(
                this.device,
                this.protocol.CreateFlashMemoryTypeQuery,
                this.protocol.ParseFlashMemoryType,
                this.logger,
                cancellationToken,
                notifier);
            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);
            }

            logger.AddUserMessage("Flash memory type: " + chipIdResponse.Value.ToString("X8"));

            // known chips
            // 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

            logger.AddUserMessage("Flash memory ID: " + chipIdResponse.Value.ToString("X8"));
            if ((chipIdResponse.Value >> 16) == 0x0001)
            {
                logger.AddUserMessage("Flash memory manufactuer: " + Amd);
            }
            if ((chipIdResponse.Value >> 16) == 0x0089)
            {
                logger.AddUserMessage("Flash memory manufactuer: " + Intel);
            }
            if ((chipIdResponse.Value & 0xFFFF) == 0x4471)
            {
                logger.AddUserMessage("Flash memory type: " + I4471);
            }
            if ((chipIdResponse.Value & 0xFFFF) == 0x2258)
            {
                logger.AddUserMessage("Flash memory type: " + A2258);
            }
            if ((chipIdResponse.Value & 0xFFFF) == 0x889D)
            {
                logger.AddUserMessage("Flash memory type: " + I889D);
            }

            await notifier.Notify();

            IList <MemoryRange> ranges = this.GetMemoryRanges(chipIdResponse.Value);

            if (ranges == null)
            {
                return(false);
            }

            logger.AddUserMessage("Computing CRCs from local file...");
            this.GetCrcFromImage(ranges, image);

            if (await this.CompareRanges(
                    ranges,
                    image,
                    relevantBlocks,
                    notifier,
                    cancellationToken))
            {
                // Don't stop here if the user just wants to test their cable.
                if (writeType != WriteType.TestWrite)
                {
                    this.logger.AddUserMessage("All ranges are identical.");
                    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);
            }

            // Erase and rewrite the required memory ranges.
            await this.device.SetTimeout(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("Erasing");

                    Query <byte> eraseRequest = new Query <byte>(
                        this.device,
                        () => this.protocol.CreateFlashEraseBlockRequest(range.Address),
                        this.protocol.ParseFlashEraseBlock,
                        this.logger,
                        cancellationToken,
                        notifier);

                    eraseRequest.MaxTimeouts = 5; // Reduce this when we know how many are likely to be needed.
                    Response <byte> eraseResponse = await eraseRequest.Execute();

                    if (eraseResponse.Status != ResponseStatus.Success)
                    {
                        this.logger.AddUserMessage("Unable to erase flash memory: " + eraseResponse.Status.ToString());
                        this.RequestDebugLogs(cancellationToken);
                        return(false);
                    }

                    if (eraseResponse.Value != 0x80)
                    {
                        this.logger.AddUserMessage("Unable to erase flash memory. Code: " + eraseResponse.Value.ToString("X2"));
                        this.RequestDebugLogs(cancellationToken);
                        return(false);
                    }
                }

                if (writeType == WriteType.TestWrite)
                {
                    this.logger.AddUserMessage("Testing...");
                }
                else
                {
                    this.logger.AddUserMessage("Writing...");
                }

                await notifier.Notify();

                await WriteMemoryRange(
                    range,
                    image,
                    writeType == WriteType.TestWrite,
                    notifier,
                    cancellationToken);
            }

            bool match = await this.CompareRanges(ranges, image, relevantBlocks, notifier, cancellationToken);

            if (writeType == WriteType.TestWrite)
            {
                // 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);
            }

            if (match)
            {
                this.logger.AddUserMessage("Flash successful!");
                return(true);
            }

            this.logger.AddUserMessage("===============================================");
            this.logger.AddUserMessage("THE CHANGES WERE -NOT- WRITTEN SUCCESSFULLY");
            this.logger.AddUserMessage("===============================================");

            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("Don't panic. Also, don't try to drive this car.");
                this.logger.AddUserMessage("Please try flashing again. Preferably now.");
                this.logger.AddUserMessage("In most cases, the second try will succeed.");
                this.logger.AddUserMessage("");
                this.logger.AddUserMessage("If this happens three times in a row, please");
                this.logger.AddUserMessage("start a new thread at pcmhacking.net, and");
                this.logger.AddUserMessage("include the contents of the debug tab.");
                this.logger.AddUserMessage("");
                this.RequestDebugLogs(cancellationToken);
            }

            return(false);
        }