/*
 *****************************************************************************
 *                                                                           *
 *                 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: readwrite.c 61529 2009-12-22 21:12:04Z dshaheen $
 *
 * Description:
 *   This sample application demonstrates how to configure the radio to use
 *   only the first antenna, configure and select the Fixed Q singulation
 *   algorithm, perform a tag access read, write, and blockErase operation.
 *   In addition, the data response mode is set to compact format.
 *   The command line specifies tag memory bank to use, the starting offset,
 *   the number of 16-bit words, and the desired operation (Read, Write, Erase).
 *   In the case of a Write, the specified data is first read, then inverted
 *   and written back to the tag.  The data is then read back once again to
 *   confirm the inverted data made it down to the tag.
 *
 *   The functional flow is as follows:
 *     - Start up the RFID Reader Library
 *     - Enumerate the attached radios
 *     - Open the first enumerated radio and
 *       - Retrieve the current antenna status and configuration for each
 *         of the enabled antennas
 *       - Disable all but the first antenna port
 *       - Configure the first antenna port for tag access performance
 *       - Retrieve the current singulation algorithm and fixedQ singulation
 *         algorithm settings
 *       - Configure the fixedQ algorithm settings for tag access performance
 *       - Select the fixdeQ singulation algorithm (not really required)
 *       - Retrieve the current data response mode.
 *       - Configure the data response mode for Compact packets
 *       - For all operations (read, write, erase) Read the requested data.
 *       	==> Sunny: If EPC is specified from command line parameter, focus operation on that target tag
 *       - If the requested operation is write
 *         - Invert the  data and write it back to the tag with write
 *           access command
 *           ==> Sunny: use the data given from command line parameter for 'write' operation
 *         - Read the data  back again and confirm the inverted data made
 *           it to the tag
 *       - If the requested operation is erase
 *         - Erase the requested tag memory
 *         - Read the data back again and confirm that it has been erased
 *       - Restore the antenna states, the first antenna configuration, the
 *         fixedQ singulation algorithm settings, current singulation
 *         algorithm, and data response mode to their original values
 *     - Close the radio
 *     - Shut down the library
 *
 * Parameters:
 *   Refer to the PrintUsage for details on command line parameters.
 *
 * NOTE - for this sample to work, it is best if a single tag is present in
 *        the field of view of the radio.
 *
 *
 *****************************************************************************
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <memory.h>
#include <ctype.h>
#include "rfid_library.h"
#include "sample_utility.h"


#pragma warning( disable : 4996)  // disable strtok deprecation warning


enum
{
    RWE_READ,
    RWE_WRITE,
    RWE_ERASE
};
typedef INT32U  RWE_OPTIONS;



#define  DEF_WORD_LENGTH     6
#define  DEF_START_OFFSET    2
#define  DEF_MEM_BANK        RFID_18K6C_MEMORY_BANK_EPC
#define  DEF_RWE             RWE_READ

#define ANTENNA_ID_SWITCH    "-ant"
#define TARGET_EPC_SWITCH    "-epc"
#define RWE_SWITCH           "-rwe"
#define MEM_BANK_SWITCH      "-mb"
#define WORD_LENGTH_SWITCH   "-wl"
#define WRITE_DATA_SWITCH    "-wd"
#define START_OFFSET_SWITCH  "-so"
#define HELP1_SWITCH         "-h"
#define HELP2_SWITCH         "-?"

#define MB_USER              "USER"
#define MB_EPC               "EPC"
#define MB_TID               "TID"
#define MB_RESERVED          "RES"

#define RWE_READ_STR         "READ"
#define RWE_WRITE_STR        "WRITE"
#define RWE_ERASE_STR        "ERASE"

#define MAX_WORD_LENGTH      1024



// default init for original behavior (read 95-bits of EPC at offset 2
static INT16U g_WordLength  = DEF_WORD_LENGTH;
static INT16U g_StartOffset = DEF_START_OFFSET;
static INT32U g_MemBank     = DEF_MEM_BANK;

static RWE_OPTIONS g_ReadWriteErase    = DEF_RWE;
static INT8U *g_TargetEpcString = NULL;
static INT8U *g_WriteDataString = NULL;
static INT16U g_AntennaId      = 0;

INT8U getbin(INT8U x)
{
	if (x >= '0' && x <= '9')
		return x - '0';
	if (x >= 'A' && x <= 'F')
		return x - 'A' + 10;
	if (x >= 'a' && x <= 'f')
		return x - 'a' + 10;
	return 0;
}

INT8U hex2bin8(INT8U *hexData) {
	INT8U byte = getbin(*hexData++);
	return(byte << 4 | getbin(*hexData));
}

INT8U hex2bin16(INT8U *hexData) {
	INT16U word = getbin(*hexData++);
	word  = word << 4 | getbin(*hexData++);
	word  = word << 4 | getbin(*hexData++);
	return(word << 4 | getbin(*hexData));
}

/* printUsage

Description:  Displays program usage

Returns: None
*/
static void printUsage(void)
{
    printf("Usage:  readwrite [-h|?] [-rwe="RWE_READ_STR"|"RWE_WRITE_STR"|"RWE_ERASE_STR"]  [-epc=<TARGET-EPC-CODE>]  [-mb="MB_USER"|"MB_RESERVED"|"MB_EPC"|"MB_TID"] [-so=<value>] [-wl=<value>] [-wd=<HexString>]\n");
    printf("  where:\n");
    printf("        -h or -? will display this usage\n");
    printf("        -ant     specify antenna port to use (default is the first antenna)\n");
    printf("        -epc     if given, specifies the target tag's EPC code for access operation\n");
    printf("        -rwe     specifies the read/write/erase operation to perform\n");
    printf("                     (default is "RWE_READ_STR")\n");
    printf("        -mb      specifies the tag memory bank (default is "MB_EPC")\n");
    printf("        -so      specifies the starting offset in the memory bank\n");
    printf("                     (default is %d)\n", DEF_START_OFFSET);
    printf("        -wl      specifies the number of 16-bit words to read/write.\n");
    printf("                     Can not be greater than %d, (default is %d)\n\n", MAX_WORD_LENGTH, DEF_WORD_LENGTH);
    printf("        -wd      specifies the data (in 16-bit words) to write.\n");
}

/*
proc_cmdline

Description:

Returns:
0  on success
non-zero on failure
*/

static int proc_cmdline(int argc, char** argv)
{
    int retval = 0;
    int argidx = 1;
    char *option;
    char *value;
    char *tmp;

    /* if no args were passed, just use defaults */
    if (argc < 2)
    {
        goto done_exit;
    }

    for (argidx = 1; argidx < argc; argidx++)
    {
        option = strtok(argv[argidx], "=");
        value  = strtok(NULL, "=");

        // first check/process the option
        if (NULL == option)
        {
            goto usage;
        }
        else  //ensure option is lower case
        {
            tmp = option;
            while (*tmp)
            {
                *tmp = tolower(*tmp);
                tmp++;
            }
        }

        if ((NULL == value) ||
            (strncmp(option, HELP1_SWITCH, strlen(HELP1_SWITCH)) == 0) ||
            (strncmp(option, HELP2_SWITCH, strlen(HELP2_SWITCH)) == 0))
        {
            goto usage;
        }

        // if we are here, we have a non-null value so ensure it is upper case
        tmp = value;
        while (*tmp)
        {
            *tmp = toupper(*tmp);
            tmp++;
        }

        // now process the arguments
        if (strncmp(option, ANTENNA_ID_SWITCH, strlen(ANTENNA_ID_SWITCH)) == 0)
        {
        	g_AntennaId = atoi(value);
        }
        else if (strncmp(option, TARGET_EPC_SWITCH, strlen(TARGET_EPC_SWITCH)) == 0)
        {
        	if(strlen(value) != 24)
                goto usage;
        	g_TargetEpcString = (INT8U *)value;
        }
        else if (strncmp(option, RWE_SWITCH, strlen(RWE_SWITCH)) == 0)
        {
            if (strncmp(value, RWE_READ_STR, strlen(RWE_READ_STR)) == 0)
            {
                g_ReadWriteErase     = RWE_READ;
            }
            else if (strncmp(value, RWE_WRITE_STR, strlen(RWE_WRITE_STR)) == 0)
            {
                g_ReadWriteErase     = RWE_WRITE;
            }
            else if (strncmp(value, RWE_ERASE_STR, strlen(RWE_ERASE_STR)) == 0)
            {
                g_ReadWriteErase     = RWE_ERASE;
            }
            else
            {
                goto usage;
            }
        }
        else if (strncmp(option, MEM_BANK_SWITCH, strlen(MEM_BANK_SWITCH)) == 0)
        {
            if (strncmp(value, MB_USER, strlen(MB_USER)) == 0)
            {
                g_MemBank     = RFID_18K6C_MEMORY_BANK_USER;
            }
            else if (strncmp(value, MB_EPC, strlen(MB_EPC)) == 0)
            {
                g_MemBank     = RFID_18K6C_MEMORY_BANK_EPC;
            }
            else if (strncmp(value, MB_RESERVED, strlen(MB_RESERVED)) == 0)
            {
                g_MemBank     = RFID_18K6C_MEMORY_BANK_RESERVED;
            }
            else if (strncmp(value, MB_TID, strlen(MB_TID)) == 0)
            {
                g_MemBank     = RFID_18K6C_MEMORY_BANK_TID;
            }
            else
            {
                goto usage;
            }
        }
        else if (strncmp(option, WORD_LENGTH_SWITCH, strlen(WORD_LENGTH_SWITCH)) == 0)
        {
            g_WordLength = atoi(value);

            if (g_WordLength > MAX_WORD_LENGTH)
            {
                goto usage;
            }
        }
        else if (strncmp(option, WRITE_DATA_SWITCH, strlen(WRITE_DATA_SWITCH)) == 0)
        {
            g_WriteDataString = (INT8U *)value;
        }
        else if (strncmp(option, START_OFFSET_SWITCH, strlen(START_OFFSET_SWITCH)) == 0)
        {
            g_StartOffset = atoi(value);
        }
        else
        {
            goto usage;
        }
    }

    goto done_exit;

usage:
    printUsage();
    retval=1;

done_exit:
    return retval;
}


int main(
    int     argc,
    char**  argv
    )
{
    RFID_STATUS                             status;
    RFID_RADIO_ENUM*                        pEnum;
    RFID_RADIO_HANDLE                       handle;
    INT8U                                   readData[MAX_WORD_LENGTH * 2];
    INT16U                                  writeData[MAX_WORD_LENGTH];
    RFID_18K6C_READ_PARMS                   readParms;
    RFID_18K6C_WRITE_PARMS                  writeParms;
    RFID_18K6C_BLOCK_ERASE_PARMS            eraseParms;
    CONTEXT_PARMS                           context;
    INT32U                                  index;
    INT32U                                  antenna;
    RFID_ANTENNA_PORT_CONFIG                antennaConfig;
    RFID_18K6C_SINGULATION_FIXEDQ_PARMS     singulationParms;
    INT16U                                  word;
    RFID_18K6C_SINGULATION_CRITERIA         singulationCriteria;
    RFID_18K6C_SINGULATION_CRITERION        singulationCriterion;
    RFID_18K6C_SINGULATION_ALGORITHM        originalSingulationAlgorithm;
    RFID_18K6C_SINGULATION_FIXEDQ_PARMS     originalFixedQSettings;
    RFID_ANTENNA_PORT_CONFIG                originalAntennaConfig;
    RFID_ANTENNA_PORT_STATUS                originalAntennaState[64];
    RFID_RESPONSE_MODE                      originalResponseMode;
    INT8U                                   maxOriginalAntennas = 0;
    INT8U                                   maxAccessAPIRetries = 6;
    INT8U                                   accessAPIRetryCount = 0;


    if (0 != proc_cmdline(argc,argv))
    {
        goto EXIT;
    }

    /* Initialialize the RFID library                                         */
    status = RFID_Startup(NULL, 0);
    if (RFID_STATUS_OK != status)
    {
        fprintf(stderr, "ERROR: RFID_Startup returned %d\n", status);
        goto EXIT;
    }

    /* Create an initial structure for enumerating the radios.  We'll adjust  */
    /* it once we know how big to make it.                                    */
    pEnum = (RFID_RADIO_ENUM *) malloc(sizeof(RFID_RADIO_ENUM));
    if (NULL == pEnum)
    {
        fprintf(stderr, "ERROR: Failed to allocate memory\n");
        goto SHUTDOWN_LIBRARY;
    }
    pEnum->length       =
        pEnum->totalLength  = sizeof(RFID_RADIO_ENUM);

    /* Enumerate the radios                                                   */
    while (RFID_ERROR_BUFFER_TOO_SMALL ==
        (status = RFID_RetrieveAttachedRadiosList(pEnum, 0)))
    {
        RFID_RADIO_ENUM* pNewEnum =
            (RFID_RADIO_ENUM *) realloc(pEnum, pEnum->totalLength);
        if (NULL == pNewEnum)
        {
            fprintf(stderr, "ERROR: Failed to allocate memory\n");
            goto CLEANUP_ENUM;
        }
        pEnum = pNewEnum;
    }
    if (RFID_STATUS_OK != status)
    {
        fprintf(
            stderr,
            "ERROR: RFID_RetrieveAttachedRadiosList returned %d\n",
            status);
        goto CLEANUP_ENUM;
    }

    /* Open up the first radio - if more than one radio is attached, so be it */
    if (!pEnum->countRadios)
    {
        fprintf(stderr, "ERROR: No radios attached to the system\n");
        goto CLEANUP_ENUM;
    }
    status = RFID_RadioOpen(pEnum->ppRadioInfo[0]->cookie, &handle, 0);
    if (RFID_STATUS_OK != status)
    {
        fprintf(stderr, "ERROR: RFID_RadioOpen returned %d\n", status);
        goto CLEANUP_ENUM;
    }

    /* Make sure the specified antenna is enabled and the rest are disabled      */
    for (antenna = 0; RFID_STATUS_OK == status; ++antenna)
    {
        /* save original state, so it can be restored at the end */
        status = RFID_AntennaPortGetStatus(
                        handle,
                        antenna,
                        &originalAntennaState[antenna]);
        if (RFID_STATUS_OK != status)
        {
            break;
        }

        status =  RFID_AntennaPortSetState(
                        handle,
                        antenna,
                        ((antenna != g_AntennaId) ? RFID_ANTENNA_PORT_STATE_DISABLED :
                                   RFID_ANTENNA_PORT_STATE_ENABLED));
    }

    /* We broke out of loop after RFID_AntennaPortGetState/SetState returned a */
    /* non ok status.  We exepect an invalid parameter error when we set state */
    /* for an invalid antenna port, which will happen when the loop exhausts   */
    /* the set of valid antennas.  Any other error is unexpected and is        */
    /* reported.                                                               */
    if (RFID_ERROR_INVALID_PARAMETER != status)
    {
        fprintf(
            stderr,
            "ERROR: RFID_AntennaPortGetState/SetState returned %d\n",
            status);
        goto CLOSE_RADIO;
    }

    /* Store the number of antennas, for use when we restore the state at the   */
    /* end                                                                     */
    maxOriginalAntennas = antenna;

    /* Set up the specified antenna for a single inventory cycle. Init the         */
    /* the antennaConfig structure with the current settings from the radio    */
    /* Retain the original antenna settings, so they can be restored when we   */
    /* are done                                                                */
    originalAntennaConfig.length = sizeof(RFID_ANTENNA_PORT_CONFIG);
    status = RFID_AntennaPortGetConfiguration(handle, g_AntennaId, &originalAntennaConfig);
    if (RFID_STATUS_OK != status)
    {
        fprintf(
            stderr,
            "ERROR: RFID_AntennaPortGetConfiguration returned %d\n",
            status);
        goto CLOSE_RADIO;
    }
    antennaConfig  = originalAntennaConfig;
    antennaConfig.numberInventoryCycles = 0;
    antennaConfig.dwellTime = 500;
    antennaConfig.powerLevel = 270;
    status = RFID_AntennaPortSetConfiguration(handle, g_AntennaId, &antennaConfig);
    if (RFID_STATUS_OK != status)
    {
        fprintf(
            stderr,
            "ERROR: RFID_AntennaPortSetConfiguration returned %d\n",
            status);
        goto CLOSE_RADIO;
    }

    /* First get the original fixedq singulation parameters and the current   */
    /* singulation algorithm selection, so they can be restored when we are   */
    /* done                                                                   */
    originalFixedQSettings.length = sizeof(RFID_18K6C_SINGULATION_FIXEDQ_PARMS);
    status = RFID_18K6CGetSingulationAlgorithmParameters(
                 handle,
                 RFID_18K6C_SINGULATION_ALGORITHM_FIXEDQ,
                 &originalFixedQSettings);
    if (RFID_STATUS_OK != status)
    {
        fprintf(
            stderr,
            "ERROR: RFID_18K6CGetSingulationAlgorithmParameters returned %d\n",
            status);
        goto CLOSE_RADIO;
    }
    status = RFID_18K6CGetCurrentSingulationAlgorithm(
                 handle,
                 &originalSingulationAlgorithm);
    if (RFID_STATUS_OK != status)
    {
        fprintf(
            stderr,
            "ERROR: RFID_18K6CGetCurrentSingulationAlgorithm returned %d\n",
            status);
        goto CLOSE_RADIO;
    }

#if 0
    /* The MAC uses the fixed-Q singulation algorithm for tag read and write, */
    /* so we'll configure the algorithm so that it works well for a single    */
    /* tag and even though it is not necessary, set the singulation algorithm */
    /* to fixed Q.                                                            */
    singulationParms.length             = sizeof(RFID_18K6C_SINGULATION_FIXEDQ_PARMS);
    singulationParms.qValue             = 0;
    singulationParms.retryCount         = 3;
    singulationParms.toggleTarget       = 0;
    singulationParms.repeatUntilNoTags  = 0;
#else
	/* Note from ErikS (20171205):
    in Multi Tags case , qValue should >0 , qValue must set to >= log2(N tags);
    toggleTarget & repeatUntilNoTags need to set to 1;
    and retryCount is beter set to 1~3
	*/
    singulationParms.length             = sizeof(RFID_18K6C_SINGULATION_FIXEDQ_PARMS);
    singulationParms.qValue             = 7;
    singulationParms.retryCount         = 3;
    singulationParms.toggleTarget       = 1;
    singulationParms.repeatUntilNoTags  = 1;
#endif
    status = RFID_18K6CSetSingulationAlgorithmParameters(
                 handle,
                 RFID_18K6C_SINGULATION_ALGORITHM_FIXEDQ,
                 &singulationParms);
    if (RFID_STATUS_OK != status)
    {
        fprintf(
            stderr,
            "ERROR: RFID_18K6CSetSingulationAlgorithmParameters returned %d\n",
            status);
        goto CLOSE_RADIO;
    }
    status = RFID_18K6CSetCurrentSingulationAlgorithm(
                 handle,
                 RFID_18K6C_SINGULATION_ALGORITHM_FIXEDQ);
    if (RFID_STATUS_OK != status)
    {
        fprintf(
            stderr,
            "ERROR: RFID_18K6CSetCurrentSingulationAlgorithm returned %d\n",
            status);
        goto CLOSE_RADIO;
    }

    /* Get the current data response mode so we can restore it when we are done */
    status = RFID_RadioGetResponseDataMode(
                 handle,
                 (RFID_RESPONSE_TYPE)RFID_RESPONSE_TYPE_DATA,
                 &originalResponseMode);

    if (RFID_STATUS_OK != status)
    {
        fprintf(
            stderr,
            "ERROR: RFID_RadioGetResponseDataMode returned %d\n",
            status);
        goto CLOSE_RADIO;
    }

    /* Configure the MAC for the data response mode (packets) we want to see */
    status = RFID_RadioSetResponseDataMode(
                 handle,
                 (RFID_RESPONSE_TYPE)RFID_RESPONSE_TYPE_DATA,
                 RFID_RESPONSE_MODE_COMPACT);

    if (RFID_STATUS_OK != status)
    {
        fprintf(
            stderr,
            "ERROR: RFID_RadioSetResponseDataMode returned %d\n",
            status);
        goto CLOSE_RADIO;
    }

    if(g_TargetEpcString) {
    	INT16U epcLength = 12;
    	char epc[12];
    	for (unsigned int tag_i = 0; tag_i < 12; tag_i++) {
    		epc[tag_i] = hex2bin8(g_TargetEpcString + tag_i * 2);
    	}
        /* Configure the singulation criteria for the data we want */
        singulationCriterion.match = 1;
        singulationCriterion.mask.count = epcLength * 8;	// bit count
        singulationCriterion.mask.offset = 0;
        memcpy(singulationCriterion.mask.mask, epc, epcLength);
        singulationCriteria.countCriteria = 1;
        singulationCriteria.pCriteria = &singulationCriterion;
        status = RFID_18K6CSetPostMatchCriteria(handle, &singulationCriteria, 0);

        if (RFID_STATUS_OK != status)
        {
            fprintf(
                stderr,
                "ERROR: RFID_18K6CSetPostMatchCriteria returned %d\n",
                status);
            goto CLOSE_RADIO;
        }
    }

    context.succesfulAccessPackets  = 0;
    context.pReadData               = readData;
    readParms.length                = sizeof(readParms);
    readParms.readCmdParms.length   = sizeof(readParms.readCmdParms);
    readParms.readCmdParms.bank     = g_MemBank;
    readParms.readCmdParms.count    = g_WordLength;
    readParms.readCmdParms.offset   = g_StartOffset;
    readParms.accessPassword        = 0;
    readParms.common.pCallback      = RfidTagAccessCallback;
    readParms.common.pCallbackCode  = NULL;
    readParms.common.tagStopCount   = 0;
    readParms.common.context        = &context;

    /* Keep attempting to read   from the tag's memory until it     */
    /* succeeds or until the tag-read function fails for some reason.         */
    accessAPIRetryCount = 0;
    while ((RFID_STATUS_OK == status) &&
           !context.succesfulAccessPackets &&
           (accessAPIRetryCount < maxAccessAPIRetries))
    {
        printf( "Attempting to read %s\n", g_TargetEpcString ? g_TargetEpcString : "");

        status = RFID_18K6CTagRead(handle, &readParms, g_TargetEpcString ? RFID_FLAG_PERFORM_POST_MATCH : 0);
        if (RFID_STATUS_OK != status)
        {
            fprintf(
                stderr,
                "ERROR: RFID_18K6CTagRead returned %d\n",
                status);
            goto CLOSE_RADIO;
        }
        RFID_MacClearError(handle);
        accessAPIRetryCount++;
    }
    if (!context.succesfulAccessPackets)
    {
        printf("Tag access read failed\n");
        goto RESTORE;
    }

    printf( "Read Data=");

    /* Flip all of the  bits so we have something to write back that is    */
    /* different from the tag's current bits.                                  */
    for (index = 0; index < g_WordLength; ++index)
    {
        word = (((INT16U) readData[index * 2]) << 8) | readData[(index * 2) + 1];
        printf( "%04X ", word);
        writeData[index] = ~word;
    }

    printf( "\n\n");

    // if it's a write, setup and perform the write
    if (RWE_WRITE == g_ReadWriteErase)
    {
        context.succesfulAccessPackets             = 0;
        writeParms.length                          = sizeof(writeParms);
        writeParms.writeType                       = RFID_18K6C_WRITE_TYPE_SEQUENTIAL;
        writeParms.writeCmdParms.sequential.length =
            sizeof(RFID_18K6C_WRITE_SEQUENTIAL_CMD_PARMS);
        writeParms.writeCmdParms.sequential.bank   = g_MemBank;
        writeParms.writeCmdParms.sequential.count  = g_WordLength;
        writeParms.writeCmdParms.sequential.offset = g_StartOffset;
        writeParms.writeCmdParms.sequential.pData  = writeData;
        writeParms.accessPassword                  = 0;
        writeParms.common.pCallback                = RfidTagAccessCallback;
        writeParms.common.pCallbackCode            = NULL;
        writeParms.common.tagStopCount             = 0;
        writeParms.common.context                  = &context;

        /* Keep attempting to write  to the tag's  memory until it      */
        /* succeeds or until the tag-write function fails for some reason.        */
        accessAPIRetryCount = 0;
        while ((RFID_STATUS_OK == status) &&
               !context.succesfulAccessPackets &&
               (accessAPIRetryCount < maxAccessAPIRetries))
        {
            printf( "Attempting to write %s\n", g_TargetEpcString ? g_TargetEpcString : "");
            status = RFID_18K6CTagWrite(handle, &writeParms, g_TargetEpcString ? RFID_FLAG_PERFORM_POST_MATCH : 0);
            if (RFID_STATUS_OK != status)
            {
                fprintf(
                    stderr,
                    "ERROR: RFID_18K6CTagWrite returned %d\n",
                    status);
                goto CLOSE_RADIO;
            }
            RFID_MacClearError(handle);
            accessAPIRetryCount++;
        }
        if (!context.succesfulAccessPackets)
        {
            printf("Tag access write failed\n");
            goto RESTORE;
        }

    }
    else if (RWE_ERASE == g_ReadWriteErase)
    {
        // if it's erase, setup and perform the erase

        context.succesfulAccessPackets             = 0;
        eraseParms.length                          = sizeof(eraseParms);
        eraseParms.blockEraseCmdParms.length       = sizeof(eraseParms.blockEraseCmdParms);
        eraseParms.blockEraseCmdParms.bank         = g_MemBank;
        eraseParms.blockEraseCmdParms.count        = g_WordLength;
        eraseParms.blockEraseCmdParms.offset       = g_StartOffset;
        eraseParms.accessPassword                  = 0;
        eraseParms.common.pCallback                = RfidTagAccessCallback;
        eraseParms.common.pCallbackCode            = NULL;
        eraseParms.common.tagStopCount             = 0;
        eraseParms.common.context                  = &context;

        /* Keep attempting to erase  the tag's  memory until it      */
        /* succeeds or until the tag-block erase function fails for some reason.        */
        accessAPIRetryCount = 0;
        while ((RFID_STATUS_OK == status) &&
               !context.succesfulAccessPackets &&
               (accessAPIRetryCount < maxAccessAPIRetries))
        {
            printf( "Attempting to erase %s\n", g_TargetEpcString ? g_TargetEpcString : "");
            status = RFID_18K6CTagBlockErase(handle, &eraseParms, g_TargetEpcString ? RFID_FLAG_PERFORM_POST_MATCH : 0);
            if (RFID_STATUS_OK != status)
            {
                fprintf(
                    stderr,
                    "ERROR: RFID_18K6CTagBlockErase returned %d\n",
                    status);
                goto CLOSE_RADIO;
            }
            RFID_MacClearError(handle);
            accessAPIRetryCount++;
        }
        if (!context.succesfulAccessPackets)
        {
            printf("Tag access erase failed\n");
            goto RESTORE;
        }
    }

    // if it was not a read operation (ie, was write or erase) then read it back again
    // following the write/erase operation to compare the data
    if (RWE_READ != g_ReadWriteErase)
    {
        context.succesfulAccessPackets = 0;

        /* Keep attempting to read  from the tag's  memory until it     */
        /* succeeds or until the tag-read function fails for some reason.         */
        accessAPIRetryCount = 0;
        while ((RFID_STATUS_OK == status) &&
               !context.succesfulAccessPackets &&
               (accessAPIRetryCount < maxAccessAPIRetries))
        {
            status = RFID_18K6CTagRead(handle, &readParms, g_TargetEpcString ? RFID_FLAG_PERFORM_POST_MATCH : 0);
            if (RFID_STATUS_OK != status)
            {
                fprintf(
                    stderr,
                    "ERROR: RFID_18K6CTagRead returned %d\n",
                    status);
                goto CLOSE_RADIO;
            }

            RFID_MacClearError(handle);
            accessAPIRetryCount++;
        }

        if (!context.succesfulAccessPackets)
        {
            printf("Tag access read validation failed\n");
            goto RESTORE;
        }

        printf( "Read data (after write or erase)\n\n");
        printf( "Read Data=");
        /* Verify that the data read back matches the data the expected data   */
        for (index = 0; index < g_WordLength; ++index)
        {
            INT16U readWord =
                (((INT16U) readData[index * 2]) << 8) | readData[(index * 2) +1];

            printf( "%04X ", readWord);
            if ((RWE_WRITE == g_ReadWriteErase) &&
                (writeData[index] != readWord))
            {
                fprintf(
                    stderr,
                    "ERROR:  word %d (0x%.4x) doesn't match written (0x%.4x)\n",
                    index + 1, readWord, writeData[index]);
                goto RESTORE;
            }
            if ((RWE_ERASE == g_ReadWriteErase) &&
                (0 != readWord))
            {
                fprintf(
                    stderr,
                    "ERROR:  word %d (0x%.4x) doesn't match expected (0x%.4x)\n",
                    index + 1, readWord, 0);
                goto RESTORE;
            }
        }

    }


RESTORE:

    printf("\n");

    /* Restore original antenna port configuration for the first antenna*/
    status = RFID_AntennaPortSetConfiguration(handle, g_AntennaId, &originalAntennaConfig);
    if (RFID_STATUS_OK != status)
    {
        fprintf(
            stderr,
            "ERROR: Restoring RFID_AntennaPortSetConfiguration returned %d\n",
            status);
        goto CLOSE_RADIO;
    }
    /* Restore original antenna state enables */
    for (antenna = 0; antenna < maxOriginalAntennas; antenna++)
    {
        status =  RFID_AntennaPortSetState(
                        handle,
                        antenna,
                        originalAntennaState[antenna].state);
    }
    if (RFID_STATUS_OK != status)
    {
        fprintf(
            stderr,
            "ERROR: Restoring RFID_AntennaPortSetState returned %d\n",
            status);
        goto CLOSE_RADIO;
    }
    /* Restore original fixedQ algorithm settings */
    status = RFID_18K6CSetSingulationAlgorithmParameters(
                 handle,
                 RFID_18K6C_SINGULATION_ALGORITHM_FIXEDQ,
                 &originalFixedQSettings);
    if (RFID_STATUS_OK != status)
    {
        fprintf(
            stderr,
            "ERROR: Restoring RFID_18K6CSetSingulationAlgorithmParameters returned %d\n",
            status);
        goto CLOSE_RADIO;
    }
    /* Restore original current singulation algorithm selection */
    status = RFID_18K6CSetCurrentSingulationAlgorithm(
                 handle,
                 originalSingulationAlgorithm);
    if (RFID_STATUS_OK != status)
    {
        fprintf(
            stderr,
            "ERROR: Restoring RFID_18K6CSetCurrentSingulationAlgorithm returned %d\n",
            status);
        goto CLOSE_RADIO;
    }
    /* Restore the original data response mode */
    status = RFID_RadioSetResponseDataMode(
                 handle,
                 (RFID_RESPONSE_TYPE)RFID_RESPONSE_TYPE_DATA,
                 originalResponseMode);

    if (RFID_STATUS_OK != status)
    {
        fprintf(
            stderr,
            "ERROR: Restoring RFID_RadioSetResponseDataMode returned %d\n",
            status);
        goto CLOSE_RADIO;
    }

CLOSE_RADIO:
    RFID_RadioClose(handle);

CLEANUP_ENUM:
    free(pEnum);

SHUTDOWN_LIBRARY:
    RFID_Shutdown();

EXIT:
    return 0;
} /* main */

