initFlsh.c -- Controller initialization and automatic firmware download
/* initFlsh.c */

/* Copyright(c) 1991-2002 Motion Engineering, Inc.  All rights reserved.
 *
 * This software  contains proprietary and  confidential information  of
 * Motion Engineering Inc., and its suppliers.  Except as may be set forth
 * in the license agreement under which  this software is supplied, use,
 * disclosure, or  reproduction is prohibited without the prior express
 * written consent of Motion Engineering, Inc.
 */

#if defined(MEI_RCS)
static const char MEIUtilRCS[] =
    "$Header: /MainTree/XMPLib/XMP/app/initFlsh.c 4     7/23/01 2:36p Kevinh $";
#endif

/*

:Controller initialization and automatic firmware download

  ********************************* Warning ***********************************
  This application will overwrite the controller configuration that is saved
  to the Flash memory.  If connected to hardware this could cause motors to
  be enabled or to run away.  Do NOT use without proper knowledge of the system
  *****************************************************************************

This sample code demonstrates how to download Firmware to the XMP-Series
 controller.  The Firmware can also be downloaded using one of the utilities
 provided by MEI.  This sample application requires the user to specify the
 location and filename for a valid, matching Firmware file and optional
 FPGA file(s).  During initialization the controller's firmware version,
 revision, option and user version numbers are checked.  If the controller's
 version doesn't match the expected version, the firmware is downloaded to
 the controller's flash memory.

Note: The flash utility program uses the same code to download firmware to the
 flash memory.

Warning!  This is a sample program to assist in the integration of the
 XMP motion controller with your application.  It may not contain all
 of the logic and safety features that your application requires.
*/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "stdmpi.h"
#include "stdmei.h"

#include "apputil.h"

/* Set desired Firmware version. these values could be read from a file */
#define FW_VERSION          332
#define FW_REVISION         'A'
#define FW_SUBREVISION      2
#define FW_OPTION           0
#define FW_VERSION_USER     0   /* user configurable */

#if defined(ARG_MAIN_RENAME)
#define main    initFlshMain

argMainRENAME(main, initFlsh)
#endif

#define FIRMWARE    "c:\\xmp332a2.bin"  /* this string could be read from a file */
#define FPGA0       (NULL)              /* specify NULL for default FPGA image */
#define FPGA1       (NULL)
#define FPGA2       (NULL)

long initXmp(MPIControl control, MEIFlashFiles *fileNames);

int
    main(int    argc,
         char   *argv[])
{
    MPIControl          control;

    long    returnValue;
    long    argIndex;

    MPIControlType      controlType;
    MPIControlAddress   controlAddress;

    MEIFlashFiles       flashFiles;

    /* Parse command line for Control type and address */
    argIndex =
        argControl(argc,
                   argv,
                   &controlType,
                   &controlAddress);

    /* Obtain a Control handle */
    control =
        mpiControlCreate(controlType,
                         &controlAddress);
    msgCHECK(mpiControlValidate(control));

    /* copy file names into structure */
    if (FIRMWARE != NULL) {
        strcpy(flashFiles.binFile, FIRMWARE);
    }
    else {
        flashFiles.binFile[0] = '\0';
    }
    if (FPGA0 != NULL) {
        strcpy(flashFiles.FPGAFile[0], FPGA0);
    }
    else {
        flashFiles.FPGAFile[0][0] = '\0';
    }
    if (FPGA1 != NULL) {
        strcpy(flashFiles.FPGAFile[1], FPGA1);
    }
    else {
        flashFiles.FPGAFile[1][0] = '\0';
    }
    if (FPGA2 != NULL) {
        strcpy(flashFiles.FPGAFile[2], FPGA2);
    }
    else {
        flashFiles.FPGAFile[2][0] = '\0';
    }

    /* Initialize the controller, load flash if firmware is different */
    returnValue =
        initXmp(control,
                &flashFiles);   /* firmware and FPGA file name(s) */
    msgCHECK(returnValue);

    /* Delete object handles */
    returnValue = mpiControlDelete(control);
    msgCHECK(returnValue);

    return ((int)returnValue);
}

long versionCheck(MPIControl control)
{
    MPIControlConfig    controlConfig;
    MEIControlVersion   version;

    long returnValue;

    returnValue =
        mpiControlConfigGet(control,
                            &controlConfig,
                            NULL);

    returnValue =
        meiControlVersionGet(control,
                             &version);

    if ((version.xmp.firmware.version != FW_VERSION)
        || (version.xmp.firmware.revision != FW_REVISION)
        || (version.xmp.firmware.subRevision != FW_SUBREVISION)
        || (version.xmp.firmware.option != FW_OPTION)
        || (controlConfig.userVersion != FW_VERSION_USER)) {

        meiPlatformConsole("ERROR: Controller firmware does not match expected version.\n"
                           " Version:  %d%c%d option:%d user:%d\n"
                           " Expected: %d%c%d option:%d user:%d\n\n",
                           version.xmp.firmware.version,
                           version.xmp.firmware.revision,
                           version.xmp.firmware.subRevision,
                           version.xmp.firmware.option,
                           controlConfig.userVersion,
                           FW_VERSION,
                           FW_REVISION,
                           FW_SUBREVISION,
                           FW_OPTION,
                           FW_VERSION_USER);

        returnValue = MEIControlMessageFIRMWARE_VERSION;
    }

    /* display check result */
    meiPlatformConsole("Firmware Version Check: %s\n", mpiMessage(returnValue, NULL));

    return (returnValue);
}

long firmwareLoad(MPIControl control, MEIFlashFiles *fileNames)
{
    MEIFlash            flash;
    MEIFlashFiles       loadedFiles;
    MEIControlVersion   version;

    long                MB0 = -1;
    long                MB1 = -1;
    long                update20Khz = 0;

    long        returnValue;

    returnValue =
        mpiControlValidate(control);

    if (returnValue == MPIMessageOK) {
        /* fake a valid version so we can still use the MPI routines */
        returnValue =
            meiControlVersionGet(control,
                                 &version);

        if (returnValue == MPIMessageOK) {
            memset(&version.xmp.firmware,
                   0,
                   sizeof(version.xmp.firmware));

            version.xmp.firmware.version = version.mpi.firmware.version;
            version.xmp.firmware.option  = version.mpi.firmware.option;

            returnValue =
                meiControlVersionSet(control,
                                     &version);
        }
    }

    if (returnValue == MPIMessageOK) {
        /* Create Flash object */
        flash =
            meiFlashCreate(control);
        returnValue = meiFlashValidate(flash);
    }

    if (returnValue == MPIMessageOK) {
        meiPlatformConsole("Loading flash memory from \"%s\" ...\n",
                                   fileNames->binFile);
    }

    if (returnValue == MPIMessageOK) {
        /* Load Firmware and FPGAs from file(s) */
        returnValue =
            meiFlashMemoryFromFile(flash,
                                   fileNames,
                                   &loadedFiles);
    }

    if (returnValue == MPIMessageOK) {
        meiPlatformConsole("Code loaded and verified from \"%s\".\n",
                           loadedFiles.binFile);

        meiPlatformConsole("FPGAs loaded and verified from\n"
                            "%s\n"
                            "%s\n"
                            "%s\n\n",
                            loadedFiles.FPGAFile[0],
                            loadedFiles.FPGAFile[1],
                            loadedFiles.FPGAFile[2]);

    }
    else {
        meiPlatformConsole("Can't load flash from \"%s\"\n"
                            "\"%s\"\n"
                            "\"%s\"\n"
                            "\"%s\"\n"
                            "(%s)\n",
                            loadedFiles.binFile,
                            loadedFiles.FPGAFile[0],
                            loadedFiles.FPGAFile[1],
                            loadedFiles.FPGAFile[2],
                            mpiMessage(returnValue, NULL));
    }

    if (returnValue == MPIMessageOK) {
        returnValue = mpiControlReset(control);
        msgCHECK(returnValue);
    }

    if (returnValue == MPIMessageOK) {
        MEIControlSocketInfo socketInfo;
        MEIControlRipTideConfig config;

        returnValue = meiControlSocketInfoGet(control,
                                              &socketInfo);
        msgCHECK(returnValue);

        /* Get default RipTide configuration */
        returnValue = meiControlRipTideDefaultGet(control,
                                                   &socketInfo,
                                                   &config);
        msgCHECK(returnValue);

        /* Configure RipTide */
        if (MB0 != -1) {
            config.motionBlocks[0] = MB0;
        }

        if (MB1 != -1) {
            config.motionBlocks[1] = MB1;
        }

        config.update20khz = update20Khz;

        if (config.update20khz) {
            MPIControlConfig controlConfig;

            config.motionBlocks[0] = 1;
            config.motionBlocks[1] = 0;

            /* Set Sample Rate */
            returnValue = mpiControlFlashConfigGet(control,
                                                   NULL,
                                                   &controlConfig,
                                                   NULL);
            msgCHECK(returnValue);

            if (returnValue == MPIMessageOK) {
                controlConfig.sampleRate = 20000;

                returnValue = mpiControlFlashConfigSet(control,
                                                       NULL,
                                                       &controlConfig,
                                                       NULL);
                msgCHECK(returnValue);
            }
        }

        if (returnValue == MPIMessageOK) {
            meiPlatformConsole("Configuring RipTide\n");

            returnValue = meiControlFlashRipTideConfigSet(control,
                                                        flash,
                                                        &config);

            if (config.update20khz) {
                meiPlatformConsole("Reconfigured RipTide:\n"
                                    "Configured for 20Khz operation\n"
                                    "Main Board has 1 Motion Block\n"
                                    "Expansion Board has 0 Motion Blocks\n\n");
            }
            else {
                meiPlatformConsole("Reconfigured RipTide:\n"
                                    "Main Board has %d Motion Blocks\n"
                                    "Expansion Board has %d Motion Blocks\n\n",
                                    config.motionBlocks[0],
                                    config.motionBlocks[1]);
            }
        }
    }

    /* clean up */
    if (returnValue == MPIMessageOK) {
        returnValue = meiFlashDelete(flash);
    }

    /* re-initialize controller */
    if (returnValue == MPIMessageOK) {
        returnValue =
            mpiControlInit(control);
    }

    /* check version values */
    if (returnValue == MPIMessageOK) {
        returnValue =
            versionCheck(control);
    }

    return returnValue;
}

long initXmp(MPIControl control, MEIFlashFiles *fileNames)
{
    long returnValue;

    /* Initialize the controller */
    returnValue =
        mpiControlInit(control);

    /* check init returnValue */
    if ((returnValue == MEIControlMessageFIRMWARE_INVALID) ||
        (returnValue == MEIControlMessageFIRMWARE_VERSION) ||
        (returnValue == MEIControlMessageFIRMWARE_VERSION_NONE)) {
        /* display warning */
        meiPlatformConsole("WARNING: %s\n\n",
                   mpiMessage(returnValue, NULL));

        /* load firmware */
        returnValue =
            firmwareLoad(control, fileNames);
    }
    else {
        if (returnValue == MPIMessageOK) {
            /* check firmware version, revision, option, and user version */
            returnValue =
                versionCheck(control);

            if (returnValue == MEIControlMessageFIRMWARE_VERSION) {
                /* load firmware */
                returnValue =
                    firmwareLoad(control, fileNames);
            }
        }
    }

    return (returnValue);
}