Sophie Dexter
/
Just4Trionic
Just4Trionic - CAN and BDM FLASH programmer for Saab cars
Diff: t5can.cpp
- Revision:
- 1:d5452e398b76
- Child:
- 3:92dae9083c83
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/t5can.cpp Tue Sep 14 21:02:04 2010 +0000 @@ -0,0 +1,1020 @@ +/******************************************************************************* + +trionic5.cpp - CAN Bus functions for Just4Trionic by Just4pLeisure +(c) 2010 by Sophie Dexter + +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 + +******************************************************************************** + +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 "t5can.h" + +// constants +#define CMD_BUF_LENGTH 32 ///< command buffer size + +// static variables +static char cmd_buffer[CMD_BUF_LENGTH]; ///< command string buffer + +//static uint32_t cmd_addr; ///< address (optional) +//static uint32_t cmd_value; ///< value (optional) +//static uint32_t cmd_result; ///< result + +static uint32_t flash_start = 0; + +// private functions +uint8_t execute_t5_cmd(); +void t5_can_show_help(); +void t5_can_show_full_help(); + +void t5_can() { + // Start the CAN bus system + // Note that at the moment this is only for T5 ECUs at 615 kbits + can_open(); + can_set_speed(615000); + + t5_can_show_help(); + + // main loop + *cmd_buffer = '\0'; + char ret; + char rx_char; + while (true) { + // read chars from USB + // 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 + t5_can_show_can_message(); + if (pc.readable()) { + // turn Error LED off for next command + led4 = 0; + rx_char = pc.getc(); + switch (rx_char) { + // 'ESC' key to go back to mbed Just4Trionic 'home' menu + case '\e': + can_close(); + return; + // end-of-command reached + case TERM_OK : + // execute command and return flag via USB + timer.reset(); + timer.start(); + ret = execute_t5_cmd(); + pc.putc(ret); + printf("Completed in %.1f seconds.\r\n", timer.read()); + // reset command buffer + *cmd_buffer = '\0'; + // light up LED +// ret == TERM_OK ? led_on(LED_ACT) : led_on(LED_ERR); + ret == TERM_OK ? led3 = 1 : led4 = 1; + break; + // another command char + default: + // store in buffer if space permits + if (StrLen(cmd_buffer) < CMD_BUF_LENGTH - 1) { + StrAddc(cmd_buffer, rx_char); + } + break; + } + } + } +} + +//----------------------------------------------------------------------------- +/** + Executes a command and returns result flag (does not transmit the flag + itself). + + @return command flag (success / failure) +*/ +uint8_t execute_t5_cmd() { + + +// uint8_t cmd_length = strlen(cmd_buffer); + // command groups + switch (*cmd_buffer) { +// CHECK_ARGLENGTH(0); + // Get the Symbol Table + case 's': + return t5_can_get_symbol_table() + ? TERM_OK : TERM_ERR; + case 'S': + return T5ReadCmnd(T5SYMBOLS) + ? TERM_OK : TERM_ERR; + + // Get the Trionic5 software version string + case 'v': + return t5_can_get_version() + ? TERM_OK : TERM_ERR; + case 'V': + return T5ReadCmnd(T5VERSION) + ? TERM_OK : TERM_ERR; + + // Read Adaption Data from RAM and write it to a file + case 'r': + case 'R': + return t5_can_get_adaption_data() + ? TERM_OK : TERM_ERR; + + // CR - send CR type message + case '\0': + return T5ReadCmnd(CR) + ? TERM_OK : TERM_ERR; + + // Get a single symbol from the Symbol Table + case 'a': + char symbol[40]; + T5GetSymbol(symbol); + printf("%s",symbol); + return TERM_OK; + + // Just send an 'ACK' message + case 'A': + return T5Ack() + ? TERM_OK : TERM_ERR; + + // Send a Bootloader file to the T5 ECU + case 'b': + case 'B': + return t5_can_send_boot_loader() + ? TERM_OK : TERM_ERR; + + // Get Checksum from ECU (Bootloader must be uploaded first) + case 'c': + case 'C': + return t5_can_get_checksum() + ? TERM_OK : TERM_ERR; + + // Exit the BootLoader and restart the T5 ECU + case 'q': + case 'Q': + return t5_can_bootloader_reset() + ? TERM_OK : TERM_ERR; + + // Erase the FLASH chips + case 'e': + case 'E': + return t5_can_erase_flash() + ? TERM_OK : TERM_ERR; + + // Read back the FLASH chip types + case 't': + case 'T': + return t5_can_get_start_and_chip_types(&flash_start) + ? TERM_OK : TERM_ERR; + + // DUMP the T5 ECU BIN file stored in the FLASH chips + case 'd': + // NOTE 'd' command Just4TESTING! only dumps T5.5 ECU + return t5_can_dump_flash(T55FLASHSTART) + ? TERM_OK : TERM_ERR; + case 'D': + if (!t5_can_get_adaption_data()) + return TERM_ERR; + if (!t5_can_send_boot_loader()) + return TERM_ERR; + if (!t5_can_get_start_and_chip_types(&flash_start)) { + t5_can_bootloader_reset(); + return TERM_ERR; + } + return (t5_can_dump_flash(flash_start) && t5_can_bootloader_reset()) + ? TERM_OK : TERM_ERR; + + // Send a FLASH update file to the T5 ECU + case 'f': + // NOTE 'f' command Just4TESTING! only FLASHes T5.5 ECU (with S19 type file) + return t5_can_send_flash_s19_update(T55FLASHSTART) + ? TERM_OK : TERM_ERR; + case 'F': + if (!t5_can_send_boot_loader()) + return TERM_ERR; + if (!t5_can_get_start_and_chip_types(&flash_start)) { + t5_can_bootloader_reset(); + return TERM_ERR; + } + if (!t5_can_get_checksum()) + led4 = 1; + if (!t5_can_erase_flash()) { + t5_can_bootloader_reset(); + return TERM_ERR; + } + return (t5_can_send_flash_bin_update(flash_start) && t5_can_get_checksum() && t5_can_bootloader_reset()) + ? TERM_OK : TERM_ERR; + + // Send the C3 message - should get last used address 0x7FFFF + case '3': + return t5_can_get_last_address() + ? TERM_OK : TERM_ERR; + + // Print help + case 'h': + t5_can_show_help(); + return TERM_OK; + case 'H': + t5_can_show_full_help(); + return TERM_OK; + default: + t5_can_show_help(); + break; + } + // unknown command + return TERM_ERR; +} + +// +// Trionic5ShowHelp +// +// Displays a list of things that can be done with the T5 ECU. +// +// inputs: none +// return: none +// +void t5_can_show_help() { + printf("Trionic 5 Command Menu\r\n"); + printf("======================\r\n"); + printf("D - Read SRAM adaption and DUMP T5 FLASH BIN file\r\n"); + printf("F - FLASH the update file to the T5 (and write SRAM - not done!)\r\n"); + printf("\r\n"); + printf("r - read SRAM and write it to ADAPTION.RAM file\r\n"); + printf("s - read Symbol Table 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("\r\n"); + printf("'ESC' - Return to Just4Trionic Main Menu\r\n"); + printf("\r\n"); + printf("h - Show this help menu\r\n"); + printf("\r\n"); + return; +} +// +// t5_can_show_full_help +// +// Displays a complete list of things that can be done with the T5 ECU. +// +// inputs: none +// return: none +// +void t5_can_show_full_help() { + printf("Trionic 5 Command Menu\r\n"); + printf("======================\r\n"); + printf("D - Read SRAM adaption and DUMP T5 FLASH BIN file\r\n"); + printf("F - FLASH the update file to the T5 (and write SRAM - not done!)\r\n"); + printf("\r\n"); + printf("b - upload and start MyBooty.S19 bootloader\r\n"); + printf("c - get T5 ECU FLASH checksum (need to upload BOOTY.S19 before using this command)\r\n"); + printf("d - dump the T5 FLASH BIN file and write it to ORIGINAL.BIN\r\n"); + printf("e - erase the FLASH chips in the T5 ECU\r\n"); + printf("f - FLASH the update file MODIFIED.S19 to the T5 ECU\r\n"); + printf("r - read SRAM and write it to ADAPTION.RAM 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 Just4Trionic Main Menu\r\n"); + printf("\r\n"); + printf("H - Show this help menu\r\n"); + printf("\r\n"); + return; +} + +// +// t5_can_show_can_message +// +// 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 t5_can_show_can_message() { + CANMessage can_MsgRx; + if (can.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; +} + +// +// t5_can_get_symbol_table +// +// Gets the T5 ECU symbol table. +// The Symbol Table is 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 t5_can_get_symbol_table() { + printf("Saving the symbol table file\r\n"); + FILE *fp = fopen("/local/symbols.txt", "w"); // Open "symbols.txt" on the local file system for writing + if (!fp) { + perror ("The following error occured"); + return FALSE; + } + char symbol[40]; + T5ReadCmnd(T5SYMBOLS); + if (T5WaitResponse() != '>') { + fclose(fp); + return FALSE; + } + T5ReadCmnd(CR); + if (T5WaitResponse() != '>') { + fclose(fp); + return FALSE; + } + do { + T5GetSymbol(symbol); +// printf("%s",symbol); + if (fprintf(fp,"%s",symbol) < 0) { + fclose (fp); + printf ("ERROR Writing to the symbols.txt file!\r\n"); + return FALSE; + }; + } while (!StrCmp(symbol,"END\r\n")); + fclose(fp); + return TRUE; +} + +// +// t5_can_get_version +// +// 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 t5_can_get_version() { + FILE *fp = fopen("/local/version.txt", "w"); // Open "version.txt" on the local file system for writing + if (!fp) { + perror ("The following error occured"); + return FALSE; + } + char symbol[40]; + T5ReadCmnd(T5VERSION); + if (T5WaitResponse() != '>') { + fclose(fp); + return FALSE; + } + T5ReadCmnd(CR); + if (T5WaitResponse() != '>') { + fclose(fp); + return FALSE; + } + T5GetSymbol(symbol); + printf("%s",symbol); + if (fprintf(fp,"%s",symbol) < 0) { + fclose (fp); + printf ("ERROR Writing to the version.txt file!\r\n"); + return FALSE; + }; + fclose(fp); + printf("The version.txt file has been saved.\r\n"); + return TRUE; +} + +// +// Trionic5GetAdaptionData +// +// Gets the adaption data from the T5's SRAM. +// The adaption data is stored in a hex file, adaption.RAM, on the mbed 'local' file system 'disk'. +// +// Reading the Adaption data from SRAM takes about 6.5 seconds. +// +// inputs: none +// return: bool TRUE if all went OK, FALSE if there was an error. +// +bool t5_can_get_adaption_data() { + printf("Saving the SRAM adaption data.\r\n"); + FILE *fp = fopen("/local/adaption.RAM", "w"); // Open "adaption.RAM" on the local file system for writing + if (!fp) { + printf("ERROR: Unable to open a file for the adaption data!\r\n"); + return FALSE; + } + unsigned int address = 5; // Mysterious reason for starting at 5 !!! + char RAMdata[6]; + while (address < T5RAMSIZE) { + if (!t5_can_read_data(RAMdata, address)) { + fclose (fp); + printf ("Error reading from the CAN bus.\r\n"); + return FALSE; + } + address += 6; + if (fwrite(RAMdata, 1, 6, fp) != 6) { + fclose (fp); + printf ("Error writing to the SRAM adaption file.\r\n"); + return FALSE; + } + } + // There are a few more bytes to get because because the SRAM file is not an exact multiple of 6 bytes! + // the % (modulo) mathematics function tells us how many bytes there are still to get + if (!t5_can_read_data(RAMdata, (T5RAMSIZE - 1))) { + fclose (fp); + printf ("Error reading from the CAN bus.\r\n"); + return FALSE; + } + if (fwrite((RAMdata + 6 - (T5RAMSIZE % 6)), 1, (T5RAMSIZE % 6), fp) != (T5RAMSIZE % 6)) { + fclose (fp); + printf ("Error writing to the SRAM adaption file.\r\n"); + return FALSE; + } + fclose(fp); + return TRUE; +} + +// +// t5_can_send_boot_loader +// +// 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 t5_can_send_boot_loader() { + printf("Starting the bootloader.\r\n"); + FILE *fp = fopen("/local/MyBooty.S19", "r"); // Open "booty.s19" on the local file system for reading + if (!fp) { + printf("Error: I could not find the bootloader file MyBooty.S19\r\n"); + return FALSE; + } + int c = 0; // for some reason fgetc returns an int instead of a char + uint8_t count = 0; // count of bytes in the S-record can be 0xFF = 255 in decimal + uint8_t asize = 0; // 2,3 or 4 bytes in address + uint32_t address = 0; // address to put S-record + uint8_t checksum = 0; // checksum check at the end of each S-record line + char msg[8]; // Construct the bootloader frame for uploading + bool sent = FALSE; + + while (!sent) { +// get characters until we get an 'S' (throws away \n,\r characters and other junk) + do c = fgetc (fp); + 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 (uint8_t i=0; i<asize; i++) { + c = SRecGetByte(fp); + checksum += c; + address <<= 8; + address |= c; + count--; + } +// send a bootloader address message + if (!t5_can_send_boot_address(address, (count-1))) return FALSE; +// 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 (uint8_t 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)) + if (!t5_can_send_boot_frame(msg)) { + fclose(fp); + return FALSE; + } + } +// get the checksum + if ((checksum += SRecGetByte(fp)) != 0xFF) { + printf("Error in S-record, checksum = %2x\r\n", checksum); + fclose(fp); + return FALSE; + } + 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! + if ((c = SRecGetByte(fp)) != (asize + 1)) break; +// if ((c = SRecGetByte(fp)) < 3) return '\a'; + checksum = c; +// get the address + for (uint8_t i=0; i<asize; i++) { + c = SRecGetByte(fp); + checksum += c; + address <<= 8; + address |= c; + } +// get the checksum + if ((checksum += SRecGetByte(fp)) != 0xFF) { + fclose(fp); + printf("Error in S-record, checksum = %2x\r\n", checksum); + return FALSE; + } + T5StartBootLoader(address); +// T5WaitResponsePrint(); + sent = TRUE; + break; + +// Some kind of invalid S-record type so break + default: + fclose(fp); + printf("oops - didn't recognise that S-Record \r\n"); + return FALSE; + } + } + fclose(fp); + return TRUE; +} + +// +// t5_can_get_checksum +// +// 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 t5_can_get_checksum() { + uint32_t checksum = 0; + if (!t5_boot_checksum_command(&checksum)) { + printf("Error The ECU's checksum is wrong!\r\n"); + return FALSE; + } + printf("The FLASH checksum value is %#010x.\r\n", checksum); + return TRUE; +} + +// +// t5_can_bootloader_reset +// +// Exit the Bootloader and restart the T5 ECU +// +// inputs: none +// outputs: bool TRUE if all went OK. +// +bool t5_can_bootloader_reset() { + printf("Exiting the bootloader and restarting the T5 ECU.\r\n"); + if (!t5_boot_reset_command()) { + printf("Error trying to reset the T5 ECU!\r\n"); + return FALSE; + } + return TRUE; +} + +// +// t5_can_get_start_and_chip_types +// +// 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: start T5 ecu start address +// return: bool TRUE if all went OK. +// +bool t5_can_get_start_and_chip_types(uint32_t* start) { + *start = 0; + uint8_t make = 0; + uint8_t type = 0; + if (!t5_boot_get_flash_type(start, &make, &type)) { + printf("Error trying to find out the ECU's first address and FLASH chip type!\r\n"); + return FALSE; + } + printf("This is a T5.%s ECU with ", ((*start == T52FLASHSTART) ? "2" : "5")); + switch (make) { + case AMD: + printf("AMD "); + break; + case CSI: + printf("CSI "); + break; + case INTEL: + printf("INTEL "); + break; + default: + printf("UNKNOWN FLASH chips - check pin65 has enough volts!\r\n"); + } + switch (type) { + case AMD28F512: + case INTEL28F512: + printf("28F512 FLASH chips.\r\n"); + break; + case AMD28F010: + case INTEL28F010: + printf("28F010 FLASH chips.\r\n"); + break; + case AMD29F010: + printf("29F010 FLASH chips.\r\n"); + break; + default: + printf("UNKNOWN - check pin65 has enough volts!\r\n"); + return FALSE; + } + return TRUE; +} + +// +// t5_can_erase_flash +// +// 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 take around 4 seconds. +// +// inputs: none +// return: bool TRUE if all went OK. +// +bool t5_can_erase_flash() { + printf("Erasing the FLASH chips.\r\n"); + if (!t5_boot_erase_command()) { + printf("Error The ECU's FLASH has not been erased!\r\n"); + return FALSE; + } + return TRUE; +} + +// +// t5_can_dump_flash +// +// Dumps the FLASH chip BIN file, original.bin to the mbed 'disk' +// +// NOTE the bootloader must be loaded in order to use this function. +// +// Dumping FLASH chips in a T5.5 ECU takes around 35 seconds. +// Dumping T5.2 ECUs should take about half of this time +// +// inputs: start start address of the T5.x FLASH + +// return: bool TRUE if all went OK, FALSE if there was an error. +// +bool t5_can_dump_flash(uint32_t start) { + printf("Saving the original FLASH BIN file.\r\n"); + FILE *fp = fopen("/local/original.bin", "w"); // Open "original.bin" on the local file system for writing + if (!fp) { + perror ("The following error occured"); + return FALSE; + } + uint32_t address = start + 5; // Mysterious reason for starting at 5 !!! + char FLASHdata[6]; + while (address < TRIONICLASTADDR) { + if (!t5_can_read_data(FLASHdata, address)) { + fclose (fp); + printf ("Error reading from the CAN bus.\r\n"); + return FALSE; + } + address += 6; + if (fwrite(FLASHdata, 1, 6, fp) != 6) { + fclose (fp); + printf ("Error writing to the FLASH BIN file.\r\n"); + return FALSE; + } + } + // There are a few more bytes to get because because the bin file is not an exact multiple of 6 bytes! + // the % (modulo) mathematics function tells us how many bytes there are still to get + if (!t5_can_read_data(FLASHdata, (TRIONICLASTADDR))) { + fclose (fp); + printf ("Error reading from the CAN bus.\r\n"); + return FALSE; + } + if (fwrite((FLASHdata + 6 - ((TRIONICLASTADDR - start +1) % 6)), 1, ((TRIONICLASTADDR - start +1) % 6), fp) != ((TRIONICLASTADDR - start +1) % 6)) { + fclose (fp); + printf ("Error writing to the FLASH BIN file.\r\n"); + return FALSE; + } + fclose(fp); + return TRUE; +} + + +// +// t5_can_send_flash_bin_update +// +// Sends a FLASH update file, modified.hex to the T5 ECU. +// The FLASH update file is stored on the local file system and must be in hex format. +// +// FLASHing a T5.5 ECU takes around 40 seconds. FLASHing T5.2 ECUs should take about half of this time +// +// inputs: start start address of the T5.x FLASH + +// return: bool TRUE if all went OK, +// FALSE if the FLASH update failed for some reason. +// +bool t5_can_send_flash_bin_update(uint32_t start) { + printf("Programming the FLASH chips.\r\n"); + FILE *fp = fopen("/local/modified.hex", "r"); // Open "modified.s19" on the local file system for reading + if (!fp) { + printf("Error: I could not find the BIN file MODIFIED.HEX\r\n"); + return FALSE; + } + + // 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 + uint8_t stack_byte = 0; + uint32_t stack_long = 0; + if (!fread(&stack_byte,1,1,fp)) return TERM_ERR; + stack_long |= (stack_byte << 24); + if (!fread(&stack_byte,1,1,fp)) return TERM_ERR; + stack_long |= (stack_byte << 16); + if (!fread(&stack_byte,1,1,fp)) return TERM_ERR; + stack_long |= (stack_byte << 8); + if (!fread(&stack_byte,1,1,fp)) return TERM_ERR; + stack_long |= stack_byte; + rewind (fp); + + if (start == T52FLASHSTART && (file_size != T52FLASHSIZE || stack_long != T5POINTER)) { + fclose(fp); + printf("The BIN file does not appear to be for a T5.2 ECU :-(\r\n"); + printf("BIN file size: %#10x, FLASH chip size: %#010x, Pointer: %#10x.\r\n", file_size, T52FLASHSIZE, stack_long); + return TERM_ERR; + } + if (start == T55FLASHSTART && (file_size != T55FLASHSIZE || stack_long != T5POINTER)) { + fclose(fp); + printf("The BIN file does not appear to be for a T5.5 ECU :-(\r\n"); + printf("BIN file size: %#10x, FLASH chip size: %#010x, Pointer: %#10x.\r\n", file_size, T55FLASHSIZE, stack_long); + return TERM_ERR; + } + + char msg[8]; // Construct the bootloader frame for uploading + uint32_t curr_addr = start; // address to put FLASH data + uint8_t byte_value = 0; + + while (curr_addr <= TRIONICLASTADDR) { + +// send a bootloader address message + if (!t5_can_send_boot_address(curr_addr, 0x80)) { + fclose(fp); + printf("Error sending CAN message"); + return FALSE; + } + curr_addr += 0x80; +// Construct and send the bootloader frames +// 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 (uint8_t i=0; i<0x80; i++) { + if (!fread(&byte_value,1,1,fp)) { + fclose(fp); + printf("Error reading the BIN file MODIFIED.HEX"); + return FALSE; + } + msg[1+(i%7)] = byte_value; + if (i%7 == 0) msg[0]=i; // set the index number + if ((i%7 == 6) || (i == 0x80 - 1)) + if (!t5_can_send_boot_frame(msg)) { + fclose(fp); + printf("Error sending CAN message"); + return FALSE; + } + } + } + fclose(fp); + return TRUE; +} + +// +// t5_can_send_flash_s19_update +// +// 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 60 seconds. FLASHing T5.2 ECUs should take about half of this time +// +// inputs: start start address of the T5.x FLASH + +// return: bool TRUE if all went OK, +// FALSE if the FLASH update failed for some reason. +// +bool t5_can_send_flash_s19_update(uint32_t start) { + printf("Programming the FLASH chips.\r\n"); + FILE *fp = fopen("/local/modified.s19", "r"); // Open "modified.s19" on the local file system for reading + if (!fp) { + printf("Error: I could not find the BIN file MODIFIED.S19\r\n"); + return FALSE; + } + int c = 0; // for some reason fgetc returns an int instead of a char + uint8_t count = 0; // count of bytes in the S-record can be 0xFF = 255 in decimal + uint8_t asize = 0; // 2,3 or 4 bytes in address + uint32_t address = 0; // address to put S-record + uint8_t checksum = 0; // checksum check at the end + char msg[8]; // Construct the bootloader frame for uploading + bool sent = FALSE; + + 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)) { + fclose(fp); + printf("Error in S-record address"); + return FALSE; + } + count = c; + checksum = c; +// get the address + for (uint8_t i=0; i<asize; i++) { + c = SRecGetByte(fp); + checksum += c; + address <<= 8; + address |= c; + count--; + } +// send a bootloader address message - Adding the start address that was supplied (for T5.2/T5.5) + if (!t5_can_send_boot_address((address + start), (count - 1))) { + fclose(fp); + printf("Error sending CAN message"); + return FALSE; + } +// 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 (uint8_t 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)) + if (!t5_can_send_boot_frame(msg)) { + fclose(fp); + printf("Error sending CAN message"); + return FALSE; + } + } +// get the checksum + if ((checksum += SRecGetByte(fp)) != 0xFF) { + fclose(fp); + printf("Error in S-record, checksum = %2x\r\n", checksum); + return FALSE; + } + break; + + case '5': + break; // Skip over S5 record types + + case '7': + case '8': + case '9': + sent = TRUE; + break; + +// Some kind of invalid S-record type so break + default: + printf("oops - didn't recognise that S-Record\r\n"); + fclose(fp); + return FALSE; + } + } + fclose(fp); + return TRUE; +} + +// +// t5_can_get_last_address +// +// Sends a C3 Message to the T5 ECU. The reply should contain the last used FLASH address +// which is always 0x0007FFFF +// +// The last 2 bytes of the message might be useful to work out whether or not the bootloader +// has been loaded. The 'mode' value will be 0x0808 when the bootloader is running or 0x89b8 +// when it isn't. +// +// inputs: none +// return: bool TRUE if all went OK, +// +bool t5_can_get_last_address() { + uint32_t last_address = 0; + uint16_t mode = 0; + if (!t5_boot_c3_command(&last_address, &mode)) { + printf("Error trying to find out the ECU's last address and mode!\r\n"); + return FALSE; + } + printf("The Last used FLASH address is: %#010x, mode %#06x\r\n", last_address, mode); + return TRUE; +}