/*
 *****************************************************************************
 *                                                                           *
 *                 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: oem_tool.c 66452 2010-12-22 20:12:49Z dshaheen $
 * 
 * Description:
 *     The "oem_tool" sample application is a functional demonstration of accessing
 *     the R1000 MAC firmware's "oem configuration" area. The oem configuration area
 *     is a specially formated area of the MAC's non volatile memory (aka flash)
 *     that holds configuration parameters.
 *     
 *     The purpose of this sample is to provide a tool that could be used during
 *     device setup at both manufacturing time and device pre-deployment setup.
 *     This tools is not fully tested, and hence is provided as a sample application
 *     that can be used and modified by the user.
 *
 *****************************************************************************
 */

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <limits.h>//needed for linux build
#include <ctype.h> //needed for linux build
#include "rfid_types.h"
#include "rfid_library.h"
#include "byte_swap.h"
#include "oem_tool.h"
#include "oem_tool_error.h"
#include "oem_tool_help.h"
#include <oemcfg.h>

/* gloabl help table - these are printed from command handlers 
   and from the oemtool_print_help function */
struct help_entry g_help_table[] = 
{
    { OEMTOOL_CMD_READ,       OEMTOOL_CMD_READ_HELP},
    { OEMTOOL_CMD_WRITE,      OEMTOOL_CMD_WRITE_HELP},
    { OEMTOOL_CMD_READRANGE,  OEMTOOL_CMD_READRANGE_HELP},
    { OEMTOOL_CMD_WRITERANGE, OEMTOOL_CMD_WRITERANGE_HELP},
    { OEMTOOL_CMD_TESTMODE,   OEMTOOL_CMD_TESTMODE_HELP},
    { OEMTOOL_CMD_LISTRADIOS, OEMTOOL_CMD_LISTRADIOS_HELP},
    { OEMTOOL_CMD_RADIO,      OEMTOOL_CMD_RADIO_HELP},
};

/* global indicating the number of help entries in g_help_table - for
   iterating through the table */
unsigned int g_num_help_entries = sizeof(g_help_table)/sizeof(struct help_entry);

/* 
   helper struct used when loading text file data into the app
   see oemtool_load_tf() function for logic details
*/
struct oemtool_tf_data
{
    unsigned int entry_type;
    unsigned int entry_addr;
    unsigned int entry_data;
};
static int oemtool_load_tf(struct oemtool_tf_data** tfdata,unsigned int * tflen,
    FILE* inputfile);

/* command line processing routine */
static int oemtool_proc_cmdline(int argc, char** argv);

/* prints help to standard output */
static int oemtool_print_help(void);

/* library and radio init / deinit helpers */
static int oemtool_init_lib_radio();
static int oemtool_deinit_lib_radio();

/* primary command handler funtions - called by oemtool_proc_cmdline */
static int oemtool_read(int argc, char** argv);
static int oemtool_write(int argc, char** argv);
static int oemtool_listradios(int argc, char** argv);

/* utilities */
static int oemtool_ishexstr(char* str, int len);
static int oemtool_hextostr(unsigned int val, char* str);
static int oemtool_tf_iscmt(char* line, int len);

/* global indicating which radio should be used during operation of this app */
unsigned int g_radio_to_use_idx = 0;

/* global that contains the list of enumerated radios
   command handlers use this to gain access and communicate with particular radios
   This struct is setup by oemtool_init_lib_radio() */
RFID_RADIO_ENUM* g_radios = NULL;

/* handle to the currently open radio */
RFID_RADIO_HANDLE g_curr_radio_handle = RFID_INVALID_RADIO_HANDLE;

/* global mode flags variable */
unsigned int g_modeflags = OEMTOOL_MODEFLAG_NONE;

/* global command handler function pointer - setup by oemtool_proc_cmdline */
cmd_handler g_cmd_handler = NULL;

/* helper structs and globals for command line args
   setup by oemtool_proc_cmdline
   used by the command handler functions */
struct oemtool_read_args
{
    unsigned int oemstartaddr;
    unsigned int num2read;
    FILE* output_file;
};
struct oemtool_read_args g_read_args = 
{
    0xFFFFFFFF,
    0,
    NULL,
};
struct oemtool_write_args
{
    unsigned int oemaddr_single;
    unsigned int oemdata_single;
    FILE* input_tf;
};
struct oemtool_write_args g_write_args = 
{
    0xFFFFFFFF,
    0,
    NULL,
};


/*
  main

  Description:
  Application entry point

  Returns:
  OEMTOOLERR_SUCCESS on success
  OEMTOOLERR_xxxx on failure on other status
*/
int main(int argc, char** argv )
{
    int retval = 0;
#if 0 /* DEBUG */
    printf("entry,%s\n",__FUNCTION__);
#endif /* DEBUG */

    /* start by calling the command line processing routine
       - if it is successful it will setup the global command handler
       g_cmd_handler for us */
    retval = oemtool_proc_cmdline(argc, argv);

    if (retval != OEMTOOLERR_SUCCESS)
    {
        goto done_exit;
    }

    /* Initialize the library and open the selected radio */
    retval = oemtool_init_lib_radio();
    if (retval != OEMTOOLERR_SUCCESS)
    {
        goto done_exit;
    }


    /* if we have reached this point successfully we
       need to call the primary command handler */
    if (g_cmd_handler != oemtool_listradios)
    {
        /* indicate which radio is being used */
        printf("fyi,using radio: %d\n", g_radio_to_use_idx);
    }

    /* now call the primary command handler - that
       functions return value will be reported to the user
       as the final app status from this app */
    retval = g_cmd_handler(argc, argv);

    /* now deinitialize the radio */
    oemtool_deinit_lib_radio();

    done_exit:
#if 0 /* DEBUG */
    printf("exit,%s\n",__FUNCTION__);
#endif /* DEBUG */
    return retval;
} /* main */

/*
  oemtool_proc_cmdline
  
  Description:

  Returns:
  OEMTOOLERR_SUCCESS on success
  OEMTOOLERR_xxxx on failure on other status
*/
static int oemtool_proc_cmdline(int argc, char** argv)
{
    int retval = 0;
    int argidx = 1;

#if 0 /* DEBUG */
    printf("entry,%s\n",__FUNCTION__);
#endif /* DEBUG */

    /* if no args were passed, we just print out the help */
    if (argc < 2)
    {
        oemtool_print_help();
        retval = OEMTOOLERR_HELPMSG;
        goto done_exit;
    }

    for (argidx = 1; argidx < argc; argidx++)
    {
        if (strcmp(argv[argidx], OEMTOOL_CMD_READ) == 0)
        {
            if (g_cmd_handler != NULL)
            {
                printf("error,ambiguous command line,%s\n",argv[argidx]);
                printf("error,another primary command already specified\n");
                retval = OEMTOOLERR_CMDLINE;
                goto done_exit;
            }

            /* set the command handler - it will be called after
           all command line args are checked and processed */
            g_cmd_handler = oemtool_read;

            /* increment arg index and get the oem address argument */
            argidx++;
            if (argidx >= argc)
            {
                /* error - not enough args */
                printf("error,--read,requires an address arg in hex: 0xN\n");
                retval = OEMTOOLERR_CMDLINE;
                goto done_exit;
            }

            /* make sure the arg is a hex string */
            if (oemtool_ishexstr(argv[argidx],(int)strlen(argv[argidx])) == 0)
            {
                /* error - not a hex value arg */
                printf("error,%s,requires address arg in hex: 0xN\n",OEMTOOL_CMD_READ);
                retval = OEMTOOLERR_CMDLINE;
                goto done_exit;
            }

            /* ok, safe to convert string string to int */
            g_read_args.oemstartaddr = (unsigned int)strtoul(argv[argidx], NULL, 16);
            if (g_read_args.oemstartaddr == 0xFFFFFFFF)
            {
                printf("error,%s,address arg is too large a number\n",OEMTOOL_CMD_READ);
                retval = OEMTOOLERR_CMDLINE;
                goto done_exit;
            }

            g_read_args.num2read = 1;
        }
        else if (strcmp(argv[argidx], OEMTOOL_CMD_WRITE) == 0)
        {
            if (g_cmd_handler != NULL)
            {
                printf("error,ambiguous command line,%s\n",argv[argidx]);
                printf("error,another primary command already specified\n");
                retval = OEMTOOLERR_CMDLINE;
                goto done_exit;
            }
            /* set the command handler - it will be called after
           all command line args are checked and processed */
            g_cmd_handler = oemtool_write;

            /* increment arg index and get the oem address argument */
            argidx++;
            if (argidx >= argc)
            {
                /* error - not enough args */
                printf("error,%s,requires an address arg in hex: 0xN\n",OEMTOOL_CMD_WRITE);
                retval = OEMTOOLERR_CMDLINE;
                goto done_exit;
            }

            /* make sure the arg is a hex string */
            if (oemtool_ishexstr(argv[argidx],(int)strlen(argv[argidx])) == 0)
            {
                /* error - not a hex value arg */
                printf("error,%s,requires address arg in hex: 0xN\n",OEMTOOL_CMD_WRITE);
                retval = OEMTOOLERR_CMDLINE;
                goto done_exit;
            }

            /* ok, safe to convert string string to int */
            g_write_args.oemaddr_single = (unsigned int)strtoul(argv[argidx], NULL, 16);
            if (g_write_args.oemaddr_single == 0xFFFFFFFF)
            {
                printf("error,%s,address arg is too large a number\n",OEMTOOL_CMD_WRITE);
                retval = OEMTOOLERR_CMDLINE;
                goto done_exit;
            }

            /* increment arg index and get the oem data argument */
            argidx++;
            if (argidx >= argc)
            {
                /* error - not enough args */
                printf("error,%s,requires an data arg in hex: 0xN\n",OEMTOOL_CMD_WRITE);
                retval = OEMTOOLERR_CMDLINE;
                goto done_exit;
            }

            /* make sure the arg is a hex string */
            if (oemtool_ishexstr(argv[argidx],(int)strlen(argv[argidx])) == 0)
            {
                /* error - not a hex value arg */
                printf("error,%s,requires data arg in hex: 0xN\n",OEMTOOL_CMD_WRITE);
                retval = OEMTOOLERR_CMDLINE;
                goto done_exit;
            }

            /* ok, safe to convert string to int */
            g_write_args.oemdata_single = (unsigned int)strtoul(argv[argidx], NULL, 16);
            if (g_write_args.oemdata_single == ULONG_MAX  && errno == ERANGE)
            {
                /* error - not a hex value arg */
                printf("error,%s,couldn't convert data value on command line\n",OEMTOOL_CMD_WRITE);
                retval = OEMTOOLERR_CMDLINE;
                goto done_exit;
            }
        }
        else if (strcmp(argv[argidx], OEMTOOL_CMD_READRANGE) == 0)
        {
            if (g_cmd_handler != NULL)
            {
                printf("error,ambiguous command line,%s\n",argv[argidx]);
                printf("error,another primary command already specified\n");
                retval = OEMTOOLERR_CMDLINE;
                goto done_exit;
            }
            /* set the command handler - it will be called after
           all command line args are checked and processed */
            g_cmd_handler = oemtool_read;

            /* increment arg index and get the oem address argument */
            argidx++;
            if (argidx >= argc)
            {
                /* error - not enough args */
                printf("error,%s,requires an address arg in hex: 0xN\n",OEMTOOL_CMD_READRANGE);
                retval = OEMTOOLERR_CMDLINE;
                goto done_exit;
            }

            /* make sure the arg is a hex string */
            if (oemtool_ishexstr(argv[argidx],(int)strlen(argv[argidx])) == 0)
            {
                /* error - not a hex value arg */
                printf("error,%s,requires address arg in hex: 0xN\n",OEMTOOL_CMD_READRANGE);
                retval = OEMTOOLERR_CMDLINE;
                goto done_exit;
            }

            /* ok, same to convert string string to int */
            g_read_args.oemstartaddr = (unsigned int)strtoul(argv[argidx], NULL, 16);
            if (g_read_args.oemstartaddr == 0xFFFFFFFF)
            {
                printf("error,%s,address arg is too large a number\n",OEMTOOL_CMD_READRANGE);
                retval = OEMTOOLERR_CMDLINE;
                goto done_exit;
            }

            /* increment arg index and get the number of entries to read */
            argidx++;
            if (argidx >= argc)
            {
                /* error - not enough args */
                printf("error,%s,requires an number_to_read arg in hex hex: 0xN\n",
                    OEMTOOL_CMD_READRANGE);
                retval = OEMTOOLERR_CMDLINE;
                goto done_exit;
            }

            /* make sure the arg is a hex string */
            if (oemtool_ishexstr(argv[argidx],(int)strlen(argv[argidx])) == 0)
            {
                /* error - not a hex value arg */
                printf("error,%s,requires the number_to_read arg in hex: 0xN\n",
                    OEMTOOL_CMD_READRANGE);
                retval = OEMTOOLERR_CMDLINE;
                goto done_exit;
            }

            /* ok, same to convert string string to int */
            g_read_args.num2read = (unsigned int)strtoul(argv[argidx], NULL, 16);
            if (g_read_args.num2read == 0xFFFFFFFF)
            {
                printf("error,%s,number_to_read arg is too large a number\n",
                    OEMTOOL_CMD_READRANGE);
                retval = OEMTOOLERR_CMDLINE;
                goto done_exit;
            }
        }
        else if (strcmp(argv[argidx], OEMTOOL_CMD_WRITERANGE) == 0)
        {
            if (g_cmd_handler != NULL)
            {
                printf("error,ambiguous command line,%s\n",argv[argidx]);
                printf("error,another primary command already specified\n");
                retval = OEMTOOLERR_CMDLINE;
                goto done_exit;
            }
            /* set the command handler - it will be called after
           all command line args are checked and processed */
            g_cmd_handler = oemtool_write;

            /* increment arg index and get the input text file name*/
            argidx++;
            if (argidx >= argc)
            {
                /* error - not enough args */
                printf("error,%s,requires an input text file arg\n",OEMTOOL_CMD_WRITERANGE);
                retval = OEMTOOLERR_CMDLINE;
                goto done_exit;
            }

            /* ok got an arg, now try to open the file for output */
            g_write_args.input_tf = fopen(argv[argidx],"r");
            if (g_write_args.input_tf == NULL)
            {
                printf("error,%s,failed to open input text file\n",OEMTOOL_CMD_WRITERANGE);
                retval = OEMTOOLERR_OUTPUTFILE;
                goto done_exit;
            }
        }
        else if (strcmp(argv[argidx], OEMTOOL_CMD_LISTRADIOS) == 0)
        {
            if (g_cmd_handler != NULL)
            {
                printf("error,ambiguous command line,%s\n",argv[argidx]);
                printf("error,another primary command already specified\n");
                retval = OEMTOOLERR_CMDLINE;
                goto done_exit;
            }
            /* set the command handler - it will be called after
           all command line args are checked and processed */
            g_cmd_handler = oemtool_listradios;
        }
        else if (strcmp(argv[argidx], OEMTOOL_CMD_RADIO) == 0)
        {
            unsigned long cnvtmp = 0;

            /* increment arg index to get other parts of this arg */
            argidx++;

            if (g_radio_to_use_idx != 0)
            {
                printf("error,ambiguous command line,radio already specified\n");
                retval = OEMTOOLERR_CMDLINE;
                goto done_exit;
            }

            if (argidx >= argc)
            {
                /* error - not enough args */
                printf("error,%s,requires a radio_number to connect to\n",OEMTOOL_CMD_RADIO);
                retval = OEMTOOLERR_CMDLINE;
                goto done_exit;
            }
            cnvtmp = strtoul(argv[argidx], NULL, 10);
            if ( cnvtmp == 0 && errno != 0)
            {
                printf("error,command line,failed to convert %s to an integer for --radio arg\n",
                    argv[argidx]);
                retval = OEMTOOLERR_CMDLINE;
                goto done_exit;
            }
            /* copy the radio to the global var */
            g_radio_to_use_idx = cnvtmp;
            /* sanity check the radio index specified on the command line */
            if (g_radio_to_use_idx == 0 || g_radio_to_use_idx > 8)
            {
                printf("error,ambiguous command line,radio number must be between 1 and 8\n");
                retval = OEMTOOLERR_CMDLINE;
                goto done_exit;
            }
        }
        else if (strcmp(argv[argidx], OEMTOOL_CMD_TESTMODE) == 0)
        {
            /* set the mode flag to indicate test mode */
            g_modeflags |= OEMTOOL_MODEFLAG_TEST;
        }
        else if (strcmp(argv[argidx], OEMTOOL_CMD_OUTPUTFILE) == 0)
        {
            /* this arg is positional - must come after a --readrange arg */
            if (g_cmd_handler != oemtool_read)
            {
                /* error - not enough args */
                printf("error,%s,can only come after %s\n",
                    OEMTOOL_CMD_OUTPUTFILE, OEMTOOL_CMD_READRANGE);
                retval = OEMTOOLERR_CMDLINE;
                goto done_exit;
            }

            /* increment arg index and get the output file name*/
            argidx++;
            if (argidx >= argc)
            {
                /* error - not enough args */
                printf("error,%s,requires a file name arg\n",OEMTOOL_CMD_OUTPUTFILE);
                retval = OEMTOOLERR_CMDLINE;
                goto done_exit;
            }

            /* ok got an arg, now try to open the file for output */
            g_read_args.output_file = fopen(argv[argidx],"wb");
            if (g_read_args.output_file == NULL)
            {
                printf("error,%s,requires a file name arg\n",OEMTOOL_CMD_OUTPUTFILE);
                retval = OEMTOOLERR_OUTPUTFILE;
                goto done_exit;
            }
        }
        else
        {
            /* error - unexpected argument */
            printf("error, unexpected argument %s\n",argv[argidx]);
            retval = OEMTOOLERR_CMDLINE;
            goto done_exit;
        }
    }

    /* if the user did not specify which radio to use, we default to radio 1 */
    if (g_radio_to_use_idx == 0)
    {
        g_radio_to_use_idx = 1;
    }

    /* if we get this far and have no primary command handler we
       print out an error message */
    if (g_cmd_handler == NULL)
    {
        printf("error,no primary command specified on command line!\n");
        retval = OEMTOOLERR_CMDLINE;
        goto done_exit;
    }

    done_exit:
#if 0 /* DEBUG */
    printf("exit,%s\n",__FUNCTION__);
#endif /* DEBUG */
    return retval;
}

/*
  oemtool_init_lib_radio
  
  Description:
  Helper function - just puts all the logic for initializing
  the RFID library and enumeration currently RFID radio devices
  in one function.

  Returns:
  OEMTOOLERR_SUCCESS on success
  OEMTOOLERR_xxxx on failure on other status
*/
static int oemtool_init_lib_radio()
{
    int retval = OEMTOOLERR_SUCCESS;
    RFID_STATUS status;
#if 0 /* DEBUG */
    printf("entry,%s\n",__FUNCTION__);
#endif /* DEBUG */

    if (g_modeflags & OEMTOOL_MODEFLAG_TEST)
    {
        /* test mode: don't touch library or radio */
        goto done_exit;
    }

    /* call the rfid library startup routine */
    status = RFID_Startup(NULL, 0);
    if (RFID_STATUS_OK != status)
    {
        printf("error,RFID_Startup failed,returned %d\n", status);
        retval = OEMTOOLERR_RFIDLIBERR;
        goto done_exit;
    }

    /* Create an initial structure for enumerating the radios.  We'll adjust  */
    /* it once we know how big to make it.                                    */
    g_radios = (RFID_RADIO_ENUM*) malloc(sizeof(RFID_RADIO_ENUM));
    if (NULL == g_radios)
    {
        printf("error,failed to allocate memory for radio list(1)\n");
        retval = OEMTOOLERR_SYSTEM;
        goto done_exit;
    }
    g_radios->length = g_radios->totalLength  = sizeof(RFID_RADIO_ENUM);

    /* Enumerate the radios */
    while (RFID_ERROR_BUFFER_TOO_SMALL == 
        (status = RFID_RetrieveAttachedRadiosList(g_radios, 0)))
    {
        RFID_RADIO_ENUM* tmpenum = 
            (RFID_RADIO_ENUM *) realloc(g_radios, g_radios->totalLength);
        if (NULL == tmpenum)
        {
            printf("error,failed to allocate memory for radio list(2)\n");
            retval = OEMTOOLERR_SYSTEM;
            goto done_exit;
        }
        g_radios = tmpenum;
    }
    if (RFID_STATUS_OK != status)
    {
        printf("error,RFID_RetrieveAttachedRadiosList returned %d\n",status);
        retval = OEMTOOLERR_RFIDLIBERR;
        goto done_exit;
    }

    /* a radio must be attached */
    if ( g_radios->countRadios == 0)
    {
        printf("error,no rfid radios attached to system!\n");
        retval = OEMTOOLERR_NORADIOS;
        goto done_exit;
    }

    if ((g_radio_to_use_idx-1) >= g_radios->countRadios)
    {
        printf("error,user specified radio (%d) is not attached\n",g_radio_to_use_idx);
        retval = OEMTOOLERR_INVALRADIO;
        goto done_exit;
    }
    /* ok - now open the specified radio */
    status = RFID_RadioOpen(g_radios->ppRadioInfo[(g_radio_to_use_idx-1)]->cookie,
                 &g_curr_radio_handle, 0);
    if (RFID_STATUS_OK != status)
    {
        printf("error,library call RFID_RadioOpen failed,0x%.8x\n", status);
        retval = OEMTOOLERR_RFIDLIBERR;
        goto done_exit;
    }
    printf("fyi,radio open successful\n");


    done_exit:
#if 0 /* DEBUG */
    printf("exit,%s\n",__FUNCTION__);
#endif /* DEBUG */
    return retval;
}

/*
  oemtool_deinit_lib_radio
  
  Description:
  Helper function - just puts all the logic for DE-INIT in one function.

  Returns:
  OEMTOOLERR_SUCCESS on success
  OEMTOOLERR_xxxx on failure on other status
*/
static int oemtool_deinit_lib_radio()
{
    int retval = OEMTOOLERR_SUCCESS;

#if 0 /* DEBUG */
    printf("entry,%s\n",__FUNCTION__);
#endif /* DEBUG */

    if (g_modeflags & OEMTOOL_MODEFLAG_TEST)
    {
        /* test mode: don't touch radio or library*/
        goto done_exit;
    }

    /* first close the currently open radio */
    if (g_curr_radio_handle != RFID_INVALID_RADIO_HANDLE)
    {
        RFID_RadioClose(g_curr_radio_handle);
        g_curr_radio_handle = RFID_INVALID_RADIO_HANDLE;
    }

    /* free allocated mem */
    free(g_radios);

    /* now shutdown the library */
    RFID_Shutdown();

    done_exit:
#if 0 /* DEBUG */
    printf("exit,%s\n",__FUNCTION__);
#endif /* DEBUG */
    return retval;
}

/*
  oemtool_print_help
  
  Description:
  prints help to standard output - from the global help table
  
  Returns:
  always returns OEMTOOLERR_SUCCESS
*/
static int oemtool_print_help(void)
{
    int retval = OEMTOOLERR_SUCCESS;
    int x = 0;

#if 0 /* DEBUG */
    printf("entry,%s\n",__FUNCTION__);
#endif /* DEBUG */

    printf("oem_tool help:\n");
    for ( x = 0; x < (int)g_num_help_entries; x++)
    {
        printf("%s\n%s\n",g_help_table[x].cmdtext,g_help_table[x].helptext);
    }
    printf("\n");

#if 0 /* DEBUG */
    printf("exit,%s\n",__FUNCTION__);
#endif /* DEBUG */
    return retval;
}


/*
  oemtool_read
  
  Description:
  
  Returns:
  OEMTOOLERR_SUCCESS on success
  OEMTOOLERR_xxxx on failure on other status
*/
static int oemtool_read(int argc, char** argv)
{
    int retval = OEMTOOLERR_SUCCESS;
    RFID_STATUS status = RFID_STATUS_OK;
    unsigned int* readdata = NULL;
    unsigned int  hostdata = 0;
    int x = 0;
    unsigned char valstr[5];
    static char line_output_buff[1024];
    INT32U numRead;

    RFID_UNREFERENCED_LOCAL(argc);
    RFID_UNREFERENCED_LOCAL(argv);

#if 0 /* DEBUG */
    printf("entry,%s\n",__FUNCTION__);
#endif /* DEBUG */
    memset(valstr,0x0,5);
    memset(line_output_buff, 0x0, 1024);

    if (g_read_args.num2read == 0 || g_read_args.oemstartaddr == 0xFFFFFFFF)
    {
        printf("error, bogus command line\n");
        retval = OEMTOOLERR_CMDLINE;
        goto done_exit;
    }

    /* allocate memory for the data to be read */
    readdata = calloc(g_read_args.num2read,sizeof(unsigned int));
    if (readdata == NULL)
    {
        printf("error,failed to alloc memory!\n");
        retval = OEMTOOLERR_SYSTEM;
        goto done_exit;
    }

    if (g_modeflags & OEMTOOL_MODEFLAG_TEST)
    {
        /* test mode: put fake test mode data in the array */
        for (x = 0; x < (int)g_read_args.num2read; x++)
        {
            readdata[x] = 0xF00DD00D;
        }
    }
    else
    {
        numRead = g_read_args.num2read;
        /* now we're ready to open the radio and read the oem config area */
        status = RFID_MacReadOemData(g_curr_radio_handle,
                     g_read_args.oemstartaddr,
                     &numRead,
                     readdata);
        if (RFID_STATUS_OK != status)
        {
            printf("error,library call RFID_MacReadOemData error,0x%.8x,numToRead=%d, numRead=%d\n", 
                status, g_read_args.num2read, numRead);
            retval = OEMTOOLERR_RFIDLIBERR;
            goto done_exit;
        }
    }


    /* good - we got the data - now output according to the defined format in oem_tool.doc */
    printf("start,read_output\n");
    if (g_read_args.output_file != NULL)
    {
        fprintf(g_read_args.output_file,
            "# range from 0x%.8X to 0x%.8X, radio: %d\n",
            g_read_args.oemstartaddr,
            g_read_args.oemstartaddr+(g_read_args.num2read-1),
            g_radio_to_use_idx);
    }
    for (x = 0; x < (int)g_read_args.num2read; x++)
    {
        /* 
           NOTE:  Need to convert from MAC format to Host format, because the data returned
                  from RFID_MacReadOemData is in MAC format
        */
        hostdata = MacToHost32(readdata[x]);
        oemtool_hextostr(hostdata,(char *)valstr);
        sprintf(line_output_buff, "single_entry,0x%.8X,0x%.8X #ascii->|%s|\n",
            (g_read_args.oemstartaddr+x),hostdata,valstr);
        if (g_read_args.output_file != NULL)
        {
            fprintf(g_read_args.output_file,line_output_buff);
            printf(".");
        }
        else
        {
            printf(line_output_buff);
        }
    }
    if (g_read_args.output_file != NULL)
    {
        printf("\n");
    }
    printf("end,read_output\n");

    done_exit:
    /* do read cleanup here on args etc.*/
    if (g_read_args.output_file != NULL) fclose(g_read_args.output_file);
    if (readdata != NULL) free(readdata);
#if 0 /* DEBUG */
    printf("exit,%s\n",__FUNCTION__);
#endif /* DEBUG */
    return retval;
}

/*
  oemtool_write
  
  Description:
  
  Returns:
  OEMTOOLERR_SUCCESS on success
  OEMTOOLERR_xxxx on failure on other status
*/
static int oemtool_write(int argc, char** argv)
{
    int retval = OEMTOOLERR_SUCCESS;
    RFID_STATUS status = RFID_STATUS_OK;
    struct oemtool_tf_data* tfdata = NULL;
    unsigned int tflen = 0;
    unsigned int macdata = 0;
    int x = 0;
    INT32U numWrite;

    RFID_UNREFERENCED_LOCAL(argc);
    RFID_UNREFERENCED_LOCAL(argv);

#if 0 /* DEBUG */
    printf("entry,%s\n",__FUNCTION__);
#endif /* DEBUG */

    /* single mode or text file mode ? */
    if (g_write_args.oemaddr_single != 0xFFFFFFFF)
    {
        if (g_modeflags & OEMTOOL_MODEFLAG_TEST)
        {
            /* write single data item */
            printf("fyi,write (test mode),0x%.8X,0x%.8X\n",
                g_write_args.oemaddr_single,
                g_write_args.oemdata_single);
        }
        else
        {
            /* write single data item */
            printf("fyi,write,0x%.8X,0x%.8X\n",
                g_write_args.oemaddr_single,
                g_write_args.oemdata_single);

            /* 
               NOTE:  Need to convert from host format to MAC format, because the data input
                      to RFID_MacWriteOemData is expected in MAC format
            */
            macdata = HostToMac32(g_write_args.oemdata_single);
            numWrite = 1;
            status = RFID_MacWriteOemData(g_curr_radio_handle,
                         g_write_args.oemaddr_single,
                         &numWrite,
                         &macdata);
            if (RFID_STATUS_OK != status)
            {
                printf("error,library call RFID_MacWriteOemData error,0x%.8x\n", status);
                retval = OEMTOOLERR_RFIDLIBERR;
                goto done_exit;
            }
        }
    }
    else
    {
#if 0 /* DEBUG */
        printf("fyi,text file mode\n");
#endif /* DEBUG */

        /* load the data */
        retval = oemtool_load_tf(&tfdata,&tflen,g_write_args.input_tf);
        if (retval != OEMTOOLERR_SUCCESS)
        {
            printf("error,configuration text file contains errors\n");
            goto done_exit;
        }

        /* write it */
        for (x = 0; x < (int)tflen; x++)
        {
            if (g_modeflags & OEMTOOL_MODEFLAG_TEST)
            {
                printf("fyi,write (test mode),0x%.8X,0x%.8X\n",
                    tfdata[x].entry_addr,
                    tfdata[x].entry_data);
            }
            else
            {

                printf("fyi,write,0x%.8X,0x%.8X\n",
                    tfdata[x].entry_addr,
                    tfdata[x].entry_data);

                /* 
                   NOTE:  Need to convert from host format to MAC format, because the data input
                          to RFID_MacWriteOemData is expected in MAC format
                */
                macdata = HostToMac32(tfdata[x].entry_data);
                numWrite = 1;
                status = RFID_MacWriteOemData(g_curr_radio_handle,
                             tfdata[x].entry_addr,
                             &numWrite,
                             &macdata);
                if (RFID_STATUS_OK != status)
                {
                    printf("warning,incomplete write,oem addr,0x%.8X,rfid lib error,0x%.8X\n",
                        tfdata[x].entry_addr,status);
                    retval = OEMTOOLERR_TF_INCMPLWR;
                }
            }
        }
        //printf("\n");
    }

    done_exit:
#if 0 /* DEBUG */
    printf("exit,%s\n",__FUNCTION__);
#endif /* DEBUG */
    /* cleanup for write operations - including args */
    if (g_write_args.input_tf != NULL) fclose(g_write_args.input_tf);
    if (tfdata != NULL) free(tfdata);
    return retval;
}

/*
  oemtool_listradios
  
  Description:
  Lists currently connected radios to standard output

  Returns:
  OEMTOOLERR_SUCCESS on success
  OEMTOOLERR_xxxx on failure on other status
*/
static int oemtool_listradios(int argc, char** argv)
{
    int retval = OEMTOOLERR_SUCCESS;
    unsigned int x = 0;
    int numrads = 0;

    RFID_UNREFERENCED_LOCAL(argc);
    RFID_UNREFERENCED_LOCAL(argv);

#if 0 /* DEBUG */
    printf("entry,%s\n",__FUNCTION__);
#endif /* DEBUG */

    if (g_modeflags & OEMTOOL_MODEFLAG_TEST)
    {
        /* test mode: we fake out 8 radios in test mode */
        numrads = 8;
    }
    else
    {
        numrads = g_radios->countRadios;
    }

    printf("radio_number,unique_id\n");
    for (x = 0; x < (unsigned int) numrads; x++)
    {
        if (g_modeflags & OEMTOOL_MODEFLAG_TEST)
        {
            /* test mode: we fake out 8 radios in test mode */
            printf("%d,test_radio_%d\n",(x+1),(x+1));
        }
        else
        {
            printf("%d,%s\n",(x+1),g_radios->ppRadioInfo[x]->pUniqueId);
        }
    }

#if 0 /* DEBUG */
    printf("exit,%s\n",__FUNCTION__);
#endif /* DEBUG */
    return retval;
}

/*
  oemtool_ishexstr

  Description:
  Utility to convert a 32bit value to printable ascii string
  
  Assumes str arg is a character array with of at least 4 characters in length.

  Returns:
  always returns OEMTOOLERR_SUCCESS
 */
static int oemtool_hextostr(unsigned int val, char* str)
{
    int x = 0;
    str[0] = (unsigned char)(val >> 24);
    str[1] = (unsigned char)(val >> 16);
    str[2] = (unsigned char)(val >> 8);
    str[3] = (unsigned char)(val);

    for (x = 0; x < 4; x++)
    {
        if (!(str[x] >= 0x20 && str[x] <=0x7E))
        {
            str[x] = '.';
        }
    }

    return OEMTOOLERR_SUCCESS;
}

/*
  oemtool_ishexstr

  Description:
  Utility function to validate that the string argument is a hex integer string

  Returns:
  1 - string is an ascii hex string in 0xn... form.
  0 - string is not a proper hex string (according to this function)
 */
static int oemtool_ishexstr(char* str, int len)
{
    int retval = 1;
    int x = 0;
    char c;

    /* check for minimum length and 0x prefix */
    if (len < 3 || strncmp(str,"0x",2) != 0 )
    {
#if 0 /* DEBUG */
        printf("%s (exit 1)\n",__FUNCTION__);
#endif
        retval = 0;
        goto done_exit;
    }

    /* now check remaining characters */
    for (x = 2; x < len; x++)
    {
        c = str[x];
        c = (char) tolower(c);
        if (!((c >= 0x61 && c <= 0x66) || (c >= 0x30 && c <=0x39)) )
        {
#if 0 /* DEBUG */
            printf("%s (exit 2) %c\n",__FUNCTION__,c);
#endif
            retval = 0;
            goto done_exit;
        }
    }

    done_exit:
    return retval;
}


/*
  oemtool_load_tf

  Description:
  Utility function to load text file data
  
  Note about test mode : this function works the same in test mode as it
  does in non-test (i.e. normal) mode.

  Returns:
  On Success, OEMTOOLERR_SUCCESS
  The callers tfdata arg is a valid pointer to an array of oemtool_tf_data structs and
  tflen indicates the numbre of elements

  On Failure:
  OEMTOOLERR_xxx error / status code and *tfdata set to NULL and *tflen set to 0
 */
static int oemtool_load_tf(struct oemtool_tf_data** tfdata,
    unsigned int* tflen,
    FILE* inputfile)
{
    int retval = OEMTOOLERR_SUCCESS;
    unsigned int numelems = 0;
    int pass = 0;
    struct tf_entry_parse
    {
        char* line;
        unsigned int line_len;
        char* type;
        /* this 10 means the code supports a max of 10 fields in a record*/
        char* fields[10]; 
    };
    struct tf_entry_parse tf_ent;
    static char line_input_buff[1024];
    static unsigned int input_buff_len = 1024;
    int curr_line_num = 1;

#if 0 /* DEBUG */
    printf("entry,%s\n",__FUNCTION__);
#endif /* DEBUG */

    tf_ent.line = line_input_buff;
    tf_ent.line_len = input_buff_len;

    /* zero out callers args to put in well known state.*/
    *tfdata = NULL;
    *tflen = 0;

#if 0 /* DEV TEST DUMMY TF DATA*/
    {
        int x = 0;

        *tfdata = calloc(5,sizeof(struct oemtool_tf_data));
        if (tfdata == NULL)
        {
            retval = OEMTOOLERR_SYSTEM;
            goto done_exit;
        }
        *tflen = 5;
        /* now fill in with dummy data */
        for (x = 0; x < 5; x++)
        {
            struct oemtool_tf_data* curr = *tfdata+x;
            curr->entry_type = 0;
            curr->entry_addr = 0x87 + x;
            curr->entry_data = 0xAAf00dAA;
        }
        goto done_exit;
    }
#endif /* DEV TEST DUMMY TF DATA*/

    /* 2 passes though the file:
       1st pass - parse elements, check syntax, determine memory requirements
       2nd pass - load */
    curr_line_num = 0;
    pass = 0;
    while (pass < 2)
    {
        char* lineret = NULL;

        /* read in a line */
        memset(tf_ent.line, 0x0, tf_ent.line_len);

        lineret = fgets(tf_ent.line, tf_ent.line_len, inputfile);
        curr_line_num++;
        if (lineret != NULL)
        {
            /* if first non-white space is a '#' continue with next line */
            if (oemtool_tf_iscmt(tf_ent.line, tf_ent.line_len))
            {
#if 0 /* DEBUG INST */
                printf("fyi, comment on line,%d\n", curr_line_num);
#endif /* DEBUG INST */       
                continue;
            }

            /* extract record type field */
            tf_ent.type = strtok(tf_ent.line, ",");
            if (tf_ent.type == NULL)
            {
                /* ERROR need a record type field */
                printf("error,line,%d,input text file missing record type field\n",
                    curr_line_num);
                retval = OEMTOOLERR_TF_RECSNTX;
                goto done_exit;
            }

            /* check for blank line */
            if (strcmp(tf_ent.type,"\n") == 0 ||
                strcmp(tf_ent.type,"\r") == 0 ||
                strcmp(tf_ent.type,"\r\n") == 0)
            {
#if 0 /* DEBUG */
                printf("fyi,blank line,%d\n",curr_line_num);
#endif /* DEBUG */
                /* next loop */
                continue;
            }

            /* validate record type field is a supported type */
            if (strcmp(tf_ent.type, "single_entry") == 0)
            {
#if 0 /* DEBUG */
                printf("fyi,rec type field,%s\n",tf_ent.type);
#endif /* DEBUG */
                /* single_entry, field 0 is a the address*/
                tf_ent.fields[0] = strtok(NULL, ",");
                if (tf_ent.fields[0] == NULL)
                {
                    /* ERROR need a address field */
                    printf("error,line,%d,input text file missing address field in single_entry record\n",
                        curr_line_num);
                    retval = OEMTOOLERR_TF_RECSNTX;
                    goto done_exit;
                }
                /* non null address field - ensure its a hex string */
                if (oemtool_ishexstr(tf_ent.fields[0],(int)strlen(tf_ent.fields[0])) == 0)
                {
                    /* ERROR need a address field */
                    printf("error,line,%d,input text file,address field not a hex string\n",
                        curr_line_num);
                    retval = OEMTOOLERR_TF_RECSNTX;
                    goto done_exit;
                }
#if 0 /* DEBUG */
                printf("fyi,single_entry rec,address field,%s\n",tf_ent.fields[0]);
#endif /* DEBUG */

                /* single_entry, field 1 is the data*/
                tf_ent.fields[1] = strtok(NULL, ", #\r\n");
                if (tf_ent.fields[1] == NULL)
                {
                    /* ERROR need a record type field */
                    printf("error,line,%d,input text file missing data field in single_entry record\n",
                        curr_line_num);
                    retval = OEMTOOLERR_TF_RECSNTX;
                    goto done_exit;
                }
                /* non null data field - ensure its a hex string */
                if (oemtool_ishexstr(tf_ent.fields[1],(int)strlen(tf_ent.fields[1])) == 0)
                {
                    /* ERROR need a address field */
                    printf("error,line,%d,input text file,data field not a hex string\n",
                        curr_line_num);
                    retval = OEMTOOLERR_TF_RECSNTX;
                    goto done_exit;
                }
            }
            else
            {
                /* ERROR need a record type field */
                printf("error,line,%d,unsupported record type field\n",
                    curr_line_num);
                retval = OEMTOOLERR_TF_RECTYPE;
                goto done_exit;
            }


            /* if we made it here the record record is good 
               do some pass specific processing */
            switch (pass)
            {
                case 0:
#if 0 /* DEBUG */
                    printf("fyi,pass 0,good rec,%s,field[0],%s,field[1],%s\n",
                        tf_ent.type,tf_ent.fields[0],tf_ent.fields[1]);
#endif /* DEBUG */
                    break;

                case 1:
                    {
                        struct oemtool_tf_data* curr_data = (*tfdata + numelems);
                        /* on pass 1 we're checking elements and adding them to the allocted array */

                        /* we only have single_entry types right now - so we just setup
                           the struct without any other checking / logic etc */
                        curr_data->entry_type = 1; /* hard coding to 1 for now - not used yet */
                        curr_data->entry_addr = (unsigned int)strtoul(tf_ent.fields[0], NULL, 16);
                        if (curr_data->entry_addr > OEMCFGADDR_LAST)
                        {
                            printf("warning,line,%d,address might be out of range for older firmware\n",
                                curr_line_num);
                        }
                        curr_data->entry_data = (unsigned int)strtoul(tf_ent.fields[1], NULL, 16);
                        if (curr_data->entry_data == ULONG_MAX && errno == ERANGE)
                        {
                            printf("error,line,%d,data value too big\n",curr_line_num);
                            retval = OEMTOOLERR_TF_RECSNTX;
                            goto done_exit;
                        }
#if 0 /* DEBUG */
                        printf("fyi,pass 1,good rec,%s,line,%d\n",tf_ent.type,curr_line_num);
#endif /* DEBUG */

                    }
                    break;

                default:
                    printf("error,invalid pass number while parsing input file,pass,%d\n",pass);
                    retval = OEMTOOLERR_TF_PARSE;
                    goto done_exit;
            }

            /* increment element counter */
            numelems++;

        }
        else
        {
#if 0 /* DEBUG */
            printf("fyi,got null line on tf,after line,%d\n",curr_line_num);
#endif /* DEBUG */
            /* line was NULL, are we at EOF ? */
            if (feof(inputfile))
            {
#if 0 /* DEBUG */
                printf("fyi,got eof on tf,after line,%d\n",curr_line_num);
#endif /* DEBUG */
                if (pass == 0)
                {
                    /* allocate memory */
                    *tfdata = calloc(numelems,sizeof(struct oemtool_tf_data));
                    if (tfdata == NULL)
                    {
                        printf("fyi,failed to alloc mem for text input file,%d\n",errno);
                        retval = OEMTOOLERR_SYSTEM;
                        goto done_exit;
                    }
                    *tflen = numelems;
                    /* reset element counter - in pass 1 it will be used to index into
                    *tfdata*/
                    numelems = 0;

                    /* seek to start of file */
                    if (fseek(inputfile, 0, SEEK_SET) != 0)
                    {
                        printf("fyi,failed to seek to start of input file,%d\n",errno);
                        retval = OEMTOOLERR_SYSTEM;
                        goto done_exit;
                    }
                }
                /* increment pass number */
                pass++;
                /* reset line number counter*/
                curr_line_num = 0;
#if 0 /* DEBUG */
                printf("fyi,starting tf file pass %d\n",pass);
#endif /* DEBUG */
            } /* if(feof)... */
            else
            {
                /* error */
                printf("error,NULL line but no EOF,line,%d\n",curr_line_num);
                retval = OEMTOOLERR_SYSTEM;
                goto done_exit;
            }

        } /* line null */

    } /* while(pass).. */

    done_exit:
    if (retval != OEMTOOLERR_SUCCESS)
    {
        *tflen = 0;
        if (*tfdata != NULL) free(*tfdata);
        *tfdata=NULL;
    }
    printf("fyi,# of entries found in configuration text file,%d\n",*tflen);
#if 0 /* DEBUG */
    printf("*tflen = %d\n", *tflen);
    printf("exit,%s\n",__FUNCTION__);
#endif /* DEBUG */
    return retval;
}

/*
  oemtool_tf_iscmt

  Utility function to detect if the specified line is a commented out line

  A commented out line is defined as such when the first non whitespace char
  is a '#' character

  Returns 0 if the line is not a commented out line
  Returns 1 if the line IS a commented out line
 */
static int oemtool_tf_iscmt(char* line, int len)
{
    unsigned int retval = 0;
    int gotcmt = 0;
    int x = 0;

    for (x = 0; x < len; x++)
    {
        /* check for null to break early */
        if (line[x] == '\0') break;

        /* check for non-whitespace */
        if (line[x] != '\t' && line[x] != ' ')
        {
            /* we found non-white space */
            if (line[x] == '#')
            {
                gotcmt = 1;
            }
            break;
        }
    }

    if (gotcmt)
    {
        retval = 1;
    }
    return retval;
}
