/****************************************************************************
**
**  Name:  LQUEUE.C
**
**  Description:
**      Interface to firmware for loading code.
**
**  $Log:   S:/tbird/mt2_186/loader/lqueue.c_v  $
** 
**    Rev 1.1   26 Feb 1997 11:50:14   Judy
** 
**    Rev 1.0   14 Jun 1996 17:25:36   Judy
** Initial revision.
** 
**    Rev 1.6   29 Aug 1994 15:03:20   nghia
** Revised comments to reflect the correct error code return.
** 
**    Rev 1.5   22 Aug 1994 17:40:02   nghia
** Revised LdrProcessVerifyError() to set the verify address to ADDR_PHYSICAL
** To get the address print out correctly.
** 
**    Rev 1.4   20 Jul 1994 09:52:32   nghia
** Fixed ER_CODEBUF_SIZE bug.  Cannot process data record larger than 2K.
** Added LdrQueueLargeDataRecord() to break large data record into multiple
** load buffers.  OMF load format support load record larger than 2K.
** 
**    Rev 1.3   14 Jul 1994 10:42:18   nghia
** Loader UI changes - LQueueInit() used load address space to initialize.
** Used Address server convert address space to string to display.
** 
**    Rev 1.2   15 Jun 1994 15:44:10   ernie
** Merged branch 1.8.1.0 from l695 directory.
** 
**    Rev 1.1   03 Jun 1994 15:00:48   nghia
** Used LdrSendMessageToCLI to output message.
** 
**    Rev 1.0   18 May 1994 17:02:12   nghia
** Initial revision.
** 
** 
**  $Header:   S:/tbird/mt2_186/loader/lqueue.c_v   1.1   26 Feb 1997 11:50:14   Judy  $
**
**  Copyright (C) 1993 Microtek International.  All rights reserved.
**
*****************************************************************************/
/****************************************************************************
** OLD HISTORY (Moved module from L695 directory)
**    Rev 1.8.1.0   17 May 1994 15:28:18   ernie
** Added serial number to load response packet.
** 
**    Rev 1.8   06 May 1994 14:49:58   ernie
** Removed one error check case--this made it incompatible with
** the PowerScope communication scheme.
** 
**    Rev 1.7   05 May 1994 10:59:44   ernie
** Implemented a serial number in load packets.  This allows the
** firmware to better detect repeated packets sent due to comm errors.
** This fixes a bug found at Ivy Biomedical where whole load packets
** were being thrown away.
** 
**    Rev 1.6   01 Nov 1993 10:00:38   nghia
** Fixed PPR 8975 - Report memory verify error
** Recorded the first verify error occurred in LoadResultCallback() to
** indexBufferError variable for later processing.
** 
**    Rev 1.5   14 Oct 1993 13:49:56   ernie
** 1. Increased load sd timeout from 10 to 30 seconds.  Legitimate packets
**    with lots of RE records were timing out.
** 2. Fixed bug in construction of load commands with byte count of 4.
**    This software was using the | operator instead of +, resulting in
**    the wrong command being sent.  This fixes ppr 8957, loading
**    intermec.abs, a file with a RE count of 0x2598C.
** 
**    Rev 1.4   30 Aug 1993 11:07:10   ron
** Changes for Load Progress dialog
** 
**    Rev 1.3   03 Aug 1993 17:46:10   nghia
** Removed LERR_xxxx to use standard error codes.
** 
**    Rev 1.2   15 Jul 1993 07:25:26   doug
** Use generic syntax error.
** 
**    Rev 1.1   13 Jul 1993 10:25:46   ernie
** TBIRDERR macro is obsolete
** 
**    Rev 1.0   13 Jul 1993 09:56:46   ernie
** Initial revision.
*****************************************************************************/

                       /****************************
                        *                          *
                        *       INCLUDE FILES      *
                        *                          *
                        ****************************/
#include <time.h>
#include <string.h>

#ifndef _BASEWIND_
#include "basewind.h"
#endif

#ifndef _LOADER_
#include "loader.h"
#endif

#ifndef _LQUEUE_
#include "lqueue.h"
#endif

#ifndef __LDRUTIL__
#include "ldrutil.h"
#endif

#ifndef _SDPROBE_
#include "sdprobe.h"
#endif

#ifndef _SSHARED_
#include "sshared.h"
#endif

#ifndef _TBIRDMEM_
#include "tbirdmem.h"
#endif

#ifndef _PVTASK_
#include "pvtask.h"
#endif

                       /****************************
                        *                          *
                        *     LOCAL DEFINITIONS    *
                        *                          *
                        ****************************/

#define BYTE_TEXT "byte"
#define WORD_TEXT "word"
#define LONG_TEXT "long"
#define DWORD_TEXT "dword"
#define UNKNOWN_TEXT "unknown"

typedef struct {
   enum { LOAD_EMPTY, LOAD_FILLING,      LOAD_FULL       } inputState;
   enum { LOAD_IDLE,  LOAD_TRANSMITTING, LOAD_PROCESSING } outputState;
   RETCODE error;
   U16 numBytes;
} LOAD_BUFFER_STATE;

STATIC ACCESS_SIZE loadAccess = SIZE_LONG; /* write by bytes, words, longs */
STATIC BOOLEAN loadAbortFlag;
STATIC U16 currentLoadBuffer;
STATIC U8 *currentLoadBufPtr;
STATIC U16 *currentCountPtr;
STATIC LOAD_BUFFER_STATE loadBufferState[NUM_LOAD];
STATIC U16 loadBufferTransmitting;
STATIC BOOLEAN sdsBusy = FALSE;
STATIC DESCRIPTOR loadDesc[NUM_LOAD];
STATIC U32 loadSerialNumber[NUM_LOAD];

/* Index of first buffer reporting error */
U16 indexBufferError;

/*
**  The interface to the box for loading code uses NUM_LOAD large
**  command buffers each of size SIZE_LOAD (from sdtempl.h).  Each
**  command buffer will usually have many individual load requests,
**  each corresponding to a single ASP, LD, or RE record from the load
**  file.  After the first load buffer is filled, the shared data
**  server is called to send it to the box.  While the box is
**  processing that buffer, the host fills the second buffer, then
**  sends that.  If the box has not finished processing the first
**  buffer, we wait.  When the first buffer is ready, it is reused. 
**
**  Shared data load command format (must match algorithm in box):
**        {repeat count}{byte count}{data}
**     or {byte count}{data}
**     or {set address}{address}
**     or {SD_EOF}
**         (byte[0] <= 0xF0) ? bytecount = byte[0]
**       : (byte[0] == 0xF1) ? bytecount = byte[1]
**       : (byte[0] == 0xF2) ? bytecount = byte[2..1]
**       : (byte[0] == 0xF4) ? bytecount = byte[4..1]
**       : (byte[0] == 0xF5) ?  repcount = byte[1]
**       : (byte[0] == 0xF6) ?  repcount = byte[2..1]
**       : (byte[0] == 0xF8) ?  repcount = byte[4..1]
**       : (byte[0] == 0xF9) ? address[0] = byte[1]
**       : (byte[0] == 0xFA) ? address[0..1] = byte[1..2]
**       : (byte[0] == 0xFC) ? address[0..3] = byte[1..4]
**       : (byte[0] == 0xFF) ?  SD_EOF
** Examples:
**       48 repeats of 12      -->  F5 48 01 12
**       1234 repeats of 87 65 -->  F6 34 12 02 87 65
**       set addr to 00987654  -->  FC 54 76 98 00
**       write 11 22 33 44 55  -->  05 11 22 33 44 55
**
*/
/* sd load commands going to box */
#define SD_BYTE_COUNT   0xF0
#define SD_REPEAT_COUNT 0xF4
#define SD_SET_ADDRESS  0xF8
#define SD_EOF          0xFF

#define LOADBUF_BYTES_USED   \
   (currentLoadBufPtr - SdnData(SDN_LOAD_DATA+currentLoadBuffer))
#define LOADBUF_SPACE_LEFT   \
   (SIZE_LOAD - LOADBUF_BYTES_USED - 1)   /* -1 allows space for SD_EOF */

#define SECOND (CLK_TCK)          /* from time.h */
#define LOAD_TIMEOUT (30*SECOND)  /* max wait for box to process packet */

                       /****************************
                        *                          *
                        *    EXTERNAL VARIABLES    *
                        *                          *
                        ****************************/

                       /****************************
                        *                          *
                        *     LOCAL PROTOTYPES     *
                        *                          *
                        ****************************/
RETCODE PRIVATE LdrQueueService(VOID);
RETCODE PRIVATE LdrQueueNeedBytes(U16 bytesNeeded, U32 currentLoadAddr);
RETCODE PRIVATE LdrQueueFindEmptyBuffer(U16 *index);
RETCODE PRIVATE LdrQueueLargeDataRecord(VOID *info, LOAD_FUNC FillInBytes,
                      U32 currentLoadAddr, U32 repCount, U32 byteCount);
VOID EXPORT LoadResultCallback(DESCRIPTOR desc);
VOID EXPORT LoadAsyncCallback(VOID);

                       /****************************
                        *                          *
                        *     EXECUTABLE CODE      *
                        *                          *
                        ****************************/

/****************************************************************************
**
**  LdrQueueCheckLoadAbort
**
**  description:
**    If user abort requested, send load abort command to box.  Box will
**    clean up and return ER_LDR_ABORT for all packets in progress.
**
*****************************************************************************/
RETCODE EXPORT LdrQueueCheckLoadAbort(VOID) {
   BOOLEAN abortFromEsc;
   RETCODE err;
   if ((err = TskCheckAbort(&abortFromEsc)) != GOOD) return(err);
   if (abortFromEsc) {
      LOAD_COMMAND command = LOAD_ABORT;
      loadAbortFlag = TRUE;
      LdrProgressDone(ER_LDR_ABORT);
      return(SdnWriteMember(SDN_LOAD_COMMAND, (U8*)&command, GOOD));
   }
   if ((err = LdrProgressInc()) != GOOD) {
      LOAD_COMMAND command = LOAD_ABORT;
      loadAbortFlag = TRUE;
      LdrProgressDone(ER_LDR_ABORT);
      return(SdnWriteMember(SDN_LOAD_COMMAND, (U8*)&command, GOOD));
   }
   return(GOOD);
}

/****************************************************************************
**
**  LdrQueueGetLoadAbort
**
**  description:
**    Return whether the load has been aborted already.
**
*****************************************************************************/
RETCODE EXPORT LdrQueueGetLoadAbort(BOOLEAN *aborted) {
   *aborted = loadAbortFlag;
   return(GOOD);
}

/****************************************************************************
**
**  LdrQueueInit
**
**  description:
**    Initializes static variables that control enqueuing load commands
**    en route to the box.  Called once before loading begins.  Must call
**    LdrQueueWait() to clean up when loading complete or if an error occurs.
**
*****************************************************************************/
RETCODE EXPORT LdrQueueInit(ADDR_SPACE addrSpace) {
   RETCODE err;
   LOAD_COMMAND command;
   LOOP_VAR i;
   BOOLEAN verify, timedOut;

   loadAbortFlag = FALSE;
   /* Set shared data members that are constant throughout load */
   if((err = MemGetVerifyWrites(&verify))!=GOOD) return(err);

   /* Reset index of first buffer reporting error - all index < NUM_LOAD */
   indexBufferError = NUM_LOAD;
   
   if((err = SdnWriteMemberIfChanged(SDN_LOAD_ACCESS, (U8*)&loadAccess, GOOD))
      != GOOD) return(err);
   if((err = SdnWriteMemberIfChanged(SDN_LOAD_VERIFY, (U8*)&verify, GOOD))
      != GOOD) return(err);
   if((err = SdnWriteMemberIfChanged(SDN_LOAD_SPACE, (U8*)&addrSpace, GOOD))
      != GOOD) return(err);
   for (i=0; i < NUM_LOAD; i++) {
      if ((err = SdnRegister(SDN_LOAD_RESULT+i,(FARPROC)LoadResultCallback,
        &loadDesc[i])) != GOOD) return(err);
   }
   currentLoadBuffer = loadBufferTransmitting = 0;
   currentLoadBufPtr = SdnData(SDN_LOAD_DATA+currentLoadBuffer) + sizeof(U32);
   currentCountPtr = NULL;
   for (i=0; i<NUM_LOAD; i++) {
      loadBufferState[i].inputState = LOAD_EMPTY;
      loadBufferState[i].outputState = LOAD_IDLE;
      loadBufferState[i].error = GOOD;
      loadSerialNumber[i] = 1ul;
   }

   /* For no interdependence, start each packet with complete address record*/
   if ((err = LdrQueueSetAddressRecord(0ul, 4)) != GOOD) return(err);

   /* Initialize firmware load.  Must send LOAD_FINISH when done. */
   command = LOAD_START;
   if((err = SdnWriteCmdReadResponse(SDN_LOAD_COMMAND, (U8*)&command, GOOD,
      SDN_LOAD_COMMAND, 0, &timedOut)) != GOOD) return(err);

   return(GOOD);
}

/****************************************************************************
**
**  LdrQueueSetAddressRecord
**
**  description:
**    Enqueues a set address record.
**
*****************************************************************************/
RETCODE EXPORT LdrQueueSetAddressRecord(U32 addr, U8 bytes) {
   RETCODE err;
   LOOP_VAR i;
   if (bytes == 0) return(GOOD);
   if ((err = LdrQueueNeedBytes(1+bytes, addr)) != GOOD) return(err);
   *currentLoadBufPtr++ = SD_SET_ADDRESS + bytes;
   for (i=0; i<bytes; i++) *currentLoadBufPtr++ = ((U8*)&addr)[i];
   currentCountPtr = NULL;   /* Cannot append next write to last command */
   return(GOOD);
}

/****************************************************************************
**
**  LdrQueueDataRecord
**
**  description:
**    Enqueues a data record.  If possible (no intervening set address or
**    repeat count commands), the data is appended to the end of the last
**    command and the byte count increased.
**
*****************************************************************************/
RETCODE EXPORT LdrQueueDataRecord(VOID *info, LOAD_FUNC FillInBytes,
      U32 currentLoadAddr, U32 repCount, U32 byteCount) {
   RETCODE err;
   if ((repCount == 0) || (byteCount == 0)) return(GOOD);
   if (repCount > 1ul) {
      LOOP_VAR i;
      U8 bytes = BYTES_TO_TRANSMIT(repCount);
      /* Need enough space for repeat record+byteCount+length of header */
      if ((err = LdrQueueNeedBytes(bytes+1+byteCount+3, currentLoadAddr))
         != GOOD) {
         if (err != ER_CODEBUF_SIZE) return err;
         /* Break the large data record into multiple load buffers */
         return LdrQueueLargeDataRecord(info, FillInBytes, currentLoadAddr,
                                           repCount, byteCount);
      }
      *currentLoadBufPtr++ = SD_REPEAT_COUNT + bytes;
      for (i=0; i<bytes; i++) *currentLoadBufPtr++ = ((U8*)&repCount)[i];
      currentCountPtr = NULL;   /* Cannot append to last command */
   } else {
      /* Need enough space for byteCount+length of header */
      if ((err = LdrQueueNeedBytes(byteCount+3, currentLoadAddr)) != GOOD) {
         if (err != ER_CODEBUF_SIZE) return err;
         /* Break the large data record into multiple load buffers */
         return LdrQueueLargeDataRecord(info, FillInBytes, currentLoadAddr,
                                        repCount, byteCount);
      }
   }
   if (currentCountPtr == NULL) {
      *currentLoadBufPtr++ = SD_BYTE_COUNT + 2;  /* count always 2 bytes */
      currentCountPtr = (U16*)currentLoadBufPtr;
      currentLoadBufPtr += 2;   /* Leave space for count to be filled in */
      *currentCountPtr = 0;
   }
   if ((err = (*FillInBytes)(info, currentLoadBufPtr, (U16)byteCount))
      != GOOD) return(err);
   *currentCountPtr += (U16)byteCount;
   currentLoadBufPtr += (U16)byteCount;
   if (repCount > 1ul)
       currentCountPtr = NULL;   /* Cannot append next command */
   return(GOOD);
}

/****************************************************************************
**
**  LdrQueueNeedBytes
**
**  description:
**    Checks for the requested number of bytes in the current load buffer.
**    If too little space available, the queue is flushed.
**
*****************************************************************************/
RETCODE PRIVATE LdrQueueNeedBytes(U16 bytesNeeded, U32 currentLoadAddr) {
   RETCODE err;
   if ((err = LdrQueueService()) != GOOD) return(err);
   if (LOADBUF_SPACE_LEFT >= bytesNeeded) return(GOOD);
   if ((err = LdrQueueFlush(currentLoadAddr)) != GOOD) return(err);
   if (LOADBUF_SPACE_LEFT < bytesNeeded) return(ER_CODEBUF_SIZE);
   return(GOOD);
}

/****************************************************************************
**
**  LdrQueueFlush
**
**  description:
**    Sends current load buffer to box.  Does not wait for box to process
**    buffer, but does update currentLoadBuffer to the next buffer.
**
*****************************************************************************/
RETCODE EXPORT LdrQueueFlush(U32 currentLoadAddr) {
   RETCODE err;
   *currentLoadBufPtr++ = SD_EOF;  /* Indicates end of command records */
   loadBufferState[currentLoadBuffer].inputState = LOAD_FULL;
   loadBufferState[currentLoadBuffer].numBytes = LOADBUF_BYTES_USED;
   
   /* Send packet if channel available */
   if ((err = LdrQueueService()) != GOOD) return(err);

   /* Select another buffer to begin filling */
   if ((err = LdrQueueFindEmptyBuffer(&currentLoadBuffer)) != GOOD)
      return(err);
   currentLoadBufPtr = SdnData(SDN_LOAD_DATA+currentLoadBuffer) + sizeof(U32);
   currentCountPtr = NULL;

   /* For no interdependence, start each packet with complete address record*/
   if ((err = LdrQueueSetAddressRecord(currentLoadAddr, 4)) != GOOD)
      return(err);

   return(GOOD);
}


/****************************************************************************
**
**  LdrQueueWait
**
**  description:
**    Waits for all load buffers to empty and propagates any errors
**    returned from the box.  Informs box that loading is completed.
**
*****************************************************************************/
RETCODE EXPORT LdrQueueWait() {
   RETCODE err=GOOD, err2=GOOD;
   LOAD_COMMAND command;
   U16 i;
   BOOLEAN timedOut;
   clock_t startTime = clock();
   for (i=0; i<NUM_LOAD; i++) {
      if (!err) err = err2;
      if ((err2 = loadBufferState[i].error) != GOOD) continue;
      while ((loadBufferState[i].outputState != LOAD_IDLE)
             || (loadBufferState[i].inputState == LOAD_FULL)) {
         if ((err2 = LdrQueueService()) != GOOD) break;
         if ((err2 = LdrQueueCheckLoadAbort()) != GOOD) break;
         if (clock() > startTime + LOAD_TIMEOUT) {
            err2 = ER_SDS_TIMEOUT;
            break;
         }
      }
   }
   if (!err) err = err2;
   for (i=0; i<NUM_LOAD; i++) {
      err2 = SdUnRegister(loadDesc[i]);
      if (!err) err = err2;
   }
   /* Tell firmware we are done with load */
   command = LOAD_FINISH;
   err2 = SdnWriteCmdReadResponse(SDN_LOAD_COMMAND, (U8*)&command, GOOD,
      SDN_LOAD_COMMAND, 0, &timedOut);

   return(err?err:err2);
}

/****************************************************************************
**
**  LdrQueueService
**
**  description:
**     Service callbacks and transmit any buffers which are ready
**
*****************************************************************************/
RETCODE PRIVATE LdrQueueService(VOID) {
   RETCODE err;
   U16 i;
   SdsInputAvailable();
   if (sdsBusy) return(GOOD);
   for (i=0; i<NUM_LOAD; i++) {
      if (loadBufferState[i].error != GOOD) return(loadBufferState[i].error);
      if ((loadBufferState[i].inputState == LOAD_FULL)
            && (loadBufferState[i].outputState == LOAD_IDLE)) {
         /* Send filled buffer */
         sdsBusy = TRUE;
         loadBufferTransmitting = i;
         loadBufferState[i].outputState = LOAD_TRANSMITTING;

         *((U32*)SdnData(SDN_LOAD_DATA+i)) = loadSerialNumber[i];
         loadSerialNumber[i]++;
         if ((err = SdnWritePartialMemberAsync(SDN_LOAD_DATA+i, 0,
               loadBufferState[i].numBytes, NULL, GOOD,
               (FARPROC)LoadAsyncCallback)) != GOOD) {
            loadBufferState[i].error = err;
            loadBufferState[i].outputState = LOAD_IDLE;
            return(err);
         }
         return(GOOD);
      }
   }
   return(GOOD);
}

/****************************************************************************
**
**  LdrQueueFindEmptyBuffer
**
**  description:
**     Return index of empty buffer.  Waits until buffer is available.
**     Search first for a buffer which is both empty and idle (not being
**     processed).  This guarantees that it will be transmittable when
**     filled.  If all buffers are in use, search for one that is being
**     processed and refill that one.
**
*****************************************************************************/
RETCODE PRIVATE LdrQueueFindEmptyBuffer(U16 *index) {
   RETCODE err;
   U16 i;
   clock_t startTime = clock();
   while (1) {
      /* Look for empty & idle buffer */
      for (i=0; i<NUM_LOAD; i++) {
         if (loadBufferState[i].error!=GOOD) return(loadBufferState[i].error);
         if ((loadBufferState[i].inputState == LOAD_EMPTY)
             && (loadBufferState[i].outputState == LOAD_IDLE)) {
            *index = i;
            loadBufferState[i].inputState = LOAD_FILLING;
            return(GOOD);
         }
      }
      /* Look for other empty buffer */
      for (i=0; i<NUM_LOAD; i++) {
         if (loadBufferState[i].error!=GOOD) return(loadBufferState[i].error);
         if (loadBufferState[i].inputState == LOAD_EMPTY) {
            *index = i;
            loadBufferState[i].inputState = LOAD_FILLING;
            return(GOOD);
         }
      }
      /* No empty buffers found...wait for one, checking for timeout, abort*/
      if ((err = LdrQueueService()) != GOOD) return(err);
      if ((err = LdrQueueCheckLoadAbort()) != GOOD) return(err);
      if (clock() > startTime + LOAD_TIMEOUT) 
         return(ER_SDS_TIMEOUT);
   }
}

/****************************************************************************
**
**  LoadResultCallback
**
**  description:
**    Called when box completes processing a buffer.  If that buffer has been
**    filled and is waiting to send, send it immediately.  If load error, set
**    loadbuffer's error code and return.
**
*****************************************************************************/
VOID EXPORT LoadResultCallback(DESCRIPTOR desc) {
   LOAD_RESULT result;
   MEMBER_INDEX index;
   
   if (SdGetMemberIndex(desc,&index) != GOOD) return;

   if (SdReadMember(desc, (U8*)&result) != GOOD) return;
     /* Ignore result if serial number is incorrect (retransmission) */
   if (result.serialNumber != loadSerialNumber[(U16)index]-1) return;
   loadBufferState[(U16)index].error = result.retcode;

   /* Record the first buffer that had ER_MEMORY_VERIFY error */
   if ((loadBufferState[(U16)index].error == ER_MEMORY_VERIFY) &&
       (indexBufferError == NUM_LOAD)) {
      indexBufferError = (U16)index;
   }
   loadBufferState[(U16)index].outputState = LOAD_IDLE;
}

/****************************************************************************
**
**  LoadAsyncCallback
**
**  description:
**    Called when shared data server completes sending the asynchronous packet.
**    If transmission error, set loadbuffer's error code.  Advance outputState
**    of loadbuffer to LOAD_PROCESSING, and the input state to LOAD_EMPTY,
**    allowing this buffer to begin refilling.
**
*****************************************************************************/
VOID EXPORT LoadAsyncCallback(VOID) {
   U16 i=loadBufferTransmitting;
   RETCODE err;
   if ((err = SdsAsyncWait()) != GOOD) {  /* Get error from transmission */
      loadBufferState[i].error = err;
      loadBufferState[i].outputState = LOAD_IDLE;
      return;
   }
   if (loadBufferState[i].outputState == LOAD_TRANSMITTING) {
      loadBufferState[i].outputState = LOAD_PROCESSING;
   }
   loadBufferState[i].inputState = LOAD_EMPTY;   /* Ready to refill */
   sdsBusy = FALSE;
}

/***************************************************************************
**
**  LdrQueueProcessVerifyError
**
*****************************************************************************/
RETCODE EXPORT LdrQueueProcessVerifyError(VOID) {
   U32 verifyOffset, expectedData, actualData;
   ADDR_SPACE addrSpace;
   S8 verBuf[ADDR_BUFF_SZ], spaceBuf[4];
   DESCRIPTOR addr;
   CHAR verifyErrorString[E_ERRSIZE];
   RETCODE err;

   /* Make sure that we are not going to crash */
   if (indexBufferError >= NUM_LOAD) 
      return(GOOD);
      
   /* collect information of the indexBufferError */
   SdnReadMember(SDN_LVERIFY_OFFSET+indexBufferError, (U8 *)&verifyOffset);
   SdnReadMember(SDN_LVERIFY_EXPECTED+indexBufferError ,(U8 *)&expectedData);
   SdnReadMember(SDN_LVERIFY_ACTUAL+indexBufferError, (U8 *)&actualData);
   SdnReadMember(SDN_LVERIFY_SPACE+indexBufferError, (U8 *)&addrSpace);
   
   if ((err = AdrCreateAddress(&addr)) != GOOD)
      return(err);
   
   // Use Address server to convert address to text
   if ((AdrSetAddrOffset(addr, verifyOffset) != GOOD) ||
       (AdrSetAddrType(addr, ADDR_PHYSICAL)  != GOOD) ||
       (AdrConvAddressToTextWithParams(addr, TRUE, TRUE, verBuf) != GOOD)) {
      // if address server failed to convert, let do it 
      wsprintf(verBuf, "0x%lX", verifyOffset);
   }
       
   AdrDestroyAddress(addr);
   if (AdrGetSpaceStr(addrSpace, (LPSTR) spaceBuf) != GOOD)
      wsprintf(spaceBuf,"   ");
   
   /* format error message */
   wsprintf(verifyErrorString,
      "address: %s %s, expected: 0x%lX, actual: 0x%lX",
      verBuf, spaceBuf, expectedData, actualData);

   /* send error text in error text server */
   return(ErrSaveMemoryVerifyInfo((LPSTR)verifyErrorString));
} /* LdrQueueProcessVerifyError */

/***************************************************************************
**
** LdrCliSetMemSize
**
** Description:
**    Cli interface to the set the global parameter for the size of data
**    when sent to the probe
**
** syntax: loadSize [byte|word|long|dword]
**
*****************************************************************************/
RETCODE EXPORT LdrCliSetMemSize(LPSTR cmdString, U32 argc, U32 argv[]) {
   LPSTR lpParam;
  
   if (argc > 2)
      return ER_CLI_SYNTAX;
   if (argc == 2) {
      lpParam = &(cmdString[(U16)argv[1]]);
      if (strncmpi(lpParam, BYTE_TEXT, strlen(lpParam))==0) {
         loadAccess = BYTE_SIZE;
      } else if (strncmpi(lpParam, WORD_TEXT, strlen(lpParam))==0) {
         loadAccess = WORD_SIZE;
      } else if (strncmpi(lpParam, LONG_TEXT, strlen(lpParam))==0) {
         loadAccess = DWORD_SIZE;
      } else if (strncmpi(lpParam, DWORD_TEXT, strlen(lpParam))==0) {
         loadAccess = DWORD_SIZE;
      } else return ER_CLI_SYNTAX;
   }
   // send the result to Shell
   return LdrSendMessageToCLI((loadAccess==BYTE_SIZE)  ? BYTE_TEXT:
                           (loadAccess==WORD_SIZE)  ? WORD_TEXT:
                           (loadAccess==DWORD_SIZE) ? LONG_TEXT:
                           UNKNOWN_TEXT);
}

/****************************************************************************
**
**  LdrQueueLargeDataRecord
**
**  description:
**     Process large data record. Break the large data record into multiple
**     load data buffers.  Return when completely process the large
**     data record, or an error occurs.
**
*****************************************************************************/
RETCODE PRIVATE LdrQueueLargeDataRecord(VOID *info, LOAD_FUNC FillInBytes,
                      U32 currentLoadAddr, U32 repCount, U32 byteCount) {
   RETCODE err = GOOD;
   U8 bytes = BYTES_TO_TRANSMIT(repCount);
   U8 bytesHdr = ((repCount > 1ul) ? bytes+1+3 : 3);
   U32 bytesToLoad = byteCount;
   
   /* Fill each Load buffer until bytesToLoad is 0 */ 
   while (bytesToLoad > 0) {
      /* Make sure that there is some space left to load */
      if ((LOADBUF_SPACE_LEFT <= 0) &&
          ((err = LdrQueueFlush(currentLoadAddr)) != GOOD))
         return err;
      /* Request for the space available in a load buffer to load */
      byteCount = min((LOADBUF_SPACE_LEFT - bytesHdr), bytesToLoad);
      /* Get the load buffer */
      if ((err = LdrQueueNeedBytes(byteCount, currentLoadAddr)) != GOOD) 
         return err; /* Failed to get any available load buffer */
      /* Setup for Repeat record */
      if (repCount > 1ul) {
         LOOP_VAR i;
         /* NOTES: Need more testing on repeat count > 1 with
         ** record larger than 2K.
         */
         *currentLoadBufPtr++ = SD_REPEAT_COUNT + bytes;
         for (i=0; i<bytes; i++) *currentLoadBufPtr++ = ((U8*) &repCount)[i];
         currentCountPtr = NULL;   /* Cannot append to last command */
      } 
      if (currentCountPtr == NULL) {
         *currentLoadBufPtr++ = SD_BYTE_COUNT + 2;  /* count always 2 bytes */
         currentCountPtr = (U16*)currentLoadBufPtr;
         currentLoadBufPtr += 2;   /* Leave space for count to be filled in */
         *currentCountPtr = 0;
      }
      if ((err = (*FillInBytes)(info, currentLoadBufPtr, (U16)byteCount))
          != GOOD) return(err);
      *currentCountPtr += (U16) byteCount;
      currentLoadBufPtr += (U16) byteCount;

      /* Update the bytesToLoad and the currentLoadAddr for the next one */
      if (repCount > 1ul) {
         currentCountPtr = NULL;   /* Cannot append next command */
         bytesToLoad -= repCount*byteCount;
         currentLoadAddr += repCount*byteCount;
      } else {
         bytesToLoad -= byteCount;
         currentLoadAddr += byteCount;
      }
   }
   /* all done */
   return GOOD;
}

/******************************** E O F ***********************************/

