Sophie Dexter
/
Just4Trionic
Just4Trionic - CAN and BDM FLASH programmer for Saab cars
Diff: bdmdriver.cpp
- Revision:
- 5:1775b4b13232
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bdmdriver.cpp Sat Apr 25 17:07:08 2015 +0000 @@ -0,0 +1,453 @@ +/******************************************************************************* + +bdmdriver.cpp +(c) 2014 by Sophie Dexter + +BD32 like 'syscall' processor for BDM resident driver functions + +******************************************************************************** + +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 "bdmdriver.h" + +FILE *fp = NULL; + +//private functions +bool bdmSyscallPuts (void); +bool bdmSyscallPutchar(void); +bool bdmSyscallGets(void); +bool bdmSyscallGetchar(void); +bool bdmSyscallGetstat(void); +bool bdmSyscallFopen(void); +bool bdmSyscallFclose(void); +bool bdmSyscallFread(void); +bool bdmSyscallFwrite(void); +bool bdmSyscallFtell(void); +bool bdmSyscallFseek(void); +bool bdmSyscallFgets(void); +bool bdmSyscallFputs(void); +bool bdmSyscallEval(void); +bool bdmSyscallFreadsrec(void); + +//----------------------------------------------------------------------------- +/** +Loads the contenst of a uint8_t array into the target's memory starting at the +specified address + +@param dataArray[] uint8_t array conataining data for BDM tartget +@param startAddress Start address to load BDM memory to + +@return succ / fail +*/ +bool bdmLoadMemory(uint8_t dataArray[], uint32_t startAddress, uint32_t dataArraySize) +{ +// for (uint32_t i = 0; i < sizeof(residentDriver); i++) { +// if(memwrite_byte(&driverAddress, residentDriver[i]) != TERM_OK) return false; +// driverAddress++; +// } + uint32_t driverSize = dataArraySize; + uint32_t driverOffset = 0; + uint32_t driverLong = 0; + uint16_t driverWord = 0; + uint8_t driverByte = 0; + // Check that there is something to send + if (driverSize == 0) return false; + // Send the first 1-4 bytes as efficiently as possible over BDM + switch (driverSize % 4) { + case 3: + driverWord = (dataArray[driverOffset++] << 8) | dataArray[driverOffset++]; + if(memwrite_word(&startAddress, driverWord) != TERM_OK) return false; + driverByte = dataArray[driverOffset++]; + if(memfill_byte(driverByte) != TERM_OK) return false; + break; + case 2: + driverWord = (dataArray[driverOffset++] << 8) | dataArray[driverOffset++]; + if(memwrite_word(&startAddress, driverWord) != TERM_OK) return false; + break; + case 1: + driverByte = dataArray[driverOffset++]; + if(memwrite_byte(&startAddress, driverByte) != TERM_OK) return false; + break; + case 0: + for (uint32_t i = 0; i < 4; i++) { + driverLong <<= 8; + driverLong |= dataArray[driverOffset++]; + } + if(memwrite_long(&startAddress, &driverLong) != TERM_OK) return false; + break; +// default: // There shouldn't be a default case + } + // transfer the rest as 'longs' to make best use of BDM transfer speed +// printf("driverOffset 0x%08x, driverSize 0x%08x\r\n", driverOffset, driverSize); + while (driverOffset < driverSize) { + for (uint32_t i = 0; i < 4; i++) { + driverLong <<= 8; + driverLong |= dataArray[driverOffset++]; + } + if(memfill_long(&driverLong) != TERM_OK) return false; + } +// printf("driverOffset 0x%08x, driverSize 0x%08x\r\n", driverOffset, driverSize); + return true; +} + +//----------------------------------------------------------------------------- +/** +Starts a BDM resident driver at the current PC (Program Counter) or a specified +if given. The driver is allowed to run for a maximum period, maxtime, specified +as milliseconds. + +@param addr BDM driver address (0 to continue from current PC) +@param maxtime how long to allow driver to execute (milliseconds) + +@return succ / fail +*/ +bool bdmRunDriver(uint32_t addr, uint32_t maxtime) +{ + // Start BDM driver and allow it up to 200 milliseconds to update 256 Bytes + // Upto 25 pulses per byte, 16us per pulse, 256 Bytes + // 25 * 16 * 256 = 102,400us plus overhead for driver code execution time + // Allowing up to 200 milliseconds seems like a good allowance. + if (run_chip(&addr) != TERM_OK) { + printf("Failed to start BDM driver.\r\n"); + return false; + } + timeout.reset(); + timeout.start(); + // T5 ECUs' BDM interface seem to have problems when the running the CPU and + // sometimes shows the CPU briefly switching between showing BDM mode or that + // the CPU is running. + // I 'debounce' the interface state to workaround this erratic bahaviour + for (uint32_t debounce = 0; debounce < 5; debounce++) { + while (IS_RUNNING) { + debounce = 0; + if (timeout.read_ms() > maxtime) { + printf("Driver did not return to BDM mode.\r\n"); + timeout.stop(); + return false; + } + } + wait_us(1); + } + timeout.stop(); + return true; +} + + +//----------------------------------------------------------------------------- +/** +Starts a BDM resident driver at the current PC (Program Counter) or a specified +if given. The driver is allowed to run for a maximum period, maxtime, specified +as milliseconds. + +@param addr BDM driver address (0 to continue from current PC) +@param maxtime how long to allow driver to execute (milliseconds) + +@return DONE, CONTINUE, ERROR + +*/ + +uint8_t bdmProcessSyscall(void) +{ + + uint32_t syscall = 0xFFFFFFFF; + if (adreg_read(&syscall, 0x0) != TERM_OK) { + printf("Failed to read BDM register.\r\n"); + return ERROR; + } + syscall &= 0xFF; +// printf("SYSCALL 0x%08x\r\n", syscall); + switch (syscall) { + case QUIT: + if (adreg_read(&syscall, 0x1) != TERM_OK) { + printf("Failed to read BDM register.\r\n"); + return ERROR; + } + return DONE; + case PUTS: + if (!bdmSyscallPuts()) return ERROR; + break; + case PUTCHAR: + if (!bdmSyscallPutchar()) return ERROR; + break; + case GETS: + if (!bdmSyscallGets()) return ERROR; + break; + case GETCHAR: + if (!bdmSyscallGetchar()) return ERROR; + break; + case GETSTAT: + if (!bdmSyscallGetstat()) return ERROR; + break; + case FOPEN: + if (!bdmSyscallFopen()) return ERROR; + break; + case FCLOSE: + if (!bdmSyscallFclose()) return ERROR; + break; + case FREAD: + if (!bdmSyscallFread()) return ERROR; + break; + case FWRITE: + if (!bdmSyscallFwrite()) return ERROR; + break; + case FTELL: + if (!bdmSyscallFtell()) return ERROR; + break; + case FSEEK: + if (!bdmSyscallFseek()) return ERROR; + break; + case FGETS: + if (!bdmSyscallFgets()) return ERROR; + break; + case FPUTS: + if (!bdmSyscallFputs()) return ERROR; + break; + case EVAL: + if (!bdmSyscallEval()) return ERROR; + break; + case FREADSREC: + if (!bdmSyscallFreadsrec()) return ERROR; + break; + default: + printf("!!! Unknown BDM Syscall !!!\r\n"); + return ERROR; + } + return CONTINUE; +} + + +bool bdmSyscallPuts() +{ + uint32_t bdm_string_address = 0, bdm_return = 0; + if (adreg_read(&bdm_string_address, 0x8) != TERM_OK) { + printf("Failed to read BDM register.\r\n"); + return false; + } +// a loop to read chars from BDM into a string + char bdm_string[256]; + for (uint32_t i = 0; i < sizeof(bdm_string); i++) { + bdm_string[i] = 0x0; + } + uint32_t i = 0; + do { + if (memread_byte((uint8_t*)(bdm_string+i), &bdm_string_address) != TERM_OK) { + printf("Failed to read BDM memory at address 0x%08x.\r\n", bdm_string_address); + return false; + } + bdm_string_address++; + } while ( (bdm_string[i++] != 0x0) && (i < sizeof(bdm_string)) ); +// print the string to stdout (USB virtual serial port) + printf(bdm_string); +// Send BDM return code in D0 (always 0x0 for PUTS) + if (adreg_write(0x0, &bdm_return) != TERM_OK) { + printf("Failed to write BDM register.\r\n"); + return false; + } + return true; +} + +bool bdmSyscallPutchar() +{ + uint32_t bdm_character = 0, bdm_return = 0; + // read char from BDM + if (adreg_read(&bdm_character, 0x1) != TERM_OK) { + printf("Failed to read BDM register.\r\n"); + return false; + } + // print the char to USB virtual serial port + pc.putc((char)bdm_character); + // Send BDM return code in D0 (always 0x0 for PUTCHAR) + if (adreg_write(0x0, &bdm_return) != TERM_OK) { + printf("Failed to write BDM register.\r\n"); + return false; + } + return true; +} + +bool bdmSyscallGets() +{ + printf("BDM GETS Syscall not supported.\r\n"); + return ERROR; +} + +bool bdmSyscallGetchar() +{ + // get a char from the USB virtual serial port + uint32_t bdm_return = (uint32_t)pc.getc(); + // Send the char to BDM in D0 + if (adreg_write(0x0, &bdm_return) != TERM_OK) { + printf("Failed to write BDM register.\r\n"); + return false; + } + return true; +} + +bool bdmSyscallGetstat(void) +{ + printf("BDM GETSTAT Syscall not supported.\r\n"); + return false; +} + +bool bdmSyscallFopen(void) +{ + uint32_t bdm_filename_address = 0, bdm_filemode_address = 0; + if (adreg_read(&bdm_filename_address, 0x8) != TERM_OK) { + printf("Failed to read BDM register.\r\n"); + return false; + } + if (adreg_read(&bdm_filemode_address, 0x9) != TERM_OK) { + printf("Failed to read BDM register.\r\n"); + return false; + } + // a loop to initialise the strings to 0x0 + char filename_string[80], filemode_string[5]; + uint32_t i = 0; + for (i = 0; i < sizeof(filename_string); i++) { + filename_string[i] = 0x0; + } + for (i = 0; i < sizeof(filemode_string); i++) { + filemode_string[i] = 0x0; + } + i = 0; + // a loop to read chars from BDM into a string + do { + if (memread_byte((uint8_t*)filename_string[i], &bdm_filename_address) != TERM_OK) { + printf("Failed to read BDM memory at address 0x%08x.\r\n", bdm_filename_address); + return false; + } + bdm_filename_address++; + } while ( (filename_string[i++] != 0x0) && (i < sizeof(filename_string)) ); + do { + if (memread_byte((uint8_t*)filemode_string[i], &bdm_filemode_address) != TERM_OK) { + printf("Failed to read BDM memory at address 0x%08x.\r\n", bdm_filemode_address); + return false; + } + bdm_filemode_address++; + } while ( (filemode_string[i++] != 0x0) && (i < sizeof(filemode_string)) ); + // Open the file + fp = fopen(filename_string, filemode_string); // Open "modified.hex" on the local file system for reading + // Send BDM return code in D0 + if (adreg_write(0x0, (uint32_t*)fp) != TERM_OK) { + printf("Failed to write BDM register.\r\n"); + return false; + } + return true; +} + +bool bdmSyscallFclose(void) +{ + uint32_t close_result = fclose(fp); + // Send BDM return code in D0 + if (adreg_write(0x0, &close_result) != TERM_OK) { + printf("Failed to write BDM register.\r\n"); + return false; + } + return true; +} + +bool bdmSyscallFread(void) +{ + uint32_t bdm_byte_count, bdm_buffer_address, bdm_file_handle = NULL; + if (adreg_read(&bdm_file_handle, 0x1) != TERM_OK) { + printf("Failed to read BDM register.\r\n"); + return false; + } + if (adreg_read(&bdm_byte_count, 0x2) != TERM_OK) { + printf("Failed to read BDM register.\r\n"); + return false; + } + if (adreg_read(&bdm_buffer_address, 0x9) != TERM_OK) { + printf("Failed to read BDM register.\r\n"); + return false; + } + uint32_t bytes_read = fread(&file_buffer[0],1,bdm_byte_count,fp); + for (uint32_t byte_count = 0; byte_count < bytes_read; byte_count++) { + if (byte_count == 0x0) { + if(memwrite_byte(&bdm_buffer_address, file_buffer[byte_count]) != TERM_OK) return false; + } else { + if(memfill_byte(file_buffer[byte_count]) != TERM_OK) return false; + } + } + if (adreg_write(0x0, &bytes_read) != TERM_OK) { + printf("Failed to write BDM register.\r\n"); + return false; + } + return true; +} + +bool bdmSyscallFwrite(void) +{ + printf("BDM FWRITE Syscall not supported.\r\n"); + return false; +} + +bool bdmSyscallFtell(void) +{ + printf("BDM FTELL Syscall not supported.\r\n"); + return false; +} + +bool bdmSyscallFseek(void) +{ + uint32_t bdm_byte_offset, bdm_file_origin, bdm_file_handle = NULL; + if (adreg_read(&bdm_file_handle, 0x1) != TERM_OK) { + printf("Failed to read BDM register.\r\n"); + return false; + } + if (adreg_read(&bdm_byte_offset, 0x2) != TERM_OK) { + printf("Failed to read BDM register.\r\n"); + return false; + } + if (adreg_read(&bdm_file_origin, 0x3) != TERM_OK) { + printf("Failed to read BDM register.\r\n"); + return false; + } + uint32_t origin; + switch (bdm_file_origin) { + case 0x2: + origin = SEEK_END; + break; + case 0x1: + origin = SEEK_CUR; + break; + case 0x0: + default: + origin = SEEK_SET; + break; + } + uint32_t fseek_result = fseek ( fp ,bdm_byte_offset ,origin ); + if (adreg_write(0x0, &fseek_result) != TERM_OK) { + printf("Failed to write BDM register.\r\n"); + return false; + } + return true; +} + +bool bdmSyscallFgets(void) +{ + printf("BDM FGETS Syscall not supported.\r\n"); + return false; +} + +bool bdmSyscallFputs(void) +{ + printf("BDM FPUTS Syscall not supported.\r\n"); + return false; +} + +bool bdmSyscallEval(void) +{ + printf("BDM EVAL Syscall not supported.\r\n"); + return false; +} + +bool bdmSyscallFreadsrec(void) +{ + printf("BDM FREADSREC Syscall not supported.\r\n"); + return false; +}