//------------------------------------------------------------------------------ /// Returns BADBLOCK if the given block of a nandflash device is bad; returns /// GOODBLOCK if the block is good; or returns a NandCommon_ERROR code. /// \param managed Pointer to a ManagedNandFlash instance. /// \param block Raw block to check. /// \param spare Pointer to allocated spare area (must be assigned) //------------------------------------------------------------------------------ static unsigned char CheckBlock( const struct ManagedNandFlash *managed, unsigned short block, unsigned char *spare) { unsigned char error; unsigned int i; unsigned char pageSpareSize = NandFlashModel_GetPageSpareSize(MODEL(managed)); ASSERT(spare, "ManagedNandFlash_CheckBlock: spare\n\r"); // Read spare area of first page of block error = RawNandFlash_ReadPage(RAW(managed), block, 0, 0, spare); if (error) { TRACE_ERROR("CheckBlock: Cannot read page #0 of block #%d\n\r", block); return error; } // Make sure it is all 0xFF for (i=0; i < pageSpareSize; i++) { if (spare[i] != 0xFF) { return BADBLOCK; } } // Read spare area of second page of block error = RawNandFlash_ReadPage(RAW(managed), block, 1, 0, spare); if (error) { TRACE_ERROR("CheckBlock: Cannot read page #1 of block #%d\n\r", block); return error; } // Make sure it is all 0xFF for (i=0; i < pageSpareSize; i++) { if (spare[i] != 0xFF) { return BADBLOCK; } } return GOODBLOCK; }
uint8_t SkipBlockNandFlash_CheckBlock( const struct SkipBlockNandFlash *skipBlock, uint16_t block) { #if !defined (OP_BOOTSTRAP_on) uint8_t spare[NandCommon_MAXPAGESPARESIZE]; uint8_t error; uint8_t badBlockMarker; const struct NandSpareScheme *scheme; /* Retrieve model scheme */ scheme = NandFlashModel_GetScheme(MODEL(skipBlock)); /* Read spare area of first page of block */ error = RawNandFlash_ReadPage(RAW(skipBlock), block, 0, 0, spare); if (error) { TRACE_ERROR("CheckBlock: Cannot read page #0 of block #%d\n\r", block); return error; } NandSpareScheme_ReadBadBlockMarker(scheme, spare, &badBlockMarker); if (badBlockMarker != 0xFF) { return BADBLOCK; } /* Read spare area of second page of block */ error = RawNandFlash_ReadPage(RAW(skipBlock), block, 1, 0, spare); if (error) { TRACE_ERROR("CheckBlock: Cannot read page #1 of block #%d\n\r", block); return error; } NandSpareScheme_ReadBadBlockMarker(scheme, spare, &badBlockMarker); if (badBlockMarker != 0xFF) { return BADBLOCK; } #endif return GOODBLOCK; }
//------------------------------------------------------------------------------ /// Reads the data and/or spare of a page of a nandflash chip, and verify that /// the data is valid using the ECC information contained in the spare. If one /// buffer pointer is 0, the corresponding area is not saved. /// Returns 0 if the data has been read and is valid; otherwise returns either /// NandCommon_ERROR_CORRUPTEDDATA or ... /// \param ecc Pointer to an EccNandFlash instance. /// \param block Number of block to read from. /// \param page Number of page to read inside given block. /// \param data Data area buffer. /// \param spare Spare area buffer. //------------------------------------------------------------------------------ unsigned char EccNandFlash_ReadPage( const struct EccNandFlash *ecc, unsigned short block, unsigned short page, void *data, void *spare) { unsigned char tmpData[NandCommon_MAXPAGEDATASIZE]; unsigned char tmpSpare[NandCommon_MAXPAGESPARESIZE]; unsigned char error; unsigned char hamming[NandCommon_MAXSPAREECCBYTES]; unsigned short pageDataSize = NandFlashModel_GetPageDataSize(MODEL(ecc)); unsigned char pageSpareSize = NandFlashModel_GetPageSpareSize(MODEL(ecc)); TRACE_DEBUG("EccNandFlash_ReadPage(B#%d:P#%d)\n\r", block, page); // Start by reading the spare and the data error = RawNandFlash_ReadPage(RAW(ecc), block, page, tmpData, tmpSpare); if (error) { TRACE_ERROR("EccNandFlash_ReadPage: Failed to read page\n\r"); return error; } // Retrieve ECC information from page and verify the data NandSpareScheme_ReadEcc(NandFlashModel_GetScheme(MODEL(ecc)), tmpSpare, hamming); error = Hamming_Verify256x(tmpData, pageDataSize, hamming); if (error && (error != Hamming_ERROR_SINGLEBIT)) { TRACE_ERROR("EccNandFlash_ReadPage: Unrecoverable data\n\r"); return NandCommon_ERROR_CORRUPTEDDATA; } // Copy data and/or spare into final buffers if (data) { memcpy(data, tmpData, pageDataSize); } if (spare) { memcpy(spare, tmpSpare, pageSpareSize); } return 0; }
/** * \brief Reads the data and/or the spare area of a page on a SkipBlock nandflash. * * \param skipBlock Pointer to a SkipBlockNandFlash instance. * \param block Number of block to read page from. * \param page Number of page to read inside the given block. * \param data Data area buffer, can be 0. * \param spare Spare area buffer, can be 0. * \note If one of the buffer pointer is 0, then the block MUST not be BAD. * \return NandCommon_ERROR_BADBLOCK if the block is BAD; Otherwise, returns EccNandFlash_ReadPage(). */ uint8_t SkipBlockNandFlash_ReadPage( const struct SkipBlockNandFlash *skipBlock, uint16_t block, uint16_t page, void *data, void *spare) { #if !defined(OP_BOOTSTRAP_on) /* Check that the block is not BAD if data is requested */ if (SkipBlockNandFlash_CheckBlock(skipBlock, block) != GOODBLOCK) { TRACE_ERROR("SkipBlockNandFlash_ReadPage: Block is BAD.\n\r"); return NandCommon_ERROR_BADBLOCK; } /* Read data with ECC verification */ return EccNandFlash_ReadPage(ECC(skipBlock), block, page, data, spare); #else return RawNandFlash_ReadPage(RAW(skipBlock), block, page, data, spare); #endif }
//------------------------------------------------------------------------------ /// Returns 1 if a nandflash device is virgin (i.e. has never been used as a /// managed nandflash); otherwise return 0. /// \param managed Pointer to a ManagedNandFlash instance. /// \param spare Pointer to allocated spare area (must be assigned) //------------------------------------------------------------------------------ static unsigned char IsDeviceVirgin(const struct ManagedNandFlash *managed, unsigned char *spare) { struct NandBlockStatus blockStatus; const struct NandSpareScheme *scheme = NandFlashModel_GetScheme(MODEL(managed)); unsigned short baseBlock = managed->baseBlock; unsigned char badBlockMarker; unsigned char error; ASSERT(spare, "ManagedNandFlash_IsDeviceVirgin: spare\n\r"); // Read spare area of page #0 error = RawNandFlash_ReadPage(RAW(managed), baseBlock, 0, 0, spare); ASSERT(!error, "ManagedNandFlash_IsDeviceVirgin: Failed to read page #0\n\r"); // Retrieve bad block marker and block status from spare area NandSpareScheme_ReadBadBlockMarker(scheme, spare, &badBlockMarker); NandSpareScheme_ReadExtra(scheme, spare, &blockStatus, 4, 0); // Check if block is marked as bad if (badBlockMarker != 0xFF) { // Device is not virgin, since page #0 is guaranteed to be good return 0; } // If device is not virgin, then block status will be set to either // FREE, DIRTY or LIVE else if (blockStatus.status != NandBlockStatus_DEFAULT) { // Device is not virgin return 0; } return 1; }
//------------------------------------------------------------------------------ /// Initializes a ManagedNandFlash instance. Scans the device to retrieve or /// create block status information. /// \param managed Pointer to a ManagedNandFlash instance. /// \param model Pointer to the underlying nand chip model. Can be 0. /// \param commandAddress Address at which commands are sent. /// \param addressAddress Address at which addresses are sent. /// \param dataAddress Address at which data is sent. /// \param pinChipEnable Pin controlling the CE signal of the NandFlash. /// \param pinReadyBusy Pin used to monitor the ready/busy signal of the Nand. /// \param baseBlock Base physical block address of managed area, managed 0. /// \param sizeInBlocks Number of blocks that is managed. //------------------------------------------------------------------------------ unsigned char ManagedNandFlash_Initialize( struct ManagedNandFlash *managed, const struct NandFlashModel *model, unsigned int commandAddress, unsigned int addressAddress, unsigned int dataAddress, const Pin pinChipEnable, const Pin pinReadyBusy, unsigned short baseBlock, unsigned short sizeInBlocks) { unsigned char error; unsigned char spare[NandCommon_MAXPAGESPARESIZE]; unsigned int numBlocks; const struct NandSpareScheme *scheme; unsigned int block, phyBlock; struct NandBlockStatus blockStatus; unsigned char badBlockMarker; unsigned int eraseCount, minEraseCount, maxEraseCount; TRACE_DEBUG("ManagedNandFlash_Initialize()\n\r"); // Initialize EccNandFlash error = EccNandFlash_Initialize(ECC(managed), model, commandAddress, addressAddress, dataAddress, pinChipEnable, pinReadyBusy); if (error) { return error; } // Retrieve model information numBlocks = NandFlashModel_GetDeviceSizeInBlocks(MODEL(managed)); scheme = NandFlashModel_GetScheme(MODEL(managed)); // Initialize base & size if (sizeInBlocks == 0) sizeInBlocks = numBlocks; if (baseBlock > numBlocks) { baseBlock = 0; } else if (baseBlock + sizeInBlocks > numBlocks) { sizeInBlocks = numBlocks - baseBlock; } TRACE_INFO("Managed NF area: %d + %d\n\r", baseBlock, sizeInBlocks); if (sizeInBlocks > NandCommon_MAXNUMBLOCKS) { TRACE_ERROR("Out of Maxmized Managed Size: %d > %d\n\r", sizeInBlocks, NandCommon_MAXNUMBLOCKS); TRACE_INFO("Change NandCommon_MAXNUMBLOCKS or sizeInBlocks\n\r"); return NandCommon_ERROR_OUTOFBOUNDS; } managed->baseBlock = baseBlock; managed->sizeInBlocks = sizeInBlocks; // Initialize block statuses // First, check if device is virgin if (IsDeviceVirgin(managed, spare)) { TRACE_WARNING("Device is virgin, doing initial block scanning ...\n\r"); // Perform initial scan of the device area for (block=0; block < sizeInBlocks; block++) { phyBlock = baseBlock + block; // Check if physical block is bad error = CheckBlock(managed, phyBlock, spare); if (error == BADBLOCK) { // Mark block as bad TRACE_DEBUG("Block #%d is bad\n\r", block); managed->blockStatuses[block].status = NandBlockStatus_BAD; } else if (error == GOODBLOCK) { // Mark block as free with erase count 0 TRACE_DEBUG("Block #%d is free\n\r", block); managed->blockStatuses[block].status = NandBlockStatus_FREE; managed->blockStatuses[block].eraseCount = 0; // Write status in spare of block first page error = WriteBlockStatus(managed, phyBlock, &(managed->blockStatuses[block]), spare); if (error) { TRACE_ERROR("ManagedNandFlash_Initialize: WR spare\n\r"); return error; } } else { TRACE_ERROR("ManagedNandFlash_Initialize: Scan device\n\r"); return error; } } } else { TRACE_INFO("Managed, retrieving information ...\n\r"); // Retrieve block statuses from their first page spare area // (find maximum and minimum wear at the same time) minEraseCount = 0xFFFFFFFF; maxEraseCount = 0; for (block=0; block < sizeInBlocks; block++) { phyBlock = baseBlock + block; // Read spare of first page error = RawNandFlash_ReadPage(RAW(managed), phyBlock, 0, 0, spare); if (error) { TRACE_ERROR("ManagedNandFlash_Initialize: Read block #%d(%d)\n\r", block, phyBlock); } // Retrieve bad block marker and block status NandSpareScheme_ReadBadBlockMarker(scheme, spare, &badBlockMarker); NandSpareScheme_ReadExtra(scheme, spare, &blockStatus, 4, 0); // If they do not match, block must be bad if ( (badBlockMarker != 0xFF) && (blockStatus.status != NandBlockStatus_BAD)) { TRACE_DEBUG("Block #%d(%d) is bad\n\r", block, phyBlock); managed->blockStatuses[block].status = NandBlockStatus_BAD; } // Check that block status is not default // (meaning block is not managed) else if (blockStatus.status == NandBlockStatus_DEFAULT) { TRACE_ERROR("Block #%d(%d) is not managed\n\r", block, phyBlock); return NandCommon_ERROR_NOMAPPING; } // Otherwise block status is accurate else { TRACE_DEBUG("Block #%03d(%d) : status = %2d | eraseCount = %d\n\r", block, phyBlock, blockStatus.status, blockStatus.eraseCount); managed->blockStatuses[block] = blockStatus; // Check for min/max erase counts if (blockStatus.eraseCount < minEraseCount) { minEraseCount = blockStatus.eraseCount; } if (blockStatus.eraseCount > maxEraseCount) { maxEraseCount = blockStatus.eraseCount; } //// Clean block //// Release LIVE blocks //if (managed->blockStatuses[block].status == NandBlockStatus_LIVE) { // // ManagedNandFlash_ReleaseBlock(managed, block); //} //// Erase DIRTY blocks //if (managed->blockStatuses[block].status == NandBlockStatus_DIRTY) { // // ManagedNandFlash_EraseBlock(managed, block); //} } } // Display erase count information TRACE_ERROR_WP("|--------|------------|--------|--------|--------|\n\r"); TRACE_ERROR_WP("| Wear | Count | Free | Live | Dirty |\n\r"); TRACE_ERROR_WP("|--------|------------|--------|--------|--------|\n\r"); for (eraseCount=minEraseCount; eraseCount <= maxEraseCount; eraseCount++) { unsigned int count = 0, live = 0, dirty = 0, free = 0; for (block=0; block < sizeInBlocks; block++) { if ((managed->blockStatuses[block].eraseCount == eraseCount) && (managed->blockStatuses[block].status != NandBlockStatus_BAD)) { count++; switch (managed->blockStatuses[block].status) { case NandBlockStatus_LIVE: live++; break; case NandBlockStatus_DIRTY: dirty++; break; case NandBlockStatus_FREE: free++; break; } } } if (count > 0) { TRACE_ERROR_WP("| %4d | %8d | %4d | %4d | %4d |\n\r", eraseCount, count, free, live, dirty); } } TRACE_ERROR_WP("|--------|------------|--------|--------|--------|\n\r"); } return 0; }
//------------------------------------------------------------------------------ /// Reads the data and/or spare of a page of a nandflash chip, and verify that /// the data is valid using the ECC information contained in the spare. If one /// buffer pointer is 0, the corresponding area is not saved. /// Returns 0 if the data has been read and is valid; otherwise returns either /// NandCommon_ERROR_CORRUPTEDDATA or ... /// \param ecc Pointer to an EccNandFlash instance. /// \param block Number of block to read from. /// \param page Number of page to read inside given block. /// \param data Data area buffer. /// \param spare Spare area buffer. //------------------------------------------------------------------------------ unsigned char EccNandFlash_ReadPage( struct EccNandFlash *ecc, unsigned short block, unsigned short page, void *data, void *spare) { unsigned char error; unsigned short pageDataSize = NandFlashModel_GetPageDataSize(MODEL(ecc)); unsigned char pageSpareSize = NandFlashModel_GetPageSpareSize(MODEL(ecc)); unsigned char *pDataBuffer; unsigned char *pSpareBuffer; TRACE_DEBUG("EccNandFlash_ReadPage(B#%d:P#%d)\n\r", block, page); pDataBuffer = (data) ? data : RawNandFlash_GetDataBuffer(RAW(ecc)); pSpareBuffer = (spare) ? spare : RawNandFlash_GetSpareBuffer(RAW(ecc)); #ifndef HARDWARE_ECC // Start by reading the spare and the data error = RawNandFlash_ReadPage(RAW(ecc), block, page, pDataBuffer, pSpareBuffer); if (error) { TRACE_ERROR("EccNandFlash_ReadPage: $page %d.%d\n\r", block, page); goto error; } // Retrieve ECC information from page and verify the data NandSpareScheme_ReadEcc(NandFlashModel_GetScheme(MODEL(ecc)), pSpareBuffer, ecc->hamming); error = Hamming_Verify256x(pDataBuffer, pageDataSize, ecc->hamming); #else error = RawNandFlash_ReadPage(RAW(ecc), block, page, (unsigned char*)data, tmpSpare); if (error) { TRACE_ERROR("EccNandFlash_ReadPage: $page %d.%d\n\r", block, page); goto error; } // Retrieve ECC information from page NandSpareScheme_ReadEcc(NandFlashModel_GetScheme(MODEL(ecc)), tmpSpare, ecc->hsiaoInSpare); HSMC4_GetEccParity(pageDataSize, hsiao, NandFlashModel_GetDataBusWidth(MODEL(ecc))); // Verify the data error = HSMC4_VerifyHsiao((unsigned char*) data, pageDataSize, ecc->hsiaoInSpare, ecc->hsiao, NandFlashModel_GetDataBusWidth(MODEL(ecc))); #endif if (error && (error != Hamming_ERROR_SINGLEBIT)) { error = NandCommon_ERROR_CORRUPTEDDATA; TRACE_ERROR("EccNandFlash_ReadPage: $page %d.%d\n\r", block, page); goto error; } #ifndef HARDWARE_ECC #else if (spare) { memcpy(spare, pSpareBuffer, pageSpareSize); } #endif error: if (data == (unsigned char *) 0) RawNandFlash_ReleaseDataBuffer(RAW(ecc)); if (spare == (unsigned char *) 0) RawNandFlash_ReleaseSpareBuffer(RAW(ecc)); return error; }
//------------------------------------------------------------------------------ /// Reads the data and/or spare of a page of a nandflash chip, and verify that /// the data is valid using the ECC information contained in the spare. If one /// buffer pointer is 0, the corresponding area is not saved. /// Returns 0 if the data has been read and is valid; otherwise returns either /// NandCommon_ERROR_CORRUPTEDDATA or ... /// \param ecc Pointer to an EccNandFlash instance. /// \param block Number of block to read from. /// \param page Number of page to read inside given block. /// \param data Data area buffer. /// \param spare Spare area buffer. //------------------------------------------------------------------------------ unsigned char EccNandFlash_ReadPage( const struct EccNandFlash *ecc, unsigned short block, unsigned short page, void *data, void *spare) { unsigned char tmpSpare[NandCommon_MAXPAGESPARESIZE]; unsigned char error; #ifndef HARDWARE_ECC unsigned char tmpData[NandCommon_MAXPAGEDATASIZE]; unsigned char hamming[NandCommon_MAXSPAREECCBYTES]; #else unsigned char hsiaoInSpare[NandCommon_MAXSPAREECCBYTES]; unsigned char hsiao[NandCommon_MAXSPAREECCBYTES]; #endif unsigned char tmpNoEcc; unsigned short pageDataSize = NandFlashModel_GetPageDataSize(MODEL(ecc)); unsigned char pageSpareSize = NandFlashModel_GetPageSpareSize(MODEL(ecc)); TRACE_DEBUG("EccNandFlash_ReadPage(B#%d:P#%d)\n\r", block, page); #ifndef HARDWARE_ECC // Start by reading the spare data error = RawNandFlash_ReadPage(RAW(ecc), block, page, 0, tmpSpare); if (error) { TRACE_ERROR("EccNandFlash_ReadPage: Failed to read page\n\r"); return error; } // Then reading the data error = RawNandFlash_ReadPage(RAW(ecc), block, page, tmpData, 0); if (error) { TRACE_ERROR("EccNandFlash_ReadPage: Failed to read page\n\r"); return error; } tmpNoEcc = EccNandlfash_GetNoECC(); if(!tmpNoEcc){ // Retrieve ECC information from page and verify the data NandSpareScheme_ReadEcc(NandFlashModel_GetScheme(MODEL(ecc)), tmpSpare, hamming); error = Hamming_Verify256x(tmpData, pageDataSize, hamming); } #else // Start by reading the spare area // Note: Can't read data and spare at the same time, otherwise, the ECC parity generation will be incorrect. error = RawNandFlash_ReadPage(RAW(ecc), block, page, 0, tmpSpare); if (error) { TRACE_ERROR("EccNandFlash_ReadPage: $page %d.%d\n\r", block, page); return error; } // Retrieve ECC information from page and verify the data NandSpareScheme_ReadEcc(NandFlashModel_GetScheme(MODEL(ecc)), tmpSpare, hsiaoInSpare); // Reading the main data area error = RawNandFlash_ReadPage(RAW(ecc), block, page, (unsigned char*)data, 0); if (error) { TRACE_ERROR("EccNandFlash_ReadPage: $page %d.%d\n\r", block, page); return error; } HSMC4_GetEccParity(pageDataSize, hsiao, NandFlashModel_GetDataBusWidth(MODEL(ecc))); error = HSMC4_VerifyHsiao((unsigned char*) data, pageDataSize, hsiaoInSpare, hsiao, NandFlashModel_GetDataBusWidth(MODEL(ecc))); #endif if (error && (error != Hamming_ERROR_SINGLEBIT) && (!tmpNoEcc)) { TRACE_ERROR("EccNandFlash_ReadPage: at B%d.P%d Unrecoverable data\n\r", block, page); return NandCommon_ERROR_CORRUPTEDDATA; } #ifndef HARDWARE_ECC // Copy data and/or spare into final buffers if (data) { memcpy(data, tmpData, pageDataSize); } if (spare) { memcpy(spare, tmpSpare, pageSpareSize); } #else if (spare) { memcpy(spare, tmpSpare, pageSpareSize); } #endif return 0; }
/** * \brief Reads the data and/or spare of a page of a nandflash chip, and verify that * the data is valid using the ECC information contained in the spare. If one * buffer pointer is 0, the corresponding area is not saved. * \param ecc Pointer to an EccNandFlash instance. * \param block Number of block to read from. * \param page Number of page to read inside given block. * \param data Data area buffer. * \param spare Spare area buffer. * \return 0 if the data has been read and is valid; otherwise returns either * NandCommon_ERROR_CORRUPTEDDATA or ... */ unsigned char EccNandFlash_ReadPage( const struct EccNandFlash *ecc, unsigned short block, unsigned short page, void *data, void *spare) { unsigned char tmpSpare[NandCommon_MAXPAGESPARESIZE]; unsigned char error; #ifndef HARDWARE_ECC // unsigned char tmpData[NandCommon_MAXPAGEDATASIZE]; unsigned char hamming[NandCommon_MAXSPAREECCBYTES]; #else unsigned char hsiaoInSpare[NandCommon_MAXSPAREECCBYTES]; unsigned char hsiao[NandCommon_MAXSPAREECCBYTES]; #endif unsigned short pageDataSize = NandFlashModel_GetPageDataSize(MODEL(ecc)); unsigned char pageSpareSize = NandFlashModel_GetPageSpareSize(MODEL(ecc)); TRACE_DEBUG("EccNandFlash_ReadPage(B#%d:P#%d)\n\r", block, page); #ifndef HARDWARE_ECC /* Start by reading the spare and the data */ error = RawNandFlash_ReadPage(RAW(ecc), block, page, gdwNandFlashTempBuffer, tmpSpare); if (error) { TRACE_ERROR("EccNandFlash_ReadPage: Failed to read page\n\r"); return error; } /* Retrieve ECC information from page and verify the data */ NandSpareScheme_ReadEcc(NandFlashModel_GetScheme(MODEL(ecc)), tmpSpare, hamming); error = Hamming_Verify256x(gdwNandFlashTempBuffer, pageDataSize, hamming); #else error = RawNandFlash_ReadPage(RAW(ecc), block, page, (unsigned char*)data, tmpSpare); if (error) { TRACE_ERROR("EccNandFlash_ReadPage: $page %d.%d\n\r", block, page); return error; } /* Retrieve ECC information from page */ NandSpareScheme_ReadEcc(NandFlashModel_GetScheme(MODEL(ecc)), tmpSpare, hsiaoInSpare); HSMC4_GetEccParity(pageDataSize, hsiao, NandFlashModel_GetDataBusWidth(MODEL(ecc))); /* Verify the data */ error = HSMC4_VerifyHsiao((unsigned char*) data, pageDataSize, hsiaoInSpare, hsiao, NandFlashModel_GetDataBusWidth(MODEL(ecc))); #endif if (error && (error != Hamming_ERROR_SINGLEBIT)) { TRACE_ERROR("EccNandFlash_ReadPage: at B%d.P%d Unrecoverable data\n\r", block, page); return NandCommon_ERROR_CORRUPTEDDATA; } #ifndef HARDWARE_ECC /* Copy data and/or spare into final buffers */ if (data) { memcpy(data, gdwNandFlashTempBuffer, pageDataSize); } if (spare) { memcpy(spare, tmpSpare, pageSpareSize); } #else if (spare) { memcpy(spare, tmpSpare, pageSpareSize); } #endif return 0; }