/*
 *****************************************************************************
 *                                                                           *
 *                 IMPINJ CONFIDENTIAL AND PROPRIETARY                       *
 *                                                                           *
 * This source code is the sole property of Impinj, Inc.  Reproduction or    *
 * utilization of this source code in whole or in part is forbidden without  *
 * the prior written consent of Impinj, Inc.                                 *
 *                                                                           *
 * (c) Copyright Impinj, Inc. 2009. All rights reserved.                     *
 *                                                                           *
 *****************************************************************************
 */

/*
 *****************************************************************************
 *
 * $Id: profile_update.c 66452 2010-12-22 20:12:49Z dshaheen $
 *
 * Description:
 *    This application is used to read a profile.xml and reconfigure a
 *    a default link profile (profiles 0-3) or a custom profile (profiles 4-5).
 *    An option is also provided to load a custom profile to its respective
 *    OEMCFG space for non-volatile storage.  The application parses and loads
 *    the xml file into memory, then writes the specified MAC configuration
 *    parameters and transceiver link profile registers (or the repsective
 *    OEMCFG space if selected), and finally in the case of non-OEMCFG
 *    loading, selects the profile causing the link profile settings to be
 *    transferred to the transceiver device.
 *
 *    Note that the values loaded are volatile, and will be lost on the next
 *    MAC reset, unless a custom profile is specified and the option to
 *    commit to OEMCFG is selected.
 *
 *****************************************************************************
 */


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

#ifdef WIN32
#include <windows.h>
#endif //WIN32

#ifndef UNDER_CE

    #include <errno.h>
    #include <fcntl.h>

    #include <sys/types.h>
    #include <sys/stat.h>
  #ifdef WIN32
    #include <io.h>
	#define strcasecmp	    _stricmp
  #endif //WIN32
#else
    static int errno;
	#define strcasecmp	    _stricmp
#endif /* !UNDER_CE */


#define FOPEN_FLAGS     "rb"

#include "rfid_types.h"
#include "rfid_library.h"
#include "sample_list.h"
#include "oemcfg.h"

#ifdef UNDER_CE

/* CE specific stat func to retrieve file size */

struct stat
{
    long    st_size;
};


int stat( const char * filename, struct stat * s )
{
    WIN32_FIND_DATA data;
    HANDLE          handle;

    wchar_t         wpath[MAX_PATH + 1];

    /* Since CE is strictly Unicode, we'll use it for both CE and XP.  So,
       let's convert the file name                                            */
    mbstowcs(wpath, filename, MAX_PATH);
    wpath[MAX_PATH] = L'\0';

    handle =  FindFirstFile( ( LPCTSTR ) wpath, & data );

    if ( INVALID_HANDLE_VALUE == handle )
    {
        errno = GetLastError( );
        return -1;
    }

    /* warning - no gigabyte files allowed */

    s->st_size = data.nFileSizeLow;

    ( void ) FindClose( handle );

    return 0;
}

#endif


#define NUM_DEFAULT_XCVR_PROFILES   4
#define NUM_CUSTOM_XCVR_PROFILES	2
#define NUM_XCVR_PROFILES           (NUM_DEFAULT_XCVR_PROFILES + NUM_CUSTOM_XCVR_PROFILES)

typedef struct _LPA
{
	INT32U header;
	INT32U mac;
	INT32U lastValidMac;
	INT32U xcvr;
	INT32U lastValidXcvr;
}_LinkProfAddr;


const _LinkProfAddr OemProfileAddr[NUM_CUSTOM_XCVR_PROFILES] =
{
	// Profiles 4 and 5 :	{Header, Mac Vregs, LastMac reg ,Tilden regs, LastTildenReg}
    //                      Last address values (-1) reflect address of required table terminator.
	{OEMCFGADDR_CUSTOM_LPROF0_HEADER_1,
        OEMCFGADDR_CUSTOM_LPROF0_MAC,
        OEMCFGADDR_CUSTOM_LPROF0_MAC + OEMCFG_LPROF_VREG_SIZE - 1,
		OEMCFGADDR_CUSTOM_LPROF0_INDY,
        OEMCFGADDR_CUSTOM_LPROF0_INDY + OEMCFG_LPROF_XCVR_SIZE - 1},

	{OEMCFGADDR_CUSTOM_LPROF1_HEADER_1,
        OEMCFGADDR_CUSTOM_LPROF1_MAC,
        OEMCFGADDR_CUSTOM_LPROF1_MAC + OEMCFG_LPROF_VREG_SIZE - 1,
		OEMCFGADDR_CUSTOM_LPROF1_INDY,
        OEMCFGADDR_CUSTOM_LPROF1_INDY + OEMCFG_LPROF_XCVR_SIZE - 1},

};


/**
    Simple help triggered by running with a -h or
    invalid program arguments.
**/

void print_help(char *prog_name )
{
    fprintf( stderr, "\nUsage: %s [OPTIONAL ARGS] -p [PROFILE] -f [FILENAME]\n\n", prog_name );

    fprintf( stderr, "If no argument is given, report current active profile ID.\n" );
    fprintf( stderr, "If only -p [PROFILE] is given, active specified profile.\n" );
    fprintf( stderr, "If only -f [FILENAME] is given, backup current active profile to 'backup-FILENAME'.\n" );
    fprintf( stderr, "If both -p and -f are given, retrieve register settings specified in 'FILENAME'\n" );
    fprintf( stderr, "   and apply to the target reader link profile.\n\n" );

    fprintf( stderr, "Example: %s -p 2 -f example.xml \n\n", prog_name  );

    fprintf( stderr, "Optional arguments to specify profile ID / new profile settings:\n" );
    fprintf( stderr, "  -p        PROFILE to use or modify (when -f is specified), valid range 0 - 5 inclusive\n" );
    fprintf( stderr, "  -f        FILENAME of file specifying new profile settings\n" );
    fprintf( stderr, "            The file is in an XML-based format:\n"
                     "               <mac>\n"
                     "                   <reg>\n"
                     "                      <loc>mac register address</loc>\n"
                     "                      <val>mac register value</val>\n"
                     "                   </reg>\n"
                     "                   ...\n"
                     "               </mac>\n"
                     "               <xcvr>\n"
                     "                   <reg>\n"
                     "                      <loc>transceiver register address</loc>\n"
                     "                      <val>transceiver register value</val>\n"
                     "                   </reg>\n"
                     "                   ...\n"
                     "               </xcvr>\n"
                     "             All other valid xml tags are ignored.\n\n");

    fprintf( stderr, "Optional arguments:\n" );
    fprintf( stderr, "  -oem      VER.STEP Optional argument to commit profile to custom\n" );
    fprintf( stderr, "              OEMCFG profile with specified device version (VER) and\n");
    fprintf( stderr, "              and device stepping value (STEP) in the OEMCFG profile\n");
    fprintf( stderr, "              header.  If -oem is specified, the profile selection must\n" );
    fprintf( stderr, "              be 4-5 inclusive\n" );
    fprintf( stderr, "  -s        SERIAL number of the target radio, if not specified\n" );
    fprintf( stderr, "              then first radio discovered is considered the target\n" );
}

RFID_RADIO_HANDLE open_target_radio( const char * serial_num )
{
    // top is cut and paste from rfid lib doc

    RFID_STATUS         status;
    RFID_RADIO_HANDLE   handle;

    RFID_RADIO_ENUM   * pEnum =
        (RFID_RADIO_ENUM *) malloc(sizeof(RFID_RADIO_ENUM));

    RFID_RADIO_INFO * targetRadio;

    RFID_RADIO_INFO * pInfo;

    INT32U index;


    if (NULL == pEnum)
    {
        fprintf( stderr, "Memory allocation error : aborting\n" );
        exit( -1 );
    }

    pEnum->length      = sizeof(RFID_RADIO_ENUM);
    pEnum->totalLength = sizeof(RFID_RADIO_ENUM);

    /* Keep trying to enumerate until successful or get an */
    /* error besides buffer too small.                     */

    while (RFID_ERROR_BUFFER_TOO_SMALL ==
        (status = RFID_RetrieveAttachedRadiosList(pEnum, 0)))
    {

        /* Allocate a buffer large enough for the radio    */
        /* enumeration buffer.                             */

        RFID_RADIO_ENUM * pNewEnum =
            (RFID_RADIO_ENUM *) realloc(pEnum,
            pEnum->totalLength);
        if (NULL == pNewEnum)
        {
            free( pEnum );
            fprintf( stderr, "Memory allocation error : aborting\n" );
            exit( -1 );
        }

        pEnum = pNewEnum;
    }

    /* Ensure that the enumeration succeeded               */

    if (RFID_STATUS_OK != status)
    {
        free( pEnum );
        fprintf( stderr, "Found multiple radios with matching serial number : aborting\n" );
        exit( -1 );
    }

    if (!pEnum->countRadios)
    {
        free( pEnum );
        fprintf(stderr, "ERROR: No radios attached to the system\n");
        exit( -1 );
    }

    /* Look for target radio and open if it exists...      */

    targetRadio = ( RFID_RADIO_INFO * ) 0;

    for ( index = 0; index < pEnum->countRadios; ++index)
    {
        pInfo = pEnum->ppRadioInfo[index];

        /* no serial num - grab 1st radio & exit */

        if ( ( char * ) 0 == serial_num )
        {
            targetRadio = pInfo;

            break;
        }

        if ( 0 == strncmp( serial_num, ( const char * ) pInfo->pUniqueId, pInfo->idLength ) )
        {
            if ( ( RFID_RADIO_INFO * ) 0 != targetRadio )
            {
                free( pEnum );
                fprintf( stderr, "Found multiple radios with matching serial number : aborting\n" );
                exit( -1 );
            }

            targetRadio = pInfo;
        }
    }

    if ( ( RFID_RADIO_INFO * ) 0 == targetRadio )
    {
        free( pEnum );
        fprintf( stderr, "Radio serial number '%s' : not found\n", serial_num );
        exit( -1 );
    }

    status = RFID_RadioOpen( targetRadio->cookie, & handle, 0 );

    if (RFID_STATUS_OK != status)
    {
        free( pEnum );
        fprintf( stderr, "Error opening target radio : aborting\n" );
        exit( -1 );
    }

    return handle;
}




/**
    Read the entire file named by filename into the buffer
    'destination' - resizing destination as necessary.
**/

int read_file_to_cptr( const char * filename, char ** destination )
{
    int             ret_val;
    FILE *          handle;
    struct stat     file_stat;
    int             op_result;
    char *          mem_ptr;
    int             file_offset;


    op_result = stat( filename, & file_stat );

    if ( 0 != op_result )
    {
        ret_val = -1;
        errno   = op_result;

        return ret_val;
    }

    if ( NULL == ( handle = fopen( filename, FOPEN_FLAGS ) ) )
    {
        ret_val = -1;
    }
    else
    {
        mem_ptr = ( char * ) realloc( * destination, ( size_t ) file_stat.st_size + 1 );

        if ( ( void * ) 0 == mem_ptr )
        {
            ret_val = -1;
        }
        else
        {
            memset( mem_ptr, 0, file_stat.st_size + 1 );

            * destination = mem_ptr;

            file_offset = 0;

            while ( !feof( handle ) && file_offset != file_stat.st_size )
            {
                op_result = (int)fread( ( char * ) mem_ptr + file_offset, 1, ( file_stat.st_size - file_offset ), handle );

                if ( 0 == op_result && ferror( handle ) )
                {
                    op_result = -1;
                    break;
                }

                file_offset += op_result;
            }

            ret_val = -1 == op_result ? op_result : 0;
        }

        ( void ) fclose( handle );
    }

    return ret_val;
}





/**
    Strip leading and trailing whitespace from the input
    string - spaces, tabs, cr and lf are considered ws.

    Destructive / modifies in_str parameter!
**/

char * strip_ws( char * in_str )
{
    char * str_end;

    size_t l_ws = strspn( in_str, " \t\r\n" );

    in_str = in_str + l_ws;

    for ( str_end = in_str + strlen( in_str ) - 1 ; str_end >= in_str ; -- str_end )
    {
        if ( ' ' == * str_end || '\t' == * str_end || '\r' == * str_end || '\n' == * str_end )
        {
            continue;
        }

        * ( str_end + 1 ) = '\0';

        break;
    }

    return in_str;
}



/**
    Chunk types and chunks are used to hold simple 'node'
    or 'element' information...

**/


typedef enum CHUNK_TYPE
{
    element_begin,  /* start tag         */
    element_data,   /* data between tags */
    element_end     /* end tag           */
}
chunk_type;


typedef struct CHUNK
{
    chunk_type   type;
    char       * value;
}
chunk;


LIST_HANDLE list = INVALID_LIST_HANDLE;


void store_chunk( LIST_HANDLE list, chunk_type type, char * value )
{
    chunk * c = ( chunk * ) malloc( sizeof( chunk ) );

    if ( ( chunk * ) 0 == c )
    {
        fprintf( stderr, "Memory allocation error while parsing file\n" );

        exit( -1 );
    }

    c->type  = type;
    c->value = value;

    ListAddEnd( list, c );
}


/**
    Break input string ( xml ) into chunks with chunks defined as
    the contained start tags, end tags and tag data. Return
    results in a list or null ( invalid handle ) on failure.

    Does NOT handle attributes etc. !

    Destructive to in_str!
**/

LIST_HANDLE parse_chunks( char * in_str )
{
    int o_brack_on = 0;

    int d_quote_on = 0;
    int s_quote_on = 0;

    char last_char = ' ';

    char * tag_start = ( char * ) 0;
    char * dat_start = in_str;

    int line_cnt = 1;

    LIST_HANDLE list = INVALID_LIST_HANDLE;

    if ( NULL == ( list = ListCreate( ) ) )
    {
        return NULL;
    }

    for ( ; '\0' != ( * in_str ) ; last_char = * in_str, ++ in_str )
    {
        switch ( * in_str )
        {
            case '\r' :
                {
                    ++ line_cnt;
                }
                break;

            case '\n' :
                {
                    if ( '\r' != last_char )
                    {
                        ++ line_cnt;
                    }
                }
                break;

            case '<'  : // eo data, so tag
                {
                    if ( 0 == ( d_quote_on | s_quote_on ) )
                    {
                        if ( 0 == o_brack_on )
                        {
                            if ( dat_start != in_str )
                            {
                                * in_str = '\0';

                                if ( 0 != strlen( ( dat_start = strip_ws( dat_start ) ) ) )
                                {
                                    store_chunk( list, element_data, dat_start );

                                    // fprintf( stderr, "Line %i Found data : \"%s\"\n", line_cnt, dat_start );
                                }

                                dat_start = ( char * ) 0;
                            }

                            tag_start = in_str + 1;

                            ++ o_brack_on;
                        }
                        else
                        {
                            // err - 2x open bracket

                            fprintf( stderr, "Line %i Parsing Error: multiple open brackets\n", line_cnt );

                            exit( -1 );
                        }
                    }
                }
                break;

            case '>'  : // eo tag, so data
                {
                    if ( 0 == ( d_quote_on | s_quote_on ) )
                    {
                        if ( 0 != o_brack_on )
                        {
                            * in_str = '\0';

                            if ( tag_start != in_str )
                            {
                                tag_start += strspn( tag_start, " \t\r\n" );

                                if ( '/' != * tag_start )
                                {
                                    if ( 0 != strlen( ( tag_start = strip_ws( tag_start ) ) ) )
                                    {
                                        store_chunk( list, element_begin, tag_start );

                                        // fprintf( stderr, "Line %i Found start element : \"%s\"\n", line_cnt, tag_start );
                                    }
                                    else
                                    {
                                        fprintf( stderr, "Line %i Parsing Error : start element with no enclosed name\n", line_cnt );

                                        exit( -1 );
                                    }
                                }
                                else
                                {
                                    ++ tag_start;

                                    if ( 0 != strlen( ( tag_start = strip_ws( tag_start ) ) ) )
                                    {
                                        store_chunk( list, element_end, tag_start );

                                        // fprintf( stderr, "Line %i Found end element : \"%s\"\n", line_cnt, tag_start );
                                    }
                                    else
                                    {
                                        fprintf( stderr, "Line %i Parsing Error : end element with no enclosed name\n", line_cnt );

                                        exit( -1 );
                                    }
                                }
                            }
                            else
                            {
                                fprintf( stderr, "Line %i Parsing Error : brackets with no enclosed name\n", line_cnt );

                                exit( -1 );
                            }

                            -- o_brack_on;

                            tag_start = ( char * ) 0;

                            dat_start = in_str + 1;
                        }
                        else
                        {
                            // err - close with no open bracket

                            fprintf( stderr, "Line %i Parsing Error: unpaired close bracket\n", line_cnt );

                            exit( -1 );
                        }
                    }
                }
                break;

            case '\'' : // so, eo squote str or embedded squote
                {
                    if ( '\\' != last_char )
                    {
                        if ( 0 == s_quote_on )
                        {
                            s_quote_on  = ( 1 + d_quote_on );
                        }
                        else
                        {
                            s_quote_on -= ( 1 + d_quote_on );

                            if ( 0 > s_quote_on )
                            {
                                fprintf( stderr, "Line %i Parsing Error: unterminated single quote character found", line_cnt );

                                exit( -1 );
                            }
                        }
                    }
                }
                break;

            case '"' : // so, eo dquote str or embedded dquote
                {
                    if ( '\\' != last_char )
                    {
                        if ( 0 == d_quote_on )
                        {
                            d_quote_on  = ( 1 + s_quote_on );
                        }
                        else
                        {
                            d_quote_on -= ( 1 + s_quote_on );

                            if ( 0 > d_quote_on )
                            {
                                fprintf( stderr, "Line %i Parsing Error: unterminated double quote character found", line_cnt );

                                exit( -1 );
                            }
                        }
                    }
                }
                break;

        };
    }

    return list;
}





// Just a strtol wrapper that determines proper base to use
// in conversion via start chars...

unsigned int convert_numeric_string( char * input )
{
    long result = 0;

    if ( 2 <= strlen( input ) && '0' == input[ 0 ] )
    {
        if ( 'x' == input[ 1 ] || 'X' == input[ 1 ] )
        {
            result = strtol( input, ( char ** ) 0, 16 );
        }
        else
        {
            // err - we don't do octal
        }
    }
    else
    {
        result = strtol( input, ( char ** ) 0, 10 );
    }

    if ( 0 > result )
    {
        fprintf( stderr, "Invalid value numeric value found : %s\n", input );

        exit( -1 );
    }

    return( unsigned int ) result;
}


/**
    To hold converted data - not necessary...
**/

typedef struct REG_STRUCT
{
    unsigned int loc;
    unsigned int val;
}
reg_struct;

#define BASE_RFTC_PROF_REG  0x0B62
#define LAST_RFTC_PROF_REG  0x0B77

enum TARGET {UNKNOWN, MAC, XCVR};

RFID_STATUS execute_ref_cmds( RFID_RADIO_HANDLE handle, INT32U profile, LIST_HANDLE list,
                              int bOemSelect, int version, int step )
{
    RFID_STATUS         status = RFID_STATUS_OK;
    reg_struct          data;
    INT32U              headerAddress_inOEM = 0;
	INT32U              macAddress_inOEM = 0;	// init oem mac address
	INT32U              xcvrAddress_inOEM = 0;	// init oem xcvr address
	INT32U              lastMacAddress_inOEM = 0;
	INT32U              lastXcvrAddress_inOEM = 0;
	INT32U              macAddressBase_inOEM = 0;
  	LINKPROFILE_HEADER0 head0;
	LINKPROFILE_HEADER1 head1;
    chunk *             c;


    int         target           = UNKNOWN;
    int	        vregCount        = 0;
    int         xcvrCount        = 0;
    INT32U      numWordsToWrite  = 1;
    INT32U      numWordsToRead   = 1;
    INT32U      dummyReadWord    = 0;
    char *      loc              = ( char * ) 0;
    char *      val              = ( char * ) 0;
    INT32U	    RegionTerminator = 0xFFFFFFFF;		// terminates vreg and xcvr lists



    /* if we are not updating OEM, setup bank select for selected profile */
    if (0 == bOemSelect)
    {
        /* Set the mac bank to the appropriate profile # */
        status = RFID_MacWriteRegister
                 (
                     handle,
                     ( INT16U ) 0xB61, /* bank sel reg */
                     ( INT32U ) profile
                     );

        if ( RFID_STATUS_OK != status )
        {
            printf("%i\n", status );
            return status;
        }
    }
    else
    {
        // profile was already validated to be custom (4-5) so we can safely subtract
        // constant to properly index the array
        //
        headerAddress_inOEM    = OemProfileAddr[profile - NUM_DEFAULT_XCVR_PROFILES].header;
        macAddress_inOEM       = OemProfileAddr[profile - NUM_DEFAULT_XCVR_PROFILES].mac;
        lastMacAddress_inOEM   = OemProfileAddr[profile - NUM_DEFAULT_XCVR_PROFILES].lastValidMac;
        xcvrAddress_inOEM      = OemProfileAddr[profile - NUM_DEFAULT_XCVR_PROFILES].xcvr;
        lastXcvrAddress_inOEM  = OemProfileAddr[profile - NUM_DEFAULT_XCVR_PROFILES].lastValidXcvr;

        macAddressBase_inOEM   = macAddress_inOEM;
    }


    while ( ! ListEmpty( list ) )
    {
        switch ( ( c = ListRemoveFront( list ) )->type )
        {
            case element_begin:
                {
                    if ( 0 == strcasecmp( "mac", c->value ) )
                    {
                        target = MAC;
                    }
                    else if ( 0 == strcasecmp( "xcvr", c->value ) )
                    {
                        target = XCVR;
                    }
                    else if ( 0 == strcasecmp( "reg", c->value ) )
                    {
                        if ( UNKNOWN == target )
                        {
                            // err - unknown target type
                        }

                        loc = ( char * ) 0;
                        val = ( char * ) 0;
                    }
                    else if ( 0 == strcasecmp( "loc", c->value ) )
                    {
                        if ( ( char * ) 0 != loc )
                        {
                            fprintf( stderr, "Found more than one <loc> tag within <reg> block : aborting\n" );
                            exit( -1 );
                        }

                        if ( NULL != ( c = ListRemoveFront( list ) ) || element_data != c->type )
                        {
                            // err - loc start tag but no data
                        }

                        loc = c->value;
                    }
                    else if ( 0 == strcasecmp( "val", c->value ) )
                    {
                        if ( ( char * ) 0 != val )
                        {
                            fprintf( stderr, "Found more than one <val> tag within <reg> block : aborting\n" );
                            exit( -1 );
                        }

                        if ( NULL != ( c = ListRemoveFront( list ) ) || element_data != c->type )
                        {
                            // err - val start tag but no data
                        }

                        val = c->value;
                    }
                }
                break;

            case element_end:
                {
                    if ( 0 == strcasecmp( "mac", c->value ) )
                    {
                        if ( MAC != target )
                        {
                            fprintf( stderr, "Expected </mac> tag but found %s : aborting\n", c->value );
                            exit( -1 );
                        }

                        target = UNKNOWN;
                    }
                    else if ( 0 == strcasecmp( "xcvr", c->value ) )
                    {
                        if ( XCVR != target )
                        {
                            fprintf( stderr, "Expected </xcvr> tag but found %s : aborting\n", c->value );
                            exit( -1 );
                        }

                        target = UNKNOWN;
                    }
                    else if ( 0 == strcasecmp( "reg", c->value ) )
                    {
                        if ( ( char * ) 0 == loc )
                        {
                            fprintf( stderr, "No <loc> tag found within <reg> </reg> block : aborting\n" );
                            exit( -1 );
                        }

                        if ( ( char * ) 0 == val )
                        {
                            fprintf( stderr, "No <val> tag found within <reg> </reg> block : aborting\n" );
                            exit( -1 );
                        }

                        data.loc = convert_numeric_string( loc );
                        data.val = convert_numeric_string( val );

                        if ( MAC == target )
                        {
                            // validate the address is in the MAC_RFTC_PROF_* range of banked registers
                            if ((data.loc < BASE_RFTC_PROF_REG) || (data.loc > LAST_RFTC_PROF_REG ))
                            {
                                fprintf( stderr, "Invalid mac profile register address specified (0x%04X): aborting\n", data.loc );
                                exit( -1 );
                            }

                            if (1 == bOemSelect)
                            {
                                if (macAddress_inOEM < lastMacAddress_inOEM)
                                {
                                    // we have room in the OEMCFG, but for OEM VREGS we don't store the address
                                    // so we need to make sure they are written in the correct MAC REG address
                                    // sequence
                                    if (data.loc == (macAddress_inOEM - macAddressBase_inOEM + BASE_RFTC_PROF_REG))
                                    {
                                        fprintf( stdout, "Set mac reg : 0x%x, to val : 0x%x at OEMCFG addr 0x%x\n",
                                                 data.loc, data.val, macAddress_inOEM);
                                        status = RFID_MacWriteOemData( handle, macAddress_inOEM++, &numWordsToWrite, (INT32U *)&(data.val));
                                        if ( RFID_STATUS_OK != status )
                                            return status;
                                        vregCount++;
                                    }
                                    else
                                    {
                                        fprintf( stderr, "MAC reg address 0x%04X is out of sequence for OEMCFG storage: aborting\n", data.loc );
                                        exit( -1 );
                                    }
                                }
                                else
                                {
                                    fprintf( stderr, "Exceeded available OEMCFG storage for mac registers : aborting\n" );
                                    exit( -1 );
                                }
                            }
                            else
                            {
                                fprintf( stdout, "Setting mac reg loc : 0x%x, val : 0x%x\n", data.loc, data.val );
                                status = RFID_MacWriteRegister(
                                             handle,
                                             ( INT16U ) data.loc,
                                             ( INT32U ) data.val);
                                if ( RFID_STATUS_OK != status )
                                {
                                    return status;
                                }
                            }
                        }
                        else if ( XCVR == target )
                        {
                            if (1 == bOemSelect)
                            {
                                if (xcvrAddress_inOEM < lastXcvrAddress_inOEM)
                                {
                                	INT32U	temp;
                                    fprintf( stdout, "Set xcvr reg: 0x%x with 0x%x at OEMCFG addr 0x%x\n",
                                               data.loc, data.val, xcvrAddress_inOEM);
                                    temp = (data.loc & 0xffff) << 16 | (data.val & 0xffff);	// pack entry
                                    status = RFID_MacWriteOemData( handle, xcvrAddress_inOEM++, &numWordsToWrite, &temp);
                                    if ( RFID_STATUS_OK != status )
                                        return status;
                                    xcvrCount++;
                                }
                                else
                                {
                                    fprintf( stderr, "Exceeded available OEMCFG storage for xcvr registers : aborting\n" );
                                    exit( -1 );
                                }
                            }
                            else
                            {
                                fprintf( stdout, "Setting xcvr reg loc : 0x%x, val : 0x%x\n", data.loc, data.val );
                                status = RFID_RadioWriteLinkProfileRegister
                                         (
                                             handle,
                                             profile,
                                             ( INT16U ) data.loc,
                                             ( INT16U ) data.val
                                             );

                                if ( RFID_STATUS_OK != status )
                                {
                                    return status;
                                }
                            }
                        }
                    }
                    // ignoring all other end element tags
                }
                break;
        };
    }

    if (1 == bOemSelect)
    {
    	fprintf(stderr, "%d VREGS written to OEMCFG\n", vregCount);
    	fprintf(stderr, "%d XCVR entries written to OEMCFG\n", xcvrCount);

    	// Load LINK PROFILE header
    	head0.valid_flag = 1;
    	head0.vreg_size = vregCount;
    	head0.xcvr_size = xcvrCount;
    	head1.tilden_version = version;
    	head1.tilden_step = step;
    	head1.rfu = 0;

    	status = RFID_MacWriteOemData( handle, headerAddress_inOEM, &numWordsToWrite, (INT32U *)&head0);
    	if ( RFID_STATUS_OK != status )
        {
    		fprintf(stderr, "Error writing header address 1: exiting\n");
            return status;
        }

    	status = RFID_MacWriteOemData( handle, headerAddress_inOEM + 1, &numWordsToWrite, (INT32U *)&head1);
    	if (RFID_STATUS_OK != status )
        {
    		fprintf(stderr, "Error writing header address 2: exiting\n");
            return status;
        }

    	status = RFID_MacWriteOemData( handle, macAddress_inOEM, &numWordsToWrite,&RegionTerminator);
    	if ( RFID_STATUS_OK != status )
        {
    		fprintf(stderr, "Error writing VREGS terminator: exiting\n");
            return status;
        }

    	status = RFID_MacWriteOemData( handle, xcvrAddress_inOEM, &numWordsToWrite, &RegionTerminator);
    	if ( RFID_STATUS_OK != status )
        {
    		fprintf(stderr, "Error writing XCVR terminator: exiting\n");
            return status;
        }

        // Do a read, just to make sure the writes get flushed/committed.
    	status = RFID_MacReadOemData(handle, xcvrAddress_inOEM, &numWordsToRead, &dummyReadWord);
    	if ( RFID_STATUS_OK != status )
        {
    		fprintf(stderr, "Error reading XCVR terminator: exiting\n");
            return status;
        }
        fprintf( stdout, "\n!!! OEMCFG profile is updated.  You must reset the MAC in order to use it\n");
    }
    else
    {
        fprintf( stdout, "Setting current link profile : %i\n", profile );
        status = RFID_RadioSetCurrentLinkProfile ( handle, profile );
    	if ( RFID_STATUS_OK != status )
        {
    		fprintf(stderr, "Error setting currnet link profile: exiting\n");
            return status;
        }
    }

    return status;
}

RFID_STATUS print_current_profile(RFID_RADIO_HANDLE handle)
{
    RFID_STATUS status;
    INT32U selector;

    status = RFID_RadioGetCurrentLinkProfile(handle, &selector);
    if ( RFID_STATUS_OK == status)
    	printf("CurrentLinkProfile=%u\n", selector);

    return status;
}

RFID_STATUS set_active_profile(RFID_RADIO_HANDLE handle, INT32U selector)
{
    RFID_STATUS status;

    status = RFID_RadioSetCurrentLinkProfile(handle, selector);
    if ( RFID_STATUS_OK == status)
    	printf("CurrentLinkProfile=%u\n", selector);

    return status;
}

RFID_STATUS backup_current_profile(RFID_RADIO_HANDLE handle, char * filename)
{
    fprintf( stderr, "TODO: If only -f [FILENAME] is given, backup current active profile to 'backup-FILENAME'.\n" );
	return RFID_STATUS_OK;
}

RFID_STATUS update_profile(RFID_RADIO_HANDLE handle, INT32U selector, char * filename,
					int bOemSelect, int version, int step )
{
    char * file_data;

    RFID_STATUS       status;
    LIST_HANDLE       list;

    file_data = ( char * ) 0;

    if ( 0 != read_file_to_cptr( filename, & file_data ) )
    {
        fprintf( stderr, "Error reading from file : %s\n", filename );
        fprintf( stderr, "Errno : %i\n", errno );
        return -1;
    }

    // the xml psuedo-parser
    list = parse_chunks( file_data );

	// attempt to execute commands found...
	status =  execute_ref_cmds( handle, selector, list, bOemSelect, version, step );
	if ( RFID_STATUS_OK != status)
	{
		fprintf( stderr, "Operation Failure - error (%d): your radio is now in an\n", status );
		fprintf( stderr, "  unknown state and should be reset before further use.\n" );
	}

    return status;
}

int main
    (
    int    argc,
    char * argv[ ]
    )
{
	char * serial      = ( char * ) 0;
	INT32S   selector    = -1;
    char * filename    = ( char * ) 0;
    char * programName = argv[0];
    int    bOemSelect  = 0;
    char   verStepStr[80];
    int    version     = 0;
    int    step        = 0;

    int i;

    RFID_VERSION      lib_version;
    RFID_STATUS       status;
    RFID_RADIO_HANDLE handle;


    for ( i = 1 ; i < argc ; ++ i )
    {
        if ( 0 == strcasecmp( argv[ i ], "-s" ) )
        {
            serial = argv[ ++ i ];
        }
        else if ( 0 == strcasecmp( argv[ i ], "-p" ) )
        {
            // need to see if we have enough args to continue
            if (i+1 == argc )
            {
                fprintf( stderr, "\nNo profile number provided with -p argument\n\n" );
                print_help(programName);
                return -1;
            }
            selector = strtol( argv[ ++ i ], ( char ** ) 0, 10 );
            if (selector >= NUM_XCVR_PROFILES)
            {
                fprintf( stderr, "\nInvalid profile specified: %d\n\n", selector );
                print_help(programName);
                return -1;
            }
        }
        else if ( 0 == strcasecmp( argv[ i ], "-oem" ) )
        {
            char * tok;
            // need to see if we have enough args to continue
            if (i+1 == argc )
            {
                fprintf( stderr, "\nNo VER.STEP provided with -oem argument\n\n" );
                print_help(programName);
                return -1;
            }
            strcpy(verStepStr, argv[++ i]);
            tok = strtok(verStepStr, ".");
            if (NULL != tok)
            {
                version = atoi(tok);
            }
            else
            {
                fprintf( stderr, "\nInvalid oem VER.STEP provided (%s)\n\n", argv[i] );
                print_help(programName);
                return -1;
            }
            tok = strtok(NULL, ".");
            if (NULL != tok)
            {
                step = atoi(tok);
            }
            else
            {
                fprintf( stderr, "\nInvalid oem VER.STEP provided (%s)\n\n", argv[i] );
                print_help(programName);
                return -1;
            }
            bOemSelect = 1;
        }
        else if ( 0 == strcasecmp( argv[ i ], "-f" ) )
        {
            filename = argv[ ++ i ];
        }
        else
        {
            fprintf( stderr, "\nUnknown argument found : %s\n\n", argv[ i ] );
            print_help(programName);
            return -1;
        }
    }

    if ((1 == bOemSelect) && (selector < NUM_DEFAULT_XCVR_PROFILES))
    {
        fprintf( stderr, "\nInvalid profile (%u) for -oem option.\n\n", selector );
        print_help(programName);
        return -1;
    }

    status = RFID_Startup( & lib_version, 0 );

    if ( RFID_STATUS_OK != status )
    {
        fprintf( stderr, "Error starting rfid library : %i\n", status );
        return -1;
    }

	// try to open target radio, func aborts internally
	// if error occurs
	handle = open_target_radio( serial );

	//	If no argument is given, report current active profile ID.
	//	If only -p [PROFILE] is given, active specified profile.
	//	If only -f [FILENAME] is given, backup current active profile to 'backup-FILENAME'
	//	If both -p and -f are given, retrieve register settings specified in 'FILENAME',
	//	   and apply to the target reader link profile.
	if ((( char * ) 0 == filename))
		status = (-1 == selector ) ? print_current_profile(handle)
									: set_active_profile(handle, selector);
	else
		status = (-1 == selector ) ? backup_current_profile(handle, filename)
									: update_profile(handle, selector, filename, bOemSelect, version, step);

	if ( RFID_STATUS_OK != status)
	{
		fprintf( stderr, "Operation Failure - error (%d)\n", status );
	}

	RFID_RadioClose(handle);

	( void ) RFID_Shutdown( );

	return(( RFID_STATUS_OK != status) ? -1 : 0);
}
