Just4Trionic - CAN and BDM FLASH programmer for Saab cars

Dependencies:   mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers t5can.cpp Source File

t5can.cpp

00001 /*******************************************************************************
00002 
00003 trionic5.cpp - CAN Bus functions for Just4Trionic by Just4pLeisure
00004 (c) 2010 by Sophie Dexter
00005 
00006 This C++ module provides functions for reading and writing the FLASH chips and
00007 SRAM in Trionic5 ECUs. (Writing the adaption data back to SRAM not done yet).
00008 
00009 Some functions need an additional 'bootloader' program to be sent to the T5 ECU
00010 before they can be used. These functions are: Identifying the T5 ECU type and
00011 FLASH chips, dumping the FLASH chips, erasing the FLASH chips, writing to the
00012 FLASH chips and calculating the FLASH chips' checksum.
00013 
00014 My version of the bootloader, BOOTY.S19, includes some features not in other
00015 bootloaders; identifying the ECU and FLASH chip types, a 'safer' way of dumping
00016 the FLASH chips and the ability to program AMD 29F010 type FLASH chips
00017 
00018 ********************************************************************************
00019 
00020 WARNING: Use at your own risk, sadly this software comes with no guarantees.
00021 This software is provided 'free' and in good faith, but the author does not
00022 accept liability for any damage arising from its use.
00023 
00024 *******************************************************************************/
00025 
00026 #include "t5can.h"
00027 
00028 // constants
00029 #define CMD_BUF_LENGTH      32              ///< command buffer size
00030 
00031 // static variables
00032 static char cmd_buffer[CMD_BUF_LENGTH];     ///< command string buffer
00033 
00034 //static uint32_t cmd_addr;                   ///< address (optional)
00035 //static uint32_t cmd_value;                  ///< value    (optional)
00036 //static uint32_t cmd_result;                 ///< result
00037 
00038 static uint32_t flash_start = 0;
00039 
00040 // private functions
00041 uint8_t execute_t5_cmd();
00042 void t5_can_show_help();
00043 void t5_can_show_full_help();
00044 
00045 void t5_can()
00046 {
00047     // Start the CAN bus system
00048     // Note that at the moment this is only for T5 ECUs at 615 kbits
00049     can_open();
00050     can_set_speed(615000);
00051 
00052     t5_can_show_help();
00053 
00054     // main loop
00055     *cmd_buffer = '\0';
00056     char ret;
00057     char rx_char;
00058     while (true) {
00059         // read chars from USB
00060         // send received messages to the pc over USB connection
00061         // This function displays any CAN messages that are 'missed' by the other functions
00062         // Can messages might be 'missed' because they are received after a 'timeout' period
00063         // or because they weren't expected, e.g. if the T5 ECU resets for some reason
00064         t5_can_show_can_message();
00065         if (pc.readable()) {
00066             // turn Error LED off for next command
00067             led4 = 0;
00068             rx_char = pc.getc();
00069             switch (rx_char) {
00070                     // 'ESC' key to go back to mbed Just4Trionic 'home' menu
00071                 case '\e':
00072                     can_close();
00073                     return;
00074                     // end-of-command reached
00075                 case TERM_OK :
00076                     // execute command and return flag via USB
00077                     timer.reset();
00078                     timer.start();
00079                     ret = execute_t5_cmd();
00080                     pc.putc(ret);
00081                     printf("Completed in %.3f seconds.\r\n", timer.read());
00082                     // reset command buffer
00083                     *cmd_buffer = '\0';
00084                     // light up LED
00085 //                    ret == TERM_OK ? led_on(LED_ACT) : led_on(LED_ERR);
00086                     ret == TERM_OK ? led3 = 1 : led4 = 1;
00087                     break;
00088                     // another command char
00089                 default:
00090                     // store in buffer if space permits
00091                     if (StrLen(cmd_buffer) < CMD_BUF_LENGTH - 1) {
00092                         StrAddc(cmd_buffer, rx_char);
00093                     }
00094                     break;
00095             }
00096         }
00097     }
00098 }
00099 
00100 //-----------------------------------------------------------------------------
00101 /**
00102     Executes a command and returns result flag (does not transmit the flag
00103     itself).
00104 
00105     @return                    command flag (success / failure)
00106 */
00107 uint8_t execute_t5_cmd()
00108 {
00109 
00110 
00111 //    uint8_t cmd_length = strlen(cmd_buffer);
00112     // command groups
00113     switch (*cmd_buffer) {
00114 //            CHECK_ARGLENGTH(0);
00115             // Get the Symbol Table
00116         case 's':
00117             return t5_can_get_symbol_table()
00118                    ? TERM_OK : TERM_ERR;
00119         case 'S':
00120             return T5ReadCmnd(T5SYMBOLS)
00121                    ? TERM_OK : TERM_ERR;
00122 
00123             // Get the Trionic5 software version string
00124         case 'v':
00125             return t5_can_get_version()
00126                    ? TERM_OK : TERM_ERR;
00127         case 'V':
00128             return T5ReadCmnd(T5VERSION)
00129                    ? TERM_OK : TERM_ERR;
00130 
00131             // Read Adaption Data from RAM and write it to a file
00132         case 'r':
00133         case 'R':
00134             return t5_can_get_adaption_data()
00135                    ? TERM_OK : TERM_ERR;
00136 
00137             // CR - send CR type message
00138         case '\0':
00139             return T5ReadCmnd(CR)
00140                    ? TERM_OK : TERM_ERR;
00141 
00142             //  Get a single symbol from the Symbol Table
00143         case 'a':
00144             char symbol[40];
00145             T5GetSymbol(symbol);
00146             printf("%s",symbol);
00147             return TERM_OK;
00148 
00149             // Just send an 'ACK' message
00150         case 'A':
00151             return T5Ack()
00152                    ? TERM_OK : TERM_ERR;
00153 
00154             // Send a Bootloader file to the T5 ECU
00155         case 'b':
00156             return (t5_can_send_boot_loader() && can_set_speed(1000000))
00157                    ? TERM_OK : TERM_ERR;
00158         case 'B':
00159             return (t5_can_send_boot_loader_S19() && can_set_speed(1000000))
00160                    ? TERM_OK : TERM_ERR;
00161 
00162             // Get Checksum from ECU (Bootloader must be uploaded first)
00163         case 'c':
00164         case 'C':
00165             return t5_can_get_checksum()
00166                    ? TERM_OK : TERM_ERR;
00167 
00168             // Exit the BootLoader and restart the T5 ECU
00169         case 'q':
00170         case 'Q':
00171             return (t5_can_bootloader_reset() && can_set_speed(615000))
00172                    ? TERM_OK : TERM_ERR;
00173 
00174             // Erase the FLASH chips
00175         case 'e':
00176         case 'E':
00177             return t5_can_erase_flash()
00178                    ? TERM_OK : TERM_ERR;
00179 
00180             // Read back the FLASH chip types
00181         case 't':
00182         case 'T':
00183             return t5_can_get_start_and_chip_types(&flash_start)
00184                    ? TERM_OK : TERM_ERR;
00185 
00186             // DUMP the T5 ECU BIN file stored in the FLASH chips
00187         case 'd':
00188             // NOTE 'd' command Just4TESTING! only dumps T5.5 ECU
00189             return t5_can_dump_flash(T55FLASHSTART)
00190                    ? TERM_OK : TERM_ERR;
00191         case 'D':
00192 //            if (!t5_can_send_boot_loader_S19())
00193             if (!t5_can_send_boot_loader())
00194                 return TERM_ERR;
00195             can_set_speed(1000000);
00196             if (!t5_can_get_start_and_chip_types(&flash_start)) {
00197                 t5_can_bootloader_reset();
00198                 can_set_speed(615000);
00199                 return TERM_ERR;
00200             }
00201             return (t5_can_dump_flash(flash_start) && t5_can_bootloader_reset() && can_set_speed(615000))
00202                    ? TERM_OK : TERM_ERR;
00203 
00204             // Send a FLASH update file to the T5 ECU
00205         case 'f':
00206             // NOTE 'f' command Just4TESTING! only FLASHes T5.5 ECU (with S19 type file)
00207             //return t5_can_send_flash_s19_update(T55FLASHSTART)
00208             return t5_can_send_flash_bin_update(T55FLASHSTART)
00209                    ? TERM_OK : TERM_ERR;
00210         case 'F':
00211 //            if (!t5_can_send_boot_loader_S19())
00212             if (!t5_can_send_boot_loader())
00213                 return TERM_ERR;
00214             can_set_speed(1000000);
00215             if (!t5_can_get_start_and_chip_types(&flash_start)) {
00216                 t5_can_bootloader_reset();
00217                 can_set_speed(615000);
00218                 return TERM_ERR;
00219             }
00220             if (!t5_can_get_checksum())
00221                 led4 = 1;
00222             if (!t5_can_erase_flash()) {
00223                 t5_can_bootloader_reset();
00224                 can_set_speed(615000);
00225                 return TERM_ERR;
00226             }
00227             return (t5_can_send_flash_bin_update(flash_start) && t5_can_get_checksum() && t5_can_bootloader_reset() && can_set_speed(615000))
00228                    ? TERM_OK : TERM_ERR;
00229 
00230             // Send the C3 message - should get last used address 0x7FFFF
00231         case '3':
00232             return t5_can_get_last_address()
00233                    ? TERM_OK : TERM_ERR;
00234 
00235             // Print help
00236         case 'h':
00237             t5_can_show_help();
00238             return TERM_OK;
00239         case 'H':
00240             t5_can_show_full_help();
00241             return TERM_OK;
00242         default:
00243             t5_can_show_help();
00244             break;
00245     }
00246     // unknown command
00247     return TERM_ERR;
00248 }
00249 
00250 //
00251 // Trionic5ShowHelp
00252 //
00253 // Displays a list of things that can be done with the T5 ECU.
00254 //
00255 // inputs:    none
00256 // return:    none
00257 //
00258 void t5_can_show_help()
00259 {
00260     printf("Trionic 5 Command Menu\r\n");
00261     printf("======================\r\n");
00262     printf("D - DUMP the T5.x ECU FLASH to a file 'ORIGINAL.BIN'\r\n");
00263     printf("F - FLASH the update file 'MODIFIED.BIN' to the T5.x\r\n");
00264     printf("\r\n");
00265     printf("r - read SRAM and write it to ADAPTION.RAM file\r\n");
00266     printf("s - read Symbol Table and write it to SYMBOLS.TXT\r\n");
00267     printf("v - read T5 ECU software version, display it and write it to VERSION.TXT\r\n");
00268     printf("\r\n");
00269     printf("'ESC' - Return to Just4Trionic Main Menu\r\n");
00270     printf("\r\n");
00271     printf("h  - Show this help menu\r\n");
00272     printf("\r\n");
00273     return;
00274 }
00275 //
00276 // t5_can_show_full_help
00277 //
00278 // Displays a complete list of things that can be done with the T5 ECU.
00279 //
00280 // inputs:    none
00281 // return:    none
00282 //
00283 void t5_can_show_full_help()
00284 {
00285     printf("Trionic 5 Command Menu\r\n");
00286     printf("======================\r\n");
00287     printf("D - DUMP the T5.x ECU FLASH to a file 'ORIGINAL.BIN'\r\n");
00288     printf("F - FLASH the update file 'MODIFIED.BIN' to the T5.x\r\n");
00289     printf("\r\n");
00290     printf("b - upload and start MyBooty.S19 bootloader\r\n");
00291     printf("c - get T5 ECU FLASH checksum (need to upload BOOTY.S19 before using this command)\r\n");
00292     printf("d - dump the T5 FLASH BIN file and write it to ORIGINAL.BIN\r\n");
00293     printf("e - erase the FLASH chips in the T5 ECU\r\n");
00294     printf("f - FLASH the update file MODIFIED.BIN to the T5 ECU\r\n");
00295     printf("r - read SRAM and write it to ADAPTION.RAM file\r\n");
00296     printf("s - read Symbol Table, display it and write it to SYMBOLS.TXT\r\n");
00297     printf("v - read T5 ECU software version, display it and write it to VERSION.TXT\r\n");
00298     printf("q - exit the bootloader and reset the T5 ECU\r\n");
00299     printf("t - read the FLASH chip type in the T5 ECU\r\n");
00300     printf("3 - read the last used FLASH address in the T5 ECU\r\n");
00301     printf("S - send 's' message (to get symbol table)\r\n");
00302     printf("V - send 'S' message (to get software version)\r\n");
00303     printf("'Enter' Key - send an CR message\r\n");
00304     printf("a - send an ACK\r\n");
00305     printf("A - read a single symbol from the symbol table\r\n");
00306     printf("\r\n");
00307     printf("'ESC' - Return to Just4Trionic Main Menu\r\n");
00308     printf("\r\n");
00309     printf("H  - Show this help menu\r\n");
00310     printf("\r\n");
00311     return;
00312 }
00313 
00314 //
00315 // t5_can_show_can_message
00316 //
00317 // Displays a CAN message in the RX buffer if there is one.
00318 //
00319 // inputs:    none
00320 // return:    bool TRUE if there was a message, FALSE if no message.
00321 //
00322 bool t5_can_show_can_message()
00323 {
00324     CANMessage can_MsgRx;
00325     if (can.read(can_MsgRx)) {
00326         printf("w%03x%d", can_MsgRx.id, can_MsgRx.len);
00327         for (char i=0; i<can_MsgRx.len; i++) {
00328             printf("%02x", can_MsgRx.data[i]);
00329         }
00330         printf(" %c ", can_MsgRx.data[2]);
00331         printf("\r\n");
00332         return TRUE;
00333     }
00334     return FALSE;
00335 }
00336 
00337 //
00338 // t5_can_get_symbol_table
00339 //
00340 // Gets the T5 ECU symbol table.
00341 // The Symbol Table is saved to a file, symbols.txt, on the mbed 'local' file system 'disk'.
00342 //
00343 // inputs:    none
00344 // return:    bool TRUE if there all went OK, FALSE if there was an error
00345 //
00346 bool t5_can_get_symbol_table()
00347 {
00348     printf("Saving the symbol table file\r\n");
00349     FILE *fp = fopen("/local/symbols.txt", "w");  // Open "symbols.txt" on the local file system for writing
00350     if (!fp) {
00351         perror ("The following error occured");
00352         return FALSE;
00353     }
00354     char symbol[40];
00355     T5ReadCmnd(T5SYMBOLS);
00356     if (T5WaitResponse() != '>') {
00357         fclose(fp);
00358         return FALSE;
00359     }
00360     T5ReadCmnd(CR);
00361     if (T5WaitResponse() != '>') {
00362         fclose(fp);
00363         return FALSE;
00364     }
00365     do {
00366         T5GetSymbol(symbol);
00367 //        printf("%s",symbol);
00368         if (fprintf(fp,"%s",symbol) < 0) {
00369             fclose (fp);
00370             printf ("ERROR Writing to the symbols.txt file!\r\n");
00371             return FALSE;
00372         };
00373     } while (!StrCmp(symbol,"END\r\n"));
00374     fclose(fp);
00375     return TRUE;
00376 }
00377 
00378 //
00379 // t5_can_get_version
00380 //
00381 // Gets the T5 software version string.
00382 // The software version is is sent to the PC and saved to a file, version.txt, on the mbed 'local' file system 'disk'.
00383 //
00384 // inputs:    none
00385 // return:    bool TRUE if there all went OK, FALSE if there was an error
00386 //
00387 bool t5_can_get_version()
00388 {
00389     FILE *fp = fopen("/local/version.txt", "w");  // Open "version.txt" on the local file system for writing
00390     if (!fp) {
00391         perror ("The following error occured");
00392         return FALSE;
00393     }
00394     char symbol[40];
00395     T5ReadCmnd(T5VERSION);
00396     if (T5WaitResponse() != '>') {
00397         fclose(fp);
00398         return FALSE;
00399     }
00400     T5ReadCmnd(CR);
00401     if (T5WaitResponse() != '>') {
00402         fclose(fp);
00403         return FALSE;
00404     }
00405     T5GetSymbol(symbol);
00406     printf("%s",symbol);
00407     if (fprintf(fp,"%s",symbol) < 0) {
00408         fclose (fp);
00409         printf ("ERROR Writing to the version.txt file!\r\n");
00410         return FALSE;
00411     };
00412     fclose(fp);
00413     printf("The version.txt file has been saved.\r\n");
00414     return TRUE;
00415 }
00416 
00417 //
00418 // Trionic5GetAdaptionData
00419 //
00420 // Gets the adaption data from the T5's SRAM.
00421 // The adaption data is stored in a hex file, adaption.RAM, on the mbed 'local' file system 'disk'.
00422 //
00423 // Reading the Adaption data from SRAM takes about 6.5 seconds.
00424 //
00425 // inputs:    none
00426 // return:    bool TRUE if all went OK, FALSE if there was an error.
00427 //
00428 bool t5_can_get_adaption_data()
00429 {
00430     printf("Saving the SRAM adaption data.\r\n");
00431     FILE *fp = fopen("/local/adaption.RAM", "w");    // Open "adaption.RAM" on the local file system for writing
00432     if (!fp) {
00433         printf("ERROR: Unable to open a file for the adaption data!\r\n");
00434         return FALSE;
00435     }
00436     unsigned int address = 5;                       // Mysterious reason for starting at 5 !!!
00437     char RAMdata[6];
00438     while (address < T5RAMSIZE) {
00439         if (!t5_can_read_data(RAMdata, address)) {
00440             fclose (fp);
00441             printf ("Error reading from the CAN bus.\r\n");
00442             return FALSE;
00443         }
00444         address += 6;
00445         if (fwrite(RAMdata, 1, 6, fp) != 6) {
00446             fclose (fp);
00447             printf ("Error writing to the SRAM adaption file.\r\n");
00448             return FALSE;
00449         }
00450     }
00451     // There are a few more bytes to get because because the SRAM file is not an exact multiple of 6 bytes!
00452     // the % (modulo) mathematics function tells us how many bytes there are still to get
00453     if (!t5_can_read_data(RAMdata, (T5RAMSIZE - 1))) {
00454         fclose (fp);
00455         printf ("Error reading from the CAN bus.\r\n");
00456         return FALSE;
00457     }
00458     if (fwrite((RAMdata + 6 - (T5RAMSIZE % 6)), 1, (T5RAMSIZE % 6), fp) != (T5RAMSIZE % 6)) {
00459         fclose (fp);
00460         printf ("Error writing to the SRAM adaption file.\r\n");
00461         return FALSE;
00462     }
00463     fclose(fp);
00464     return TRUE;
00465 }
00466 
00467 //
00468 // t5_can_send_boot_loader
00469 //
00470 // Sends a 'bootloader' file, booty.s19 to the T5 ECU.
00471 // The 'bootloader' is stored on the mbed 'local' file system 'disk' and must be in S19 format.
00472 //
00473 // The 'bootloader' is then able to dump or reFLASH the T5 ECU FLASH chips - this is the whole point of the exercise :-)
00474 //
00475 // Sending the 'bootloader' to the T5 ECU takes just over 1 second.
00476 //
00477 // inputs:    none
00478 // return:    bool TRUE if all went OK,
00479 //                 FALSE if the 'bootloader' wasn't sent for some reason.
00480 //
00481 bool t5_can_send_boot_loader()
00482 {
00483     uint32_t BootloaderSize = sizeof(T5BootLoader);
00484     uint32_t address = MYBOOTY_START; // Start and execute from address of T5Bootloader
00485     uint32_t count = 0; // progress count of bootloader bytes transferred
00486     char msg[8]; // Construct the bootloader frame for uploading
00487 
00488     printf("Starting the bootloader.\r\n");
00489     while (count < BootloaderSize) {
00490 // send a bootloader address message
00491         if (!t5_can_send_boot_address((address+count), MYBOOTY_CHUNK)) return FALSE;
00492 // send bootloader frames
00493 // 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
00494 // are repeated from the previous frame. This is OK because the T5 ECU knows how many bytes to expect (because the count of bytes
00495 // is sent with the upload address) and ignores any extra bytes in the last frame.
00496         for (uint8_t i=0; i<MYBOOTY_CHUNK; i++) {
00497             msg[1+(i%7)] = T5BootLoader[count+i];
00498             if (i%7 == 0) msg[0]=i;     // set the index number
00499             if ((i%7 == 6) || (i == MYBOOTY_CHUNK-1 )) {
00500                 if (!t5_can_send_boot_frame(msg)) return FALSE;
00501             }
00502         }
00503         count += MYBOOTY_CHUNK;
00504     }
00505 // These two lines really shouldn't be necessary but for some reason the first start
00506 // command is ignored and a short delay is required before repeating. Using only a
00507 // delay, even a very long one, doesn't work.
00508 //
00509 // NOTE: This measure isn't required when uploading an external S19 bootloader file!
00510 //
00511     T5StartBootLoader(address);
00512     wait_ms(1);
00513 //
00514     return T5StartBootLoader(address);
00515 }
00516 
00517 
00518 //
00519 // t5_can_send_boot_loader_S19
00520 //
00521 // Sends a 'bootloader' file, booty.s19 to the T5 ECU.
00522 // The 'bootloader' is stored on the mbed 'local' file system 'disk' and must be in S19 format.
00523 //
00524 // The 'bootloader' is then able to dump or reFLASH the T5 ECU FLASH chips - this is the whole point of the exercise :-)
00525 //
00526 // Sending the 'bootloader' to the T5 ECU takes just over 1 second.
00527 //
00528 // inputs:    none
00529 // return:    bool TRUE if all went OK,
00530 //                 FALSE if the 'bootloader' wasn't sent for some reason.
00531 //
00532 bool t5_can_send_boot_loader_S19()
00533 {
00534     printf("Starting the bootloader.\r\n");
00535     FILE *fp = fopen("/local/MyBooty.S19", "r");    // Open "booty.s19" on the local file system for reading
00536     if (!fp) {
00537         printf("Error: I could not find the bootloader file MyBooty.S19\r\n");
00538         return FALSE;
00539     }
00540     int c = 0; // for some reason fgetc returns an int instead of a char
00541     uint8_t count = 0; // count of bytes in the S-record can be 0xFF = 255 in decimal
00542     uint8_t asize = 0; // 2,3 or 4 bytes in address
00543     uint32_t address = 0; // address to put S-record
00544     uint8_t checksum = 0; // checksum check at the end of each S-record line
00545     char msg[8]; // Construct the bootloader frame for uploading
00546     bool sent = FALSE;
00547 
00548     while (!sent) {
00549 // get characters until we get an 'S' (throws away \n,\r characters and other junk)
00550         do c = fgetc (fp);
00551         while (c != 'S' && c != EOF);
00552 //                if (c == EOF) return '\a';
00553         c = fgetc(fp);        // get the S-record type 1/2/3 5 7/8/9 (Return if EOF reached)
00554 //                if ((c = fgetc(fp)) == EOF) return '\a';        // get the S-record type 1/2/3 5 7/8/9 (Return if EOF reached)
00555         switch (c) {
00556             case '0':
00557                 break;                  // Skip over S0 header record
00558 
00559             case '1':
00560             case '2':
00561             case '3':
00562                 asize = 1 + c - '0';    // 2, 3 or 4 bytes for address
00563                 address = 0;
00564 // get the number of bytes (in ascii format) in this S-record line
00565 // there must be at least the address and the checksum so return with an error if less than this!
00566                 if ((c = SRecGetByte(fp)) < (asize + 1)) break;
00567 //                        if ((c = SRecGetByte(fp)) < 3) return '\a';
00568                 count = c;
00569                 checksum = c;
00570 // get the address
00571                 for (uint8_t i=0; i<asize; i++) {
00572                     c = SRecGetByte(fp);
00573                     checksum += c;
00574                     address <<= 8;
00575                     address |= c;
00576                     count--;
00577                 }
00578 // send a bootloader address message
00579 ////                printf("address %x count %x\r\n",address ,count-1 );
00580                 if (!t5_can_send_boot_address(address, (count-1))) return FALSE;
00581 // get and send the bootloader frames for this S-record
00582 // 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
00583 // are repeated from the previous frame. This is OK because the T5 ECU knows how many bytes to expect (because the count of bytes
00584 // in the S-Record is sent with the upload address) and ignores any extra bytes in the last frame.
00585                 for (uint8_t i=0; i<count-1; i++) {
00586                     c = SRecGetByte(fp);
00587                     checksum += c;
00588                     msg[1+(i%7)] = c;
00589                     if (i%7 == 0) msg[0]=i;     // set the index number
00590                     if ((i%7 == 6) || (i == count - 2)) {
00591 ////                        printf("Sending %2x %2x %2x %2x %2x %2x %2x %2x \r\n", msg[0], msg[1], msg[2], msg[3], msg[4], msg[5], msg[6], msg[7] );
00592                         if (!t5_can_send_boot_frame(msg)) {
00593                             fclose(fp);
00594                             return FALSE;
00595                         }
00596                     }
00597                 }
00598 // get the checksum
00599                 if ((checksum += SRecGetByte(fp)) != 0xFF) {
00600                     printf("Error in S-record, checksum = %2x\r\n", checksum);
00601                     fclose(fp);
00602                     return FALSE;
00603                 }
00604                 break;
00605 
00606             case '5':
00607                 break;                  // Skip over S5 record types
00608 
00609             case '7':
00610             case '8':
00611             case '9':
00612                 asize = 11 - (c - '0');  // 2, 3 or 4 bytes for address
00613                 address = 0;
00614 // get the number of bytes (in ascii format) in this S-record line there must be just the address and the checksum
00615 // so return with an error if other than this!
00616                 if ((c = SRecGetByte(fp)) != (asize + 1)) break;
00617 //                if ((c = SRecGetByte(fp)) < 3) return '\a';
00618                 checksum = c;
00619 // get the address
00620                 for (uint8_t i=0; i<asize; i++) {
00621                     c = SRecGetByte(fp);
00622                     checksum += c;
00623                     address <<= 8;
00624                     address |= c;
00625                 }
00626 // get the checksum
00627                 if ((checksum += SRecGetByte(fp)) != 0xFF) {
00628                     fclose(fp);
00629                     printf("Error in S-record, checksum = %2x\r\n", checksum);
00630                     return FALSE;
00631                 }
00632                 T5StartBootLoader(address);
00633 //                T5WaitResponsePrint();
00634                 sent = TRUE;
00635                 break;
00636 
00637 // Some kind of invalid S-record type so break
00638             default:
00639                 fclose(fp);
00640                 printf("oops - didn't recognise that S-Record \r\n");
00641                 return FALSE;
00642         }
00643     }
00644     fclose(fp);
00645     return TRUE;
00646 }
00647 
00648 //
00649 // t5_can_get_checksum
00650 //
00651 // Calculates the checksum of the FLASH in the T5 ECU.
00652 // The 'bootloader', booty.s19, must be loaded before this function can be used.
00653 // The 'bootloader' actually calculates the checksum and compares it with the
00654 // value stored in the 'header' region at the end of the FLASH.
00655 //
00656 // The bootloader sends a single CAN message with the result e.g.
00657 // w00C8C800CAFEBABE0808
00658 //  00C - T5 response messages have an CAN id of 00C
00659 //     8 - All T5 messages have a message length of 8 bytes
00660 //      C8 - This is the checksum message type
00661 //        00 - 00 means OK, the checksum calculation matches the stored value which in this case is:
00662 //          CAFEBABE - lol :-)
00663 //
00664 // w00C8C801FFFFFFFF0808
00665 //        01 - 01 means calculated value doesn't matched the stored value
00666 //          FFFFFFFF - in this case the stored value is FFFFFFFF - the chips might be erased
00667 //
00668 // Calculating the checksum takes a little under 2 seconds.
00669 //
00670 // inputs:    none
00671 // return:    bool TRUE if all went OK,
00672 //
00673 bool t5_can_get_checksum()
00674 {
00675     uint32_t checksum = 0;
00676     if (!t5_boot_checksum_command(&checksum)) {
00677         printf("Error The ECU's checksum is wrong!\r\n");
00678         return FALSE;
00679     }
00680     printf("The FLASH checksum value is %#010x.\r\n", checksum);
00681     return TRUE;
00682 }
00683 
00684 //
00685 // t5_can_bootloader_reset
00686 //
00687 // Exit the Bootloader and restart the T5 ECU
00688 //
00689 // inputs:    none
00690 // outputs:    bool TRUE if all went OK.
00691 //
00692 bool t5_can_bootloader_reset()
00693 {
00694     printf("Exiting the bootloader and restarting the T5 ECU.\r\n");
00695     if (!t5_boot_reset_command()) {
00696         printf("Error trying to reset the T5 ECU!\r\n");
00697         return FALSE;
00698     }
00699     return TRUE;
00700 }
00701 
00702 //
00703 // t5_can_get_start_and_chip_types
00704 //
00705 // Gets the FLASH chip type fitted.
00706 //
00707 // NOTE the bootloader must be loaded in order to use this function.
00708 //
00709 // CAN messages from the T5 ECU with FLASH data look like this:
00710 //
00711 // C9,00,aa,aa,aa,aa,mm,dd,
00712 //
00713 // C9 lets us know its a FLASH id message
00714 //
00715 // 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
00716 // 0x00020000 - T5.2
00717 // 0x00040000 - T5.5
00718 //
00719 // mm = Manufacturer id. These can be:
00720 // 0x89 - Intel
00721 // 0x31 - CSI/CAT
00722 // 0x01 - AMD
00723 // 0x1F - Atmel
00724 //
00725 // dd = Device id. These can be:
00726 // 0xB8 - Intel _or_ CSI 28F512 (Fiited by Saab in T5.2)
00727 // 0xB4 - Intel _or_ CSI 28F010 (Fitted by Saab in T5.5)
00728 // 0x25 - AMD 28F512 (Fiited by Saab in T5.2)
00729 // 0xA7 - AMD 28F010 (Fitted by Saab in T5.5)
00730 // 0x20 - AMD 29F010 (Some people have put these in their T5.5)
00731 // 0x5D - Atmel 29C512 (Some people mave have put these in their T5.2)
00732 // 0xD5 - Atmel 29C010 (Some people have put these in their T5.5)
00733 //
00734 // mm = 0xFF, dd == 0xF7 probably means that the programming voltage isn't right.
00735 //
00736 // Finding out which ECU type and FLASH chips are fitted takes under a second.
00737 //
00738 // inputs:    start     T5 ecu start address
00739 // return:    bool      TRUE if all went OK.
00740 //
00741 bool t5_can_get_start_and_chip_types(uint32_t* start)
00742 {
00743     *start = 0;
00744     uint8_t make = 0;
00745     uint8_t type = 0;
00746     if (!t5_boot_get_flash_type(start, &make, &type)) {
00747         printf("Error trying to find out the ECU's first address and FLASH chip type!\r\n");
00748         return FALSE;
00749     }
00750     printf("This is a T5.%s ECU with ", ((*start == T52FLASHSTART) ? "2" : "5"));
00751     switch (make) {
00752         case AMD:
00753             printf("AMD ");
00754             break;
00755         case CSI:
00756             printf("CSI ");
00757             break;
00758         case INTEL:
00759             printf("INTEL ");
00760             break;
00761         case ATMEL:
00762             printf("ATMEL ");
00763             break;
00764         case SST:
00765             printf("SST ");
00766             break;
00767         case ST:
00768             printf("ST ");
00769             break;
00770         case AMIC:
00771             printf("AMIC ");
00772             break;
00773         default:
00774             printf("\r\nUNKNOWN Manufacturer Id: %02x - Also check pin65 has enough volts!\r\n", make);
00775     }
00776     switch (type) {
00777         case AMD28F512:
00778         case INTEL28F512:
00779             printf("28F512 FLASH chips.\r\n");
00780             break;
00781         case ATMEL29C512:
00782             printf("29C512 FLASH chips.\r\n");
00783             break;
00784         case ATMEL29C010:
00785             printf("29C010 FLASH chips.\r\n");
00786             break;
00787         case AMD28F010:
00788         case INTEL28F010:
00789             printf("28F010 FLASH chips.\r\n");
00790             break;
00791         case AMD29F010:
00792         case SST39SF010:
00793 //        case ST29F010:      // Same as AMD29F010
00794         case AMICA29010L:
00795             printf("29F010 FLASH chips.\r\n");
00796             break;
00797         default:
00798             printf("UNKNOWN Device Id: %02x - Also check pin65 has enough volts!\r\n", type);
00799             return FALSE;
00800     }
00801     return TRUE;
00802 }
00803 
00804 //
00805 // t5_can_erase_flash
00806 //
00807 // Erases the FLASH Chips.
00808 //
00809 // NOTE the bootloader must be loaded in order to use this function.
00810 //
00811 // CAN messages from the T5 ECU with FLASH erase command look like this:
00812 //
00813 // C0,cc,08,08,08,08,08,08
00814 //
00815 // C0 tells us this is a response to the FLASH erase command.
00816 //
00817 // cc is a code that tells us what happened:
00818 //      00 - FLASH was erased OK
00819 //      01 - Could not erase FLASH chips to 0xFF
00820 //      02 - Could not write 0x00 to 28F FLASH chips
00821 //      03 - Unrecognised FLASH chip type (or maybe programming voltage isn't right)
00822 //      04 - Intel chips found, but unrecognised type
00823 //      05 - AMD chips found, but unrecognised type
00824 //      06 - CSI/Catalyst chips found, but unrecognised type
00825 //      07 - Atmel chips found - Atmel chips don't need to be erased
00826 //
00827 // Erasing 28F type FLASH chips takes around 22 seconds. 29F chips take around 4 seconds.
00828 //
00829 // inputs:    none
00830 // return:    bool TRUE if all went OK.
00831 //
00832 bool t5_can_erase_flash()
00833 {
00834     printf("Erasing the FLASH chips.\r\n");
00835     if (!t5_boot_erase_command()) {
00836         printf("Error The ECU's FLASH has not been erased!\r\n");
00837         return FALSE;
00838     }
00839     return TRUE;
00840 }
00841 
00842 //
00843 // t5_can_dump_flash
00844 //
00845 // Dumps the FLASH chip BIN file, original.bin to the mbed 'disk'
00846 //
00847 // NOTE the bootloader must be loaded in order to use this function.
00848 //
00849 // Dumping FLASH chips in a T5.5 ECU takes around 35 seconds.
00850 // Dumping T5.2 ECUs should take about half of this time
00851 //
00852 // inputs:    start     start address of the T5.x FLASH
00853 
00854 // return:    bool TRUE if all went OK, FALSE if there was an error.
00855 //
00856 bool t5_can_dump_flash(uint32_t start)
00857 {
00858     printf("Saving the original FLASH BIN file.\r\n");
00859     FILE *fp = fopen("/local/original.bin", "w");    // Open "original.bin" on the local file system for writing
00860     if (!fp) {
00861         perror ("The following error occured");
00862         return FALSE;
00863     }
00864     uint32_t address = start + 5;                  // Mysterious reason for starting at 5 !!!
00865     char FLASHdata[6];
00866     printf("  0.00 %% complete.\r");
00867     while (address < TRIONICLASTADDR) {
00868         if (!t5_can_read_data(FLASHdata, address)) {
00869             fclose (fp);
00870             printf ("Error reading from the CAN bus.\r\n");
00871             return FALSE;
00872         }
00873         address += 6;
00874         if (fwrite(FLASHdata, 1, 6, fp) != 6) {
00875             fclose (fp);
00876             printf ("Error writing to the FLASH BIN file.\r\n");
00877             return FALSE;
00878         }
00879         printf("%6.2f\r", 100*(float)(address-start)/(float)(TRIONICLASTADDR - start) );
00880     }
00881     // There are a few more bytes to get because because the bin file is not an exact multiple of 6 bytes!
00882     // the % (modulo) mathematics function tells us how many bytes there are still to get
00883     if (!t5_can_read_data(FLASHdata, (TRIONICLASTADDR))) {
00884         fclose (fp);
00885         printf ("Error reading from the CAN bus.\r\n");
00886         return FALSE;
00887     }
00888     if (fwrite((FLASHdata + 6 - ((TRIONICLASTADDR - start +1) % 6)), 1, ((TRIONICLASTADDR - start +1) % 6), fp) != ((TRIONICLASTADDR - start +1) % 6)) {
00889         fclose (fp);
00890         printf ("Error writing to the FLASH BIN file.\r\n");
00891         return FALSE;
00892     }
00893     printf("100.00 %% complete.\r\n");
00894     fclose(fp);
00895     return TRUE;
00896 }
00897 
00898 
00899 //
00900 // t5_can_send_flash_bin_update
00901 //
00902 // Sends a FLASH update file, modified.bin to the T5 ECU.
00903 // The FLASH update file is stored on the local file system and must be in hex format.
00904 //
00905 // FLASHing a T5.5 ECU takes around 40 seconds. FLASHing T5.2 ECUs should take about half of this time
00906 //
00907 // inputs:    start     start address of the T5.x FLASH
00908 
00909 // return:    bool      TRUE if all went OK,
00910 //                      FALSE if the FLASH update failed for some reason.
00911 //
00912 bool t5_can_send_flash_bin_update(uint32_t start)
00913 {
00914     printf("Programming the FLASH chips.\r\n");
00915     FILE *fp = fopen("/local/modified.bin", "r");    // Open "modified.bin" on the local file system for reading
00916     if (!fp) {
00917         printf("Error: I could not find the BIN file MODIFIED.BIN\r\n");
00918         return FALSE;
00919     }
00920 
00921     // obtain file size - it should match the size of the FLASH chips:
00922     fseek (fp , 0 , SEEK_END);
00923     uint32_t file_size = ftell (fp);
00924     rewind (fp);
00925 
00926     // read the initial stack pointer value in the BIN file - it should match the value expected for the type of ECU
00927     uint8_t stack_byte = 0;
00928     uint32_t stack_long = 0;
00929     if (!fread(&stack_byte,1,1,fp)) return TERM_ERR;
00930     stack_long |= (stack_byte << 24);
00931     if (!fread(&stack_byte,1,1,fp)) return TERM_ERR;
00932     stack_long |= (stack_byte << 16);
00933     if (!fread(&stack_byte,1,1,fp)) return TERM_ERR;
00934     stack_long |= (stack_byte << 8);
00935     if (!fread(&stack_byte,1,1,fp)) return TERM_ERR;
00936     stack_long |= stack_byte;
00937     rewind (fp);
00938 
00939     if (start == T52FLASHSTART && (file_size != T52FLASHSIZE || stack_long != T5POINTER)) {
00940         fclose(fp);
00941         printf("The BIN file does not appear to be for a T5.2 ECU :-(\r\n");
00942         printf("BIN file size: %#10x, FLASH chip size: %#010x, Pointer: %#10x.\r\n", file_size, T52FLASHSIZE, stack_long);
00943         return TERM_ERR;
00944     }
00945     if (start == T55FLASHSTART && (file_size != T55FLASHSIZE || stack_long != T5POINTER)) {
00946         fclose(fp);
00947         printf("The BIN file does not appear to be for a T5.5 ECU :-(\r\n");
00948         printf("BIN file size: %#10x, FLASH chip size: %#010x, Pointer: %#10x.\r\n", file_size, T55FLASHSIZE, stack_long);
00949         return TERM_ERR;
00950     }
00951 
00952     char msg[8]; // Construct the bootloader frame for uploading
00953     uint32_t curr_addr = start; // address to put FLASH data
00954     uint8_t byte_value = 0;
00955 
00956     printf("  0.00 %% complete.\r");
00957     while (curr_addr <= TRIONICLASTADDR) {
00958 
00959 // send a bootloader address message
00960         if (!t5_can_send_boot_address(curr_addr, 0x80)) {
00961             fclose(fp);
00962             printf("\r\nError sending Block Start message. Address: %08x\r\n",curr_addr);
00963             return FALSE;
00964         }
00965 // Construct and send the bootloader frames
00966 // 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
00967 // are repeated from the previous frame. This is OK because the T5 ECU knows how many bytes to expect (because the count of bytes
00968 // in the S-Record is sent with the upload address) and ignores any extra bytes in the last frame.
00969         for (uint8_t i=0; i<0x80; i++) {
00970             if (!fread(&byte_value,1,1,fp)) {
00971                 fclose(fp);
00972                 printf("\r\nError reading the BIN file MODIFIED.BIN\r\n");
00973                 return FALSE;
00974             }
00975             msg[1+(i%7)] = byte_value;
00976             if (i%7 == 0) msg[0]=i;     // set the index number
00977             if ((i%7 == 6) || (i == 0x80 - 1))
00978                 if (!t5_can_send_boot_frame(msg)) {
00979                     fclose(fp);
00980                     printf("\r\nError sending a Block data message. Address: %08x, Index: %02x\r\n",curr_addr, i);
00981                     return FALSE;
00982                 }
00983 
00984         }
00985         curr_addr += 0x80;
00986         printf("%6.2f\r", 100*(float)(curr_addr - start)/(float)(TRIONICLASTADDR - start) );
00987     }
00988     printf("100.00 %% complete.\r\n");
00989     fclose(fp);
00990     return TRUE;
00991 }
00992 
00993 //
00994 // t5_can_send_flash_s19_update
00995 //
00996 // Sends a FLASH update file, modified.s19 to the T5 ECU.
00997 // The FLASH update file is stored on the local file system and must be in S19 format.
00998 //
00999 // FLASHing a T5.5 ECU takes around 60 seconds. FLASHing T5.2 ECUs should take about half of this time
01000 //
01001 // inputs:    start     start address of the T5.x FLASH
01002 
01003 // return:    bool TRUE if all went OK,
01004 //                 FALSE if the FLASH update failed for some reason.
01005 //
01006 bool t5_can_send_flash_s19_update(uint32_t start)
01007 {
01008     printf("Programming the FLASH chips.\r\n");
01009     FILE *fp = fopen("/local/modified.s19", "r");    // Open "modified.s19" on the local file system for reading
01010     if (!fp) {
01011         printf("Error: I could not find the BIN file MODIFIED.S19\r\n");
01012         return FALSE;
01013     }
01014     int c = 0; // for some reason fgetc returns an int instead of a char
01015     uint8_t count = 0; // count of bytes in the S-record can be 0xFF = 255 in decimal
01016     uint8_t asize = 0; // 2,3 or 4 bytes in address
01017     uint32_t address = 0; // address to put S-record
01018     uint8_t checksum = 0; // checksum check at the end
01019     char msg[8]; // Construct the bootloader frame for uploading
01020     bool sent = FALSE;
01021 
01022     while (!sent) {
01023 
01024         do c = fgetc (fp); // get characters until we get an 'S' (throws away \n,\r characters and other junk)
01025         while (c != 'S' && c != EOF);
01026 //                if (c == EOF) return '\a';
01027         c = fgetc(fp);        // get the S-record type 1/2/3 5 7/8/9 (Return if EOF reached)
01028 //                if ((c = fgetc(fp)) == EOF) return '\a';        // get the S-record type 1/2/3 5 7/8/9 (Return if EOF reached)
01029         switch (c) {
01030             case '0':
01031                 break;                  // Skip over S0 header record
01032 
01033             case '1':
01034             case '2':
01035             case '3':
01036                 asize = 1 + c - '0';    // 2, 3 or 4 bytes for address
01037                 address = 0;
01038 // get the number of bytes (in ascii format) in this S-record line
01039 // there must be at least the address and the checksum so return with an error if less than this!
01040                 if ((c = SRecGetByte(fp)) < (asize + 1)) {
01041                     fclose(fp);
01042                     printf("Error in S-record address");
01043                     return FALSE;
01044                 }
01045                 count = c;
01046                 checksum = c;
01047 // get the address
01048                 for (uint8_t i=0; i<asize; i++) {
01049                     c = SRecGetByte(fp);
01050                     checksum += c;
01051                     address <<= 8;
01052                     address |= c;
01053                     count--;
01054                 }
01055 // send a bootloader address message - Adding the start address that was supplied (for T5.2/T5.5)
01056                 if (!t5_can_send_boot_address((address + start), (count - 1))) {
01057                     fclose(fp);
01058                     printf("Error sending CAN message");
01059                     return FALSE;
01060                 }
01061 // get and send the bootloader frames for this S-record
01062 // 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
01063 // are repeated from the previous frame. This is OK because the T5 ECU knows how many bytes to expect (because the count of bytes
01064 // in the S-Record is sent with the upload address) and ignores any extra bytes in the last frame.
01065                 for (uint8_t i=0; i<count-1; i++) {
01066                     c = SRecGetByte(fp);
01067                     checksum += c;
01068                     msg[1+(i%7)] = c;
01069                     if (i%7 == 0) msg[0]=i;     // set the index number
01070                     if ((i%7 == 6) || (i == count - 2))
01071                         if (!t5_can_send_boot_frame(msg)) {
01072                             fclose(fp);
01073                             printf("Error sending CAN message");
01074                             return FALSE;
01075                         }
01076                 }
01077 // get the checksum
01078                 if ((checksum += SRecGetByte(fp)) != 0xFF) {
01079                     fclose(fp);
01080                     printf("Error in S-record, checksum = %2x\r\n", checksum);
01081                     return FALSE;
01082                 }
01083                 break;
01084 
01085             case '5':
01086                 break;                  // Skip over S5 record types
01087 
01088             case '7':
01089             case '8':
01090             case '9':
01091                 sent = TRUE;
01092                 break;
01093 
01094 // Some kind of invalid S-record type so break
01095             default:
01096                 printf("oops - didn't recognise that S-Record\r\n");
01097                 fclose(fp);
01098                 return FALSE;
01099         }
01100     }
01101     fclose(fp);
01102     return TRUE;
01103 }
01104 
01105 //
01106 // t5_can_get_last_address
01107 //
01108 // Sends a C3 Message to the T5 ECU. The reply should contain the last used FLASH address
01109 // which is always 0x0007FFFF
01110 //
01111 // The last 2 bytes of the message might be useful to work out whether or not the bootloader
01112 // has been loaded. The 'mode' value will be 0x0808 when the bootloader is running or 0x89b8
01113 // when it isn't.
01114 //
01115 // inputs:    none
01116 // return:    bool TRUE if all went OK,
01117 //
01118 bool t5_can_get_last_address()
01119 {
01120     uint32_t last_address = 0;
01121     uint16_t mode = 0;
01122     if (!t5_boot_c3_command(&last_address, &mode)) {
01123         printf("Error trying to find out the ECU's last address and mode!\r\n");
01124         return FALSE;
01125     }
01126     printf("The Last used FLASH address is: %#010x, mode %#06x\r\n", last_address, mode);
01127     return TRUE;
01128 }