//------------------------------------------------------------------------------ //-- -- //-- Downloads S19 formatted files to the MegaSquirt EFI controller via -- //-- the embedded bootloader routines. All documentation is contained -- //-- within this file. -- //-- -- //-- Copyright (c) 2002 by Eric Fahlgren. All rights reserved. -- //-- -- //-- This program is free software; you can redistribute it and/or -- //-- modify it under the terms of the GNU General Public License -- //-- as published by the Free Software Foundation; either version -- //-- 2 of the License, or (at your option) any later version. -- //-- See http://www.gnu.org/licenses/gpl.txt -- //-- -- //-- For support contact . -- //-- -- //------------------------------------------------------------------------------ //-- -- //-- History -- //-- 1.0 - Eric Fahlgren's original code. -- //-- 1.1 - Added -b, -c switches. -- //-- 1.2 - Added voltage read after download, -o for non-B&G voltage loc. -- //-- 1.3 - Allow multiple files (probably doesn't work). -- //-- 1.4 - Add -d switch to allow users to delay between s19 lines. -- //-- 1.5 - Modify -d switch to allow different types of delays; -- //-- add wipe only. -- //-- 1.6 - Ignore empty lines, spit out and then ignore S0 comment recs. -- //-- -- //------------------------------------------------------------------------------ #include #include static char *rcsId = "$Id$"; #ifdef _DEBUG # define new DEBUG_NEW # undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif //------------------------------------------------------------------------------ #define MAJOR 1 #define MINOR 6 static int commPortNumber = 1; static HANDLE hPortHandle = NULL; static char s19Name[10][128]; static int nFiles = 0; static FILE *s19 = NULL; static char *progName = NULL; static bool forceBootloader = false; static bool wipeOnly = false; static int BatOfs = 8; static int delay[3] = {0, 0, 500}; enum { afterLine = 0, afterChar = 1, afterWipe = 2 }; //------------------------------------------------------------------------------ static void usage(void) { // Make sure usage fits on an 80-column screen. // 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789x fprintf(stderr, "usage: %s [options] [-c] [s19-file]...\n" " as in\n" "\n" " %s -c2 mysquirt.s19\n" "\n", progName, progName); fprintf(stderr, "Download requires that the bootloader already be resident on your MCU, it\n" "cannot burn a blank chip.\n" "\n" " -b - send \"force bootloader\" command to MS, only works with Guy\n" " Hill embedded code.\n" " -c - specify comm port to which MegaSquirt is connected, default is\n" " port 1.\n" " -d MS - delay MS milliseconds between lines, increase if you cannot\n" " reboot after download, default is zero.\n" " -dc MS - add an inter-character delay, completely useless.\n" " -dw MS - set after-wipe delay to MS, default is 500 ms.\n" " -o n - change the byte offset in the runtime variables to get battery\n" " voltage, default is 8 (B&G standard).\n" " -w - wipe only, don't try to download.\n" " s19-file - the list of names of the s19 files to download, default file is\n" " is megasquirt.s19.\n" "\n"); exit(1); } //------------------------------------------------------------------------------ #define AdjustI(n) {argv[iarg] += (n); if (argv[iarg][0] == '\0') ++iarg;} static void parseArgs(int argc, char *argv[]) { int which; progName = argv[0]; for (int iarg = 1; iarg < argc; iarg++) { if (argv[iarg][0] == '-') { switch (argv[iarg][1]) { case 'b': forceBootloader = true; break; case 'c': AdjustI(2); commPortNumber = strtol(argv[iarg], NULL, 10); break; case 'd': if (argv[iarg][2] == 'c') { which = afterChar; AdjustI(3); } else if (argv[iarg][2] == 'w') { which = afterWipe; AdjustI(3); } else { which = afterLine; AdjustI(2); } delay[which] = strtol(argv[iarg], NULL, 10); break; case 'o': AdjustI(2); BatOfs = strtol(argv[iarg], NULL, 10); break; case 'w': wipeOnly = true; break; default: usage(); break; } } else { if (nFiles > 9) usage(); strcpy(s19Name[nFiles], argv[iarg]); nFiles++; } } if (nFiles == 0) { strcpy(s19Name[0], "megasquirt.s19"); nFiles++; } } //------------------------------------------------------------------------------ #include static void error(int exitCode, char *fmt, ...) { va_list args; va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); exit(exitCode); } //------------------------------------------------------------------------------ bool openCommPort() { char pPortName[6]; sprintf(pPortName, "com%d", commPortNumber); hPortHandle = CreateFile(pPortName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hPortHandle == INVALID_HANDLE_VALUE) return false; DCB dcb; if (!GetCommState(hPortHandle, &dcb)) return false; dcb.BaudRate = 9600; dcb.ByteSize = 8; dcb.Parity = NOPARITY; dcb.StopBits = ONESTOPBIT; if (!SetCommState(hPortHandle, &dcb)) return false; COMMTIMEOUTS timeout; if (!GetCommTimeouts(hPortHandle, &timeout)) return false; timeout.ReadIntervalTimeout = 0; // ms between chars on read. timeout.ReadTotalTimeoutMultiplier = 0; timeout.ReadTotalTimeoutConstant = 250; timeout.WriteTotalTimeoutMultiplier = 0; timeout.WriteTotalTimeoutConstant = 0; if (!SetCommTimeouts(hPortHandle, &timeout)) return false; return true; } //------------------------------------------------------------------------------ void closeCommPort() { if (hPortHandle != NULL) { CloseHandle(hPortHandle); hPortHandle = NULL; } } //------------------------------------------------------------------------------ // readCommPort - Read char from from the COM port. Non-blocking, timeout set // in openCommPort. bool readCommPort(BYTE &pChar) { DWORD dwActualRead; bool bOK = 0 != ReadFile(hPortHandle, &pChar, 1, &dwActualRead, NULL); if (dwActualRead != 1) bOK = false; return bOK; } //------------------------------------------------------------------------------ bool writeCommPort(char *pBuf, DWORD dwCount) { DWORD dwBytesWritten; bool bOK = 0 != WriteFile(hPortHandle, pBuf, dwCount, &dwBytesWritten, NULL); if (dwBytesWritten != dwCount) bOK = false; return bOK; } //------------------------------------------------------------------------------ //-- Possible boot loader messages, culled from boot_r12.asm: -- //-- -- //-- CR,LF,'Boot>' -- //-- ' (P)rogram (W)ipe (U)pgrade e(X)it' -- //-- ' Complete' -- //-- ' - waiting ...' -- //-- ' - error' -- //-- ' - what?' -- //-- ' - Reset Vector Invalid' -- //-- -- //------------------------------------------------------------------------------ #define LF "\012" #define CR "\015" //------------------------------------------------------------------------------ void send(char *msg) { writeCommPort(msg, strlen(msg)); } //------------------------------------------------------------------------------ double getVoltage() { send("A"); BYTE rtv[22]; for (int i = 0; i < 22; i++ ) { if (!readCommPort(rtv[i])) return -1.0; } return rtv[BatOfs]*30.0 / 255.0; } //------------------------------------------------------------------------------ void recv(char *response) { char inputBuffer[128]; BYTE p; int len = strlen(response); int j = 0; for (int i = 0; i <= len; i++ ) { if (readCommPort(p)) { inputBuffer[j++] = p; putchar(p); } } if (j > len) j = len; inputBuffer[j] = '\0'; if (strcmp(response, inputBuffer)) { error(4, "Download failed:\n" " Expected response \"%s\",\n" " but received this \"%s\".\n", response, inputBuffer); } } //------------------------------------------------------------------------------ void sendFile() { char lineBuffer[128]; int lineNumber = 0; int totalBytes = 0; printf("\n"); while (fgets(lineBuffer, sizeof(lineBuffer), s19)) { if (lineBuffer[0] != '\0') { // Skip blank lines. if (strncmp(lineBuffer, "S0", 2) == 0) { // Comment S-rec, just echo it out to the screen. // Like S0550000443A5C67726970... printf("S19 comment: \""); for (int i = 8; i < strlen(lineBuffer); i += 2) { char hex[3] = { lineBuffer[i], lineBuffer[i+1], '\0'}; printf("%c", strtol(hex, NULL, 16)); } printf("\"\n"); } else { // Data record, so send it. ++lineNumber; totalBytes += (strlen(lineBuffer) - 12) / 2; // 12 characters overhead per line. if (lineNumber % 10 == 0) printf("Line %4d\r", lineNumber); if (delay[afterChar] == 0) writeCommPort(lineBuffer, strlen(lineBuffer)); else { for (int i = 0; i < strlen(lineBuffer); i++) { writeCommPort(lineBuffer+i, 1); Sleep(delay[afterChar]); } } Sleep(delay[afterLine]); } } } printf("File sent, %d lines, %d bytes.\n", lineNumber, totalBytes); } //------------------------------------------------------------------------------ int main(int argc, char *argv[]) { printf("MS Download %d.%02d\n", MAJOR, MINOR); parseArgs(argc, argv); if (!wipeOnly) { for (int iFile = 0; iFile < nFiles; iFile++) { // Make sure we can read all the files, first. s19 = fopen(s19Name[iFile], "rb"); if (s19 == NULL) error(3, "Download failed:\n" " Could not open file '%s'.\n", s19Name[iFile]); // Read them all and make sure they don't have overlapping // addresses, or the wipe/program will fail later. fclose(s19); } } if (!openCommPort()) error(2, "Download failed:\n" " Could not open comm port %d.", commPortNumber); if (forceBootloader) send("!!"); else send(CR); recv(CR LF "Boot>"); send("w"); // Wipe. Sleep(delay[afterWipe]); recv("w Complete" CR LF "Boot>"); if (wipeOnly) { printf("\nWiped.\n"); } else { for (int iFile = 0; iFile < nFiles; iFile++) { s19 = fopen(s19Name[iFile], "rb"); if (s19 == NULL) // We did this before, but belt & suspenders. error(3, "Download failed:\n" " Could not open file '%s'.\n", s19Name[iFile]); send("p"); // Program. recv("p - waiting ..."); printf("\nSending file %d of %d - %s", iFile+1, nFiles, s19Name[iFile]); sendFile(); recv(" Complete" CR LF "Boot>"); fclose(s19); } Sleep(100); send("x"); // Reboot MS. Sleep(100); send("S"); Sleep(100); printf("\n"); BYTE p; while (readCommPort(p)) { putchar(p); if (p == 'x') printf("\nSignature: "); } printf("\n"); double v = getVoltage(); if (v < 0.0) printf("Could not fetch voltage from MS, download may have failed.\n"); else { printf("Controller battery voltage: %5.2f\n", v); if (v < 7.0) { printf( "\bController voltage too low for reliable FLASH burning.\n" "Don't be surprised if you can't reboot MS.\n" ); } } } closeCommPort(); exit(0); return 0; // So lint doesn't complain. } //------------------------------------------------------------------------------