/*************
 *
 * Filename:    fwDld_9x15.c
 *
 * Purpose:     Contains routines needed to update MDM 9x15 based devices
 *
 * Copyright: © 2013 Sierra Wireless Inc., all rights reserved
 *
 **************/
#include "fwDld_9x15.h"

static BOOL fwDwlDone = FALSE;
static BOOL fwdwfail = FALSE;

/*
 * Name:     DevStateChgCbk
 *
 * Purpose:  Device State change callback
 *
 * Return:   None
 *
 * Notes:    none
 */
void DevStateChgCbk_9x15(eDevState devstatus)
{
    /* If device is ready to communicate */
    if( devstatus ==  DEVICE_STATE_READY )
    {
        fprintf( stderr, "\nDevice Ready\n" );

        /* Unsubscribe from the callback */
        SetDeviceStateChangeCbk(NULL);
        fwDwlDone = TRUE;
    }
}

/*
 * Name:     FwDwldCbk_9x15
 *
 * Purpose:  Firmware download completion callback
 *
 * Return:   None
 *
 * Notes:    none
 */
void FwDwldCbk_9x15(ULONG status)
{
    if ( eQCWWAN_ERR_NONE == status)
    {
        fprintf( stderr, "\nFirmware Download Completed" );
    }
    else if (eQCWWAN_ERR_SWIIM_FIRMWARE_NOT_DOWNLOADED == status)
    {
        fprintf( stderr, "\nFirmware Not Downloaded" );
        fwdwfail = TRUE;
    }
    else
    {
        fwdwfail = TRUE;
    }

    /* set firmware complete to true */
    fwDwlDone = TRUE;

    /* Unsubscribe from the callback */
    SetFwDldCompletionCbk(NULL);
}


/*
 * Name:     FwDloader_9x15_NoGIM
 *
 * Purpose:  Download firmware on a 9x15 based device using non-GIM mode
 *
 * Return:   None
 *
 * Notes:    none
 */
void FwDloader_9x15_NoGIM( CHAR *pImagePath )
{
    ULONG rc = 0;
    CHAR  completeImagePath[512];
    ULONG len = 0;

    /* Reset the firmware download completion and device ready flags */
    fwDwlDone = FALSE;
    fwdwfail = FALSE;

    rc = SetFwDldCompletionCbk( FwDwldCbk_9x15 );
    if( eQCWWAN_ERR_NONE != rc )
    {
        fprintf( stderr, "ERROR: Failed to register firmware Download Completion Callback\n"\
                         "Failure Code: %ld\n", rc );
        exit(0);
    }

    /* Concatenate image Path with '/' */
    memset( completeImagePath, 0, sizeof(completeImagePath) );
    
    len = strlen(pImagePath);
    if (len > (sizeof(completeImagePath) - 2) )
    {
        fprintf( stderr, "Error: ImagePath too long to process\n" );
        exit(0);
    }
    strncpy( completeImagePath,
             pImagePath,
             len );
    CHAR *pr = &completeImagePath[len - 1];
    if( *pr != '/' )
        *(pr + 1) = '/';

    /* Start downloading the firmware */
    rc = UpgradeFirmware2k( completeImagePath );
    if( eQCWWAN_ERR_NONE != rc )
    {
        /* some firmware of MC7700 may reboot immediately after receiving
         * set firmware id command without sending out respond,
         * neglect this error to bypass issue*/
        if(eQCWWAN_ERR_NO_DEVICE !=rc )
        {
            fprintf( stderr, "Firmware Download Failed\n"\
                             "Failure Code: %ld\n", rc );
            exit(0);
        }
        return;
    }

    fprintf( stderr, "\n\nDownloading Firmware");
    while( !fwDwlDone )
    {
        /* Display "." while firmware downloads */
        fprintf( stderr, ".");
        sleep(2);
    }

    if (fwdwfail)
    {
        fprintf( stderr, "ERROR: Firmware Download Failed\n");
        exit(0);
    }

    fprintf( stderr, "INFO: Firmware Download Succeeded\n");
}

/*
 * Name:     FwDloader_9x15_GIM
 *
 * Purpose:  Download firmware on a 9x15 based device using GIM mode
 *
 * Return:   None
 *
 * Notes:    none
 */
void FwDloader_9x15_GIM( CHAR *pImagePath )
{
    ULONG              resultCode = 0;
    struct qmifwinfo_s FwImgInfo, NVImgInfo;
    CurrentImgList     CurrImgList;
    CurrImageInfo      currImgInfo[5];
    BYTE               numEntries  = 5;
    CHAR               priVer[16];
    CHAR               pkgVer[SLQSFWINFO_PACKAGEID_SZ];
    CHAR               carrier[SLQSFWINFO_PACKAGEID_SZ];
    CHAR               AppVer[SLQSFWINFO_PACKAGEID_SZ];
    CHAR               completeImagePath[512];
    ULONG              len = 0;
    BOOL               skipPRIChk = FALSE;
    BOOL               skipMDMChk = FALSE;

    /* Initialize variables */
    memset( priVer, 0, sizeof(priVer) );
    memset( pkgVer, 0, sizeof(pkgVer) );
    memset( carrier, 0, sizeof(carrier) );
    memset( AppVer, 0, sizeof(AppVer) );

    CurrImgList.pCurrImgInfo = currImgInfo;
    CurrImgList.numEntries   = numEntries;

    memset( completeImagePath, 0, sizeof(completeImagePath) );
    len = strlen(pImagePath);
    if (len > (sizeof(completeImagePath) - 2) )
    {
        fprintf( stderr, "Error: ImagePath too long to process\n" );
        exit(0);
    }
    strncpy( completeImagePath,
             pImagePath,
             strlen(pImagePath) );
    len = strlen(completeImagePath);
    CHAR *pr = &completeImagePath[len - 1];
    if( *pr != '/' )
        *(pr + 1) = '/';

    /* Get Firmware image details */
    resultCode = SLQSGetImageInfo_9x15( completeImagePath,
                                        IMG_FW_TYPE_CWE,
                                        &FwImgInfo.dev.s );

    if (eQCWWAN_ERR_NONE != resultCode)
    {
        resultCode = SLQSGetImageInfo_9x15( completeImagePath,
                                     IMG_FW_TYPE_SPK,
                                     &FwImgInfo.dev.s );
    }

    if ( eQCWWAN_ERR_NONE != resultCode )
    {
        fprintf( stderr,
                 "INFO: Could not get details from the firmware image at the path: - "\
                 "will use firmware image present on device\n" );
        fprintf( stderr, "Failure Code : %lu\n", resultCode );
        skipMDMChk = TRUE;
    }
    else
    {
        /* Retrieve appversion( firmware version ) from fw image */
        strncpy( AppVer,
                 FwImgInfo.dev.s.appversion_str,
                 strlen(FwImgInfo.dev.s.appversion_str) );
    }

    /* Get PRI image details */
    resultCode = SLQSGetImageInfo_9x15( completeImagePath,
                                        IMG_PRI_TYPE_NVU,
                                        &NVImgInfo.dev.s );

    if ( eQCWWAN_ERR_NONE != resultCode )
    {
        fprintf( stderr,
                 "INFO: Could not get details from the PRI image at the path: - "
                 "will use PRI image present on device\n");
        fprintf( stderr, "Failure Code : %lu\n", resultCode );
        skipPRIChk = TRUE;
    }
    else
    {
        /* Save the firmware details for the image being downloaded */
        /* Retrieve PRI version, pkg version, carrier from PRI image */
        strncpy( priVer,
                 NVImgInfo.dev.s.priversion_str,
                 strlen(NVImgInfo.dev.s.priversion_str) );

        strncpy( pkgVer,
                 NVImgInfo.dev.s.packageid_str,
                 strlen(NVImgInfo.dev.s.packageid_str) );

        strncpy( carrier,
                 NVImgInfo.dev.s.carrier_str,
                 strlen(NVImgInfo.dev.s.carrier_str) );
    }

    /* If both images were not found, it indicates an issue */
    if( TRUE == skipMDMChk && TRUE == skipPRIChk )
    {
        fprintf( stderr,
                 "ERROR: Failed to retrieve both PRI and firmware image information"\
                 " - Please check path and try again\n");
        exit(0);
    }

    /* Reaching here means, image has been found */
    /* Subscribe to Device State Change callback */
    resultCode = SetDeviceStateChangeCbk(DevStateChgCbk_9x15);
    if( eQCWWAN_ERR_NONE != resultCode )
    {
        fprintf( stderr, "ERROR: Failed to register to device State Callback\n"\
                         "Failure Code: %ld\n", resultCode );
        exit(0);
    }

    /* Subscribe to Firmware Download completion callback */
    resultCode = SetFwDldCompletionCbk(FwDwldCbk_9x15);
    if( eQCWWAN_ERR_NONE != resultCode )
    {
        fprintf( stderr, "ERROR: Failed to register firmware Download Completion Callback\n"\
                         "Failure Code : %lu\n", resultCode );
        exit(0);
    }

    /* Start downloading the firmware */
    resultCode = UpgradeFirmware2k( completeImagePath );
    if( eQCWWAN_ERR_NONE != resultCode )
    {
        fprintf( stderr, "ERROR: Firmware Download Failed\n"\
                         "Failure Code : %lu\n", resultCode );

        /* Deregister device state change and firmware download completion
         * callback.
         */
        SetFwDldCompletionCbk( NULL );
        SetDeviceStateChangeCbk( NULL );
        exit(0);
    }

    /* Keep displaying "." until fimrware downloads complete */
    fprintf( stderr, "Downloading Firmware. . .");
    fwDwlDone = FALSE;
    while( !fwDwlDone )
    {
        fprintf( stderr, " .");
        sleep(1);
    }

    /* Intialize current running image structure */
    memset( (void *)&CurrImgList, 0, sizeof( CurrImgList ) );
    memset( (void *)&currImgInfo, 0, sizeof( currImgInfo ) );

    CurrImgList.numEntries = numEntries;
    CurrImgList.pCurrImgInfo = currImgInfo;

    resultCode = SLQSSwiGetFirmwareCurr( &CurrImgList );
    if( eQCWWAN_ERR_NONE != resultCode)
    {
        fprintf( stderr, "ERROR: Failed to get firmware details after download\n"\
                         "Failure Code: %ld\n", resultCode );
        exit(0);
    }

    /* Check if the firmware download is success */
    /* Compare PRI, package, carrier and app version strings */
    if( FALSE == skipPRIChk )
    {
        if( 0 != strncmp( CurrImgList.priver, priVer, sizeof(priVer) ) ||
            0 != strncmp( CurrImgList.pkgver, pkgVer, sizeof(pkgVer) ) ||
            0 != strncmp( CurrImgList.carrier, carrier, sizeof(carrier) ) )
        {
            fprintf( stderr, "ERROR: Firmware Upgrade Failed!!!\n" );
            exit(0);
        }
    }
    if( FALSE == skipMDMChk )
    {
        if(  0 != strncmp( CurrImgList.fwvers, AppVer, sizeof(AppVer) ) )
        {
            fprintf( stderr, "ERROR: Firmware Upgrade Failed!!!\n" );
            exit(0);
        }
    }

    fprintf( stderr, "INFO: Firmware Upgrade successful!!!\n" );
    exit(1);
}

/*
 * Name:     FwDloader_9x15
 *
 * Purpose:  Download firmware on a 9x15 based device
 *
 * Return:   None
 *
 * Notes:    none
 */
void FwDloader_9x15( CHAR *pImagePath )
{
    ULONG              resultCode = 0;
    CurrentImgList     CurrImgList;
    CurrImageInfo      currImgInfo[5];
    BYTE               numEntries  = 5;

    memset( (void *)&CurrImgList, 0, sizeof( CurrImgList ) );
    memset( (void *)&currImgInfo, 0, sizeof( currImgInfo ) );
    CurrImgList.pCurrImgInfo = currImgInfo;
    CurrImgList.numEntries   = numEntries;

    /* There are 2 possible scenario's determined by calling SLQSSwiGetFwCurr
     * 1) Device does not support Gobi IM - In this case use same procedure as
     *    FwDloader_9x00. In this case, we need only one file( spkg format).
     * 2) Device supports GIM but data returned is blank. Use normal procedure. */
    resultCode = SLQSSwiGetFirmwareCurr( &CurrImgList );
    if( eQCWWAN_ERR_NONE != resultCode)
    {
        /* Assume that device is in non GIM Mode. Device issues can be detected
         * on subsequent SLQS API calls */
        FwDloader_9x15_NoGIM( pImagePath );
    }
    else
    {
        /* Device supports GIM. We do not need the firmware details as of now. */
        FwDloader_9x15_GIM( pImagePath );
    }
}
