/*
 *****************************************************************************
 *                                                                           *
 *                 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: qtreadwrite.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 QT operation with the optional
 *   accompanied tag access Read or Write operation. Refer to the
 *   PrintUsage for a description of the command line arguments.
 *
 *   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)
 *       - If an optional access write is specified, then first do a tag
 *         access Read to seed the write data, starting at the specified offset,
 *         from the specified memory bank, for the specified number of words
 *         - Invert the read data to include in the optional write access
 *           command that will be sent along with the QT access operation
 *       - Prepare the QT Parameter settings from the command line arguments
 *         and include the read/write access settings as appropriate for
 *         the optional access command.
 *       - Perform the QT access operation
 *       - If the optional access write was specified, then perform a read
 *         again to confirm the inverted data was committed to the tag on
 *         the write.
 *       - Restore the antenna states, the first antenna configuration, the
 *         fixedQ singulation algorithm settings, and current singulation
 *         algorithm to their original values
 *     - Close the radio
 *     - Shut down the library
 *
 * NOTE - for this sample to work, it is best if a single tag is present in
 *        the field of view of the radio, and this tag must support QT
 *        technology.
 *
 *****************************************************************************
 */

#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 strok deprecation warning



#define  DEF_WORD_LENGTH        6
#define  DEF_START_OFFSET       2
#define  DEF_MEM_BANK           RFID_18K6C_MEMORY_BANK_EPC
#define  DEF_QT_OPT_ACCESS_CMD  RFID_18K6C_QT_OPT_NONE
#define  DEF_QT_READ_WRITE      -1
#define  DEF_QT_PERSISTENCE     -1
#define  DEF_QT_MEM_MAP         -1
#define  DEF_QT_SHORT_RANGE     -1

#define QTRW_SWITCH          "-qtrw"
#define QTP_SWITCH           "-qtp"
#define QTMM_SWITCH          "-qtmm"
#define QTSR_SWITCH          "-qtsr"
#define OPT_SWITCH           "-opt"
#define MEM_BANK_SWITCH      "-mb"
#define WORD_LENGTH_SWITCH   "-wl"
#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 OPT_NONE             "NONE"
#define OPT_READ             "READ"
#define OPT_WRITE            "WRITE"

#define PERS_TEMP            "TEMP"
#define PERS_PERM            "PERM"

#define MEM_PUB              "PUB"
#define MEM_PRIV             "PRIV"

#define SR_OFF               "OFF"
#define SR_ON                "ON"

#define MAX_WORD_LENGTH      1024



// default init
static INT16U g_WordLength     = DEF_WORD_LENGTH;
static INT16U g_StartOffset    = DEF_START_OFFSET;
static INT32U g_MemBank        = DEF_MEM_BANK;
static INT32U g_OptAccessCmd   = DEF_QT_OPT_ACCESS_CMD;
static INT32U g_qtReadWrite    = DEF_QT_READ_WRITE;
static INT32U g_qtPersistence  = DEF_QT_PERSISTENCE;
static INT32U g_qtMemMap       = DEF_QT_MEM_MAP;
static INT32U g_qtShortRange   = DEF_QT_SHORT_RANGE;

//static BOOL32 g_ReadOnly    = 0;

/* printUsage

Description:  Displays program usage

Returns: None
*/
static void printUsage(void)
{
    printf("Usage:  qtreadwrite [-h|?] -qtrw="OPT_READ"|"OPT_WRITE" -qtp="PERS_TEMP"|"PERS_PERM" -qtmm="MEM_PUB"|"MEM_PRIV"\n");
    printf("                    -qtsr="SR_OFF"|"SR_ON" [-opt="OPT_NONE"|"OPT_READ"|"OPT_WRITE"]\n");
    printf("                    [-mb="MB_USER"|"MB_RESERVED"|"MB_EPC"|"MB_TID"] [-so=<value>] [-wl=<value>]\n");
    printf("  where:\n");
    printf("        -h or -? will display this usage\n");
    printf("        -qtrw    Read/Write QT Control bits\n");
    printf("        -qtp     QT Persistence (Temporary or Permanent)\n");
    printf("        -qtmm    QT Memory Map (Public or Private)\n");
    printf("        -qtsr    QT Short Range (OFF=tag range not reduced, ON=tag range\n");
    printf("                     is reduced\n");
    printf("        -opt     optional QT access command (default is "OPT_NONE")\n");
    printf("        -mb      specifies the tag memory bank (only for read/write,\n");
    printf("                     default is "MB_EPC")\n");
    printf("        -so      specifies the starting offset in the memory bank\n");
    printf("                     (only for read/write, default is %d)\n", DEF_START_OFFSET);
    printf("        -wl      specifies the number of 16-bit words to read/write,\n");
    printf("                     (only for read/write and can not be greater than %d,\n", MAX_WORD_LENGTH);
    printf("                     default is %d)\n\n",  DEF_WORD_LENGTH);
}

/*
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;

    /* must have at least 5 args, otherwise we know its invalid */
    if (argc < 5)
    {
        goto usage;
    }

    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, QTRW_SWITCH, strlen(QTRW_SWITCH)) == 0)
        {
            if (strncmp(value, OPT_READ, strlen(OPT_READ)) == 0)
            {
                g_qtReadWrite     = RFID_18K6C_QT_CTRL_READ;
            }
            else if (strncmp(value, OPT_WRITE, strlen(OPT_WRITE)) == 0)
            {
                g_qtReadWrite     = RFID_18K6C_QT_CTRL_WRITE;
            }
            else
            {
                goto usage;
            }
        }
        else if (strncmp(option, QTP_SWITCH, strlen(QTP_SWITCH)) == 0)
        {
            if (strncmp(value, PERS_TEMP, strlen(PERS_TEMP)) == 0)
            {
                g_qtPersistence     = RFID_18K6C_QT_PERSISTENCE_TEMPORARY;
            }
            else if (strncmp(value, PERS_PERM, strlen(PERS_PERM)) == 0)
            {
                g_qtPersistence     = RFID_18K6C_QT_PERSISTENCE_PERMANENT;
            }
            else
            {
                goto usage;
            }
        }
        else if (strncmp(option, QTMM_SWITCH, strlen(QTMM_SWITCH)) == 0)
        {
            if (strncmp(value, MEM_PUB, strlen(MEM_PUB)) == 0)
            {
                g_qtMemMap     = RFID_18K6C_QT_MEMMAP_PUBLIC;
            }
            else if (strncmp(value, MEM_PRIV, strlen(MEM_PRIV)) == 0)
            {
                g_qtMemMap     = RFID_18K6C_QT_MEMMAP_PRIVATE;
            }
            else
            {
                goto usage;
            }
        }
        else if (strncmp(option, QTSR_SWITCH, strlen(QTSR_SWITCH)) == 0)
        {
            if (strncmp(value, SR_OFF, strlen(SR_OFF)) == 0)
            {
                g_qtShortRange     = RFID_18K6C_QT_SR_DISABLE;
            }
            else if (strncmp(value, SR_ON, strlen(SR_ON)) == 0)
            {
                g_qtShortRange     = RFID_18K6C_QT_SR_ENABLE;
            }
            else
            {
                goto usage;
            }
        }
        else if (strncmp(option, OPT_SWITCH, strlen(OPT_SWITCH)) == 0)
        {
            if (strncmp(value, OPT_NONE, strlen(OPT_NONE)) == 0)
            {
                g_OptAccessCmd     = RFID_18K6C_QT_OPT_NONE;
            }
            else if (strncmp(value, OPT_READ, strlen(OPT_READ)) == 0)
            {
                g_OptAccessCmd     = RFID_18K6C_QT_OPT_READ;
            }
            else if (strncmp(value, OPT_WRITE, strlen(OPT_WRITE)) == 0)
            {
                g_OptAccessCmd     = RFID_18K6C_QT_OPT_WRITE_TYPE_SEQUENTIAL;
            }
            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, START_OFFSET_SWITCH, strlen(START_OFFSET_SWITCH)) == 0)
        {
            g_StartOffset = atoi(value);
        }
        else
        {
            goto usage;
        }
    }

    // perform sanity check to ensure required arguments were provided
    if ((DEF_QT_READ_WRITE  == g_qtReadWrite)   ||
        (DEF_QT_PERSISTENCE == g_qtPersistence) ||
        (DEF_QT_MEM_MAP     == g_qtMemMap)      ||
        (DEF_QT_SHORT_RANGE == g_qtShortRange))
    {
        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_QT_PARMS                     qtParms;
    CONTEXT_PARMS                           context;
    INT32U                                  index;
    INT32U                                  antenna;
    RFID_ANTENNA_PORT_CONFIG                antennaConfig;
    RFID_18K6C_SINGULATION_FIXEDQ_PARMS     singulationParms;
    INT16U                                  word;
    RFID_STATUS                             qtCallbackStatus;
    INT8U                                   expectedAccessPackets;
    RFID_18K6C_SINGULATION_ALGORITHM        originalSingulationAlgorithm;
    RFID_18K6C_SINGULATION_FIXEDQ_PARMS     originalFixedQSettings;
    RFID_ANTENNA_PORT_CONFIG                originalAntennaConfig;
    RFID_ANTENNA_PORT_STATUS                originalAntennaState[64];
    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 first 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 ? 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 numbe of antennas, for use when we restore the state at the   */
    /* end                                                                     */
    maxOriginalAntennas = antenna;

    /* Set up the first 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, 0, &originalAntennaConfig);
    if (RFID_STATUS_OK != status)
    {
        fprintf(
            stderr,
            "ERROR: RFID_AntennaPortGetConfiguration returned %d\n",
            status);
        goto CLOSE_RADIO;
    }
    antennaConfig  = originalAntennaConfig;
    antennaConfig.numberInventoryCycles = 1;
    status = RFID_AntennaPortSetConfiguration(handle, 0, &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;
    }

    /* 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         = 0;
    singulationParms.toggleTarget       = 0;
    singulationParms.repeatUntilNoTags  = 0;
    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;
    }


    // if we will be doing a write as part of the qt, need to first do a read so we have data to write
    if (RFID_18K6C_QT_OPT_WRITE_TYPE_SEQUENTIAL == g_OptAccessCmd)
    {
        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 \n\n");

            status = RFID_18K6CTagRead(handle, &readParms, 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");
    }

    // now setup for the QT operation

    context.succesfulAccessPackets    = 0;
    context.pReadData                 = readData;        // only needed if g_OptAccessCmd is read
    qtParms.length                    = sizeof(qtParms);
    qtParms.optCmdType                = g_OptAccessCmd;
    qtParms.accessPassword            = 0;
    qtParms.qtCmdParms.length         = sizeof(qtParms.qtCmdParms);
    qtParms.qtCmdParms.qtMemoryMap    = g_qtMemMap;
    qtParms.qtCmdParms.qtPersistence  = g_qtPersistence;
    qtParms.qtCmdParms.qtReadWrite    = g_qtReadWrite;
    qtParms.qtCmdParms.qtShortRange   = g_qtShortRange;
    switch (g_OptAccessCmd)
    {
        case RFID_18K6C_QT_OPT_READ:
            {
                expectedAccessPackets                    = 2;

                qtParms.parameters.readCmdParms.length   = sizeof(qtParms.parameters.readCmdParms);
                qtParms.parameters.readCmdParms.bank     = g_MemBank;
                qtParms.parameters.readCmdParms.count    = g_WordLength;
                qtParms.parameters.readCmdParms.offset   = g_StartOffset;
                break;
            }
        case RFID_18K6C_QT_OPT_WRITE_TYPE_SEQUENTIAL:
            {

                expectedAccessPackets                    = 2;

                qtParms.parameters.seqWriteCmdParms.length = sizeof(qtParms.parameters.seqWriteCmdParms);
                qtParms.parameters.seqWriteCmdParms.bank   = g_MemBank;
                qtParms.parameters.seqWriteCmdParms.count  = g_WordLength;
                qtParms.parameters.seqWriteCmdParms.offset = g_StartOffset;
                qtParms.parameters.seqWriteCmdParms.pData  = writeData;
                break;
            }
        default:
            {
                expectedAccessPackets                    = 1;

                break;
            }
    }
    qtParms.common.pCallback        = RfidTagAccessCallback;
    qtParms.common.pCallbackCode    = &qtCallbackStatus;
    qtParms.common.tagStopCount     = 0;
    qtParms.common.context          = &context;

    /* Keep attempting QT operation until it succeeds or until the            */
    /* RFID_18K6CTagQT function fails for some reason.                        */
    /* Note:  We repeat until we successfully get the expected number of      */
    /*        access packets.  Since it is a counter, and we can return with  */
    /*        only 1, the success counter needs to be reset each time we try  */
    /*        again.                                                          */
    accessAPIRetryCount = 0;
    while ((RFID_STATUS_OK        == status) &&
        (expectedAccessPackets != context.succesfulAccessPackets) &&
        (accessAPIRetryCount < maxAccessAPIRetries))
    {
        printf( "Attempting QT \n\n");

        context.succesfulAccessPackets = 0;

        status = RFID_18K6CTagQT(handle, &qtParms, 0);

        /* error out if the API call fails or the callback returns an error */
        if ((RFID_STATUS_OK != status) || (RFID_STATUS_OK != qtCallbackStatus))
        {
            fprintf(
                stderr,
                "ERROR: RFID_18K6CQT returned %d, RfidPacketCallback returned %d\n",
                status, qtCallbackStatus);
            goto CLOSE_RADIO;
        }
        RFID_MacClearError(handle);
        accessAPIRetryCount++;
    }

    if (expectedAccessPackets != context.succesfulAccessPackets)
    {
        printf( "Tag access QT failed\n");
        goto RESTORE;
    }

    printf( "QT Operation was Successful\n");

    // if we did a read as part of the qt, dump the data we read
    if (RFID_18K6C_QT_OPT_READ == g_OptAccessCmd)
    {

        printf( "Post QT Read Data=");

        for (index = 0; index < g_WordLength; ++index)
        {
            word = (((INT16U) readData[index * 2]) << 8) | readData[(index * 2) + 1];
            printf( "%04X ", word);
        }
    }
    else if (RFID_18K6C_QT_OPT_WRITE_TYPE_SEQUENTIAL == g_OptAccessCmd) // we did a write as part of qt
    {
        // need to read it back and compare the data was written correctly

        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, 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("Post QT read access failed\n");
            goto RESTORE;
        }

        printf( "Post QT Read data (after being inverted)\n\n");
        printf( "Read Data=");
        /* Verify that the data read back matches the data that was just written    */
        for (index = 0; index < g_WordLength; ++index)
        {
            INT16U readWord =
                (((INT16U) readData[index * 2]) << 8) | readData[(index * 2) +1];

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

RESTORE:

    printf("\n\n");

    /* Restore original antenna port configuration for the first antenna*/
    status = RFID_AntennaPortSetConfiguration(handle, 0, &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;
    }

CLOSE_RADIO:
    RFID_RadioClose(handle);

CLEANUP_ENUM:
    free(pEnum);

SHUTDOWN_LIBRARY:
    RFID_Shutdown();

EXIT:
    return 0;
} /* main */

