Just4Trionic - CAN and BDM FLASH programmer for Saab cars

Dependencies:   mbed

Revision:
1:d5452e398b76
Parent:
0:e0b964252a05
Child:
2:bf3a2b29259a
--- a/Trionic5.cpp	Wed May 19 12:39:18 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,728 +0,0 @@
-/*******************************************************************************
-
-Trionic5.cpp - By Sophie Dexter, April 2010
-
-This C++ module provides functions for reading and writing the FLASH chips and
-SRAM in Trionic5 ECUs. (Writing the adaption data back to SRAM not done yet).
-
-Some functions need an additional 'bootloader' program to be sent to the T5 ECU
-before they can be used. These functions are: Identifying the T5 ECU type and
-FLASH chips, dumping the FLASH chips, erasing the FLASH chips, writing to the
-FLASH chips and calculating the FLASH chips' checksum.
-
-My version of the bootloader, BOOTY.S19, includes some features not in other
-bootloaders; identifying the ECU and FLASH chip types, a 'safer' way of dumping
-the FLASH chips and the ability to program AMD 29F010 type FLASH chips (NOTE
-that AMD 29F010 FLASH programming is experimental at this stage to say the least)
-
-********************************************************************************
-
-WARNING: Use at your own risk, sadly this software comes with no guarantees.
-This software is provided 'free' and in good faith, but the author does not
-accept liability for any damage arising from its use.
-
-*******************************************************************************/
-
-#include "Trionic5.h"
-
-// RS232 on USB connection
-Serial pc2(USBTX, USBRX); // tx, rx
-
-// A timer for timing how long things take to happen
-Timer timer;
-
-// We use CAN on mbed pins 29(CAN_TXD) and 30(CAN_RXD).
-CAN can_temp(p30, p29);
-
-// Need to create this to be able to read and write files on the mbed 'disk'
-LocalFileSystem local("local");
-
-void Trionic5() {
-    // Start the CAN bus system
-    // Note that at the moment this is only for T5 ECUs at 615 kbits
-    CANOpen();
-
-    int counter = 0;        //counter used for testing things !?!
-
-    Trionic5ShowHelp();
-
-    while (1) {
-        // send received messages to the pc over USB connection
-        // This function displays any CAN messages that are 'missed' by the other functions
-        // Can messages might be 'missed' because they are received after a 'timeout' period
-        // or because they weren't expected, e.g. if the T5 ECU resets for some reason
-        Trionic5ShowCANMessage();
-
-//        Wait for a single command character from the PC
-        if (pc2.readable()) {
-//            char command[30] = "w0068c600000000000000";
-//            command = pc2.gets();
-            char c = (pc2.getc());
-            if ((c=='h') || (c=='H')) Trionic5ShowHelp();            // Print help
-            if (c=='\e') return;                                    // 'ESC' key to go back to mbed CAN tool menu
-//            if ((c=='s') or (c=='S')) {
-            if (c=='s') Trionic5GetSymbolTable();                    // Get the Symbol Table
-            if (c=='S') T5ReadCmnd(T5SYMBOLS);
-//            if ((c=='v') or (c=='V')) {
-            if (c=='v') Trionic5GetVersion();                        // Get the Trionic5 software version string
-            if (c=='V') T5ReadCmnd(T5VERSION);
-//            if ((c=='r') or (c=='R')) {
-            if (c=='r') Trionic5GetAdaptionData();                    // Read Adaption Data from RAM and write it to a file
-            if (c=='R') T5RAMCmnd(counter++);
-            if (c=='\r') T5ReadCmnd(CR);                            // CR - send CR type message
-
-//            if ((c=='a') or (c=='A')) {
-            if (c=='a') {                                            //  Get a single symbol from the Symbol Table
-                char symbol[40];
-                T5GetSymbol(symbol);
-                printf("%s",symbol);
-            }
-            if (c=='A') T5Ack();                                    // Just send an 'ACK' message
-            if ((c=='b') or (c=='B')) Trionic5SendBootLoader();        // Send a Bootloader file to the T5 ECU
-            if ((c=='c') or (c=='C')) Trionic5GetChecksum();        // Get Checksum from ECU (Bootloader must be uploaded first)
-            if ((c=='q') or (c=='Q')) Trionic5BootloaderReset();    // Exit the BootLoader and restart the T5 ECU
-            if ((c=='e') or (c=='E')) Trionic5EraseFLASH();            // Erase the FLASH chips
-            if ((c=='t') or (c=='T')) Trionic5GetChipTypes();        // Read back the FLASH chip types
-//            if ((c=='d') or (c=='D')) Trionic5DumpFLASH();            // DUMP the T5 ECU BIN file stored in the FLASH chips
-//            if (c=='d') Trionic5DumpFLASH();            // DUMP the T5 ECU BIN file stored in the FLASH chips
-//            if (c=='D') Trionic5DumpFLASH2();            // DUMP the T5 ECU BIN file stored in the FLASH chips
-            if ((c=='d') or (c=='D')) Trionic5DumpFLASH2();            // DUMP the T5 ECU BIN file stored in the FLASH chips
-            if ((c=='f') or (c=='F')) Trionic5SendFLASHUpdate();    // Send a FLASH update file to the T5 ECU
-            if (c=='3') Trionic5SendC3Message();            // Send the C3 message - should get last used address 0x7FFFF
-        }
-    }
-}
-
-//
-// Trionic5ShowHelp
-//
-// Displays a list of things that can be done with the T5 ECU.
-//
-// inputs:    none
-// return:    none
-//
-void Trionic5ShowHelp() {
-    printf("Trionic 5 Command Menu\r\n");
-    printf("======================\r\n");
-    printf("b - upload and start MyBooty.S19 bootloader\r\n");
-    printf("c - get T5 ECU FLASH checksum (need toupload BOOTY.S19 before using this command)\r\n");
-    printf("d - dump the T5 FLASH BIN file and write it to ORIGINAL.HEX\r\n");
-    printf("e - erase the FLASH chips in the T5 ECU\r\n");
-    printf("f - FLASH the upate file MODIFIED.S19 to the T5 ECU\r\n");
-    printf("e - erase the FLASH chips in the T5 ECU\r\n");
-    printf("r - read SRAM and write it to ADAPTION.HEX file\r\n");
-    printf("s - read Symbol Table, display it and write it to SYMBOLS.TXT\r\n");
-    printf("v - read T5 ECU software version, display it and write it to VERSION.TXT\r\n");
-    printf("q - exit the bootloader and reset the T5 ECU\r\n");
-    printf("t - read the FLASH chip type in the T5 ECU\r\n");
-    printf("3 - read the last used FLASH address in the T5 ECU\r\n");
-    printf("S - send 's' message (to get symbol table)\r\n");
-    printf("V - send 'S' message (to get software version)\r\n");
-    printf("'Enter' Key - send an CR message\r\n");
-    printf("a - send an ACK\r\n");
-    printf("A - read a single symbol from the symbol table\r\n");
-    printf("\r\n");
-    printf("'ESC' - return to mbed CAN tool Menu\r\n");
-    printf("\r\n");
-    printf("h/H - show this help menu\r\n");
-    return;
-}
-
-//
-// Trionic5ShowCANMessage
-//
-// Displays a CAN message in the RX buffer if there is one.
-//
-// inputs:    none
-// return:    bool TRUE if there was a message, FALSE if no message.
-//
-bool Trionic5ShowCANMessage() {
-    CANMessage can_MsgRx;
-    if (can_temp.read(can_MsgRx)) {
-        printf("w%03x%d", can_MsgRx.id, can_MsgRx.len);
-        for (char i=0; i<can_MsgRx.len; i++) {
-            printf("%02x", can_MsgRx.data[i]);
-        }
-        printf(" %c ", can_MsgRx.data[2]);
-        printf("\r\n");
-        return TRUE;
-    }
-    return FALSE;
-}
-
-//
-// Trionic5GetSymbolTable
-//
-// Gets the T5 ECU symbol table.
-// The Symbol Table is sent to the PC and saved to a file, symbols.txt, on the mbed 'local' file system 'disk'.
-//
-// inputs:    none
-// return:    bool TRUE if there all went OK, FALSE if there was an error
-//
-bool Trionic5GetSymbolTable() {
-    FILE *fp = fopen("/local/symbols.txt", "w");  // Open "symbols.txt" on the local file system for writing
-    if (!fp) return FALSE;
-    char symbol[40];
-    char response = '\0';
-    T5ReadCmnd(T5SYMBOLS);
-    response = T5WaitResponse();
-    if (response != '>')
-        return FALSE;
-//                printf("%c",response);
-    T5ReadCmnd(CR);
-    response = T5WaitResponse();
-    if (response != '>')
-        return FALSE;
-//                printf("%c",response);
-    timer.reset();
-    timer.start();
-    do {
-        T5GetSymbol(symbol);
-        printf("%s",symbol);
-        fprintf(fp,"%s",symbol);
-    } while (!StrCmp(symbol,"END\r\n"));
-    timer.stop();
-    printf("Getting the Symbol Table took %f seconds.\r\n",timer.read());
-    fclose(fp);
-    return TRUE;
-}
-
-//
-// Trionic5GetVersion
-//
-// Gets the T5 software version string.
-// The software version is is sent to the PC and saved to a file, version.txt, on the mbed 'local' file system 'disk'.
-//
-// inputs:    none
-// return:    bool TRUE if there all went OK, FALSE if there was an error
-//
-bool Trionic5GetVersion() {
-    FILE *fp = fopen("/local/version.txt", "w");  // Open "version.txt" on the local file system for writing
-    if (!fp) return FALSE;
-    char symbol[40];
-    char response = '\0';
-    T5ReadCmnd(T5VERSION);
-    response = T5WaitResponse();
-    if (response != '>')
-        return FALSE;
-//                printf("%c",response);
-    T5ReadCmnd(CR);
-    response = T5WaitResponse();
-    if (response != '>')
-        return FALSE;
-//                printf("%c",response);
-    T5GetSymbol(symbol);
-    printf("%s",symbol);
-    if (fp)
-        fprintf(fp,"%s",symbol);
-    else
-        printf("ERROR Could not open version.txt for saving\r\n");
-    fclose(fp);
-    return TRUE;
-}
-
-//
-// Trionic5GetAdaptionData
-//
-// Gets the adaption data from the T5's SRAM. 
-// The adaption data is stored in a hex file, adaption.hex, on the mbed 'local' file system 'disk'.
-//
-// Reading the Adaption data from SRAM takes about 16 seconds.
-//
-// inputs:    none
-// return:    bool TRUE if all went OK, FALSE if there was an error.
-//
-bool Trionic5GetAdaptionData() {
-    printf("getting adaption data\r\n");
-    FILE *fp = fopen("/local/adaption.hex", "w");    // Open "adaption.hex" on the local file system for writing
-    if (!fp) return FALSE;
-    printf("opened adaption data file\r\n");
-    unsigned int address = 5;                       // Mysterious reason for starting at 5 !!!
-    char RAMdata[6];
-    timer.reset();
-    timer.start();
-    for (int i=0; i<5462; i++) {                    // Mysterious number 5462 is 0x8000 / 6 - rounded up !!!
-        T5ReadRAM(RAMdata, address);
-        address += 6;
-        for (int j=0; j<6; j++) {
-            if (((i*6) + j) < T5RAMSIZE)
-                fputc(RAMdata[j],fp);
-        }
-    }
-    timer.stop();
-    printf("Getting the RAM Data took %f seconds.\r\n",timer.read());
-    fclose(fp);
-    return TRUE;
-}
-
-//
-// Trionic5SendBootLoader
-//
-// Sends a 'bootloader' file, booty.s19 to the T5 ECU.
-// The 'bootloader' is stored on the mbed 'local' file system 'disk' and must be in S19 format.
-//
-// The 'bootloader' is then able to dump or reFLASH the T5 ECU FLASH chips - this is the whole point of the exercise :-)
-//
-// Sending the 'bootloader' to the T5 ECU takes just over 1 second.
-//
-// inputs:    none
-// return:    bool TRUE if all went OK, 
-//                 FALSE if the 'bootloader' wasn't sent for some reason.
-//
-bool Trionic5SendBootLoader() {
-    FILE *fp = fopen("/local/MyBooty.S19", "r");    // Open "booty.s19" on the local file system for reading
-    if (!fp) return FALSE;
-    int c = 0; // for some reason fgetc returns an int instead of a char
-    int count = 0; // count of bytes in the S-record can be 0xFF = 255 in decimal
-    int asize = 0; // 2,3 or 4 bytes in address
-    unsigned int address = 0; // address to put S-record
-    int checksum = 0; // checksum check at the end of each S-record line
-    char msg[8]; // Construct the bootloader frame for uploading
-    bool sent = FALSE;
-
-    timer.reset();
-    timer.start();
-    while (!sent) {
-
-        do c = fgetc (fp); // get characters until we get an 'S' (throws away \n,\r characters and other junk)
-        while (c != 'S' && c != EOF);
-//                if (c == EOF) return '\a';
-        c = fgetc(fp);        // get the S-record type 1/2/3 5 7/8/9 (Return if EOF reached)
-//                if ((c = fgetc(fp)) == EOF) return '\a';        // get the S-record type 1/2/3 5 7/8/9 (Return if EOF reached)
-        switch (c) {
-            case '0':
-                break;                  // Skip over S0 header record
-
-            case '1':
-            case '2':
-            case '3':
-                asize = 1 + c - '0';    // 2, 3 or 4 bytes for address
-                address = 0;
-// get the number of bytes (in ascii format) in this S-record line
-// there must be at least the address and the checksum so return with an error if less than this!
-                if ((c = SRecGetByte(fp)) < (asize + 1)) break;
-//                        if ((c = SRecGetByte(fp)) < 3) return '\a';
-                count = c;
-                checksum = c;
-// get the address
-                for (int i=0; i<asize; i++) {
-                    c = SRecGetByte(fp);
-                    checksum += c;
-                    address <<= 8;
-                    address |= c;
-                    count--;
-                }
-// send a bootloader address message
-                T5SendBootAddress(address, (count-1));
-//                T5WaitResponsePrint();
-                T5WaitResponse();
-// get and send the bootloader frames for this S-record
-// NOTE the last frame sent may have less than 7 real data bytes but 7 bytes are always sent. In this case the unnecessary bytes
-// are repeated from the previous frame. This is OK because the T5 ECU knows how many bytes to expect (because the count of bytes
-// in the S-Record is sent with the upload address) and ignores any extra bytes in the last frame.
-                for (int i=0; i<count-1; i++) {
-                    c = SRecGetByte(fp);
-                    checksum += c;
-                    msg[1+(i%7)] = c;
-                    if (i%7 == 0) msg[0]=i;     // set the index number
-                    if ((i%7 == 6) || (i == count - 2)) {
-                        T5SendBootFrame(msg);
-//                        T5WaitResponsePrint();
-                        T5WaitResponse();
-                    }
-                }
-// get the checksum
-                if (((checksum += SRecGetByte(fp)) | 0xFF) != 0xFF) break;
-//                if ((checksum += SRecGetByte(fp)) != 0xff) return '\a';
-                break;
-
-            case '5':
-                break;                  // Skip over S5 record types
-
-            case '7':
-            case '8':
-            case '9':
-                asize = 11 - (c - '0');  // 2, 3 or 4 bytes for address
-// get the number of bytes (in ascii format) in this S-record line there must be just the address and the checksum
-// so return with an error if other than this!
-                printf("Starting the bootloader, ");
-                if ((c = SRecGetByte(fp)) != (asize + 1)) break;
-//                if ((c = SRecGetByte(fp)) < 3) return '\a';
-                printf("so far so good");
-                checksum = c;
-// get the address
-                for (int i=0; i<asize; i++) {
-                    c = SRecGetByte(fp);
-                    checksum += c;
-                    address <<= 8;
-                    address |= c;
-                }
-                printf("calculated the address, ");
-// get the checksum
-//                if (((checksum += SRecGetByte(fp)) | 0xFF) != 0xFF) break;
-//                if ((checksum += SRecGetByte(fp)) != 0xff) return '\a';
-                printf("checksum is OK, ");
-                T5StartBootLoader(address);
-//                T5WaitResponsePrint();
-                sent = TRUE;
-                printf("and we're done :-) \r\n");
-                break;
-
-// Some kind of invalid S-record type so break
-            default:
-                printf("oops - didn't recognise that S-Record \r\n");
-                return FALSE;
-//                            return SREC_FORMAT;
-        }
-    }
-    timer.stop();
-    printf("Uploading and starting the bootloader took %f seconds. \r\n",timer.read());
-    fclose(fp);
-    return TRUE;
-}
-
-//
-// Trionic5GetChecksum
-//
-// Calculates the checksum of the FLASH in the T5 ECU.
-// The 'bootloader', booty.s19, must be loaded before this function can be used.
-// The 'bootloader' actually calculates the checksum and compares it with the
-// value stored in the 'header' region at the end of the FLASH.
-//
-// The bootloader sends a single CAN message with the result e.g.
-// w00C8C800CAFEBABE0808
-//  00C - T5 response messages have an CAN id of 00C
-//     8 - All T5 messages have a message length of 8 bytes
-//      C8 - This is the checksum message type
-//        00 - 00 means OK, the checksum calculation matches the stored value which in this case is:
-//          CAFEBABE - lol :-)
-//
-// w00C8C801FFFFFFFF0808
-//        01 - 01 means calculated value doesn't matched the stored value
-//          FFFFFFFF - in this case the stored value is FFFFFFFF - the chips might be erased
-//
-// Calculating the checksum takes a little under 2 seconds.
-//
-// inputs:    none
-// return:    bool TRUE if all went OK, 
-//
-bool Trionic5GetChecksum() {
-    timer.reset();
-    timer.start();
-    T5BootCSumCmnd();
-    T5WaitResponsePrint();
-    timer.stop();
-    printf("T5 ECU took took %f seconds to calculate it's checksum.\r\n",timer.read());
-    return TRUE;
-}
-
-//
-// Trionic5BootloaderReset
-//
-// Exits the Bootloader and restart the T5 ECU
-//
-// inputs:    none
-// outputs:    bool TRUE if all went OK. 
-//
-bool Trionic5BootloaderReset() {
-    T5BootResetCmnd();
-    T5WaitResponsePrint();
-    return TRUE;
-}
-
-//
-// Trionic5GetChipTypes
-//
-// Gets the FLASH chip type fitted.
-//
-// NOTE the bootloader must be loaded in order to use this function.
-//
-// CAN messages from the T5 ECU with FLASH data look like this:
-//
-// C9,00,aa,aa,aa,aa,mm,dd,
-//
-// C9 lets us know its a FLASH id message
-//
-// aa,aa,aa,aa is the FLASH start address which we can use to work out if this is a T5.2 or T5.5 ECU
-// 0x00020000 - T5.2
-// 0x00040000 - T5.5
-//
-// mm = Manufacturer id. These can be:
-// 0x89 - Intel
-// 0x31 - CSI/CAT
-// 0x01 - AMD
-// 0x1F - Atmel
-//
-// dd = Device id. These can be:
-// 0xB8 - Intel _or_ CSI 28F512 (Fiited by Saab in T5.2)
-// 0xB4 - Intel _or_ CSI 28F010 (Fitted by Saab in T5.5)
-// 0x25 - AMD 28F512 (Fiited by Saab in T5.2)
-// 0xA7 - AMD 28F010 (Fitted by Saab in T5.5)
-// 0x20 - AMD 29F010 (Some people have put these in their T5.5)
-// 0x5D - Atmel 29C512 (Some people mave have put these in their T5.2)
-// 0xD5 - Atmel 29C010 (Some people have put these in their T5.5)
-//
-// mm = 0xFF, dd == 0xF7 probably means that the programming voltage isn't right.
-//
-// Finding out which ECU type and FLASH chips are fitted takes under a second.
-//
-// inputs:    none
-// return:    bool TRUE if all went OK.
-//
-bool Trionic5GetChipTypes() {
-    timer.reset();
-    timer.start();
-    T5BootFLASHType();
-    T5WaitResponsePrint();
-    timer.stop();
-    printf("Getting the FLASH chip id took %f seconds.\r\n",timer.read());
-    return TRUE;
-}
-
-//
-// Trionic5EraseFLASH
-//
-// Erases the FLASH Chips.
-//
-// NOTE the bootloader must be loaded in order to use this function.
-//
-// CAN messages from the T5 ECU with FLASH erase command look like this:
-//
-// C0,cc,08,08,08,08,08,08
-//
-// C0 tells us this is a response to the FLASH erase command.
-//
-// cc is a code that tells us what happened:
-//      00 - FLASH was erased OK
-//      01 - Could not erase FLASH chips to 0xFF
-//      02 - Could not write 0x00 to 28F FLASH chips
-//      03 - Unrecognised FLASH chip type (or maybe programming voltage isn't right)
-//      04 - Intel chips found, but unrecognised type
-//      05 - AMD chips found, but unrecognised type
-//      06 - CSI/Catalyst chips found, but unrecognised type
-//      07 - Atmel chips found - Atmel chips don't need to be erased
-//
-// Erasing 28F type FLASH chips takes around 22 seconds. 29F chips should be erased much more quickly.
-//
-// inputs:    none
-// return:    bool TRUE if all went OK.
-//
-bool Trionic5EraseFLASH() {
-    timer.reset();
-    timer.start();
-    T5BootEraseCmnd();
-    T5WaitResponsePrint();
-    timer.stop();
-    printf("Erasing the FLASH took %f seconds.\r\n",timer.read());
-    return TRUE;
-}
-
-//
-// Trionic5DumpFLASH
-//
-// Dumps the FLASH chip BIN file, original.hex to the mbed 'disk'
-//
-// NOTE the bootloader must be loaded in order to use this function.
-//
-// CAN messages from the T5 ECU with FLASH data look like this:
-//
-// A6,mm,dd,dd,dd,dd,08,08
-//
-// A6 lets us know its a FLASH data message
-// mm tells us if there is any more data
-//      mm == 04 means at least 4 more bytes to come
-//      mm == 00 means that these are the last FLASH data bytes
-// dd,dd,dd,dd are 4 FLASH data bytes 
-// The last 2 bytes are 08,08 they aren't and don't meean anything
-// (It would be more efficient to dump 6 bytes at a time because less
-// CAN messages would be needed but it was easier to write the
-// bootloader to send just 4 bytes so that's what I did)
-//
-// Dumping FLASH chips in a T5.5 ECU takes around 88 seconds. Dumping T5.2 ECUs should take about half of this time
-//
-// inputs:    none
-// return:    bool TRUE if all went OK.
-//
-bool Trionic5DumpFLASH() {
-    printf("getting FLASH BIN file\r\n");
-    FILE *fp = fopen("/local/original.hex", "w");    // Open "original.hex" on the local file system for writing
-    if (!fp) return FALSE;
-    printf("opened FLASH BIN file\r\n");
-    char FLASHdata[8];
-    timer.reset();
-    timer.start();
-//    int count = 0;
-    do {
-        T5BootDumpFLASHCmnd();
-//        T5WaitResponsePrint();
-        T5WaitFLASH(FLASHdata);
-        for (int j=0; j<4; j++) {
-            fputc(FLASHdata[j+2],fp);
-//        count++;
-        }
-    } while (FLASHdata[1] != 0);
-    timer.stop();
-//    printf("count = 0x%x\r\n", count);
-    printf("Getting the FLASH BIN took %f seconds.\r\n",timer.read());
-    fclose(fp);
-    return TRUE;
-}
-
-
-//
-// Trionic5DumpFLASH2
-//
-// Gets the adaption data from the T5's SRAM. 
-// The adaption data is stored in a hex file, adaption.hex, on the mbed 'local' file system 'disk'.
-//
-// Reading the Adaption data from SRAM takes about 16 seconds.
-//
-// inputs:    none
-// return:    bool TRUE if all went OK, FALSE if there was an error.
-//
-bool Trionic5DumpFLASH2() {
-    printf("getting FLASH BIN file\r\n");
-    FILE *fp = fopen("/local/original.hex", "w");    // Open "adaption.hex" on the local file system for writing
-    if (!fp) return FALSE;
-    printf("opened FLASH BIN file\r\n");
-    unsigned int address = 0x40005;                  // Mysterious reason for starting at 5 !!!
-    char FLASHdata[6];
-    timer.reset();
-    timer.start();
-    for (int i=0; i<43690; i++) {                    // Mysterious number 43690 is 0x40000 / 6
-        T5ReadFLASH(FLASHdata, address);
-        address += 6;
-        for (int j=0; j<6; j++) {
-            fputc(FLASHdata[j],fp);
-        }
-    }
-    address = 0x7FFFF;
-    T5ReadFLASH(FLASHdata, address);
-    for (int j=0; j<4; j++) {
-        fputc(FLASHdata[j+2],fp);
-    }
-    timer.stop();
-    printf("Getting the FLASH BIN took %f seconds.\r\n",timer.read());
-    fclose(fp);
-    return TRUE;
-}
-
-//
-// Trionic5SendFLASHUpdate
-//
-// Sends a FLASH update file, modified.s19 to the T5 ECU.
-// The FLASH update file is stored on the local file system and must be in S19 format.
-//
-// FLASHing a T5.5 ECU takes around 90 seconds. FLASHing T5.2 ECUs should take about half of this time
-//
-// inputs:    none
-// return:    bool TRUE if all went OK,
-//                 FALSE if the FLASH update failed for some reason.
-//
-bool Trionic5SendFLASHUpdate() {
-    FILE *fp = fopen("/local/modified.s19", "r");    // Open "modified.s19" on the local file system for reading
-    if (!fp) return FALSE;
-    printf("Opened MODIFIED.S19 ");
-    int c = 0; // for some reason fgetc returns an int instead of a char
-    int count = 0; // count of bytes in the S-record can be 0xFF = 255 in decimal
-    int asize = 0; // 2,3 or 4 bytes in address
-    unsigned int address = 0; // address to put S-record
-    int checksum = 0; // checksum check at the end
-    char msg[8]; // Construct the bootloader frame for uploading
-    bool sent = FALSE;
-
-    timer.reset();
-    timer.start();
-    while (!sent) {
-
-        do c = fgetc (fp); // get characters until we get an 'S' (throws away \n,\r characters and other junk)
-        while (c != 'S' && c != EOF);
-//                if (c == EOF) return '\a';
-        c = fgetc(fp);        // get the S-record type 1/2/3 5 7/8/9 (Return if EOF reached)
-//                if ((c = fgetc(fp)) == EOF) return '\a';        // get the S-record type 1/2/3 5 7/8/9 (Return if EOF reached)
-        switch (c) {
-            case '0':
-                break;                  // Skip over S0 header record
-
-            case '1':
-            case '2':
-            case '3':
-                asize = 1 + c - '0';    // 2, 3 or 4 bytes for address
-                address = 0;
-// get the number of bytes (in ascii format) in this S-record line
-// there must be at least the address and the checksum so return with an error if less than this!
-                if ((c = SRecGetByte(fp)) < (asize + 1)) break;
-//                        if ((c = SRecGetByte(fp)) < 3) return '\a';
-                count = c;
-                checksum = c;
-// get the address
-                for (int i=0; i<asize; i++) {
-                    c = SRecGetByte(fp);
-                    checksum += c;
-                    address <<= 8;
-                    address |= c;
-                    count--;
-                }
-                address += 0x40000;         // Only for T5.5 at the moment !!!
-// send a bootloader address message
-                T5SendBootAddress(address, (count-1));
-//                T5WaitResponsePrint();
-                T5WaitResponse();
-// get and send the bootloader frames for this S-record
-// NOTE the last frame sent may have less than 7 real data bytes but 7 bytes are always sent. In this case the unnecessary bytes
-// are repeated from the previous frame. This is OK because the T5 ECU knows how many bytes to expect (because the count of bytes
-// in the S-Record is sent with the upload address) and ignores any extra bytes in the last frame.
-                for (int i=0; i<count-1; i++) {
-                    c = SRecGetByte(fp);
-                    checksum += c;
-                    msg[1+(i%7)] = c;
-                    if (i%7 == 0) msg[0]=i;     // set the index number
-                    if ((i%7 == 6) || (i == count - 2)) {
-                        T5SendBootFrame(msg);
-//                        T5WaitResponsePrint();
-                        T5WaitResponse();
-                    }
-                }
-// get the checksum
-                if (((checksum += SRecGetByte(fp)) | 0xFF) != 0xFF) break;
-//                if ((checksum += SRecGetByte(fp)) != 0xff) return '\a';
-                break;
-
-            case '5':
-                break;                  // Skip over S5 record types
-
-            case '7':
-            case '8':
-            case '9':
-                sent = TRUE;
-                printf("and we're done :-) \r\n");
-                break;
-
-// Some kind of invalid S-record type so break
-            default:
-                printf("oops - didn't recognise that S-Record \r\n");
-                return FALSE;
-//                            return SREC_FORMAT;
-        }
-    }
-    timer.stop();
-    printf("Uploading the FLASH update file took %f seconds. \r\n",timer.read());
-    fclose(fp);
-    return TRUE;
-}
-
-//
-// Trionic5SendC3Message
-//
-// Sends a C3 Message to the T5 ECU. The reply should contain the last used FLASH address
-// which is always 0x0007FFFF
-//
-// inputs:    none
-// return:    bool TRUE if all went OK,
-//
-bool Trionic5SendC3Message() {
-    T5BootC3Command();
-    T5WaitResponsePrint();
-    printf("The Last used FLASH address message is:\r\n");
-    return TRUE;
-}