//------------------------------------------------------------------------------
//--                                                                          --
//--  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 <eric.fahlgren@mscsoftware.com>.                    --
//--                                                                          --
//------------------------------------------------------------------------------
//--                                                                          --
//--  History                                                                 --
//--  1.0 - Eric Fahlgren's original code.                                    --
//--                                                                          --
//------------------------------------------------------------------------------

#include <windows.h>
#include <stdio.h>

static char *rcsId = "$Id$";

#ifdef _DEBUG
#  define new DEBUG_NEW
#  undef THIS_FILE
   static char THIS_FILE[] = __FILE__;
#endif

//------------------------------------------------------------------------------

#define MAJOR 1
#define MINOR 1

static int     commPortNumber  = 1;
static HANDLE  hPortHandle     = NULL;
static char    s19Name[128]    = "megasquirt.s19";
static FILE   *s19             = NULL;
static char   *progName        = NULL;
static bool    forceBootloader = false;

//------------------------------------------------------------------------------

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 [-b] [-c<port>] [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"
                   "   s19-file - the name of the s19 file to download, default 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[])
{
   progName = argv[0];

   for (int iarg = 1; iarg < argc; iarg++) {
      switch (argv[iarg][1]) {
         case 'b':
            forceBootloader = true;
            break;

         case 'c':
            AdjustI(2);
            commPortNumber = strtol(argv[iarg], NULL, 10);
            break;

         default:
            if (argv[iarg][0] == '-') usage();
            if (strcmp(s19Name, "megasquirt.s19") != 0) usage();
            strcpy(s19Name, argv[iarg]);
            break;
      }
   }
}

//------------------------------------------------------------------------------

#include <stdarg.h>

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));
}

//------------------------------------------------------------------------------

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;
   printf("\n");
   while (fgets(lineBuffer, sizeof(lineBuffer), s19)) {
      ++lineNumber;
      if (lineNumber % 10 == 0) printf("Line %4d\r", lineNumber);
      writeCommPort(lineBuffer, strlen(lineBuffer));
   }
   printf("File sent, %d lines.\n", lineNumber);
}

//------------------------------------------------------------------------------

int main(int argc, char *argv[])
{
   printf("MS Download %d.%02d\n", MAJOR, MINOR);

   parseArgs(argc, argv);

   s19 = fopen(s19Name, "rb");
   if (s19 == NULL)
      error(3,  "Download failed:\n"
                "   Could not open file '%s'.\n", s19Name);

   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("u");
   recv("u - waiting ...");
   sendFile();
   recv("  Complete" CR);
   send("x"); // Reboot MS.

   send("S");
   printf("\n");
   BYTE p;
   while (readCommPort(p)) {
      putchar(p);
      if (p == 'x') printf("\n");
   }
   printf("\n");

   fclose(s19);
   closeCommPort();
   exit(0);
   return 0; // So lint doesn't complain.
}

//------------------------------------------------------------------------------

