static int nxffs_format(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 */ int i; int ret; /* Create an image of one properly formatted erase sector */ memset(volume->pack, CONFIG_NXFFS_ERASEDSTATE, volume->geo.erasesize); 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; memcpy(blkhdr->magic, g_blockmagic, NXFFS_MAGICSIZE); blkhdr->state = BLOCK_STATE_GOOD; } /* Erase and format each erase block */ for (eblock = 0; eblock < volume->geo.neraseblocks; eblock++) { /* Erase the block */ ret = MTD_ERASE(volume->mtd, eblock, 1); if (ret < 0) { fdbg("Erase block %d failed: %d\n", eblock, ret); return ret; } /* Write the formatted image to the erase block */ lblock = eblock * volume->blkper; 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; }
int nxffs_wrcache(FAR struct nxffs_volume_s *volume) { size_t nxfrd; /* Write the current block from the cache */ nxfrd = MTD_BWRITE(volume->mtd, volume->cblock, 1, volume->cache); if (nxfrd != 1) { fdbg("ERROR: Write block %d failed: %d\n", volume->cblock, nxfrd); return -EIO; } /* Write was successful */ return OK; }
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 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 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; }