//###########################################################################
// FILE:   dcan_flash_programmer.cpp
// TITLE:  DCAN Flash Programmer for firmware upgrades through CAN-FD (host).
//
//###########################################################################
// $Copyright:
// Copyright (C) 2022 Texas Instruments Incorporated - http://www.ti.com
//
// Redistribution and use in source and binary forms, with or without 
// modification, are permitted provided that the following conditions 
// are met:
// 
//   Redistributions of source code must retain the above copyright 
//   notice, this list of conditions and the following disclaimer.
// 
//   Redistributions in binary form must reproduce the above copyright
//   notice, this list of conditions and the following disclaimer in the 
//   documentation and/or other materials provided with the   
//   distribution.
// 
//   Neither the name of Texas Instruments Incorporated nor the names of
//   its contributors may be used to endorse or promote products derived
//   from this software without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// $
//###########################################################################

#include "stdafx.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <iostream>
#include <iomanip>
using namespace std;

#include "include/f021_DownloadImage.h"

// Headers required for PCAN API
#pragma once
#include <conio.h>
#include <windows.h>
#include <dos.h>
#include "include/PCANBasic.h"

//Prototypes
void ExitApp(int iRetcode);
void PrintWelcome(void);
void ShowHelp(void);
int ParseCommandLine(int argc, wchar_t *argv[]);
void setDeviceName(void);

//*****************************************************************************
//
// Helpful macros for generating output depending upon verbose and quiet flags.
//
//*****************************************************************************
#define VERBOSEPRINT(...) if(g_bVerbose) { _tprintf(__VA_ARGS__); }
#define QUIETPRINT(...) if(!g_bQuiet) { _tprintf(__VA_ARGS__); }

//*****************************************************************************
//
// Globals whose values are set or overridden via command line parameters.
//
//*****************************************************************************
bool g_bVerbose = false;
bool g_bQuiet = false;
bool g_bWaitOnExit = false;

//
//Device name
//
bool g_bf28388x = false;
bool g_bf28003x = false;
bool g_bf28p65x = false;
bool g_bf280015x = false;

//
//type of Flash that will be used
// 
bool g_bf021 = false;

//
//String names
//
wchar_t *g_pszAppFile = NULL;
wchar_t *g_pszKernelFile = NULL;
wchar_t *g_pszComPort = NULL;
wchar_t *g_pszBaudRate;
wchar_t *g_pszDeviceName = NULL;

//
// Bit rate config
// 
TPCANStatus gStatus;
TPCANBitrateFD bitrate;

//*****************************************************************************
//
// The main entry point of the DFU programmer example application.
//
//*****************************************************************************
int
_tmain(int argc, TCHAR* argv[])
{
	int iExitCode = 0;
	int iRetCode = 0;

	//
	// Parse the command line parameters, print the welcome banner and
	// tell the user about any errors they made.
	//
	ParseCommandLine(argc, argv);
	
	//
	// DCAN Devices
	//
	if (g_bf28003x || g_bf28p65x || g_bf280015x || g_bf28388x) {
		VERBOSEPRINT(_T("\nInitializing CAN USB-C1 with Bit rate set to 1 MBit/s\n"));
		if (g_bf28003x || g_bf28388x) {
			sleep_time = 10; // in msec
		}
		else if (g_bf28p65x) {
			sleep_time = 1; // in msec
		}
		else if (g_bf280015x) {
			sleep_time = 1; // in msec
		}

		// Defines a DCAN Bit rate string with nominal Bit rate set to 100kbps
		// From PEAK bit timing calculator: Nominal Sample point 75%
		// Default value for bitrate set at 85.0% Sample Point, TQ=20
		bitrate = "f_clock_mhz = 80, nom_brp = 40, nom_tseg1 = 16, nom_tseg2 = 3, nom_sjw = 2"; // 100kbps
		//bitrate = "f_clock_mhz = 80, nom_brp = 10, nom_tseg1 = 12, nom_tseg2 = 3, nom_sjw = 3"; // alt 500kbps
		//bitrate = "f_clock_mhz = 80, nom_brp = 10, nom_tseg1 = 5, nom_tseg2 = 2, nom_sjw = 1"; // alt 1Mbps
		
		//
	    // Initialize USB/CAN Dongle
	    //
		gStatus = CAN_Initialize(PCAN_USBBUS1, PCAN_BAUD_100K);
	}

	if (gStatus != PCAN_ERROR_OK)
	{
		VERBOSEPRINT(_T("\nError initializing CAN USB-C1, check connection\n"));
		ExitApp(iExitCode);
	}


	/***********************************************************************/
	if (g_bf021 == true)
	{
		//download kernel and application for F021 devices
		_tprintf(_T("\ncalling f021_CANFD_DownloadImage\n"));
		iRetCode = f021_CANFD_DownloadImage();
	}

}

//*****************************************************************************
//
// Exit the application, optionally pausing for a key press first.
//
//*****************************************************************************
void
ExitApp(int iRetcode)
{
	//
	// Has the caller asked us to pause before exiting?
	//
	if (g_bWaitOnExit)
	{
		_tprintf(_T("\nPress any key to exit...\n"));
		while (!_kbhit())
		{
			//
			// Wait for a key press.
			//
		}
	}

	exit(iRetcode);
}

//*****************************************************************************
//
// Display the welcome banner when the program is started.
//
//*****************************************************************************
void
PrintWelcome(void)
{
	if (g_bQuiet)
	{
		return;
	}

	_tprintf(_T("\nC2000 CAN Firmware Upgrader\n"));
	_tprintf(_T("Copyright (c) 2022 Texas Instruments Incorporated.  All rights reserved.\n\n"));
}

//*****************************************************************************
//
// Show help on the application command line parameters.
//
//*****************************************************************************
void
ShowHelp(void)
{
	//
	// Only print help if we are not in quiet mode.
	//
	if (g_bQuiet)
	{
		return;
	}

	_tprintf(_T("This application may be used to download images to a Texas Instruments\n"));
	_tprintf(_T("C2000 microcontroller in the CAN boot mode.\n\n"));
	_tprintf(_T("Supported parameters are:\n\n"));
	_tprintf(_T("-d <device>   - The name of the device to load to.\n"));
	_tprintf(_T("                f28388x, f28003x, f28p65x, f280015x\n"));

	_tprintf(_T("-k <file>     - The file name for flash kernel.\n"));
	_tprintf(_T("               This file must be in the sci8 boot format.\n"));
    _tprintf(_T("-a <file>     - The file name for download use.\n"));
	_tprintf(_T("               This file must be in the sci8 boot format.\n"));

	_tprintf(_T("-? or -h      - Show this help.\n"));
	_tprintf(_T("-q            - Quiet mode. Disable output to stdout.\n"));
	_tprintf(_T("-w            - Wait for a key press before exiting.\n"));
	_tprintf(_T("-v            - Enable verbose output\n\n"));
	_tprintf(_T("-d is a mandatory parameter. \n"));

	_tprintf(_T("Application and kernel files must be in the SCI8 ascii boot format. \nThese can be generated using the hex2000 utility.  An example of how to do \nthis follows:\nhex2000 application.out -boot -sci8 -a -o application.txt\n\n"));
}

//*****************************************************************************
//
// Parse the command line, extracting all parameters.
//
// Returns 0 on success. On failure, calls ExitApp(1).
//
//*****************************************************************************
int
ParseCommandLine(int argc, wchar_t *argv[])
{
	int iParm;
	bool bShowHelp;
	wchar_t *pcOptArg;

	//
	// By default, don't show the help screen.
	//
	bShowHelp = false;

	//
	// Walk through each of the parameters in the list, skipping the first one
	// which is the executable name itself.
	//
	for (iParm = 1; iParm < argc; iParm++)
	{
		//
		// Does this look like a valid switch?
		//
		if (!argv || ((argv[iParm][0] != L'-') && (argv[iParm][0] != L'/')) ||
			(argv[iParm][1] == L'\0'))
		{
        	//
			// We found something on the command line that didn't look like a
			// switch so ExitApp.
			//
			_tprintf(_T("Unrecognized or invalid argument: %s\n"), argv[iParm]);
			ExitApp(1);
		}
		else
		{
			//
			// Get a pointer to the next argument since it may be used
			// as a parameter for the case statements 
            //
			pcOptArg = ((iParm + 1) < argc) ? argv[iParm + 1] : NULL;
		}

		switch (argv[iParm][1])
		{
		case 'd':
			g_pszDeviceName = pcOptArg;
			iParm++;
			setDeviceName();
			break;

		case 'k':
			g_pszKernelFile = pcOptArg;
			iParm++;
			break;

		case 'a':
			g_pszAppFile = pcOptArg;
			iParm++;
			break;

		case 'v':
			g_bVerbose = TRUE;
			break;

		case 'q':
			g_bQuiet = TRUE;
			break;

		case 'w':
			g_bWaitOnExit = TRUE;
			break;		

		case '?':
		case 'h':
			bShowHelp = TRUE;
			break;

		default:
			_tprintf(_T("Unrecognized argument: %s\n\n"), argv[iParm]);
			ShowHelp();
			ExitApp(1);

		}
	}

	//
	// Show the welcome banner unless we have been told to be quiet.
	//
	PrintWelcome();

	//
	// Show the help screen if requested.
	//
	if (bShowHelp)
	{
		ShowHelp();
		ExitApp(0);
	}
	
	return(0);
}

void setDeviceName(void)
{
	// if not a proper input device name
	// 0 == false, !false = !0 = true. strncmp && wcsncmp returns !0 = true if different.

	if (wcsncmp(g_pszDeviceName, (wchar_t*)L"f28p65x", 7) && wcsncmp(g_pszDeviceName, (wchar_t*)L"f28003x", 7) && wcsncmp(g_pszDeviceName, (wchar_t*)L"f28388x", 7) && wcsncmp(g_pszDeviceName, (wchar_t*)L"f280015x", 8))
	{
		_tprintf(_T("\nUnrecognized device name: X%sX\n\n"), g_pszDeviceName);
		ShowHelp();
		ExitApp(2);
	}
	else if (!wcsncmp(g_pszDeviceName, (wchar_t*)L"f28388x", 7))
	{
		g_bf021 = true;
		g_bf28388x = true;
	}
	else if (!wcsncmp(g_pszDeviceName, (wchar_t*)L"f28003x", 7))
	{
		g_bf021 = true;
		g_bf28003x = true;
	}
	else if (!wcsncmp(g_pszDeviceName, (wchar_t*)L"f28p65x", 7))
	{
		g_bf021 = true;
		g_bf28p65x = true;
	}
	else if (!wcsncmp(g_pszDeviceName, (wchar_t*)L"f280015x", 8))
	{
		g_bf021 = true;
		g_bf280015x = true;
	}
	else
	{
		QUIETPRINT(_T("ERROR: Device name is not recognized.\n"));
		ExitApp(7);
	}
    return;
}
