packages feed

mercury-api-0.1.0.0: cbits/api/tm_reader_async.c

/**
 *  @file tm_reader_async.c
 *  @brief Mercury API - background reading implementation
 *  @author Nathan Williams
 *  @date 11/18/2009
 */

 /*
 * Copyright (c) 2009 ThingMagic, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#include "tm_config.h"
#include "tm_reader.h"
#include "serial_reader_imp.h"
#include <stdio.h>

bool IsDutyCycleEnabled(TMR_Reader *reader);
TMR_Status restart_reading(struct TMR_Reader *reader);
#ifdef TMR_ENABLE_BACKGROUND_READS

#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <time.h>
#include <stdio.h>
#include <string.h>

#ifndef WIN32
#include <sys/time.h>
#endif

#ifdef TMR_ENABLE_LLRP_READER
#include "llrp_reader_imp.h"
#endif
#include "osdep.h"
#include "tmr_utils.h"

static void *do_background_reads(void *arg);
static void *parse_tag_reads(void *arg);
static void process_async_response(TMR_Reader *reader);
bool isBufferOverFlow = false;
#endif /* TMR_ENABLE_BACKGROUND_READS */

TMR_Status
TMR_startReading(struct TMR_Reader *reader)
{
#ifdef SINGLE_THREAD_ASYNC_READ
  TMR_Status ret;
  uint32_t ontime;
  
  TMR_paramGet(reader, TMR_PARAM_READ_ASYNCONTIME, &ontime);
  reader->continuousReading = true; 
  if (((TMR_SR_MODEL_M6E == reader->u.serialReader.versionInfo.hardware[0])||
	(TMR_SR_MODEL_M6E_I == reader->u.serialReader.versionInfo.hardware[0]) ||
	(TMR_SR_MODEL_MICRO == reader->u.serialReader.versionInfo.hardware[0]) ||
	(TMR_SR_MODEL_M6E_NANO == reader->u.serialReader.versionInfo.hardware[0])) &&
	(reader->readParams.asyncOffTime == 0 || (reader->readParams.asyncOffTime != 0 && IsDutyCycleEnabled(reader)) ) &&
	((TMR_READ_PLAN_TYPE_SIMPLE == reader->readParams.readPlan->type) ||
	((TMR_READ_PLAN_TYPE_MULTI == reader->readParams.readPlan->type)  &&
	(compareAntennas(&reader->readParams.readPlan->u.multi))))
	)
  {
	if (reader->readParams.asyncOffTime == 0)
	{
		reader->dutyCycle = false;
	}
	else
	{
		reader->dutyCycle = true;
	}
  }
  else
  {
	reader->dutyCycle = false;
  }
  ret = TMR_read(reader, ontime, NULL);
  if(TMR_SUCCESS != ret)
	return ret;
	 
#else
#ifdef TMR_ENABLE_BACKGROUND_READS
  int ret;
  bool createParser = true;

  if (TMR_READER_TYPE_SERIAL == reader->readerType)
  {
#ifdef TMR_ENABLE_SERIAL_READER
    /**
     * Currently we are not supporting stop N trigger for async read case.
     * This is also true for pseudo continuous case as well. Pop up the error
     * if user is trying to do so.
     **/
    if (TMR_READ_PLAN_TYPE_MULTI == reader->readParams.readPlan->type)
    {
      uint8_t loop = 0;
      TMR_MultiReadPlan *multi;

      multi = &reader->readParams.readPlan->u.multi;

      for (loop = 0; loop < multi->planCount; loop++)
      {
        if (multi->plans[loop]->u.simple.stopOnCount.stopNTriggerStatus)
        {
          /* Not supporting stop N trigger */
          return TMR_ERROR_UNSUPPORTED; 
        }
      }
    }
    else if (TMR_READ_PLAN_TYPE_SIMPLE == reader->readParams.readPlan->type)
    {
      if (reader->readParams.readPlan->u.simple.stopOnCount.stopNTriggerStatus)
      {
        /* Not supporting stop N trigger */
        return TMR_ERROR_UNSUPPORTED;
      }
    }
    else
    {
      /* do nothing */
    }

    /**
	  * if model is M6e and it's varient
	  * asyncOffTime == 0
	  * only then use streaming
	  */
    if (
        ((TMR_SR_MODEL_M6E == reader->u.serialReader.versionInfo.hardware[0])||
         (TMR_SR_MODEL_M6E_I == reader->u.serialReader.versionInfo.hardware[0]) ||
         (TMR_SR_MODEL_MICRO == reader->u.serialReader.versionInfo.hardware[0]) ||
         (TMR_SR_MODEL_M6E_NANO == reader->u.serialReader.versionInfo.hardware[0])) &&
        (reader->readParams.asyncOffTime == 0 || (reader->readParams.asyncOffTime != 0 && IsDutyCycleEnabled(reader)) ) &&
        ((TMR_READ_PLAN_TYPE_SIMPLE == reader->readParams.readPlan->type) || 
         ((TMR_READ_PLAN_TYPE_MULTI == reader->readParams.readPlan->type)  && 
          (compareAntennas(&reader->readParams.readPlan->u.multi))))
       )
    {
		if (reader->readParams.asyncOffTime == 0)
		{
			reader->dutyCycle = false;
		}
		else
		{
			reader->dutyCycle = true;
		}
    }
    else
    {
      createParser = false;
	  reader->dutyCycle = false;
    }
#else
    return TMR_ERROR_UNSUPPORTED;
#endif/* TMR_ENABLE_SERIAL_READER */    
  }
#ifdef TMR_ENABLE_LLRP_READER
  if (TMR_READER_TYPE_LLRP == reader->readerType)
  {
    /**
     * In case of LLRP reader and continuous reading, disable the
     * LLRP background receiver.
     **/
    TMR_LLRP_setBackgroundReceiverState(reader, false);
    /**
     * Note the keepalive start time
     * Keepalive monitoring happens only 
     * for async reads.
     **/
    reader->u.llrpReader.ka_start = tmr_gettime();
  }
#endif

  /**
   * Initialize read_started semaphore
   **/
  pthread_mutex_lock(&reader->backgroundLock);
  reader->readState = TMR_READ_STATE_STARTING;
  pthread_cond_broadcast(&reader->readCond);
  pthread_mutex_unlock(&reader->backgroundLock);

  if (true == createParser)
  {
    /** Background parser thread initialization
     *
     * Only M6e supports Streaming, and in case of other readers
     * we still use pseudo-async mechanism for continuous read.
     * To achieve continuous reading, create a parser thread
     */
    pthread_mutex_lock(&reader->parserLock);
    
    if (false == reader->parserSetup)
    {
      ret = pthread_create(&reader->backgroundParser, NULL,
                       parse_tag_reads, reader);
      if (0 != ret)
      {
        pthread_mutex_unlock(&reader->parserLock);
        return TMR_ERROR_NO_THREADS;
      }
      pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
      pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
      pthread_detach(reader->backgroundParser);
    /** Initialize semaphores only for the first time
     *  These semaphores are used only in case of streaming
     */
      reader->queue_depth = 0;
      sem_init(&reader->queue_length, 0, 0);
      sem_init(&reader->queue_slots, 0, TMR_MAX_QUEUE_SLOTS);
      reader->parserSetup = true;
    }

    reader->parserEnabled = true;


    /* Enable streaming */
    reader->continuousReading = true;
    reader->finishedReading = false;
    pthread_cond_signal(&reader->parserCond);
    pthread_mutex_unlock(&reader->parserLock);
  }

  /* Background reader thread initialization */
  pthread_mutex_lock(&reader->backgroundLock);

  if (false == reader->backgroundSetup)
  {
    ret = pthread_create(&reader->backgroundReader, NULL,
                         do_background_reads, reader);
    if (0 != ret)
    {
      pthread_mutex_unlock(&reader->backgroundLock);
      return TMR_ERROR_NO_THREADS;
    }
    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
    reader->backgroundSetup = true;
  }

  reader->backgroundEnabled = true;
  reader->searchStatus = true;

#ifdef TMR_ENABLE_SERIAL_READER    
  if (TMR_READER_TYPE_SERIAL == reader->readerType)
  {
    reader->u.serialReader.tagopFailureCount = 0;
    reader->u.serialReader.tagopSuccessCount = 0;
  }
#endif/* TMR_ENABLE_SERIAL_READER */    
  pthread_cond_signal(&reader->backgroundCond);
  pthread_mutex_unlock(&reader->backgroundLock);

  /* End of Background reader thread initialization */
  /**
   * Wait for the Background thread to send the read command.
   * This will prevent of adding extra sleep in the application
   * after TMR_startReading() call.
   **/ 
  pthread_mutex_lock(&reader->backgroundLock);
  while (TMR_READ_STATE_STARTING == reader->readState)
  {
    pthread_cond_wait(&reader->readCond, &reader->backgroundLock);
  }
  pthread_mutex_unlock(&reader->backgroundLock);
#endif /* TMR_ENABLE_BACKGROUND_READS */
#endif

  return TMR_SUCCESS;
}

#if defined(TMR_ENABLE_BACKGROUND_READS)|| defined(SINGLE_THREAD_ASYNC_READ)
bool IsDutyCycleEnabled(struct TMR_Reader *reader)
{
	uint16_t i; 
	uint8_t *readerVersion = reader->u.serialReader.versionInfo.fwVersion ;
	uint8_t checkVersion[4];
	switch (reader->u.serialReader.versionInfo.hardware[0])
	{
		case TMR_SR_MODEL_M6E:
		case TMR_SR_MODEL_M6E_I:
			checkVersion[0] = 0x01; checkVersion[1] = 0x21; checkVersion[2] = 0x01; checkVersion[3] = 0x07;
			break;
		case TMR_SR_MODEL_MICRO:
			checkVersion[0] = 0x01; checkVersion[1] = 0x09; checkVersion[2] = 0x00; checkVersion[3] = 0x02;
			break;
		case TMR_SR_MODEL_M6E_NANO:
			checkVersion[0] = 0x01; checkVersion[1] = 0x07; checkVersion[2] = 0x00; checkVersion[3] = 0x02;
			break;
		default:
			checkVersion[0] = 0xFF; checkVersion[1] = 0xFF; checkVersion[2] = 0xFF; checkVersion[3] = 0xFF;
	}
	for (i = 0; i < 4; i++)
	{
		if (readerVersion[i] < checkVersion[i])
			return false;
		else if (readerVersion[i] > checkVersion[i])
			return true;
	}
	return true;
}

#endif /* TMR_ENABLE_BACKGROUND_READS */

void
reset_continuous_reading(struct TMR_Reader *reader, bool dueToError)
{
  if (true == reader->continuousReading)
  {
#ifndef SINGLE_THREAD_ASYNC_READ
#ifdef TMR_ENABLE_LLRP_READER
    if (TMR_READER_TYPE_LLRP == reader->readerType)
    {
      /**
       * In case of LLRP reader, re-enable the
       * LLRP background receiver as continuous reading is finished
       **/
      TMR_LLRP_setBackgroundReceiverState(reader, true);
    }
#endif

#ifdef TMR_ENABLE_SERIAL_READER      
    if ((false == dueToError) && (TMR_READER_TYPE_SERIAL == reader->readerType))
    {
      /**
       * Disable filtering on module
       **/
      TMR_Status ret;
      bool value = reader->u.serialReader.enableReadFiltering;
	  reader->hasContinuousReadStarted = false;
      ret = TMR_SR_cmdSetReaderConfiguration(reader, TMR_SR_CONFIGURATION_ENABLE_READ_FILTER, &value);
      if (TMR_SUCCESS != ret)
      {
#ifndef BARE_METAL
        notify_exception_listeners(reader, ret);
#endif
      }
    }
#endif/* TMR_ENABLE_SERIAL_READER */      
#endif /*SINGLE_THREAD_ASYNC_READ*/
    /* disable streaming */
    reader->continuousReading = false;
  }
}

TMR_Status
TMR_stopReading(struct TMR_Reader *reader)
{
  reader->hasContinuousReadStarted = false;
#ifdef SINGLE_THREAD_ASYNC_READ
  reader->cmdStopReading(reader);
#else
#ifdef TMR_ENABLE_BACKGROUND_READS

  /* Check if background setup is active */
  pthread_mutex_lock(&reader->backgroundLock);

  if (false == reader->backgroundSetup)
  {
    pthread_mutex_unlock(&reader->backgroundLock);
    return TMR_SUCCESS;
  }

  if (false == reader->searchStatus)
  {
    /**
     * searchStatus is false, i.e., reading is already
     * stopped. Returen success.
     **/
    pthread_mutex_unlock(&reader->backgroundLock);
    return TMR_SUCCESS;
  }
  /**
   * Else, read is in progress. Set
   * searchStatus to false;
   **/
  reader->searchStatus = false;
  pthread_mutex_unlock(&reader->backgroundLock);

  /**
   * Wait until the reading has started
   **/
  pthread_mutex_lock(&reader->backgroundLock);
  while (TMR_READ_STATE_STARTING == reader->readState)
  {
    pthread_cond_wait(&reader->readCond, &reader->backgroundLock);
    }
  pthread_mutex_unlock(&reader->backgroundLock);

  if ((true == reader->continuousReading) && (true == reader->trueAsyncflag))
    {
      /**
     * In case of true continuous reading, we need to send
     * stop reading message immediately.
       **/
    if(!isBufferOverFlow)
    {
      reader->cmdStopReading(reader);
    }

    /**
     * Wait logic has been changed in case of continuous reading.
     * Wait while the background reader is still reading.
     **/
    pthread_mutex_lock(&reader->backgroundLock);
    while (TMR_READ_STATE_DONE != reader->readState)
    {
      pthread_cond_wait(&reader->readCond, &reader->backgroundLock);
    }
    pthread_mutex_unlock(&reader->backgroundLock);
    /**
     * By this time, reader->backgroundEnabled is
     * already set to false. i.e., background reader thread
     * is suspended.
     **/
  }

  /**
   * wait until background reader thread finishes.
   * This is needed for pseudo-async reads and also
   * worst case of continuous reading, when read isn't success
   **/
  pthread_mutex_lock(&reader->backgroundLock);
  reader->backgroundEnabled = false;
  while (true == reader->backgroundRunning)
  {
    pthread_cond_wait(&reader->backgroundCond, &reader->backgroundLock);
  }
  pthread_mutex_unlock(&reader->backgroundLock);

  /**
   * Reset continuous reading settings, so that
   * the subsequent startReading() call doesn't have
   * any surprises.
   **/
#else
	reader->cmdStopReading(reader);
#endif
  reset_continuous_reading(reader, false);
#endif
  return TMR_SUCCESS;
}

void
notify_read_listeners(TMR_Reader *reader, TMR_TagReadData *trd)
{
  TMR_ReadListenerBlock *rlb;

  /* notify tag read to listener */
  if (NULL != reader)
  {
#ifndef SINGLE_THREAD_ASYNC_READ
    pthread_mutex_lock(&reader->listenerLock);
#endif
	rlb = reader->readListeners;
    while (rlb)
    {
      rlb->listener(reader, trd, rlb->cookie);
      rlb = rlb->next;
    }
#ifndef SINGLE_THREAD_ASYNC_READ
    pthread_mutex_unlock(&reader->listenerLock);
#endif
  }
}

void
notify_stats_listeners(TMR_Reader *reader, TMR_Reader_StatsValues *stats)
{
  TMR_StatsListenerBlock *slb;

  /* notify stats to the listener */
#ifndef SINGLE_THREAD_ASYNC_READ
  pthread_mutex_lock(&reader->listenerLock);
#endif
  slb = reader->statsListeners;
  while (slb)
  {
    slb->listener(reader, stats, slb->cookie);
    slb = slb->next;
  }
#ifndef SINGLE_THREAD_ASYNC_READ
  pthread_mutex_unlock(&reader->listenerLock);
#endif
}


TMR_Status restart_reading(struct TMR_Reader *reader)
{
	TMR_Status ret = TMR_SUCCESS;

	//Stop continuous reading
	ret = TMR_stopReading(reader);
	if(ret != TMR_SUCCESS)
	{
		return ret;
	}

	#ifdef SINGLE_THREAD_ASYNC_READ
	//Receive all tags from the previous reading
	{
		TMR_TagReadData trd;
		while(true)
		{
			ret = TMR_hasMoreTags(reader);
			if (TMR_SUCCESS == ret)
			{
				TMR_getNextTag(reader, &trd);
				notify_read_listeners(reader, &trd);
			}
			else if(ret == TMR_ERROR_END_OF_READING)
			break;
		}
	}
	#endif
	//Restart reading
	ret = TMR_startReading(reader);

	return ret;
}


#ifdef TMR_ENABLE_BACKGROUND_READS
/* NOTE: There is only one auth object for all the authreq listeners, so whichever listener touches it last wins.
 * For now (2012 Jul 20) we only anticipate having a single authreq listener, but there may be future cases which 
 * require multiples.  Revise this design if necessary. */
void
notify_authreq_listeners(TMR_Reader *reader, TMR_TagReadData *trd, TMR_TagAuthentication *auth)
{
  TMR_AuthReqListenerBlock *arlb;

  /* notify tag read to listener */
  pthread_mutex_lock(&reader->listenerLock);
  arlb = reader->authReqListeners;
  while (arlb)
  {
    arlb->listener(reader, trd, arlb->cookie, auth);
    arlb = arlb->next;
  }
  pthread_mutex_unlock(&reader->listenerLock);
}
#endif /* TMR_ENABLE_BACKGROUND_READS */

TMR_Status
TMR_addReadExceptionListener(TMR_Reader *reader,
                             TMR_ReadExceptionListenerBlock *b)
{
#ifndef SINGLE_THREAD_ASYNC_READ
  if (0 != pthread_mutex_lock(&reader->listenerLock))
    return TMR_ERROR_TRYAGAIN;
#endif
  b->next = reader->readExceptionListeners;
  reader->readExceptionListeners = b;

#ifndef SINGLE_THREAD_ASYNC_READ
  pthread_mutex_unlock(&reader->listenerLock);
#endif
  return TMR_SUCCESS;
}

#ifdef TMR_ENABLE_BACKGROUND_READS
TMR_Status
TMR_removeReadExceptionListener(TMR_Reader *reader,
                                TMR_ReadExceptionListenerBlock *b)
{
  TMR_ReadExceptionListenerBlock *block, **prev;

  if (0 != pthread_mutex_lock(&reader->listenerLock))
    return TMR_ERROR_TRYAGAIN;

  prev = &reader->readExceptionListeners;
  block = reader->readExceptionListeners;
  while (NULL != block)
  {
    if (block == b)
    {
      *prev = block->next;
      break;
    }
    prev = &block->next;
    block = block->next;
  }

  pthread_mutex_unlock(&reader->listenerLock);

  if (block == NULL)
  {
    return TMR_ERROR_INVALID;
  }

  return TMR_SUCCESS;
}
#endif

void
notify_exception_listeners(TMR_Reader *reader, TMR_Status status)
{
  TMR_ReadExceptionListenerBlock *relb;

  if (NULL != reader)
  {
#ifndef SINGLE_THREAD_ASYNC_READ
    pthread_mutex_lock(&reader->listenerLock);
#endif
    relb = reader->readExceptionListeners;
    while (relb)
    {
      relb->listener(reader, status, relb->cookie);
      relb = relb->next;
    }
#ifndef SINGLE_THREAD_ASYNC_READ
    pthread_mutex_unlock(&reader->listenerLock);
#endif
  }
}

#ifdef TMR_ENABLE_BACKGROUND_READS
TMR_Queue_tagReads *
dequeue(TMR_Reader *reader)
{
  TMR_Queue_tagReads *tagRead = NULL;
  pthread_mutex_lock(&reader->queue_lock);
  if (NULL != reader->tagQueueHead)
  {
    /* Fetch the head always */
    tagRead = reader->tagQueueHead;
    reader->tagQueueHead = reader->tagQueueHead->next;
  }
  reader->queue_depth --;
  pthread_mutex_unlock(&reader->queue_lock);
  return(tagRead);
}


void enqueue(TMR_Reader *reader, TMR_Queue_tagReads *tagRead)
{
  pthread_mutex_lock(&reader->queue_lock);
  if (NULL == reader->tagQueueHead)
  {
    /* first tag */
    reader->tagQueueHead = tagRead;
    reader->tagQueueHead->next = NULL;
    reader->tagQueueTail = reader->tagQueueHead;
  }
  else
  {
    reader->tagQueueTail->next = tagRead;
    reader->tagQueueTail = tagRead;
    tagRead->next = NULL;
  }
  reader->queue_depth ++;
  pthread_mutex_unlock(&reader->queue_lock);
}

static void *
parse_tag_reads(void *arg)
{
  TMR_Reader *reader;
  TMR_Queue_tagReads *tagRead;
  reader = arg;  

  while (1)
  {
    pthread_mutex_lock(&reader->parserLock);
    reader->parserRunning = false;
    pthread_cond_broadcast(&reader->parserCond);
    while (false == reader->parserEnabled)
    {
      pthread_cond_wait(&reader->parserCond, &reader->parserLock);
    }

    reader->parserRunning = true;
    pthread_mutex_unlock(&reader->parserLock);

    /**
     * Wait until queue_length is more than zero,
     * i.e., Queue should have atleast one tagRead to process
     */
    sem_wait(&reader->queue_length);

    if (NULL != reader->tagQueueHead)
    {
      /**
       * At this point there is a tagEntry in the queue
       * dequeue it and parse it.
       */          
      tagRead = dequeue(reader);
      if (false == tagRead->isStatusResponse)
      {
        /* Tag Buffer stream response */

#ifdef TMR_ENABLE_SERIAL_READER          
        if (TMR_READER_TYPE_SERIAL == reader->readerType)
        {
          /**
          * For serial readers, the tags results are already processed
          * and placed in the queue. Just notify that to the listener.
          */
          notify_read_listeners(reader, &tagRead->trd);
        }
#endif/* TMR_ENABLE_SERIAL_READER */           
#ifdef TMR_ENABLE_LLRP_READER
        if (TMR_READER_TYPE_LLRP == reader->readerType)
        {
          /* Else it is LLRP message, parse it */
          LLRP_tSRO_ACCESS_REPORT *pReport;
          LLRP_tSTagReportData *pTagReportData;

          pReport = (LLRP_tSRO_ACCESS_REPORT *)tagRead->tagEntry.lMsg;

          for(pTagReportData = pReport->listTagReportData;
              NULL != pTagReportData;
              pTagReportData = (LLRP_tSTagReportData *)pTagReportData->hdr.pNextSubParameter)
          {
            TMR_TagReadData trd;

            TMR_TRD_init(&trd);
            TMR_LLRP_parseMetadataFromMessage(reader, &trd, pTagReportData);
          
            trd.reader = reader;
            notify_read_listeners(reader, &trd);
        }
        }
#endif
        }
      else
      {
       /* A status stream response */

        if (TMR_READER_TYPE_SERIAL == reader->readerType)
        {
          TMR_Reader_StatsValues stats;
          uint8_t offset, i,j;
          uint16_t flags = 0;                 

          TMR_STATS_init(&stats);
          offset = tagRead->bufPointer;

          if (NULL != reader->statusListeners && NULL== reader->statsListeners)
          {
            /* A status stream response */
            TMR_StatusListenerBlock *slb;
            uint8_t index = 0, j;
            TMR_SR_StatusReport report[TMR_SR_STATUS_MAX];


            /* Get status content flags */
            flags = GETU16(tagRead->tagEntry.sMsg, offset);

            if (0 != (flags & TMR_SR_STATUS_FREQUENCY))
            {
              report[index].type = TMR_SR_STATUS_FREQUENCY;
              report[index].u.fsr.freq = (uint32_t)(GETU24(tagRead->tagEntry.sMsg, offset));
              index ++;
            }
            if (0 != (flags & TMR_SR_STATUS_TEMPERATURE))
            {
              report[index].type = TMR_SR_STATUS_TEMPERATURE;
              report[index].u.tsr.temp = GETU8(tagRead->tagEntry.sMsg, offset);
              index ++;
            }
            if (0 != (flags & TMR_SR_STATUS_ANTENNA))
            {
              uint8_t tx, rx;
              report[index].type = TMR_SR_STATUS_ANTENNA;
              tx = GETU8(tagRead->tagEntry.sMsg, offset);
              rx = GETU8(tagRead->tagEntry.sMsg, offset);

              for (j = 0; j < reader->u.serialReader.txRxMap->len; j++)
              {
                if ((rx == reader->u.serialReader.txRxMap->list[j].rxPort) && (tx == reader->u.serialReader.txRxMap->list[j].txPort))
                {
                  report[index].u.asr.ant = reader->u.serialReader.txRxMap->list[j].antenna;
                  break;
                }
              }
              index ++;
            }

            report[index].type = TMR_SR_STATUS_NONE;
            /* notify status response to listener */
            pthread_mutex_lock(&reader->listenerLock);
            slb = reader->statusListeners;
            while (slb)
            {
              slb->listener(reader, report, slb->cookie);
              slb = slb->next;
            }
            pthread_mutex_unlock(&reader->listenerLock);

          }
          else if (NULL != reader->statsListeners && NULL== reader->statusListeners)
          {
            /* Get status content flags */
            if ((0x80) > reader->statsFlag)
            {
              offset += 1;
            }
            else
            {
              offset += 2;
            }

            /**
             * preinitialize the rf ontime and the noise floor value to zero
             * berfore getting the reader stats
             */
            for (i = 0; i < stats.perAntenna.max; i++)
            {
              stats.perAntenna.list[i].antenna = 0;
              stats.perAntenna.list[i].rfOnTime = 0;
              stats.perAntenna.list[i].noiseFloor = 0;
            }

            TMR_fillReaderStats(reader, &stats, flags, tagRead->tagEntry.sMsg, offset);

            /**
             * iterate through the per antenna values,
             * If found  any 0-antenna rows, copy the
             * later rows down to compact out the empty space.
             */
            for (i = 0; i < reader->u.serialReader.txRxMap->len; i++)
            {
              if (!stats.perAntenna.list[i].antenna)
              {
                for (j = i + 1; j < reader->u.serialReader.txRxMap->len; j++)
                {
                  if (stats.perAntenna.list[j].antenna)
                  {
                    stats.perAntenna.list[i].antenna = stats.perAntenna.list[j].antenna;
                    stats.perAntenna.list[i].rfOnTime = stats.perAntenna.list[j].rfOnTime;
                    stats.perAntenna.list[i].noiseFloor = stats.perAntenna.list[j].noiseFloor;
                    stats.perAntenna.list[j].antenna = 0;
                    stats.perAntenna.list[j].rfOnTime = 0;
                    stats.perAntenna.list[j].noiseFloor = 0;

                    stats.perAntenna.len++;
                    break;
                  }
                }
              }
              else
              {
                /* Increment the length */
                stats.perAntenna.len++;
              }
            }

            /* store the requested flags for future use */
            stats.valid = reader->statsFlag;

            /* notify status response to listener */
            notify_stats_listeners(reader, &stats);
          }
          else
          {
            /**
             * Control comes here when, user added both the listeners,
             * We should pop up error for that
             **/
            TMR_Status ret;
            ret = TMR_ERROR_UNSUPPORTED;
            notify_exception_listeners(reader, ret);
          }
        }
#ifdef TMR_ENABLE_LLRP_READER
        else
        {
          /**
           * TODO: Handle RFSurveyReports in case of
           * async read
           **/
        }
#endif
      }

      /* Free the memory */
      if (TMR_READER_TYPE_SERIAL == reader->readerType)
      {
      	free(tagRead->tagEntry.sMsg);
      }
#ifdef TMR_ENABLE_LLRP_READER
      else
      {
      	TMR_LLRP_freeMessage(tagRead->tagEntry.lMsg);
      }
#endif
      free(tagRead);

      /* Now, increment the queue_slots as we have removed one entry */
      sem_post(&reader->queue_slots);
    }
  }
  return NULL;
}


static void
process_async_response(TMR_Reader *reader)
{
  TMR_Queue_tagReads *tagRead;
  uint16_t flags = 0;

  /* Decrement Queue slots */
  sem_wait(&reader->queue_slots);

  tagRead = (TMR_Queue_tagReads *) malloc(sizeof(TMR_Queue_tagReads));
  if (TMR_READER_TYPE_SERIAL == reader->readerType)
  {
    tagRead->tagEntry.sMsg = (uint8_t *) malloc(TMR_SR_MAX_PACKET_SIZE); /* size of bufResponse */
    memcpy(tagRead->tagEntry.sMsg, reader->u.serialReader.bufResponse, TMR_SR_MAX_PACKET_SIZE);
    tagRead->bufPointer = reader->u.serialReader.bufPointer;
  }
#ifdef TMR_ENABLE_LLRP_READER
  else
  {
    tagRead->tagEntry.lMsg = reader->u.llrpReader.bufResponse[0];
    reader->u.llrpReader.bufResponse[0] = NULL;
  }
#endif

  tagRead->isStatusResponse = reader->isStatusResponse;
  /**
   * Process the tag results here. The stats responses will be extracted
   * later by the parser thread.
   */
  if (TMR_READER_TYPE_SERIAL == reader->readerType)
  {
    if (false == tagRead->isStatusResponse)
    {
      TMR_TRD_init(&tagRead->trd);
      flags = GETU16AT(tagRead->tagEntry.sMsg, 8);
      TMR_SR_parseMetadataFromMessage(reader, &tagRead->trd, flags, &tagRead->bufPointer, tagRead->tagEntry.sMsg);
      TMR_SR_postprocessReaderSpecificMetadata(&tagRead->trd, &reader->u.serialReader);
      tagRead->trd.reader = reader;
    }
  }

  /* Enqueue the tagRead into Queue */
  enqueue(reader, tagRead);
  /* Increment queue_length */
  sem_post(&reader->queue_length);

  if ((false == reader->isStatusResponse) && (TMR_READER_TYPE_SERIAL == reader->readerType))
  {
    reader->u.serialReader.tagsRemainingInBuffer--;
  }
}

static void *
do_background_reads(void *arg)
{
  TMR_Status ret;
  TMR_Reader *reader;
  uint32_t onTime, offTime;
  int32_t sleepTime;
  uint64_t end, now, difftime;

  reader = arg;
  reader->trueAsyncflag = false;

  TMR_paramGet(reader, TMR_PARAM_READ_ASYNCOFFTIME, &offTime);

  while (1)
  {
    /* Wait for reads to be enabled */
    pthread_mutex_lock(&reader->backgroundLock);
    reader->backgroundRunning = false;

    pthread_cond_broadcast(&reader->backgroundCond);
    while (false == reader->backgroundEnabled)
    {
      reader->trueAsyncflag = false;
      pthread_cond_wait(&reader->backgroundCond, &reader->backgroundLock);
      if (true == reader->backgroundThreadCancel)
      {
        /**
         * thread is no more, required,
         * hence, making it terminated
         **/ 
        goto EXIT;
      }
    }

    TMR_paramGet(reader, TMR_PARAM_READ_ASYNCONTIME, &onTime);

    if (!reader->trueAsyncflag)
    {
      ret = TMR_read(reader, onTime, NULL);
      if (TMR_SUCCESS != ret)
      {
        if ((TMR_ERROR_TIMEOUT == ret) || (TMR_ERROR_CRC_ERROR == ret) ||
              (TMR_ERROR_SYSTEM_UNKNOWN_ERROR == ret) || (TMR_ERROR_TM_ASSERT_FAILED == ret))
        {
          if (TMR_READER_TYPE_SERIAL == reader->readerType)
          {
            reader->u.serialReader.transport.flush(&reader->u.serialReader.transport);
          }
          reader->backgroundEnabled = false;
        }

        /**
         * M5e and its variants hardware does not have a real PA protection.So, doing the read with out
         * antenna may cause the damage to the reader.
         *
         * it's okay to let M6e and its variants continue to operate because it has a PA protection mechanism.
         **/
        if (((TMR_ERROR_HIGH_RETURN_LOSS == ret) || (TMR_ERROR_NO_ANTENNA == ret))
            &&
            ((TMR_SR_MODEL_M6E != reader->u.serialReader.versionInfo.hardware[0]) &&
             (TMR_SR_MODEL_MICRO != reader->u.serialReader.versionInfo.hardware[0]) &&
             (TMR_SR_MODEL_M6E_NANO != reader->u.serialReader.versionInfo.hardware[0]) &&
             (TMR_SR_MODEL_M6E_I != reader->u.serialReader.versionInfo.hardware[0])))
        {
          reader->backgroundEnabled = false;
          reader->readState = TMR_READ_STATE_DONE;
          pthread_mutex_unlock(&reader->backgroundLock);
          notify_exception_listeners(reader, ret);
          break;
        }

        notify_exception_listeners(reader, ret);
        if(false == reader->searchStatus)
        {
          /**
           * There could be something wrong in initiating a search, continue the
           * effort to initiate a search. But meanwhile if stopReading()
           * is called, it will be blocked as the search itself is not started.
           * sem_post on read_started will unblock it.
           **/
          reader->readState = TMR_READ_STATE_STARTED;
          pthread_cond_broadcast(&reader->readCond);
          reader->backgroundEnabled = false;
        }
        pthread_mutex_unlock(&reader->backgroundLock);
        continue;
      }
      if(reader->continuousReading)
      {
        /**
         * Set this flag, In case of true async reading
         * we have to send the command only once.
         */
        reader->trueAsyncflag = true;
      }

      /**
       * Set an indication that the reading is started
       **/
      reader->readState = TMR_READ_STATE_ACTIVE;
      pthread_cond_broadcast(&reader->readCond);
    }

    reader->backgroundRunning = true;
    pthread_mutex_unlock(&reader->backgroundLock);

    if (true == reader->continuousReading)
    {
      /**  
       * Streaming is enabled only in case of M6e, 
       * read till the end of stream.
       */
      /* Make the time stamp zero for serial reader */
      while (true)
      {
        if (TMR_READER_TYPE_SERIAL == reader->readerType)
        {
          if (false == reader->u.serialReader.isBasetimeUpdated)
          {
            /* Update the base time stamp */
            TMR_SR_updateBaseTimeStamp(reader);
            reader->u.serialReader.isBasetimeUpdated = true;
          }
        }
        ret = TMR_hasMoreTags(reader);
        if (TMR_SUCCESS == ret)
        {
          /* Got a valid message, before posting it to queue
           * check whether we have slots free in the queue or
           * not. Validate this only for Serial reader.
           */
          if (TMR_READER_TYPE_SERIAL == reader->readerType)
          {
            int slotsFree = 0;
            int semret;
            /* Get the semaphore value */
            semret = sem_getvalue(&reader->queue_slots, &slotsFree);
            if (0 == semret)
            {
              if (10 > slotsFree)
              {
                tmr_sleep(20);
              }
              if (0 >= slotsFree)
              {
                /* In a normal case we should not come here.
                 * we are here means there is no place to
                 * store the tags. May be the read listener
                 * is not fast enough.
                 * In this case stop the read and exit.
                 */
                if (true == reader->searchStatus)
                {
                  isBufferOverFlow = true;
                  ret = TMR_ERROR_BUFFER_OVERFLOW;
                  notify_exception_listeners(reader, ret);
                  ret = verifySearchStatus(reader);
                  /*isBufferOverFlow = false;
                  pthread_mutex_lock(&reader->backgroundLock);
                  reader->backgroundEnabled = false;
                  reader->readState = TMR_READ_STATE_DONE;
                  pthread_cond_broadcast(&reader->readCond);
                  pthread_mutex_unlock(&reader->backgroundLock);
                  reader->searchStatus = false;*/
				  /* Waiting till all slots are free */
				  while(slotsFree < TMR_MAX_QUEUE_SLOTS)
				  {
					  tmr_sleep(20);
					  semret = sem_getvalue(&reader->queue_slots, &slotsFree);
				  }
				  reader->trueAsyncflag = false;
                  break;
                }
              }
            }
          }

          /* There is place to store the response. Post it */
          process_async_response(reader);
        }
        else if (TMR_ERROR_CRC_ERROR == ret)
        {
          /* Currently, just drop the corrupted packet,
           * inform the user about the error and move on.
           *
           * TODO: Fix the error by tracing the exact reason of failour
           */
          notify_exception_listeners(reader, ret);
        }
        else if (TMR_ERROR_TAG_ID_BUFFER_FULL == ret)
        {
          /* In case of buffer full error, notify the exception */
          notify_exception_listeners(reader, ret);

          /**
           * If stop read is already called, no need to resumbit the seach again.
           * Just spin in the curent loop and wait for the stop read command response.
           **/ 
          if (true == reader->searchStatus)
          {
            /**
             * Stop read is not called. Resubmit the search immediately, without user interaction
             * Resetting the trueAsyncFlag will send the continuous read command again.
             */

            ret = TMR_hasMoreTags(reader);
            reader->trueAsyncflag = false;
			reader->hasContinuousReadStarted = false;
            break;
          }
        }
        else
        {
          if ((TMR_ERROR_TIMEOUT == ret) || (TMR_ERROR_SYSTEM_UNKNOWN_ERROR == ret) || 
            (TMR_ERROR_TM_ASSERT_FAILED == ret) || (TMR_ERROR_LLRP_READER_CONNECTION_LOST == ret))
          {
            notify_exception_listeners(reader, ret);
            /** 
             * In case of timeout error or CRC error, flush the transport buffer.
             * this avoids receiving of junk response.
             */
            if (TMR_READER_TYPE_SERIAL == reader->readerType)
            {
              /* Handling this fix for serial reader now */
		          reader->u.serialReader.transport.flush(&reader->u.serialReader.transport);
            }

            /**
             * Check if reading is finished.
             * If not, send stop command.
             **/
            if (!reader->finishedReading)
            {
              reader->cmdStopReading(reader);
            }

            pthread_mutex_lock(&reader->backgroundLock);
            reader->backgroundEnabled = false;
            reader->readState = TMR_READ_STATE_DONE;
            pthread_cond_broadcast(&reader->readCond);
            pthread_mutex_unlock(&reader->backgroundLock);


            /**
             * Forced stop 
             * Reset continuous reading settings, so that
             * the subsequent startReading() call doesn't have
             * any surprises.
             **/
            reader->searchStatus = false;
            reset_continuous_reading(reader, true);
          }
          else if (TMR_ERROR_END_OF_READING == ret)
          {
            while(0 < reader->queue_depth)
            {
              /**
               * reader->queue_depth is greater than zero. i.e.,
               * there are still some tags left in queue.
               * Give some time for the parser to parse all of them.
               * 5 ms sleep shouldn't cause much delay.
               **/
              tmr_sleep(5);
            }

            /**
             * Since the reading is finished, disable this
             * thread.
             **/
            pthread_mutex_lock(&reader->backgroundLock);
            reader->backgroundEnabled = false;
            reader->readState = TMR_READ_STATE_DONE;
            pthread_cond_broadcast(&reader->readCond);
            pthread_mutex_unlock(&reader->backgroundLock);
            break;
          }
          else if ((TMR_ERROR_NO_TAGS_FOUND != ret) && (TMR_ERROR_NO_TAGS != ret) && (TMR_ERROR_TAG_ID_BUFFER_AUTH_REQUEST != ret) && (TMR_ERROR_TOO_BIG != ret))
          {
            /* Any exception other than 0x400 should be notified */
            notify_exception_listeners(reader, ret);
          }
          break;
        }
      }
    }
    else
    {
      /** 
       * On M5e and its variants, streaming is not supported
       * So still, retain the pseudo-async mechanism
       * Also, when asyncOffTime is non-zero the API should fallback to 
       * pseudo async mode.
       */

      end = tmr_gettime();

      while (TMR_SUCCESS == TMR_hasMoreTags(reader))
      {
        TMR_TagReadData trd;
        TMR_ReadListenerBlock *rlb;

        TMR_TRD_init(&trd);

        ret = TMR_getNextTag(reader, &trd);
        if (TMR_SUCCESS != ret)
        {
          pthread_mutex_lock(&reader->backgroundLock);
          reader->backgroundEnabled = false;
          pthread_mutex_unlock(&reader->backgroundLock);
          notify_exception_listeners(reader, ret);
          break;
        }

        pthread_mutex_lock(&reader->listenerLock);
        rlb = reader->readListeners;
        while (rlb)
        { 
          rlb->listener(reader, &trd, rlb->cookie);
          rlb = rlb->next;
        }
        pthread_mutex_unlock(&reader->listenerLock);
      }

      /* Wait for the asyncOffTime duration to pass */
      now = tmr_gettime();
      difftime = now - end;

      sleepTime = offTime - (uint32_t)difftime;
      if(sleepTime > 0)
      {
        tmr_sleep(sleepTime);
      }
    }
EXIT:
    if (reader->backgroundThreadCancel)
    {
      /**
       * oops.. time to exit
       **/ 
      pthread_exit(NULL);
    }
  }
  return NULL;
}
#endif /* TMR_ENABLE_BACKGROUND_READS */

TMR_Status
TMR_addReadListener(TMR_Reader *reader, TMR_ReadListenerBlock *b)
{
#ifndef SINGLE_THREAD_ASYNC_READ
  if (0 != pthread_mutex_lock(&reader->listenerLock))
    return TMR_ERROR_TRYAGAIN;
#endif
  b->next = reader->readListeners;
  reader->readListeners = b;
#ifndef SINGLE_THREAD_ASYNC_READ
  pthread_mutex_unlock(&reader->listenerLock);
#endif
  return TMR_SUCCESS;
}
#ifdef TMR_ENABLE_BACKGROUND_READS

TMR_Status
TMR_removeReadListener(TMR_Reader *reader, TMR_ReadListenerBlock *b)
{
  TMR_ReadListenerBlock *block, **prev;

  if (0 != pthread_mutex_lock(&reader->listenerLock))
    return TMR_ERROR_TRYAGAIN;

  prev = &reader->readListeners;
  block = reader->readListeners;
  while (NULL != block)
  {
    if (block == b)
    {
      *prev = block->next;
      break;
    }
    prev = &block->next;
    block = block->next;
  }

  pthread_mutex_unlock(&reader->listenerLock);

  if (block == NULL)
  {
    return TMR_ERROR_INVALID;
  }

  return TMR_SUCCESS;
}


TMR_Status
TMR_addAuthReqListener(TMR_Reader *reader, TMR_AuthReqListenerBlock *b)
{

  if (0 != pthread_mutex_lock(&reader->listenerLock))
    return TMR_ERROR_TRYAGAIN;

  b->next = reader->authReqListeners;
  reader->authReqListeners = b;

  pthread_mutex_unlock(&reader->listenerLock);

  return TMR_SUCCESS;
}


TMR_Status
TMR_removeAuthReqListener(TMR_Reader *reader, TMR_AuthReqListenerBlock *b)
{
  TMR_AuthReqListenerBlock *block, **prev;

  if (0 != pthread_mutex_lock(&reader->listenerLock))
    return TMR_ERROR_TRYAGAIN;

  prev = &reader->authReqListeners;
  block = reader->authReqListeners;
  while (NULL != block)
  {
    if (block == b)
    {
      *prev = block->next;
      break;
    }
    prev = &block->next;
    block = block->next;
  }

  pthread_mutex_unlock(&reader->listenerLock);

  if (block == NULL)
  {
    return TMR_ERROR_INVALID;
  }

  return TMR_SUCCESS;
}

TMR_Status
TMR_addStatusListener(TMR_Reader *reader, TMR_StatusListenerBlock *b)
{

  if (0 != pthread_mutex_lock(&reader->listenerLock))
    return TMR_ERROR_TRYAGAIN;

  b->next = reader->statusListeners;
  reader->statusListeners = b;

  /*reader->streamStats |= b->statusFlags & TMR_SR_STATUS_CONTENT_FLAGS_ALL;*/

  pthread_mutex_unlock(&reader->listenerLock);

  return TMR_SUCCESS;
}
#endif /* TMR_ENABLE_BACKGROUND_READS */

TMR_Status
TMR_addStatsListener(TMR_Reader *reader, TMR_StatsListenerBlock *b)
{
#ifndef SINGLE_THREAD_ASYNC_READ
  if (0 != pthread_mutex_lock(&reader->listenerLock))
    return TMR_ERROR_TRYAGAIN;
#endif
  b->next = reader->statsListeners;
  reader->statsListeners = b;

  /*reader->streamStats |= b->statusFlags & TMR_SR_STATUS_CONTENT_FLAGS_ALL; */
#ifndef SINGLE_THREAD_ASYNC_READ
  pthread_mutex_unlock(&reader->listenerLock);
#endif
  return TMR_SUCCESS;
}
#ifdef TMR_ENABLE_BACKGROUND_READS

TMR_Status
TMR_removeStatsListener(TMR_Reader *reader, TMR_StatsListenerBlock *b)
{
  TMR_StatsListenerBlock *block, **prev;

  if (0 != pthread_mutex_lock(&reader->listenerLock))
    return TMR_ERROR_TRYAGAIN;

  prev = &reader->statsListeners;
  block = reader->statsListeners;
  while (NULL != block)
  {
    if (block == b)
    {
      *prev = block->next;
      break;
    }
    prev = &block->next;
    block = block->next;
  }

  /* Remove the status flags requested by this listener and reframe */
  /*reader->streamStats = TMR_SR_STATUS_CONTENT_FLAG_NONE;
  {
    TMR_StatusListenerBlock *current;
    current = reader->statusListeners;
    while (NULL != current)
    {
      reader->streamStats |= current->statusFlags;
      current = current->next;
    }    
  }*/
  
  pthread_mutex_unlock(&reader->listenerLock);

  if (block == NULL)
  {
    return TMR_ERROR_INVALID;
  }

  return TMR_SUCCESS;
}

TMR_Status
TMR_removeStatusListener(TMR_Reader *reader, TMR_StatusListenerBlock *b)
{
  TMR_StatusListenerBlock *block, **prev;

  if (0 != pthread_mutex_lock(&reader->listenerLock))
    return TMR_ERROR_TRYAGAIN;

  prev = &reader->statusListeners;
  block = reader->statusListeners;
  while (NULL != block)
  {
    if (block == b)
    {
      *prev = block->next;
      break;
    }
    prev = &block->next;
    block = block->next;
  }

  /* Remove the status flags requested by this listener and reframe */
  /*reader->streamStats = TMR_SR_STATUS_CONTENT_FLAG_NONE;
    {
    TMR_StatusListenerBlock *current;
    current = reader->statusListeners;
    while (NULL != current)
    {
    reader->streamStats |= current->statusFlags;
    current = current->next;
    }
    }*/

  pthread_mutex_unlock(&reader->listenerLock);

  if (block == NULL)
  {
    return TMR_ERROR_INVALID;
  }

  return TMR_SUCCESS;
}

void cleanup_background_threads(TMR_Reader *reader)
{
  if (NULL != reader)
  {
    pthread_mutex_lock(&reader->backgroundLock);
    pthread_mutex_lock(&reader->listenerLock);
    reader->readExceptionListeners = NULL;
    reader->statsListeners = NULL;
    if (true == reader->backgroundSetup)
    {
      /**
       * Signal for the thread exit by 
       * removing all the pthread lock dependency
       **/
      reader->backgroundThreadCancel = true;
      pthread_cond_broadcast(&reader->backgroundCond);
    }
    pthread_mutex_unlock(&reader->listenerLock);
    pthread_mutex_unlock(&reader->backgroundLock);

    if (true == reader->backgroundSetup)
    {
      /**
       * Wait for the back ground thread to exit
       **/ 
      pthread_join(reader->backgroundReader, NULL);
    }

    pthread_mutex_lock(&reader->parserLock);
    pthread_mutex_lock(&reader->listenerLock);
    reader->readListeners = NULL;
    if (true == reader->parserSetup)
    {
      pthread_cancel(reader->backgroundParser);
    }
    pthread_mutex_unlock(&reader->listenerLock);
    pthread_mutex_unlock(&reader->parserLock);
  }
}

void*
do_background_receiveAutonomousReading(void * arg)
{
  TMR_Status ret;
  TMR_TagReadData trd;
  TMR_Reader *reader;
  TMR_Reader_StatsValues stats;  

  reader = arg;
  TMR_TRD_init(&trd);

  while (1)
  {
    ret = TMR_SR_receiveAutonomousReading(reader, &trd, &stats);
    if (TMR_SUCCESS == ret)
    {
      if (false == reader->isStatusResponse)
      {
        /* Notify the read listener */
        notify_read_listeners(reader, &trd);
      }
      else
      {
        notify_stats_listeners(reader, &stats);
      }
    }
  }
  return NULL;
}
#endif /* TMR_ENABLE_BACKGROUND_READS */