Just4Trionic - CAN and BDM FLASH programmer for Saab cars

Dependencies:   mbed

Revision:
5:1775b4b13232
Parent:
4:682d96ff6d79
Child:
6:2fbcbebed28c
--- a/t8utils.cpp	Wed Sep 11 11:55:51 2013 +0000
+++ b/t8utils.cpp	Sat Apr 25 17:07:08 2015 +0000
@@ -38,7 +38,7 @@
 
 bool t8_show_VIN()
 {
-    uint16_t i;
+    uint32_t i;
     char T8TxFlo[] = T8FLOCTL;
     char T8TxMsg[] = T8REQVIN;
     char T8RxMsg[8];
@@ -87,7 +87,7 @@
     char T8RxMsg[8];
     char k = 0;
 
-//    GMLANTesterPresentT8();
+//    GMLANTesterPresent(T8REQID, T8RESPID);
 //    wait_ms(2000);
 //
 //    printf("Requesting Security Access\r\n");
@@ -97,10 +97,10 @@
 //    }
 //    printf("Security Access Granted\r\n");
 //
-//    GMLANTesterPresentT8();
+//    GMLANTesterPresent(T8REQID, T8RESPID);
 //    wait_ms(2000);
 //
-//    GMLANTesterPresentT8();
+//    GMLANTesterPresent(T8REQID, T8RESPID);
 //    wait_ms(2000);
 //
     if (!can_send_timeout (T8TSTRID, SetVin10, 8, T8MESSAGETIMEOUT)) {
@@ -132,7 +132,7 @@
     for (k = 0; k < 8; k++ ) printf("0x%02X ", T8RxMsg[k] );
     printf("\r\n");
     return TRUE;
-//    GMLANTesterPresentT8();
+//    GMLANTesterPresent(T8REQID, T8RESPID);
 //    wait_ms(2000);
 //
 }
@@ -147,13 +147,25 @@
 // return:    bool TRUE if there was a message, FALSE if no message.
 //
 
-bool t8_authenticate(char level)
+bool t8_authenticate(uint32_t ReqID, uint32_t RespID, char level)
 {
-    uint16_t seed, key;
-    if (!GMLANSecurityAccessRequest(level, seed)) {
+    uint16_t i, seed, key;
+//    if (!GMLANSecurityAccessRequest(ReqID, RespID, level, seed)) {
+//        printf("Unable to request SEED value for security access\r\n");
+//        return FALSE;
+//    }
+
+    for (i=0; i < 20; i++) {
+        if (GMLANSecurityAccessRequest(ReqID, RespID, level, seed))
+            break;
+        wait(1);
+        GMLANTesterPresent(ReqID, RespID);
+    }
+    if (i == 20) {
         printf("Unable to request SEED value for security access\r\n");
         return FALSE;
     }
+
     if ( seed == 0x0000 ) {
         printf("T8 ECU is already unlocked\r\n");
         return TRUE;
@@ -175,16 +187,17 @@
         key = (key >> 8) | (key << 8);
         key -= 0x3FC7;
     */
-    if (!GMLANSecurityAccessSendKey(level, key)) {
+    wait_ms(1);
+    if (!GMLANSecurityAccessSendKey(ReqID, RespID, level, key)) {
         printf("Unable to send KEY value for security access\r\n");
         return FALSE;
     }
     printf("Key Accepted\r\n");
+    wait_ms(500);       // was 5
     return TRUE;
 }
 
 
-
 //
 // t8_dump
 //
@@ -197,7 +210,7 @@
 
 bool t8_dump()
 {
-    uint16_t i = 0, k = 0;
+    uint32_t i = 0, k = 0;
     char T8TxMsg[8];
     char T8RxMsg[8];
 
@@ -206,28 +219,19 @@
     printf("Creating FLASH dump file...\r\n");
 
 //
-    GMLANTesterPresentT8();
-//
-    if (!GMLANprogrammingSetupProcess())
+    if (!GMLANprogrammingSetupProcess(T8REQID, T8RESPID))
         return FALSE;
 //
-    wait_ms(500);
     printf("Requesting Security Access\r\n");
-    if (!t8_authenticate(0x01)) {
+    if (!t8_authenticate(T8REQID, T8RESPID, 0x01)) {
         printf("Unable to get Security Access\r\n");
         return FALSE;
     }
     printf("Security Access Granted\r\n");
-    wait_ms(500);
-    GMLANTesterPresentT8();
 //
-    char BootLoader[] = T8Bootloader;
-    if(!GMLANprogrammingUtilityFileProcess(BootLoader))
+    if(!GMLANprogrammingUtilityFileProcess(T8REQID, T8RESPID, T8BootloaderRead))
         return FALSE;
 //
-    uint32_t StartAddress = 0x0;
-    uint16_t txpnt = 0;
-    char iFrameNumber = 0x21;
 //
     printf("Downloading FLASH BIN file...\r\n");
     printf("Creating FLASH dump file...\r\n");
@@ -238,7 +242,34 @@
     }
     printf("  0.00 %% complete.\r");
     TesterPresent.start();
-    while (StartAddress < 0x100000) {     // 0x100000
+
+// It is possible to save some time by only reading the program code and CAL data
+// This is just a rough calculation, and slight overestimate of the number of blocks of data needed to send the BIN file
+    T8TxMsg[0] = 0x06;
+    T8TxMsg[1] = 0x21;
+    T8TxMsg[2] = 0x80;  // Blocksize
+    T8TxMsg[3] = 0x00;  // This address (0x020140) points to the Header at the end of the BIN
+    T8TxMsg[4] = 0x02;
+    T8TxMsg[5] = 0x01;
+    T8TxMsg[6] = 0x40;
+    T8TxMsg[7] = 0xaa;
+    if (!can_send_timeout (T8TSTRID, T8TxMsg, 7, T8MESSAGETIMEOUT)) {
+        printf("Unable to download FLASH\r\n");
+        return FALSE;
+    }
+    if (!can_wait_timeout(T8ECU_ID, T8RxMsg, 8, T8MESSAGETIMEOUT))
+        return FALSE;
+    uint32_t EndAddress = (T8RxMsg[5] << 16) | (T8RxMsg[6] << 8) | T8RxMsg[7];
+    EndAddress += 0x200;    // Add some bytes for the Footer itself and to account for division rounded down later
+    char T8TxFlo[] = T8FLOCTL;
+    can_send_timeout (T8TSTRID, T8TxFlo, 8, T8MESSAGETIMEOUT);
+    for (i = 0; i < 0x12; i++) {
+        if (!can_wait_timeout(T8ECU_ID, T8RxMsg, 8, T8MESSAGETIMEOUT))
+            return FALSE;
+    }
+    printf("Reading your BIN file adjusted for footer = 0x%06X Bytes\r\n", EndAddress );
+
+    for ( uint32_t StartAddress = 0x0; StartAddress < EndAddress; StartAddress +=0x80 ) {     // 0x100000
         T8TxMsg[0] = 0x06;
         T8TxMsg[1] = 0x21;
         T8TxMsg[2] = 0x80;  // Blocksize
@@ -258,26 +289,22 @@
             return FALSE;
 #ifdef DEBUG
         printf("first %#.3f\r\n",timer.read());
-        for (k = 0; k < 8; k++ ) printf("0x%02X ", T8RxMsg[k] );
-        printf("\r\n");
 #endif
-        txpnt = 0;
+        uint32_t txpnt = 0;
         for (k = 4; k < 8; k++ ) file_buffer[txpnt++] = T8RxMsg[k];
 
         uint8_t DataFrames = 0x12;
-        iFrameNumber = 0x21;
+        char iFrameNumber = 0x21;
         char T8TxFlo[] = T8FLOCTL;
+        can_send_timeout (T8TSTRID, T8TxFlo, 8, T8MESSAGETIMEOUT);
 #ifdef DEBUG
         printf("flowCtrl %#.3f\r\n",timer.read());
 #endif
-        can_send_timeout (T8TSTRID, T8TxFlo, 8, T8MESSAGETIMEOUT);
         for (i = 0; i < DataFrames; i++) {
             if (!can_wait_timeout(T8ECU_ID, T8RxMsg, 8, T8MESSAGETIMEOUT))
                 return FALSE;
 #ifdef DEBUG
             printf("Consec %#.3f\r\n",timer.read());
-            for (k = 0; k < 8; k++ ) printf("0x%02X ", T8RxMsg[k] );
-            printf("\r\n");
 #endif
             iFrameNumber++;
             for (k = 1; k < 8; k++ ) file_buffer[txpnt++] = T8RxMsg[k];
@@ -288,12 +315,22 @@
             printf ("Error writing to the FLASH BIN file.\r\n");
             return TERM_ERR;
         }
-        StartAddress +=0x80;
-        printf("%6.2f\r", (100.0*(float)StartAddress)/(float)(0x100000) );
+        printf("%6.2f\r", (100.0*(float)StartAddress)/(float)(EndAddress) );
         if (TesterPresent.read_ms() > 2000) {
-            GMLANTesterPresentT8();
+            GMLANTesterPresent(T8REQID, T8RESPID);
             TesterPresent.reset();
-            ACTIVITYLEDON;
+        }
+    }
+
+    for (uint32_t i = 0; i < 0x80; i++)
+        file_buffer[i] = 0xFF;
+    while ( ftell(fp) < 0x100000 ) {
+//  for ( uint32_t StartAddress = EndAddress; StartAddress < 0x100000; StartAddress +=0x80 ) {
+        fwrite((file_buffer), 1, 0x80, fp);
+        if (ferror (fp)) {
+            fclose (fp);
+            printf ("Error writing to the FLASH BIN file.\r\n");
+            return TERM_ERR;
         }
     }
 
@@ -304,69 +341,44 @@
     return TRUE;
 }
 
-bool t8_erase()
-{
-    printf("Erasing T8 ECU FLASH...\r\n");
-    printf("SUCCESS: The FLASH has been erased.\r\n");
-    return TRUE;
-}
-
-bool t8_flash_raw()
-{
-    timer.reset();
-    timer.start();
-    printf("Checking the FLASH BIN file...\r\n");
-    timer.stop();
-    printf("SUCCESS! Programming the FLASH took %#.1f seconds.\r\n",timer.read());
-    return TRUE;
-}
-
 
 bool t8_flash()
 {
-    uint16_t i = 0, j = 0, k = 0;
+    uint32_t i = 0, j = 0, k = 0;
 
     timer.reset();
     timer.start();
     printf("FLASHing T8 BIN file...\r\n");
 
 //
-    GMLANTesterPresentT8();
-//
-    if (!GMLANprogrammingSetupProcess())
+    if (!GMLANprogrammingSetupProcess(T8REQID, T8RESPID))
         return FALSE;
 //
-    wait_ms(500);
     printf("Requesting Security Access\r\n");
-    if (!t8_authenticate(0x01)) {
+    if (!t8_authenticate(T8REQID, T8RESPID, 0x01)) {
         printf("Unable to get Security Access\r\n");
         return FALSE;
     }
     printf("Security Access Granted\r\n");
-    wait_ms(500);
-    GMLANTesterPresentT8();
 //
-    char BootLoader[] = T8BootloaderProg;
-    if(!GMLANprogrammingUtilityFileProcess(BootLoader))
+//    const uint8_t BootLoader[] = T8BootloaderProg;
+//    if(!GMLANprogrammingUtilityFileProcess(T8REQID, T8RESPID, BootLoader))
+    if(!GMLANprogrammingUtilityFileProcess(T8REQID, T8RESPID, T8BootLoaderWrite))
         return FALSE;
 //
-
-
 // All steps needed to transfer and start a bootloader ('Utility File' in GMLAN parlance)
-//    bool GMLANprogrammingUtilityFileProcess(char UtilityFile[]) {
-//        uint16_t i = 0, j = 0, k = 0;
     uint32_t StartAddress = 0x020000;
-    uint16_t txpnt = 0;
+    uint32_t txpnt = 0;
     char iFrameNumber = 0x21;
     char GMLANMsg[8];
     char data2Send[0xE0];
 //
-    // fopen modified.hex here, check it is OK and work out how much data I need to send
+    // fopen modified.bin here, check it is OK and work out how much data I need to send
     // need lots of fcloses though
     printf("Checking the FLASH BIN file...\r\n");
-    FILE *fp = fopen("/local/modified.hex", "r");    // Open "modified.hex" on the local file system for reading
+    FILE *fp = fopen("/local/modified.bin", "r");    // Open "modified.bin" on the local file system for reading
     if (!fp) {
-        printf("Error: I could not find the BIN file MODIFIED.HEX\r\n");;
+        printf("Error: I could not find the BIN file MODIFIED.BIN\r\n");;
         return TERM_ERR;
     }
     // obtain file size - it should match the size of the FLASH chips:
@@ -376,7 +388,10 @@
 
     // read the initial stack pointer value in the BIN file - it should match the value expected for the type of ECU
     uint32_t stack_long = 0;
-    if (!fread(&stack_long,4,1,fp)) return TERM_ERR;
+    if (!fread(&stack_long,4,1,fp)) {
+        fclose(fp);
+        return TERM_ERR;
+    }
     stack_long = (stack_long >> 24) | ((stack_long << 8) & 0x00FF0000) | ((stack_long >> 8) & 0x0000FF00) |  (stack_long << 24);
 //
     if (file_size != T8FLASHSIZE || stack_long != T8POINTER) {
@@ -389,7 +404,10 @@
 // This is just a rough calculation, and slight overestimate of the number of blocks of data needed to send the BIN file
     uint32_t blocks2Send;
     fseek(fp,0x020140,SEEK_SET);
-    if (!fread(&blocks2Send,4,1,fp)) return TERM_ERR;
+    if (!fread(&blocks2Send,4,1,fp)) {
+        fclose(fp);
+        return TERM_ERR;
+    }
     blocks2Send = (blocks2Send >> 24) | ((blocks2Send << 8) & 0x00FF0000) | ((blocks2Send >> 8) & 0x0000FF00) |  (blocks2Send << 24);
     printf("Start address of BIN file's Footer area = 0x%06X\r\n", blocks2Send );
     blocks2Send += 0x200;       // Add some bytes for the Footer itself and to account for division rounded down later
@@ -401,45 +419,31 @@
     fseek (fp , 0x020000 , SEEK_SET);
 // Erase the FLASH
     printf("Waiting for FLASH to be Erased\r\n");
-    if (!GMLANRequestDownload(GMLANRequestDownloadModeEncrypted)) {
+    if (!GMLANRequestDownload(T8REQID, T8RESPID, GMLANRequestDownloadModeEncrypted)) {
+        fclose(fp);
         printf("Unable to erase the FLASH chip!\r\n");
         return FALSE;
     }
 // Now send the BIN file
+    GMLANTesterPresent(T8REQID, T8RESPID);
     TesterPresent.start();
     printf("Sending FLASH BIN file\r\n");
     printf("  0.00 %% complete.\r");
     for (i=0; i<blocks2Send; i++) {
         // get a block of 0xE0 bytes in an array called data2Send
-        for ( j = 0; j < 0xE0; j++ ) {
-            //data[k] = *(bin + bin_count++);
-            if (!fread(&data2Send[j],1,1,fp)) {
-                fclose(fp);
-                printf("Error reading the BIN file MODIFIED.HEX\r\n");
-                return FALSE;
-            }
-            // encrypt data2Send array by XORing with 6 different in a ring (modulo function)
-            switch ( ((0xE0*i)+j) %6) {
-                case 1:
-                    data2Send[j] ^= 0x68;
-                    break;
-                case 2:
-                    data2Send[j] ^= 0x77;
-                    break;
-                case 3:
-                    data2Send[j] ^= 0x6D;
-                    break;
-                case 4:
-                    data2Send[j] ^= 0x47;
-                    break;
-                default:        // remainder 0 and 5 both XOR with 0x39
-                    data2Send[j] ^= 0x39;
-            }
+        if (!fread(data2Send,0xE0,1,fp)) {
+            fclose(fp);
+            printf("\r\nError reading the BIN file MODIFIED.BIN\r\n");
+            return FALSE;
         }
+        // encrypt data2Send array by XORing with 6 different values in a ring (modulo function)
+        char key[6] = { 0x39, 0x68, 0x77, 0x6D, 0x47, 0x39 };
+        for ( j = 0; j < 0xE0; j++ )
+            data2Send[j] ^= key[(((0xE0*i)+j) % 6)];
         // Send the block of data
-        if (!GMLANDataTransferFirstFrame(0xE6, GMLANDOWNLOAD, StartAddress)) {
+        if (!GMLANDataTransferFirstFrame(T8REQID, T8RESPID, 0xE6, GMLANDOWNLOAD, StartAddress)) {
             fclose(fp);
-            printf("Unable to start BIN File Upload\r\n");
+            printf("\r\nUnable to start BIN File Upload\r\n");
             return FALSE;
         }
         // Send 0x20 messages of 0x07 bytes each (0x20 * 0x07 = 0xE0)
@@ -449,50 +453,21 @@
             GMLANMsg[0] = iFrameNumber;
             for (k=1; k<8; k++)
                 GMLANMsg[k] = data2Send[txpnt++];
-#ifdef DEBUG
-            for (k = 0; k < 8; k++ ) printf("0x%02X ", GMLANMsg[k] );
-            printf("\r\n");
-#endif
-            if (!can_send_timeout(T8RequestId, GMLANMsg, 8, GMLANPTCT)) {
+            if (!can_send_timeout(T8REQID, GMLANMsg, 8, GMLANPTCT)) {
                 fclose(fp);
-                printf("Unable to send BIN File\r\n");
+                printf("\r\nUnable to send BIN File\r\n");
                 return FALSE;
             }
             ++iFrameNumber &= 0x2F;
-//            wait_ms(1);
-            wait_us(250);
-        }
-        if (!can_wait_timeout(T8ResponseId, GMLANMsg, 8, GMLANPTCT)) {
-            fclose(fp);
-            printf("I did not receive a block acknowledge message\r\n");
-            return FALSE;
+            wait_us(1000);   // can be as low as 250 for an ECU on its own, but need 1000 (1ms) to work in a car (use a longer delay to be ultrasafe??? )
         }
-        while (GMLANMsg[0] == 0x03 && GMLANMsg[1] == 0x7F && GMLANMsg[2] == 0x36 && GMLANMsg[3] == 0x78) {
-            printf("I'm waiting for a Block to be programmed into FLASH\r\n");
-            if (!can_wait_timeout(T8ResponseId, GMLANMsg, 8, GMLANPTCTENHANCED)) {
-                printf("I did not receive a block acknowledge message after enhanced timeout\r\n");
-                fclose(fp);
-                return FALSE;
-            }
-        }
-#ifdef DEBUG
-        for (k = 0; k < 8; k++ ) printf("0x%02X ", GMLANMsg[k] );
-        printf("\r\n");
-#endif
-        if ( GMLANMsg[0] == 0x03 && GMLANMsg[1] == 0x7F && GMLANMsg[2] == 0x36 ) {
-            GMLANShowReturnCode(GMLANMsg[3]);
-            fclose(fp);
-            return FALSE;
-        }
-        if (GMLANMsg[0] != 0x01 && GMLANMsg[1] != 0x76) {
-            printf("EXITING due to an unexpected CAN message");
+        if (!GMLANDataTransferBlockAcknowledge(T8RESPID)) {
             fclose(fp);
             return FALSE;
         }
         if (TesterPresent.read_ms() > 2000) {
-            GMLANTesterPresentT8();
+            GMLANTesterPresent(T8REQID, T8RESPID);
             TesterPresent.reset();
-            ACTIVITYLEDON;
         }
         StartAddress += 0xE0;
         printf("%6.2f\r", (100.0*(float)i)/(float)(blocks2Send) );
@@ -500,14 +475,158 @@
 // FLASHing complete
     printf("%6.2f\r\n", (float)100 );
 // End programming session and return to normal mode
-    if (!GMLANReturnToNormalMode()) {
+    if (!GMLANReturnToNormalMode(T8REQID, T8RESPID)) {
         fclose(fp);
         printf("UH-OH! T8 ECU did not Return To Normal Mode!!\r\n");
         return FALSE;
     }
-    wait_ms(1000);
     timer.stop();
     printf("SUCCESS! FLASHing the BIN file took %#.1f seconds.\r\n",timer.read());
     fclose(fp);
     return TRUE;
 }
+
+bool t8_recover()
+{
+    uint32_t i = 0, j = 0, k = 0;
+
+    timer.reset();
+    timer.start();
+    printf("Recovering your T8 ECU ...\r\n");
+//
+    if (!GMLANprogrammingSetupProcess(T8USDTREQID, T8UUDTRESPID))
+        return FALSE;
+//
+    printf("Requesting Security Access\r\n");
+    if (!t8_authenticate(T8USDTREQID, T8UUDTRESPID, 0x01)) {
+        printf("Unable to get Security Access\r\n");
+        return FALSE;
+    }
+    printf("Security Access Granted\r\n");
+//
+//    const uint8_t BootLoader[] = T8BootloaderProg;
+//    if(!GMLANprogrammingUtilityFileProcess(T8USDTREQID, T8UUDTRESPID, BootLoader))
+    if(!GMLANprogrammingUtilityFileProcess(T8USDTREQID, T8UUDTRESPID, T8BootLoaderWrite))
+        return FALSE;
+//
+
+
+// All steps needed to transfer and start a bootloader ('Utility File' in GMLAN parlance)
+    uint32_t StartAddress = 0x020000;
+    uint32_t txpnt = 0;
+    char iFrameNumber = 0x21;
+    char GMLANMsg[8];
+    char data2Send[0xE0];
+//
+    // fopen modified.bin here, check it is OK and work out how much data I need to send
+    // need lots of fcloses though
+    printf("Checking the FLASH BIN file...\r\n");
+    FILE *fp = fopen("/local/modified.bin", "r");    // Open "modified.bin" on the local file system for reading
+    if (!fp) {
+        printf("Error: I could not find the BIN file MODIFIED.BIN\r\n");;
+        return TERM_ERR;
+    }
+    // obtain file size - it should match the size of the FLASH chips:
+    fseek (fp , 0 , SEEK_END);
+    uint32_t file_size = ftell (fp);
+    rewind (fp);
+
+    // read the initial stack pointer value in the BIN file - it should match the value expected for the type of ECU
+    uint32_t stack_long = 0;
+    if (!fread(&stack_long,4,1,fp)) {
+        fclose(fp);
+        return TERM_ERR;
+    }
+    stack_long = (stack_long >> 24) | ((stack_long << 8) & 0x00FF0000) | ((stack_long >> 8) & 0x0000FF00) |  (stack_long << 24);
+//
+    if (file_size != T8FLASHSIZE || stack_long != T8POINTER) {
+        fclose(fp);
+        printf("The BIN file does not appear to be for a T8 ECU :-(\r\n");
+        printf("BIN file size: %#010x, FLASH chip size: %#010x, Pointer: %#010x.\r\n", file_size, T7FLASHSIZE, stack_long);
+        return TERM_ERR;
+    }
+// It is possible to save some time by only sending the program code and CAL data
+// This is just a rough calculation, and slight overestimate of the number of blocks of data needed to send the BIN file
+    uint32_t blocks2Send;
+    fseek(fp,0x020140,SEEK_SET);
+    if (!fread(&blocks2Send,4,1,fp)) {
+        fclose(fp);
+        return TERM_ERR;
+    }
+    blocks2Send = (blocks2Send >> 24) | ((blocks2Send << 8) & 0x00FF0000) | ((blocks2Send >> 8) & 0x0000FF00) |  (blocks2Send << 24);
+    printf("Start address of BIN file's Footer area = 0x%06X\r\n", blocks2Send );
+    blocks2Send += 0x200;       // Add some bytes for the Footer itself and to account for division rounded down later
+    blocks2Send -= 0x020000;    // Remove 0x020000 because we don't send the bootblock and adaptation blocks
+    printf("Amount of data to send BIN file adjusted for footer = 0x%06X Bytes\r\n", blocks2Send );
+    blocks2Send /= 0xE0;
+    printf("Number of Blocks of 0xE0 Bytes needed to send BIN file = 0x%04X\r\n", blocks2Send );
+// Move BIN file pointer to start of data
+    fseek (fp , 0x020000 , SEEK_SET);
+// Erase the FLASH
+    printf("Waiting for FLASH to be Erased\r\n");
+    if (!GMLANRequestDownload(T8REQID, T8RESPID, GMLANRequestDownloadModeEncrypted)) {
+        fclose(fp);
+        printf("Unable to erase the FLASH chip!\r\n");
+        return FALSE;
+    }
+// Now send the BIN file
+    GMLANTesterPresent(T8REQID, T8RESPID);
+    TesterPresent.start();
+    printf("Sending FLASH BIN file\r\n");
+    printf("  0.00 %% complete.\r");
+    for (i=0; i<blocks2Send; i++) {
+        // get a block of 0xE0 bytes in an array called data2Send
+        if (!fread(data2Send,0xE0,1,fp)) {
+            fclose(fp);
+            printf("\r\nError reading the BIN file MODIFIED.BIN\r\n");
+            return FALSE;
+        }
+        // encrypt data2Send array by XORing with 6 different values in a ring (modulo function)
+        char key[6] = { 0x39, 0x68, 0x77, 0x6D, 0x47, 0x39 };
+        for ( j = 0; j < 0xE0; j++ )
+            data2Send[j] ^= key[(((0xE0*i)+j) % 6)];
+        // Send the block of data
+        if (!GMLANDataTransferFirstFrame(T8REQID, T8RESPID, 0xE6, GMLANDOWNLOAD, StartAddress)) {
+            fclose(fp);
+            printf("\r\nUnable to start BIN File Upload\r\n");
+            return FALSE;
+        }
+        // Send 0x20 messages of 0x07 bytes each (0x20 * 0x07 = 0xE0)
+        txpnt = 0;
+        iFrameNumber = 0x21;
+        for (j=0; j < 0x20; j++) {
+            GMLANMsg[0] = iFrameNumber;
+            for (k=1; k<8; k++)
+                GMLANMsg[k] = data2Send[txpnt++];
+            if (!can_send_timeout(T8REQID, GMLANMsg, 8, GMLANPTCT)) {
+                fclose(fp);
+                printf("\r\nUnable to send BIN File\r\n");
+                return FALSE;
+            }
+            ++iFrameNumber &= 0x2F;
+            wait_us(1000);   // was 250 use 1000 or wait_ms(1) to be ultrasafe
+        }
+        if (!GMLANDataTransferBlockAcknowledge(T8RESPID)) {
+            fclose(fp);
+            return FALSE;
+        }
+        if (TesterPresent.read_ms() > 2000) {
+            GMLANTesterPresent(T8REQID, T8RESPID);
+            TesterPresent.reset();
+        }
+        StartAddress += 0xE0;
+        printf("%6.2f\r", (100.0*(float)i)/(float)(blocks2Send) );
+    }
+// FLASHing complete
+    printf("%6.2f\r\n", (float)100 );
+// End programming session and return to normal mode
+    if (!GMLANReturnToNormalMode(T8REQID, T8RESPID)) {
+        fclose(fp);
+        printf("UH-OH! T8 ECU did not Return To Normal Mode!!\r\n");
+        return FALSE;
+    }
+    timer.stop();
+    fclose(fp);
+    printf("SUCCESS: Your T8 ECU has been recovered.\r\n");
+    return TRUE;
+}