예제 #1
0
파일: main.c 프로젝트: kl80/nrf24le1prog
usbMsgLen_t usbFunctionSetup(uint8_t data[8])
{
    usbRequest_t *rq = (void *)data;

    usbMsgPtr = usbOutputBuffer;

    if (rq->bRequest == REQ_TEST_PROGRAMMER) {
        usbOutputBuffer[0] = ERROR_OK;
        usbOutputBuffer[1] = MAJOR_VERSION;
        usbOutputBuffer[2] = MINOR_VERSION;
        usbOutputBuffer[3] = rq->wValue.bytes[0];
        usbOutputBuffer[4] = rq->wValue.bytes[1];
        usbOutputBuffer[5] = rq->wIndex.bytes[0];
        usbOutputBuffer[6] = rq->wIndex.bytes[1];
        return 7;
    } else if (rq->bRequest == REQ_TURN_PROG_ON) {
        return turnProgOn();
    } else if (rq->bRequest == REQ_TURN_PROG_OFF) {
        return turnProgOff();
    } else if (rq->bRequest == REQ_TEST_NRF) {
        return nrfTest();
    } else if (rq->bRequest == REQ_PROGRAM_PAGE) {
        return programPage(rq->wValue.word, rq->wIndex.word);
    } else if(rq->bRequest == REQ_READ) {
        return readMemory(rq->wValue.word, rq->wIndex.word);
    } else if(rq->bRequest == REQ_ERASE_PAGE) {
        return erasePage(rq->wValue.word);
    }

    return 0;
}
예제 #2
0
/*********************************************************************
 * @fn      cleanErasedPage
 *
 * @brief   Erases a page in Flash if the page is not completely erased.
 *
 * @param   pg - Valid NV page to erase.
 *
 * @return  none
 */
static void cleanErasedPage( uint8 pg )
{
  uint8 buf;
  uint16 offset;

  for (offset = 0; offset < OSAL_NV_PAGE_SIZE; offset ++)
  {
    HalFlashRead(pg, offset, &buf, 1);
    if (buf != OSAL_NV_ERASED)
    {
      erasePage(pg);
      break;
    }
  }
}
예제 #3
0
/*********************************************************************
 * @fn      compactPage
 *
 * @brief   Compacts the page specified.
 *
 * @param   srcPg - Valid NV page to compact from.
 *                  The page must have changed its state (header) to xfer state
 *                  prior to this function call. This function will not
 *                  modify the state of its header to xfer state before starting
 *                  to compact.
 *
 * @return  none.
 */
static void compactPage( uint8 srcPg )
{
  uint16 srcOff, dstOff;
  uint8 dstPg;
  osalSnvId_t lastId = (osalSnvId_t) 0xFFFF;

  dstPg = (srcPg == OSAL_NV_PAGE_BEG)? OSAL_NV_PAGE_END : OSAL_NV_PAGE_BEG;

  dstOff = OSAL_NV_PAGE_HDR_SIZE;

  // Read from the latest value
  srcOff = pgOff - sizeof(osalNvItemHdr_t);

  while (srcOff >= OSAL_NV_PAGE_HDR_SIZE)
  {
    osalNvItemHdr_t hdr;

    if (failF)
    {
      // Failure during transfer item will make next findItem error prone.
      return;
    }

    HalFlashRead(srcPg, srcOff, (uint8 *) &hdr, OSAL_NV_WORD_SIZE);

    if (hdr.id == 0xFFFF)
    {
      // Invalid entry. Skip this one.
      if (hdr.len & OSAL_NV_INVALID_LEN_MARK)
      {
        srcOff -= OSAL_NV_WORD_SIZE;
      }
      else
      {
        if (hdr.len + OSAL_NV_WORD_SIZE <= srcOff)
        {
          srcOff -= hdr.len + OSAL_NV_WORD_SIZE;
        }
        else
        {
          // invalid length. Source page must be a corrupt page.
          // This is possible only if the NV initialization failed upon erasing
          // what is selected as active page.
          // This is supposed to be a very rare case, as power should be
          // shutdown exactly during erase and then the page header is
          // still retained as either the Xfer or the Active state.

          // For production code, it might be useful to attempt to erase the page
          // so that at next power cycle at least the device is runnable
          // (with all entries removed).
          // However, it might be still better not to attempt erasing the page
          // just to see if this very rare case actually happened.
          //erasePage(srcPg);

          HAL_ASSERT_FORCED();
          return;
        }
      }

      continue;
    }

    // Consider only valid item
    if (!(hdr.id & OSAL_NV_INVALID_ID_MARK) && hdr.id != lastId)
    {
      // lastId is used to speed up compacting in case the same item ID
      // items were neighboring each other contiguously.
      lastId = (osalSnvId_t) hdr.id;

      // Check if the latest value of the item was already written
      if (findItem(dstPg, dstOff, lastId) == 0)
      {
        // This item was not copied over yet.
        // This must be the latest value.
        // Write the latest value to the destination page

        xferItem(dstPg, dstOff, hdr.len, srcOff - hdr.len);

        dstOff += hdr.len + OSAL_NV_WORD_SIZE;
      }
    }
    srcOff -= hdr.len + OSAL_NV_WORD_SIZE;
  }

  // All items copied.
  // Activate the new page
  setActivePage(dstPg);

  if (!failF)
  {
    pgOff = dstOff; // update active page offset
  }

  // Erase the currently active page
  erasePage(srcPg);
}
예제 #4
0
/*********************************************************************
 * @fn      initNV
 *
 * @brief   Initialize the NV flash pages.
 *
 * @param   none
 *
 * @return  TRUE if initialization succeeds. FALSE, otherwise.
 */
static uint8 initNV( void )
{
  uint32 pgHdr;
  uint8 xferPg = OSAL_NV_PAGE_NULL;
  uint8 pg;

  failF = FALSE;
  activePg = OSAL_NV_PAGE_NULL;

  // Pick active page and clean up erased page if necessary
  for ( pg = OSAL_NV_PAGE_BEG; pg <= OSAL_NV_PAGE_END; pg++ )
  {
    HalFlashRead(pg, OSAL_NV_PAGE_HDR_OFFSET, (uint8 *)(&pgHdr), OSAL_NV_PAGE_HDR_SIZE);

    if ( pgHdr == OSAL_NV_ACTIVE_PAGE_STATE)
    {
      if (activePg != OSAL_NV_PAGE_NULL)
      {
        // Both pages are active only when power failed during flash erase and
        // with very low probability.
        // As it is hard (code size intensive) to figure out which page is the real active page,
        // and theoretically impossible as well in lowest probability, erase both pages
        // in this case
        cleanErasedPage(activePg);
        cleanErasedPage(pg);
        activePg = OSAL_NV_PAGE_NULL;
      }
      else
      {
        activePg = pg;
      }
    }
    else if ( pgHdr == OSAL_NV_XFER_PAGE_STATE)
    {
      xferPg = pg;
    }
    else
    {
      // Erase this page if it is not erased.
      // This is to ensure that any page that were in the middle of
      // compacting gets erased.
      cleanErasedPage(pg);
    }
  }

  if (activePg == OSAL_NV_PAGE_NULL)
  {
    if (xferPg == OSAL_NV_PAGE_NULL)
    {
      // Both pages are erased. This must be initial state.
      // Pick one page as active page.
      setActivePage(OSAL_NV_PAGE_BEG);
      pgOff = OSAL_NV_PAGE_HDR_SIZE;

      // If setting active page from a completely erased page failed,
      // it is not recommended to operate any further.
      // Other cases, even if non-active page is corrupt, NV module can still read
      // the active page content and hence this function could return TRUE.
      return (!failF);
    }
    else
    {
      // Compacting a page hasn't completed in previous power cycle.
      // Complete the compacting.
      activePg = xferPg;
      findOffset();

      compactPage(xferPg);
    }
  }
  else
  {
    if (xferPg != OSAL_NV_PAGE_NULL)
    {
      // Compacting has completed except for the final step of erasing
      // the xferPage.
      erasePage(xferPg);
    }

    // find the active page offset to write a new variable location item
    findOffset();
  }

  return TRUE;
}
예제 #5
0
파일: OSAL_Nv.c 프로젝트: pelucky/Location
/*********************************************************************
 * @fn      compactPage
 *
 * @brief   Compacts the page specified.
 *
 * @param   srcPg - Valid NV page to erase.
 *
 * @return  none
 */
static void compactPage( uint8 srcPg )
{
  uint16 dstOff = pgOff[pgRes-OSAL_NV_PAGE_BEG];
  uint16 srcOff = OSAL_NV_ZEROED_ID;
  osalNvHdr_t hdr;

  // Mark page as being in process of compaction.
  writeWordH( srcPg, OSAL_NV_PG_XFER, (uint8*)(&srcOff) );

  srcOff = OSAL_NV_PAGE_HDR_SIZE;

  do
  {
    uint16 sz;
    HalFlashRead(srcPg, srcOff, (uint8 *)(&hdr), OSAL_NV_HDR_SIZE);

    if ( hdr.id == OSAL_NV_ERASED_ID )
    {
      break;
    }

    srcOff += OSAL_NV_HDR_SIZE;

    if ( (srcOff + hdr.len) > OSAL_NV_PAGE_FREE )
    {
      break;
    }

    sz = OSAL_NV_DATA_SIZE( hdr.len );

    if ( hdr.id != OSAL_NV_ZEROED_ID )
    {
      if ( hdr.chk == calcChkF( srcPg, srcOff, hdr.len ) )
      {
        setItem( srcPg, srcOff, eNvXfer );
        writeBuf( pgRes, dstOff, OSAL_NV_HDR_SIZE, (byte *)(&hdr) );
        dstOff += OSAL_NV_HDR_SIZE;
        xferBuf( srcPg, srcOff, pgRes, dstOff, sz );
        dstOff += sz;
      }

      setItem( srcPg, srcOff, eNvZero );  // Mark old location as invalid.
    }

    srcOff += sz;

  } while ( TRUE );

  pgOff[pgRes-OSAL_NV_PAGE_BEG] = dstOff;

  /* In order to recover from a page compaction that is interrupted,
   * the logic in osal_nv_init() depends upon the following order:
   * 1. Compacted page is erased.
   * 2. State of the target of compaction is changed ePgActive to ePgInUse.
   */
  erasePage( srcPg );

  // Mark the reserve page as being in use.
  setPageUse( pgRes, TRUE );

  // Set the reserve page to be the newly erased page.
  pgRes = srcPg;
}
예제 #6
0
파일: OSAL_Nv.c 프로젝트: pelucky/Location
/*********************************************************************
 * @fn      initNV
 *
 * @brief   Initialize the NV flash pages.
 *
 * @param   none
 *
 * @return  TRUE
 */
static uint8 initNV( void )
{
  osalNvPgHdr_t pgHdr;
  uint8 oldPg = OSAL_NV_PAGE_NULL;
  uint8 newPg = OSAL_NV_PAGE_NULL;
  uint8 findDups = FALSE;
  uint8 pg;

  pgRes = OSAL_NV_PAGE_NULL;

  for ( pg = OSAL_NV_PAGE_BEG; pg <= OSAL_NV_PAGE_END; pg++ )
  {
    HalFlashRead(pg, OSAL_NV_PAGE_HDR_OFFSET, (uint8 *)(&pgHdr), OSAL_NV_HDR_SIZE);

    if ( pgHdr.active == OSAL_NV_ERASED_ID )
    {
      if ( pgRes == OSAL_NV_PAGE_NULL )
      {
        pgRes = pg;
      }
      else
      {
        setPageUse( pg, TRUE );
      }
    }
    else  // Page is active.
    {
      // If the page is not yet in use, it is the tgt of items from an xfer.
      if ( pgHdr.inUse == OSAL_NV_ERASED_ID )
      {
        newPg = pg;
      }
      // An Xfer from this page was in progress.
      else if ( pgHdr.xfer != OSAL_NV_ERASED_ID )
      {
        oldPg = pg;
      }
    }

    // Calculate page offset and lost bytes - any "old" item triggers an N^2 re-scan from start.
    if ( initPage( pg, OSAL_NV_ITEM_NULL, findDups ) != OSAL_NV_ITEM_NULL )
    {
      findDups = TRUE;
      pg = OSAL_NV_PAGE_BEG-1;
      continue;
    }
  }  // for ( pg = OSAL_NV_PAGE_BEG; pg <= OSAL_NV_PAGE_END; pg++ )

  /* First the old page is erased, and then the new page is put into use.
   * So if a transfer was in progress, the new page will always not yet be
   * marked as in use, since that is the last step to ending a transfer.
   */
  if ( newPg != OSAL_NV_PAGE_NULL )
  {
    /* If there is already a fallow page reserved, keep it and put the newPg in use.
     * An unfinished compaction will finish to the new reserve page and the old page
     * will be erased and reserved.
     */
    if ( pgRes != OSAL_NV_PAGE_NULL )
    {
      setPageUse( newPg, TRUE );
    }
    /* If setting old page to 'xfer' failed or board reset before it was effected, there is no way
     * to know which page was the 'old page' - so just reset all NV pages to start clean.
     */
    else if ( oldPg != OSAL_NV_PAGE_NULL )
    {
      pgRes = newPg;
    }

    /* If a page compaction was interrupted and the page being compacted is not
     * yet erased, then there may be items remaining to xfer before erasing.
     */
    if ( oldPg != OSAL_NV_PAGE_NULL )
    {
      compactPage( oldPg );
    }
  }

  /* If no page met the criteria to be the reserve page:
   *  - A compactPage() failed or board reset before doing so.
   *  - Perhaps the user changed which Flash pages are dedicated to NV and downloaded the code
   *    without erasing Flash?
   */
  if ( pgRes == OSAL_NV_PAGE_NULL )
  {
    for ( pg = OSAL_NV_PAGE_BEG; pg <= OSAL_NV_PAGE_END; pg++ )
    {
      erasePage( pg );
    }
    initNV();
  }

  return TRUE;
}
예제 #7
0
/*********************************************************************
 * @fn      compactPage
 *
 * @brief   Compacts the page specified.
 *
 * @param   srcPg - Valid NV page to erase.
 * @param   skipId - Item Id to not compact.
 *
 * @return  TRUE if valid items from 'srcPg' are successully compacted onto the 'pgRes';
 *          FALSE otherwise.
 *          Note that on a failure, this could loop, re-erasing the 'pgRes' and re-compacting with
 *          the risk of infinitely looping on HAL flash failure.
 *          Worst case scenario: HAL flash starts failing in general, perhaps low Vdd?
 *          All page compactions will fail which will cause all osal_nv_write() calls to return
 *          NV_OPER_FAILED.
 *          Eventually, all pages in use may also be in the state of "pending compaction" where
 *          the page header member OSAL_NV_PG_XFER is zeroed out.
 *          During this "HAL flash brown-out", the code will run and OTA should work (until low Vdd
 *          causes an actual chip brown-out, of course.) Although no new NV items will be created
 *          or written, the last value written with a return value of SUCCESS can continue to be
 *          read successfully.
 *          If eventually HAL flash starts working again, all of the pages marked as
 *          "pending compaction" may or may not be eventually compacted. But, initNV() will
 *          deterministically clean-up one page pending compaction per power-cycle
 *          (if HAL flash is working.) Nevertheless, one erased reserve page will be maintained
 *          through such a scenario.
 */
static uint8 compactPage( uint8 srcPg, uint16 skipId )
{
  uint16 srcOff;
  uint8 rtrn;

  // To minimize code size, only check for a clean page here where it's absolutely required.
  for (srcOff = 0; srcOff < OSAL_NV_PAGE_SIZE; srcOff++)
  {
    HalFlashRead(pgRes, srcOff, &rtrn, 1);
    if (rtrn != OSAL_NV_ERASED)
    {
      erasePage(pgRes);
      return FALSE;
    }
  }

  srcOff = OSAL_NV_PAGE_HDR_SIZE;
  rtrn = TRUE;

  while ( srcOff < (OSAL_NV_PAGE_SIZE - OSAL_NV_HDR_SIZE ) )
  {
    osalNvHdr_t hdr;
    uint16 sz, dstOff = pgOff[pgRes-OSAL_NV_PAGE_BEG];

    HalFlashRead(srcPg, srcOff, (uint8 *)(&hdr), OSAL_NV_HDR_SIZE);

    if ( hdr.id == OSAL_NV_ERASED_ID )
    {
      break;
    }

    // Get the actual size in bytes which is the ceiling(hdr.len)
    sz = OSAL_NV_DATA_SIZE( hdr.len );

    if ( sz > (OSAL_NV_PAGE_SIZE - OSAL_NV_HDR_SIZE - srcOff) )
    {
      break;
    }

    if ( sz > (OSAL_NV_PAGE_SIZE - OSAL_NV_HDR_SIZE - dstOff) )
    {
      rtrn = FALSE;
      break;
    }

    srcOff += OSAL_NV_HDR_SIZE;

    if ( (hdr.id != OSAL_NV_ZEROED_ID) && (hdr.id != skipId) )
    {
      if ( hdr.chk == calcChkF( srcPg, srcOff, hdr.len ) )
      {
        /* Prevent excessive re-writes to item header caused by numerous, rapid, & successive
         * OSAL_Nv interruptions caused by resets.
         */
        if ( hdr.stat == OSAL_NV_ERASED_ID )
        {
          setItem( srcPg, srcOff, eNvXfer );
        }

        if ( writeItem( pgRes, hdr.id, hdr.len, NULL, FALSE ) )
        {
          dstOff += OSAL_NV_HDR_SIZE;
          xferBuf( srcPg, srcOff, pgRes, dstOff, sz );
          // Calculate and write the new checksum.
          if (hdr.chk == calcChkF(pgRes, dstOff, hdr.len))
          {
            if ( hdr.chk != setChk( pgRes, dstOff, hdr.chk ) )
            {
              rtrn = FALSE;
              break;
            }
            else
            {
              hotItemUpdate(pgRes, dstOff, hdr.id);
            }
          }
          else
          {
            rtrn = FALSE;
            break;
          }
        }
        else
        {
          rtrn = FALSE;
          break;
        }
      }
    }

    srcOff += sz;
  }

  if (rtrn == FALSE)
  {
    erasePage(pgRes);
  }
  else if (skipId == OSAL_NV_ITEM_NULL)
  {
    COMPACT_PAGE_CLEANUP(srcPg);
  }
  // else invoking function must cleanup.

  return rtrn;
}
예제 #8
0
/*********************************************************************
 * @fn      initNV
 *
 * @brief   Initialize the NV flash pages.
 *
 * @param   none
 *
 * @return  TRUE
 */
static uint8 initNV( void )
{
  osalNvPgHdr_t pgHdr;
  uint8 oldPg = OSAL_NV_PAGE_NULL;
  uint8 findDups = FALSE;
  uint8 pg;

  pgRes = OSAL_NV_PAGE_NULL;

  for ( pg = OSAL_NV_PAGE_BEG; pg <= OSAL_NV_PAGE_END; pg++ )
  {
    HalFlashRead(pg, OSAL_NV_PAGE_HDR_OFFSET, (uint8 *)(&pgHdr), OSAL_NV_HDR_SIZE);

    if ( pgHdr.active == OSAL_NV_ERASED_ID )
    {
      if ( pgRes == OSAL_NV_PAGE_NULL )
      {
        pgRes = pg;
      }
      else
      {
        setPageUse( pg, TRUE );
      }
    }
    // An Xfer from this page was in progress.
    else if ( pgHdr.xfer != OSAL_NV_ERASED_ID )
    {
      oldPg = pg;
    }
  }

  // If a page compaction was interrupted before the old page was erased.
  if ( oldPg != OSAL_NV_PAGE_NULL )
  {
    /* Interrupted compaction before the target of compaction was put in use;
     * so erase the target of compaction and start again.
     */
    if ( pgRes != OSAL_NV_PAGE_NULL )
    {
      erasePage( pgRes );
      (void)compactPage( oldPg, OSAL_NV_ITEM_NULL );
    }
    /* Interrupted compaction after the target of compaction was put in use,
     * but before the old page was erased; so erase it now and create a new reserve page.
     */
    else
    {
      erasePage( oldPg );
      pgRes = oldPg;
    }
  }
  else if ( pgRes != OSAL_NV_PAGE_NULL )
  {
    erasePage( pgRes );  // The last page erase could have been interrupted by a power-cycle.
  }
  /* else if there is no reserve page, COMPACT_PAGE_CLEANUP() must have succeeded to put the old
   * reserve page (i.e. the target of the compacted items) into use but got interrupted by a reset
   * while trying to erase the page to be compacted. Such a page should only contain duplicate items
   * (i.e. all items will be marked 'Xfer') and thus should have the lost count equal to the page
   * size less the page header.
   */

  for ( pg = OSAL_NV_PAGE_BEG; pg <= OSAL_NV_PAGE_END; pg++ )
  {
    // Calculate page offset and lost bytes - any "old" item triggers an N^2 re-scan from start.
    if ( initPage( pg, OSAL_NV_ITEM_NULL, findDups ) != OSAL_NV_ITEM_NULL )
    {
      findDups = TRUE;
      pg = (OSAL_NV_PAGE_BEG - 1);  // Pre-decrement so that loop increment will start over at zero.
      continue;
    }
  }

  if (findDups)
  {
    // Final pass to calculate page lost after invalidating duplicate items.
    for ( pg = OSAL_NV_PAGE_BEG; pg <= OSAL_NV_PAGE_END; pg++ )
    {
      (void)initPage( pg, OSAL_NV_ITEM_NULL, FALSE );
    }
  }

  if ( pgRes == OSAL_NV_PAGE_NULL )
  {
    uint8 idx, mostLost = 0;

    for ( idx = 0; idx < OSAL_NV_PAGES_USED; idx++ )
    {
      // Is this the page that was compacted?
      if (pgLost[idx] == (OSAL_NV_PAGE_SIZE - OSAL_NV_PAGE_HDR_SIZE))
      {
        mostLost = idx;
        break;
      }
      /* This check is not expected to be necessary because the above test should always succeed
       * with an early loop exit.
       */
      else if (pgLost[idx] > pgLost[mostLost])
      {
        mostLost = idx;
      }
    }

    pgRes = mostLost + OSAL_NV_PAGE_BEG;
    erasePage( pgRes );  // The last page erase had been interrupted by a power-cycle.
  }

  return TRUE;
}
예제 #9
0
/*********************************************************************
 * @fn      osal_nv_write
 *
 * @brief   Write a data item to NV. Function can write an entire item to NV or
 *          an element of an item by indexing into the item with an offset.
 *
 * @param   id  - Valid NV item Id.
 * @param   ndx - Index offset into item
 * @param   len - Length of data to write.
 * @param  *buf - Data to write.
 *
 * @return  SUCCESS if successful, NV_ITEM_UNINIT if item did not
 *          exist in NV and offset is non-zero, NV_OPER_FAILED if failure.
 */
uint8 osal_nv_write( uint16 id, uint16 ndx, uint16 len, void *buf )
{
  uint8 rtrn = SUCCESS;

  if ( !OSAL_NV_CHECK_BUS_VOLTAGE )
  {
    return NV_OPER_FAILED;
  }
  else if ( len != 0 )
  {
    osalNvHdr_t hdr;
    uint16 origOff, srcOff;
    uint16 cnt, chk;
    uint8 *ptr, srcPg;

    origOff = srcOff = findItem( id );
    srcPg = findPg;
    if ( srcOff == OSAL_NV_ITEM_NULL )
    {
      return NV_ITEM_UNINIT;
    }

    HalFlashRead(srcPg, (srcOff - OSAL_NV_HDR_SIZE), (uint8 *)(&hdr), OSAL_NV_HDR_SIZE);
    if ( hdr.len < (ndx + len) )
    {
      return NV_OPER_FAILED;
    }

    srcOff += ndx;
    ptr = buf;
    cnt = len;
    chk = 0;
    while ( cnt-- )
    {
      uint8 tmp;
      HalFlashRead(srcPg, srcOff, &tmp, 1);
      if ( tmp != *ptr )
      {
        chk = 1;  // Mark that at least one byte is different.
        // Calculate expected checksum after transferring old data and writing new data.
        hdr.chk -= tmp;
        hdr.chk += *ptr;
      }
      srcOff++;
      ptr++;
    }

    if ( chk != 0 )  // If the buffer to write is different in one or more bytes.
    {
      uint8 comPg = OSAL_NV_PAGE_NULL;
      uint8 dstPg = initItem( FALSE, id, hdr.len, &comPg );

      if ( dstPg != OSAL_NV_PAGE_NULL )
      {
        uint16 tmp = OSAL_NV_DATA_SIZE( hdr.len );
        uint16 dstOff = pgOff[dstPg-OSAL_NV_PAGE_BEG] - tmp;
        srcOff = origOff;

        /* Prevent excessive re-writes to item header caused by numerous, rapid, & successive
         * OSAL_Nv interruptions caused by resets.
         */
        if ( hdr.stat == OSAL_NV_ERASED_ID )
        {
          setItem( srcPg, srcOff, eNvXfer );
        }

        xferBuf( srcPg, srcOff, dstPg, dstOff, ndx );
        srcOff += ndx;
        dstOff += ndx;

        writeBuf( dstPg, dstOff, len, buf );
        srcOff += len;
        dstOff += len;

        xferBuf( srcPg, srcOff, dstPg, dstOff, (hdr.len-ndx-len) );

        // Calculate and write the new checksum.
        dstOff = pgOff[dstPg-OSAL_NV_PAGE_BEG] - tmp;

        if ( hdr.chk == calcChkF( dstPg, dstOff, hdr.len ) )
        {
          if ( hdr.chk != setChk( dstPg, dstOff, hdr.chk ) )
          {
            rtrn = NV_OPER_FAILED;
          }
          else
          {
            hotItemUpdate(dstPg, dstOff, hdr.id);
          }
        }
        else
        {
          rtrn = NV_OPER_FAILED;
        }
      }
      else
      {
        rtrn = NV_OPER_FAILED;
      }

      if ( comPg != OSAL_NV_PAGE_NULL )
      {
        /* Even though the page compaction succeeded, if the new item is coming from the compacted
         * page and writing the new value failed, then the compaction must be aborted.
         */
        if ( (srcPg == comPg) && (rtrn == NV_OPER_FAILED) )
        {
          erasePage( pgRes );
        }
        else
        {
          COMPACT_PAGE_CLEANUP( comPg );
        }
      }

      /* Zero of the old item must wait until after compact page cleanup has finished - if the item
       * is zeroed before and cleanup is interrupted by a power-cycle, the new item can be lost.
       */
      if ( (srcPg != comPg) && (rtrn != NV_OPER_FAILED) )
      {
        setItem( srcPg, origOff, eNvZero );
      }
    }
  }

  return rtrn;
}