static ssize_t ftl_reload(FAR void *priv, FAR uint8_t *buffer, off_t startblock, size_t nblocks) { struct ftl_struct_s *dev = (struct ftl_struct_s *)priv; ssize_t nread; /* Read the full erase block into the buffer */ nread = MTD_BREAD(dev->mtd, startblock, nblocks, buffer); if (nread != nblocks) { fdbg("Read %d blocks starting at block %d failed: %d\n", nblocks, startblock, nread); } return nread; }
int nxffs_rdcache(FAR struct nxffs_volume_s *volume, off_t block) { size_t nxfrd; /* Check if the requested data is already in the cache */ if (block != volume->cblock) { /* Read the specified blocks into cache */ nxfrd = MTD_BREAD(volume->mtd, block, 1, volume->cache); if (nxfrd != 1) { fdbg("ERROR: Read block %d failed: %d\n", block, nxfrd); return -EIO; } /* Remember what is in the cache */ volume->cblock = block; } return OK; }
int nxffs_dump(FAR struct mtd_dev_s *mtd, bool verbose) { #if defined(CONFIG_DEBUG) && defined(CONFIG_DEBUG_FS) struct nxffs_blkinfo_s blkinfo; int ret; /* Get the volume geometry. (casting to uintptr_t first eliminates * complaints on some architectures where the sizeof long is different * from the size of a pointer). */ memset(&blkinfo, 0, sizeof(struct nxffs_blkinfo_s)); ret = MTD_IOCTL(mtd, MTDIOC_GEOMETRY, (unsigned long)((uintptr_t)&blkinfo.geo)); if (ret < 0) { fdbg("ERROR: MTD ioctl(MTDIOC_GEOMETRY) failed: %d\n", -ret); goto errout; } /* Save the verbose output indication */ blkinfo.verbose = verbose; /* Allocate a buffer to hold one block */ blkinfo.buffer = (FAR uint8_t *)kmalloc(blkinfo.geo.blocksize); if (!blkinfo.buffer) { fdbg("ERROR: Failed to allocate block cache\n"); ret = -ENOMEM; goto errout; } /* Now read every block on the device */ fdbg("NXFFS Dump:\n"); fdbg(g_hdrformat); blkinfo.nblocks = blkinfo.geo.erasesize * blkinfo.geo.neraseblocks / blkinfo.geo.blocksize; for (blkinfo.block = 0, blkinfo.offset = 0; blkinfo.block < blkinfo.nblocks; blkinfo.block++, blkinfo.offset += blkinfo.geo.blocksize) { /* Read the next block */ ret = MTD_BREAD(mtd, blkinfo.block, 1, blkinfo.buffer); if (ret < 0) { fdbg("ERROR: Failed to read block %d\n", blkinfo.block); goto errout_with_block; } /* Analyze the block */ nxffs_analyze(&blkinfo); } fdbg("%d blocks analyzed\n", blkinfo.nblocks); errout_with_block: kfree(blkinfo.buffer); errout: return ret; #else return -ENOSYS; #endif }
static int nxffs_badblocks(FAR struct nxffs_volume_s *volume) { FAR uint8_t *blkptr; /* Pointer to next block data */ off_t eblock; /* Erase block number */ off_t lblock; /* Logical block number */ ssize_t nxfrd; /* Number of blocks transferred */ bool good; /* TRUE: block is good */ bool modified; /* TRUE: The erase block has been modified */ int i; /* Read and verify each erase block */ for (eblock = 0; eblock < volume->geo.neraseblocks; eblock++) { /* Read the entire erase block */ lblock = eblock * volume->blkper; nxfrd = MTD_BREAD(volume->mtd, lblock, volume->blkper, volume->pack); if (nxfrd != volume->blkper) { fdbg("Read erase block %d failed: %d\n", lblock, nxfrd); return -EIO; } /* Process each logical block */ modified = false; for (blkptr = volume->pack, i = 0; i < volume->blkper; blkptr += volume->geo.blocksize, i++) { FAR struct nxffs_block_s *blkhdr = (FAR struct nxffs_block_s*)blkptr; /* Check block header */ good = true; if (memcmp(blkhdr->magic, g_blockmagic, NXFFS_MAGICSIZE) != 0 || blkhdr->state != BLOCK_STATE_GOOD) { good = false; } /* Check that block data is erased */ else { size_t blocksize = volume->geo.blocksize - SIZEOF_NXFFS_BLOCK_HDR; size_t erasesize = nxffs_erased(&blkptr[SIZEOF_NXFFS_BLOCK_HDR], blocksize); good = (blocksize == erasesize); } /* If the block is bad, attempt to re-write the block header indicating * a bad block (of course, if the block has failed, this may not be * possible, depending upon failure modes. */ if (!good) { memcpy(blkhdr->magic, g_blockmagic, NXFFS_MAGICSIZE); blkhdr->state = BLOCK_STATE_BAD; modified = true; } } /* If the erase block was modified, then re-write it */ if (modified) { nxfrd = MTD_BWRITE(volume->mtd, lblock, volume->blkper, volume->pack); if (nxfrd != volume->blkper) { fdbg("Write erase block %d failed: %d\n", lblock, nxfrd); return -EIO; } } } return OK; }
static off_t mtdconfig_consolidate(FAR struct mtdconfig_struct_s *dev) { off_t src_block, dst_block; off_t src_offset, dst_offset; uint16_t blkper, x, bytes, bytes_left_in_block; struct mtdconfig_header_s hdr; int ret; uint8_t sig[CONFIGDATA_BLOCK_HDR_SIZE]; uint8_t *pBuf; /* Prepare to copy block 0 to the last block (erase blocks) */ src_block = 0; dst_block = dev->neraseblocks - 1; /* Ensure the last block is erased */ MTD_ERASE(dev->mtd, dst_block, 1); blkper = dev->erasesize / dev->blocksize; dst_block *= blkper; /* Convert to read/write blocks */ /* Allocate a small buffer for moving data */ pBuf = (uint8_t *)kmm_malloc(dev->blocksize); if (pBuf == NULL) { return 0; } /* Now copy block zero to last block */ for (x = 0; x < blkper; x++) { ret = MTD_BREAD(dev->mtd, src_block++, 1, dev->buffer); if (ret < 0) { /* I/O Error! */ goto errout; } ret = MTD_BWRITE(dev->mtd, dst_block++, 1, dev->buffer); if (ret < 0) { /* I/O Error! */ goto errout; } } /* Erase block zero and write a format signature. */ MTD_ERASE(dev->mtd, 0, 1); sig[0] = 'C'; sig[1] = 'D'; sig[2] = CONFIGDATA_FORMAT_VERSION; mtdconfig_writebytes(dev, 0, sig, sizeof(sig)); /* Now consolidate entries */ src_block = 1; dst_block = 0; src_offset = src_block * dev->erasesize + CONFIGDATA_BLOCK_HDR_SIZE; dst_offset = CONFIGDATA_BLOCK_HDR_SIZE; while (src_block < dev->neraseblocks) { /* Scan all headers and move them to the src_offset */ retry_relocate: MTD_READ(dev->mtd, src_offset, sizeof(hdr), (uint8_t *) &hdr); if (hdr.flags == MTD_ERASED_FLAGS) { /* Test if the source entry is active or if we are at the end * of data for this erase block. */ if (hdr.id == MTD_ERASED_ID) { /* No more data in this erase block. Advance to the * next one. */ src_offset = (src_block + 1) * dev->erasesize + CONFIGDATA_BLOCK_HDR_SIZE; } else { /* Test if this entry will fit in the current destination block */ bytes_left_in_block = (dst_block + 1) * dev->erasesize - dst_offset; if (hdr.len + sizeof(hdr) > bytes_left_in_block) { /* Item doesn't fit in the block. Advance to the next one */ /* Update control variables */ dst_block++; dst_offset = dst_block * dev->erasesize + CONFIGDATA_BLOCK_HDR_SIZE; DEBUGASSERT(dst_block != src_block); /* Retry the relocate */ goto retry_relocate; } /* Copy this entry to the destination */ //printf("REL HDR: ID=%04X,%02X Len=%4d Off=%5d Src off=%4d\n", // hdr.id, hdr.instance, hdr.len, dst_offset, src_offset); mtdconfig_writebytes(dev, dst_offset, (uint8_t *) &hdr, sizeof(hdr)); src_offset += sizeof(hdr); dst_offset += sizeof(hdr); /* Now copy the data */ while (hdr.len) { bytes = hdr.len; if (bytes > dev->blocksize) { bytes = dev->blocksize; } /* Move the data. */ mtdconfig_readbytes(dev, src_offset, pBuf, bytes); mtdconfig_writebytes(dev, dst_offset, pBuf, bytes); /* Update control variables */ hdr.len -= bytes; src_offset += bytes; dst_offset += bytes; } } } else { /* This item has been released. Skip it! */ src_offset += sizeof(hdr) + hdr.len; if (src_offset + sizeof(hdr) >= (src_block + 1) * dev->erasesize || src_offset == (src_block +1 ) * dev->erasesize) { /* No room left at end of source block */ src_offset = (src_block + 1) * dev->erasesize + CONFIGDATA_BLOCK_HDR_SIZE; } } /* Test if we are out of space in the src block */ if (src_offset + sizeof(hdr) >= (src_block + 1) * dev->erasesize) { /* No room at end of src block for another header. Go to next * source block. */ src_offset = (src_block + 1) * dev->erasesize + CONFIGDATA_BLOCK_HDR_SIZE; } /* Test if we advanced to the next block. If we did, then erase the * old block. */ if (src_block != src_offset / dev->erasesize) { /* Erase the block ... we have emptied it */ MTD_ERASE(dev->mtd, src_block, 1); src_block++; } /* Test if we are out of space in the dst block */ if (dst_offset + sizeof(hdr) >= (dst_block + 1) * dev->erasesize) { /* No room at end of dst block for another header. Go to next block. */ dst_block++; dst_offset = dst_block * dev->erasesize + CONFIGDATA_BLOCK_HDR_SIZE; DEBUGASSERT(dst_block != src_block); } } errout: kmm_free(pBuf); return 0; }
static off_t mtdconfig_ramconsolidate(FAR struct mtdconfig_struct_s *dev) { FAR uint8_t *pBuf; FAR struct mtdconfig_header_s *phdr; struct mtdconfig_header_s hdr; uint16_t src_block = 0, dst_block = 0, blkper; off_t dst_offset = CONFIGDATA_BLOCK_HDR_SIZE; off_t src_offset = CONFIGDATA_BLOCK_HDR_SIZE; off_t bytes_left_in_block; uint8_t sig[CONFIGDATA_BLOCK_HDR_SIZE]; int ret; /* Allocate a consolidation buffer */ pBuf = (uint8_t *)kmm_malloc(dev->erasesize); if (pBuf == NULL) { /* Unable to allocate buffer, can't consolidate! */ return 0; } /* Loop for all blocks and consolidate them */ blkper = dev->erasesize / dev->blocksize; while (src_block < dev->neraseblocks) { /* Point to beginning of pBuf and read the next erase block */ ret = MTD_BREAD(dev->mtd, src_block * blkper, blkper, pBuf); if (ret < 0) { /* Error doing block read */ dst_offset = 0; goto errout; } /* Now erase the block */ ret = MTD_ERASE(dev->mtd, src_block, 1); if (ret < 0) { /* Error erasing the block */ dst_offset = 0; goto errout; } /* If this is block zero, then write a format signature */ if (src_block == 0) { sig[0] = 'C'; sig[1] = 'D'; sig[2] = CONFIGDATA_FORMAT_VERSION; mtdconfig_writebytes(dev, 0, sig, sizeof(sig)); } /* Copy active items back to the MTD device */ while (src_offset < dev->erasesize) { phdr = (FAR struct mtdconfig_header_s *) &pBuf[src_offset]; if (phdr->id == MTD_ERASED_ID) { /* No more data in this erase block */ src_offset = dev->erasesize; continue; } if (phdr->flags == MTD_ERASED_FLAGS) { /* This is an active entry. Copy it. Check if it * fits in the current destination block. */ bytes_left_in_block = (dst_block + 1) * dev->erasesize - dst_offset; if (bytes_left_in_block < sizeof(*phdr) + phdr->len) { /* Item won't fit in the destination block. Move to * the next block */ dst_block++; dst_offset = dst_block * dev->erasesize + CONFIGDATA_BLOCK_HDR_SIZE; /* Test for program bug. We shouldn't ever overflow * even if no entries were inactive. */ DEBUGASSERT(dst_block != dev->neraseblocks); } /* Now Write the item to the current dst_offset location */ //printf("REL HDR: ID=%04X,%02X Len=%4d Off=%5d Src off=%4d\n", // phdr->id, phdr->instance, phdr->len, dst_offset, src_offset); ret = mtdconfig_writebytes(dev, dst_offset, (uint8_t *) phdr, sizeof(hdr)); if (ret < 0) { dst_offset = 0; goto errout; } dst_offset += sizeof(hdr); ret = mtdconfig_writebytes(dev, dst_offset, &pBuf[src_offset + sizeof(hdr)], phdr->len); dst_offset += phdr->len; /* Test if enough space in dst block for another header */ if (dst_offset + sizeof(hdr) >= (dst_block + 1) * dev->erasesize || dst_offset == (dst_block + 1) * dev->erasesize) { dst_block++; dst_offset = dst_block * dev->erasesize + CONFIGDATA_BLOCK_HDR_SIZE; } } /* Increment past the current source item */ src_offset += sizeof(hdr) + phdr->len; if (src_offset + sizeof(hdr) > dev->erasesize) { src_offset = dev->erasesize; } DEBUGASSERT(src_offset <= dev->erasesize); } /* Increment to next source block */ src_block++; src_offset = CONFIGDATA_BLOCK_HDR_SIZE; } errout: kmm_free(pBuf); return dst_offset; }
static int mtdconfig_writebytes(FAR struct mtdconfig_struct_s *dev, int offset, FAR const uint8_t *pdata, int writelen) { int ret = OK; #ifdef CONFIG_MTD_BYTE_WRITE /* Test if this MTD device supports byte write */ if (dev->mtd->write != NULL) { ret = MTD_WRITE(dev->mtd, offset, writelen, pdata); } else #endif /* Perform the write using the block write method of the MTD */ { uint16_t block; uint16_t index; off_t bytes_this_block; off_t bytes_written = 0; while (writelen) { /* Read existing data from the the block into the buffer */ block = offset / dev->blocksize; ret = MTD_BREAD(dev->mtd, block, 1, dev->buffer); if (ret != 1) { ret = -EIO; goto errout; } index = offset - block * dev->blocksize; bytes_this_block = dev->blocksize - index; if (bytes_this_block > writelen) { bytes_this_block = writelen; } /* Now write data to the block */ memcpy(&dev->buffer[index], pdata, bytes_this_block); ret = MTD_BWRITE(dev->mtd, block, 1, dev->buffer); if (ret != 1) { ret = -EIO; goto errout; } /* Update writelen, etc. */ writelen -= bytes_this_block; pdata += bytes_this_block; offset += bytes_this_block; bytes_written += bytes_this_block; } /* Return the number of bytes written */ ret = bytes_written; } errout: return ret; }
static int mtdconfig_readbytes(FAR struct mtdconfig_struct_s *dev, int offset, FAR uint8_t *pdata, int readlen) { off_t bytestoread = readlen; off_t bytesthisblock, firstbyte; off_t block, index; int ret = OK; size_t bytes; /* Test if read interface supported. If it is, use it directly */ if ((dev->mtd->read == NULL) && (readlen < dev->blocksize)) { /* Read interface available. Read directly to buffer */ bytes = MTD_READ(dev->mtd, offset, readlen, pdata); if (bytes != readlen) { /* Error reading data! */ ret = -EIO; } } else { /* Read interface not available, do a block read into our buffer */ block = offset / dev->blocksize; firstbyte = offset - (block * dev->blocksize); bytesthisblock = dev->blocksize - firstbyte; if (bytesthisblock > readlen) { bytesthisblock = readlen; } index = 0; while (bytestoread > 0) { if (bytesthisblock < dev->blocksize || bytestoread < dev->blocksize) { /* Copy to temp buffer first...don't need the whole block */ bytes = MTD_BREAD(dev->mtd, block, 1, dev->buffer); if (bytes != 1) { /* Error reading data! */ ret = -EIO; goto errout; } /* Copy data to the output buffer */ memcpy(&pdata[index], &dev->buffer[firstbyte], bytesthisblock); } else { /* We are reading a whole block. Read directly to buffer */ bytes = MTD_BREAD(dev->mtd, block, 1, &pdata[index]); if (bytes != 1) { /* Error reading data! */ ret = -EIO; goto errout; } } /* Update values for next block read */ bytestoread -= bytesthisblock; index += bytesthisblock; bytesthisblock = dev->blocksize; if (bytesthisblock > bytestoread) { bytesthisblock = bytestoread; } firstbyte = 0; block++; } } errout: return ret; }
static ssize_t ftl_flush(FAR void *priv, FAR const uint8_t *buffer, off_t startblock, size_t nblocks) { struct ftl_struct_s *dev = (struct ftl_struct_s *)priv; off_t alignedblock; off_t mask; off_t rwblock; off_t eraseblock; off_t offset; size_t remaining; size_t nxfrd; int nbytes; int ret; /* Get the aligned block. Here is is assumed: (1) The number of R/W blocks * per erase block is a power of 2, and (2) the erase begins with that same * alignment. */ mask = dev->blkper - 1; alignedblock = (startblock + mask) & ~mask; /* Handle partial erase blocks before the first unaligned block */ remaining = nblocks; if (alignedblock > startblock) { /* Check if the write is shorter than to the end of the erase block */ bool short_write = (remaining < (alignedblock - startblock)); /* Read the full erase block into the buffer */ rwblock = startblock & ~mask; nxfrd = MTD_BREAD(dev->mtd, rwblock, dev->blkper, dev->eblock); if (nxfrd != dev->blkper) { ferr("ERROR: Read erase block %d failed: %d\n", rwblock, nxfrd); return -EIO; } /* Then erase the erase block */ eraseblock = rwblock / dev->blkper; ret = MTD_ERASE(dev->mtd, eraseblock, 1); if (ret < 0) { ferr("ERROR: Erase block=%d failed: %d\n", eraseblock, ret); return ret; } /* Copy the user data at the end of the buffered erase block */ offset = (startblock & mask) * dev->geo.blocksize; if (short_write) { nbytes = remaining * dev->geo.blocksize; } else { nbytes = dev->geo.erasesize - offset; } finfo("Copy %d bytes into erase block=%d at offset=%d\n", nbytes, eraseblock, offset); memcpy(dev->eblock + offset, buffer, nbytes); /* And write the erase block back to flash */ nxfrd = MTD_BWRITE(dev->mtd, rwblock, dev->blkper, dev->eblock); if (nxfrd != dev->blkper) { ferr("ERROR: Write erase block %d failed: %d\n", rwblock, nxfrd); return -EIO; } /* Then update for amount written */ if (short_write) { remaining = 0; } else { remaining -= dev->blkper - (startblock & mask); } buffer += nbytes; } /* How handle full erase pages in the middle */ while (remaining >= dev->blkper) { /* Erase the erase block */ eraseblock = alignedblock / dev->blkper; ret = MTD_ERASE(dev->mtd, eraseblock, 1); if (ret < 0) { ferr("ERROR: Erase block=%d failed: %d\n", eraseblock, ret); return ret; } /* Write a full erase back to flash */ finfo("Write %d bytes into erase block=%d at offset=0\n", dev->geo.erasesize, alignedblock); nxfrd = MTD_BWRITE(dev->mtd, alignedblock, dev->blkper, buffer); if (nxfrd != dev->blkper) { ferr("ERROR: Write erase block %d failed: %d\n", alignedblock, nxfrd); return -EIO; } /* Then update for amount written */ alignedblock += dev->blkper; remaining -= dev->blkper; buffer += dev->geo.erasesize; } /* Finally, handle any partial blocks after the last full erase block */ if (remaining > 0) { /* Read the full erase block into the buffer */ nxfrd = MTD_BREAD(dev->mtd, alignedblock, dev->blkper, dev->eblock); if (nxfrd != dev->blkper) { ferr("ERROR: Read erase block %d failed: %d\n", alignedblock, nxfrd); return -EIO; } /* Then erase the erase block */ eraseblock = alignedblock / dev->blkper; ret = MTD_ERASE(dev->mtd, eraseblock, 1); if (ret < 0) { ferr("ERROR: Erase block=%d failed: %d\n", eraseblock, ret); return ret; } /* Copy the user data at the beginning the buffered erase block */ nbytes = remaining * dev->geo.blocksize; finfo("Copy %d bytes into erase block=%d at offset=0\n", nbytes, alignedblock); memcpy(dev->eblock, buffer, nbytes); /* And write the erase back to flash */ nxfrd = MTD_BWRITE(dev->mtd, alignedblock, dev->blkper, dev->eblock); if (nxfrd != dev->blkper) { ferr("ERROR: Write erase block %d failed: %d\n", alignedblock, nxfrd); return -EIO; } } return nblocks; }
int nxffs_blockstats(FAR struct nxffs_volume_s *volume, FAR struct nxffs_blkstats_s *stats) { #ifndef CONFIG_NXFFS_NAND FAR uint8_t *bptr; /* Pointer to next block data */ int lblock; /* Logical block index */ #endif off_t ioblock; /* I/O block number */ int ret; /* Process each erase block */ memset(stats, 0, sizeof(struct nxffs_blkstats_s)); #ifndef CONFIG_NXFFS_NAND for (ioblock = 0; ioblock < volume->nblocks; ioblock += volume->blkper) { /* Read the full erase block */ ret = MTD_BREAD(volume->mtd, ioblock, volume->blkper, volume->pack); if (ret < volume->blkper) { ferr("ERROR: Failed to read erase block %d: %d\n", ioblock / volume->blkper, ret); return ret; } /* Then examine each logical block in the erase block */ for (bptr = volume->pack, lblock = 0; lblock < volume->blkper; bptr += volume->geo.blocksize, lblock++) { /* We read the block successfully, now check for errors tagged * in the NXFFS data. */ FAR struct nxffs_block_s *blkhdr = (FAR struct nxffs_block_s *)bptr; /* Increment the total count of blocks examined */ stats->nblocks++; /* Collect statistics */ /* Check if this is a block that should be recognized by NXFFS */ if (memcmp(blkhdr->magic, g_blockmagic, NXFFS_MAGICSIZE) != 0) { /* Nope.. block must not be formatted */ stats->nunformat++; } else if (blkhdr->state == BLOCK_STATE_BAD) { /* The block is marked as bad */ stats->nbad++; } else if (blkhdr->state == BLOCK_STATE_GOOD) { /* The block is marked as good */ stats-> ngood++; } else { /* The good/bad mark is not recognized. Let's call this * corrupt (vs. unformatted). */ stats->ncorrupt++; } } } finfo("Number blocks: %d\n", stats->nblocks); finfo(" Good blocks: %d\n", stats->ngood); finfo(" Bad blocks: %d\n", stats->nbad); finfo(" Unformatted blocks: %d\n", stats->nunformat); finfo(" Corrupt blocks: %d\n", stats->ncorrupt); #else for (ioblock = 0; ioblock < volume->nblocks; ioblock++) { /* Increment the total count of blocks examined */ stats->nblocks++; /* Read each logical block, one at a time. We could read all of the * blocks in the erase block into volume->pack at once. But this would * be a problem for NAND which may generate read errors due to bad ECC * on individual blocks. */ ret = MTD_BREAD(volume->mtd, ioblock, 1, volume->pack); if (ret < 1) { /* This should not happen at all on most kinds of FLASH. But a * bad read will happen normally with a NAND device that has * uncorrectable blocks. So, just for NAND, we keep the count * of unreadable blocks. */ ferr("ERROR: Failed to read block %d: %d\n", ioblock, ret); /* Increment the count of un-readable blocks */ stats->nbadread++; } else { /* We read the block successfully, now check for errors tagged * in the NXFFS data. */ FAR struct nxffs_block_s *blkhdr = (FAR struct nxffs_block_s *)volume->pack; /* Collect statistics */ /* Check if this is a block that should be recognized by NXFFS */ if (memcmp(blkhdr->magic, g_blockmagic, NXFFS_MAGICSIZE) != 0) { /* Nope.. block must not be formatted */ stats->nunformat++; } else if (blkhdr->state == BLOCK_STATE_BAD) { /* The block is marked as bad */ stats->nbad++; } else if (blkhdr->state == BLOCK_STATE_GOOD) { /* The block is marked as good */ stats-> ngood++; } else { /* The good/bad mark is not recognized. Let's call this * corrupt (vs. unformatted). */ stats->ncorrupt++; } } } finfo("Number blocks: %d\n", stats->nblocks); finfo(" Good blocks: %d\n", stats->ngood); finfo(" Bad blocks: %d\n", stats->nbad); finfo(" Unformatted blocks: %d\n", stats->nunformat); finfo(" Corrupt blocks: %d\n", stats->ncorrupt); finfo(" Unreadable blocks: %d\n", stats->nbadread); #endif return OK; }
int nxffs_dump(FAR struct mtd_dev_s *mtd, bool verbose) { #if defined(CONFIG_DEBUG_FEATURES) && defined(CONFIG_DEBUG_FS) struct nxffs_blkinfo_s blkinfo; int ret; /* Get the volume geometry. (casting to uintptr_t first eliminates * complaints on some architectures where the sizeof long is different * from the size of a pointer). */ memset(&blkinfo, 0, sizeof(struct nxffs_blkinfo_s)); ret = MTD_IOCTL(mtd, MTDIOC_GEOMETRY, (unsigned long)((uintptr_t)&blkinfo.geo)); if (ret < 0) { ferr("ERROR: MTD ioctl(MTDIOC_GEOMETRY) failed: %d\n", -ret); return ret; } /* Save the verbose output indication */ blkinfo.verbose = verbose; /* Allocate a buffer to hold one block */ blkinfo.buffer = (FAR uint8_t *)kmm_malloc(blkinfo.geo.blocksize); if (!blkinfo.buffer) { ferr("ERROR: Failed to allocate block cache\n"); return -ENOMEM; } /* Now read every block on the device */ syslog(LOG_NOTICE, "NXFFS Dump:\n"); syslog(LOG_NOTICE, g_hdrformat); blkinfo.nblocks = blkinfo.geo.erasesize * blkinfo.geo.neraseblocks / blkinfo.geo.blocksize; for (blkinfo.block = 0, blkinfo.offset = 0; blkinfo.block < blkinfo.nblocks; blkinfo.block++, blkinfo.offset += blkinfo.geo.blocksize) { /* Read the next block */ ret = MTD_BREAD(mtd, blkinfo.block, 1, blkinfo.buffer); if (ret < 0) { #ifndef CONFIG_NXFFS_NAND /* Read errors are fatal */ ferr("ERROR: Failed to read block %d\n", blkinfo.block); kmm_free(blkinfo.buffer); return ret; #else /* A read error is probably fatal on all media but NAND. * On NAND, the read error probably just signifies a block * with an uncorrectable ECC failure. So, to handle NAND, * just report the read error and continue. */ syslog(LOG_NOTICE, g_format, blkinfo.block, 0, "BLOCK", "RD FAIL", blkinfo.geo.blocksize); #endif } else { /* Analyze the block that we just read */ nxffs_analyze(&blkinfo); } } syslog(LOG_NOTICE, "%d blocks analyzed\n", blkinfo.nblocks); kmm_free(blkinfo.buffer); return OK; #else return -ENOSYS; #endif }