public bool Checksum()
        {
            SAE.SAEMessage Command  = new SAE.SAEMessage(Address: 0x10);
            SAE.SAEMessage Response = new SAE.SAEMessage();

            Command.Mode = SAE.SAEModes.START_DIAG_ROUTINE_BY_NUMBER;
            Command.AddInt8((int)DiagRoutine.Checksum);
            Command.AddInt16(firmware_image.Checksum);

            Response = session.SAETxRx(Command, 0);

            if (Response.Response != SAE.SAE_responses.ROUTINE_NOT_COMPLETE)
            {
                throw new Exception("Failure attempting to execute checksum routine on PCM");
            }

            System.Threading.Thread.Sleep(2000);

            Command.Mode = SAE.SAEModes.STOP_DIAG_ROUTINE_BY_NUMBER;
            Command.AddInt8((int)DiagRoutine.Checksum);

            Response = session.SAETxRx(Command, 0);

            if (Response.Response == SAE.SAE_responses.AFFIRMITIVE_RESPONSE)
            {
                return(true);
            }
            return(false);
        }
        private RomSize ProbeMemLayout()
        {
            SAE.SAEMessage Command  = new SAE.SAEMessage(Address: 0x10);
            SAE.SAEMessage Response = new SAE.SAEMessage();

            Command.Mode = SAE.SAEModes.DATA_BY_ADDRESS;
            Command.AddInt24(0x09FF00);

            Response = session.SAETxRx(Command, 2);

            if (Response.Mode == SAE.SAEModes.DATA_BY_ADDRESS_RESPONSE)
            {
                return(RomSize._216k);
            }

            Command.AddInt24(0x01FFFC);

            Response = session.SAETxRx(Command, 2);

            if (Response.Mode == SAE.SAEModes.DATA_BY_ADDRESS_RESPONSE)
            {
                return(RomSize._112k);
            }

            Command.AddInt24(0x019FFC);

            Response = session.SAETxRx(Command, 2);

            if (Response.Mode == SAE.SAEModes.DATA_BY_ADDRESS_RESPONSE)
            {
                return(RomSize._88k);
            }

            throw new Exception("Memory layout is unknown!");   //No known matching memory models
        }
        public void EraseFlash()
        {
            SAE.SAEMessage Command  = new SAE.SAEMessage(Address: 0x10);
            SAE.SAEMessage Response = new SAE.SAEMessage();

            Command.Mode = SAE.SAEModes.START_DIAG_ROUTINE_BY_NUMBER;
            Command.AddInt8((int)DiagRoutine.EraseFlash);

            Response = session.SAETxRx(Command, 0);

            if (Response.Response != SAE.SAE_responses.ROUTINE_NOT_COMPLETE)
            {
                throw new Exception("Failure executing flash erase routine");
            }

            System.Threading.Thread.Sleep(4000);

            Command.Mode = SAE.SAEModes.STOP_DIAG_ROUTINE_BY_NUMBER;
            Command.AddInt8((int)DiagRoutine.EraseFlash);

            Response = session.SAETxRx(Command, 0);
            if (Response.Response != SAE.SAE_responses.AFFIRMITIVE_RESPONSE)
            {
                throw new Exception("Failure stopping flash erase routine!");
            }
        }
        private int VinAddress()
        {
            SAE.SAEMessage Command  = new SAE.SAEMessage(Address: 0x10);
            SAE.SAEMessage Response = new SAE.SAEMessage();

            Command.Mode = SAE.SAEModes.DATA_BY_PID;
            Command.AddInt16(0x1100);

            Response = session.SAETxRx(Command, 1);

            if (Response.IsValid)
            {
                return(BitConverter.ToInt32(Response.Data, 0));
            }
            throw new J2534Exception("Failure in 'VinAddress'");
        }
        private byte [] ReadLocation(int Address)
        {
            SAE.SAEMessage Command  = new SAE.SAEMessage(Address: 0x10);
            SAE.SAEMessage Response = new SAE.SAEMessage();

            Command.Mode = SAE.SAEModes.DATA_BY_ADDRESS;
            Command.AddInt24(Address);

            Response = session.SAETxRx(Command, 2);

            if (Response.Mode != SAE.SAEModes.DATA_BY_ADDRESS_RESPONSE)
            {
                throw new Exception("Failure reading memory address");
            }

            return(Response.Data);
        }
        public void ProgramBank(int Address, byte [] Data)
        {
            SAE.SAEMessage Command  = new SAE.SAEMessage(Address: 0x10);
            SAE.SAEMessage Response = new SAE.SAEMessage();

            int block_length      = 0x0400;
            int packets_per_block = block_length / 6 + 1;

            for (int offset = Data.Length - block_length; offset >= 0; offset -= block_length)
            {
                int retry_count = 3;
                do
                {
                    Command.Mode = SAE.SAEModes.REQ_DOWNLOAD;
                    Command.AddInt8(0x80);
                    Command.AddInt16(block_length);
                    Command.AddInt24(Address + offset);

                    Response = session.SAETxRx(Command, 2);

                    if (Response.Response != SAE.SAE_responses.AFFIRMITIVE_RESPONSE)
                    {
                        throw new Exception("failure initiatiing block transfer");  //fail
                    }

                    Command.Mode = SAE.SAEModes.DATA_TRANSFER;
                    int BlockCheckSum = Data.Skip(offset).Take(block_length).Sum(b => (int)b);
                    int p             = 0;
                    for (; p < (block_length - 4); p += 6)  //Create 170 packets with 6 bytes each
                    {
                        Command.DataList.Add(Data.Skip(offset + p).Take(6).ToArray());
                    }
                    //Create the last packet with the remaining 4 bytes of data and 2 byte checksum
                    Command.DataList.Add(Data.Skip(offset + p).Take(4).Concat(BitConverter.GetBytes((short)BlockCheckSum).Reverse()).ToArray());

                    //send all the packets at one time
                    session.SAETx(Command);

                    Command.Mode = SAE.SAEModes.TRANSFER_ROUTINE_EXIT;
                    Command.AddInt8(0x80);

                    Response = session.SAETxRx(Command, 0);

                    retry_count--;
                    if (Response.Response == SAE.SAE_responses.AFFIRMITIVE_RESPONSE)
                    {
                        retry_count = 0;
                    }
                    else if (Response.Response != SAE.SAE_responses.FAIL_WITHOUT_RESULTS ||
                             retry_count < 1)
                    {
                        throw new Exception("Too many retries or general failure"); //fail
                    }
                } while (retry_count > 0);

                Command.Mode = SAE.SAEModes.START_DIAG_ROUTINE_BY_NUMBER;
                Command.AddInt8((int)DiagRoutine.WriteFlash);

                Response = session.SAETxRx(Command, 0);

                if (Response.Response != SAE.SAE_responses.ROUTINE_NOT_COMPLETE &&
                    Response.Response != SAE.SAE_responses.AFFIRMITIVE_RESPONSE)
                {
                    throw new Exception("Something failed attempting to write block to flash"); //fail
                }

                Command.Mode = SAE.SAEModes.STOP_DIAG_ROUTINE_BY_NUMBER;
                Command.AddInt8((int)DiagRoutine.WriteFlash);

                Response = session.SAETxRx(Command, 0);

                if (Response.Response != SAE.SAE_responses.AFFIRMITIVE_RESPONSE)
                {
                    throw new Exception("Something failed attempting to write block to flash");//fail
                }
            }
        }
        private byte[] BlockRead(int RangeStart, int RangeEnd)
        {
            SAE.SAEMessage Command  = new SAE.SAEMessage(Address: 0x10);
            SAE.SAEMessage Response = new SAE.SAEMessage();

            int block_length      = 0x0400;
            int packets_per_block = block_length / 6 + 1;

            if (block_length % packets_per_block == 0)
            {
            }

            List <byte> data = new List <byte>();

            object block_transfer_handle = session.CreateRxHandle(Command.Address, SAE.SAEModes.DATA_TRANSFER);

            for (int block_address = RangeStart; block_address < RangeEnd; block_address += block_length)
            {
                Command.Mode = SAE.SAEModes.REQ_UPLOAD;
                byte[] Data = GetBytes.Int8(0x80).Concat(
                    GetBytes.Int16(block_length)).Concat(
                    GetBytes.Int24(block_address)).ToArray();
                //Data.C
                Command.AddInt8(0x80);    //bank selection?  (note from old code)
                Command.AddInt16(block_length);
                Command.AddInt24(block_address);

                Response = session.SAETxRx(Command, 2);

                if (Response.Response != SAE.SAE_responses.AFFIRMITIVE_RESPONSE)
                {
                    throw new Exception();
                }

                List <byte[]> rx_data = session.SAERx(block_transfer_handle, packets_per_block, 600);

                if (rx_data.Count != packets_per_block)
                {
                    throw new Exception();
                    //fail
                }
                else
                {
                    int a = 1;
                }

                data.AddRange(rx_data.Aggregate((acumulator, next_packet) => acumulator.Concat(next_packet).ToArray()).Take(block_length));

                Command.Mode = SAE.SAEModes.TRANSFER_ROUTINE_EXIT;
                Command.AddInt8(0x80);

                Response = session.SAETxRx(Command, 0);

                if (Response.Response != SAE.SAE_responses.AFFIRMITIVE_RESPONSE)
                {
                    throw new Exception();//fail
                }
            }
            session.DestroyRxHandle(block_transfer_handle);
            return(data.ToArray());
        }
        private bool IVFERHandshake(bool HighSpeed = true)
        {
            SAE.SAEMessage Command  = new SAE.SAEMessage(Address: 0x10);
            SAE.SAEMessage Response = new SAE.SAEMessage();

            session.InitializeIVEFERConfig();
            session.FEPSOn();
            do
            {
                //break on key press or timeout?
            } while (session.CatchBroadcastMessage() == false);

            Command.Mode = SAE.SAEModes.DIAG_HEARTBEAT;

            session.StartPeriodicMessage(Command);

            Command.Mode = SAE.SAEModes.START_DIAG_ROUTINE_BY_NUMBER;
            Command.AddInt8((int)DiagRoutine.IVFEREntry);
            Command.AddInt16(0x00D8);   //Number of blocks to write (not used for reading)
            Command.AddInt8(salt);
            Command.AddInt8(0x00);

            Response = session.SAETxRx(Command, 0);

            if (Response.Response != SAE.SAE_responses.AFFIRMITIVE_RESPONSE)
            {
                System.Windows.Forms.MessageBox.Show("IVFER entry failed!");
                return(false);
            }

            Command.Mode = SAE.SAEModes.REQ_SECURITY_ACCESS;
            Command.AddInt8(0x01);

            Response = session.SAETxRx(Command, 1);

            if (Response.Mode != SAE.SAEModes.REQ_SECURITY_ACCESS_RESPONSE &&
                Response.Data.Length == 3)
            {
                System.Windows.Forms.MessageBox.Show("Security Access Level 1 entry failed!");
                return(false);
            }

            Command.Mode = SAE.SAEModes.REQ_SECURITY_ACCESS;
            Command.AddInt8(0x02);
            Command.AddBytes(ComputeKey1(Response.Data));

            Response = session.SAETxRx(Command, 1);

            if (Response.Response != SAE.SAE_responses.SECURITY_ACCESS_ALLOWED)
            {
                System.Windows.Forms.MessageBox.Show("Security Access Level 1 Challenge failed!");
                return(false);
            }

            //Request level two security for high data rate
            Command.Mode = SAE.SAEModes.REQ_SECURITY_ACCESS;
            Command.AddInt8(0x01);

            Response = session.SAETxRx(Command, 1);

            if (Response.Mode == SAE.SAEModes.REQ_SECURITY_ACCESS_RESPONSE &&
                Response.Data.Length == 3)
            {
                Command.Mode = SAE.SAEModes.REQ_SECURITY_ACCESS;
                Command.AddInt8(0x02);
                Command.AddBytes(ComputeKey2(Response.Data));

                Response = session.SAETxRx(Command, 1);

                if (Response.Response != SAE.SAE_responses.SECURITY_ACCESS_ALLOWED)
                {
                    System.Windows.Forms.MessageBox.Show("Security Access Level 2 Challenge failed!");
                    return(false);
                }

                if (!HighSpeed)
                {
                    return(true);
                }

                Command.Mode = SAE.SAEModes.START_DIAG_ROUTINE_BY_NUMBER;
                Command.AddInt8((int)DiagRoutine.BaudRate);
                Command.AddInt8(0x03);    //83.2k baud rate

                Response = session.SAETxRx(Command, 0);

                if (Response.Response == SAE.SAE_responses.AFFIRMITIVE_RESPONSE)
                {
                    session.HighSpeedMode();
                }
            }
            return(true);
        }