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; }
static int erase_write (struct mtd_info *mtd, unsigned long pos, int len, const char *buf) { struct erase_info erase; DECLARE_WAITQUEUE(wait, current); wait_queue_head_t wait_q; size_t retlen; int ret; /* * First, let's erase the flash block. */ init_waitqueue_head(&wait_q); erase.mtd = mtd; erase.callback = erase_callback; erase.addr = pos; erase.len = len; erase.priv = (u_long)&wait_q; set_current_state(TASK_INTERRUPTIBLE); add_wait_queue(&wait_q, &wait); ret = MTD_ERASE(mtd, &erase); if (ret) { set_current_state(TASK_RUNNING); remove_wait_queue(&wait_q, &wait); printk (KERN_WARNING "mtdblock: erase of region [0x%lx, 0x%x] " "on \"%s\" failed\n", pos, len, mtd->name); return ret; } schedule(); /* Wait for erase to finish. */ remove_wait_queue(&wait_q, &wait); /* * Next, writhe data to flash. */ ret = MTD_WRITE (mtd, pos, len, &retlen, buf); if (ret) return ret; if (retlen != len) return -EIO; return 0; }
/* * INFTL_format: format a Erase Unit by erasing ALL Erase Zones in the Erase * Unit and Update INFTL metadata. Each erase operation is * checked with check_free_sectors. * * Return: 0 when succeed, -1 on error. * * ToDo: 1. Is it neceressary to check_free_sector after erasing ?? * 2. UnitSizeFactor != 0xFF */ int INFTL_formatblock(struct INFTLrecord *inftl, int block) { int retlen; struct inftl_unittail uci; struct erase_info *instr = &inftl->instr; DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_formatblock(inftl=0x%x," "block=%d)\n", (int)inftl, block); memset(instr, 0, sizeof(struct erase_info)); /* Use async erase interface, test return code */ instr->addr = block * inftl->EraseSize; instr->len = inftl->EraseSize; MTD_ERASE(inftl->mtd, instr); if (instr->state == MTD_ERASE_FAILED) { /* * Could not format, FixMe: We should update the BadUnitTable * both in memory and on disk. */ printk(KERN_WARNING "INFTL: error while formatting block %d\n", block); return -1; } /* * Check the "freeness" of Erase Unit before updating metadata. * FixMe: is this check really necessary? Since we have check the * return code after the erase operation. */ if (check_free_sectors(inftl, instr->addr, inftl->EraseSize, 1) != 0) return -1; uci.EraseMark = cpu_to_le16(ERASE_MARK); uci.EraseMark1 = cpu_to_le16(ERASE_MARK); uci.Reserved[0] = 0; uci.Reserved[1] = 0; uci.Reserved[2] = 0; uci.Reserved[3] = 0; if (MTD_WRITEOOB(inftl->mtd, block * inftl->EraseSize + SECTORSIZE * 2 + 8, 8, &retlen, (char *)&uci) < 0) return -1; return 0; }
int nettel_eraseconfig(void) { struct mtd_info *mtd; DECLARE_WAITQUEUE(wait, current); wait_queue_head_t wait_q; int ret; init_waitqueue_head(&wait_q); mtd = get_mtd_device(NULL, 2); if (mtd) { nettel_erase.mtd = mtd; nettel_erase.callback = nettel_erasecallback; nettel_erase.callback = NULL; nettel_erase.addr = 0; nettel_erase.len = mtd->size; nettel_erase.priv = (u_long) &wait_q; nettel_erase.priv = 0; set_current_state(TASK_INTERRUPTIBLE); add_wait_queue(&wait_q, &wait); ret = MTD_ERASE(mtd, &nettel_erase); if (ret) { set_current_state(TASK_RUNNING); remove_wait_queue(&wait_q, &wait); put_mtd_device(mtd); return(ret); } schedule(); /* Wait for erase to finish. */ remove_wait_queue(&wait_q, &wait); put_mtd_device(mtd); } return(0); }
static int erase_write (struct mtd_info *mtd, unsigned long pos, int len, const char *buf) { struct erase_info erase; DECLARE_WAITQUEUE(wait, current); wait_queue_head_t wait_q; size_t retlen; int ret; /* * First, let's erase the flash block. */ init_waitqueue_head(&wait_q); erase.mtd = mtd; erase.callback = erase_callback; erase.addr = pos; erase.len = len; erase.priv = (u_long)&wait_q; set_current_state(TASK_INTERRUPTIBLE); add_wait_queue(&wait_q, &wait); ret = MTD_ERASE(mtd, &erase); if (ret) { set_current_state(TASK_RUNNING); remove_wait_queue(&wait_q, &wait); printk (KERN_WARNING "mtdblock: erase of region [0x%lx, 0x%x] " "on \"%s\" failed\n", pos, len, mtd->name); return ret; } schedule(); /* Wait for erase to finish. */ remove_wait_queue(&wait_q, &wait); /* * Next, writhe data to flash. */ #ifdef CONFIG_MOT_FEAT_MTD_AUTO_BBM bbm_retry: ret = MTD_WRITE (mtd, pos, len, &retlen, buf); if (ret) { if (mtd->block_replace) { DEBUG(MTD_DEBUG_LEVEL0, "mtdblock: block_replace with pos %08x\n", (unsigned int)pos); if (mtd->block_replace(mtd, pos, 0)) { printk (KERN_ERR "mtdblock: out of replacement block for pos %08x\n", (unsigned int)pos); return ret; } /* try to write again with replacement block */ goto bbm_retry; } return ret; } #else ret = MTD_WRITE (mtd, pos, len, &retlen, buf); if (ret) return ret; #endif if (retlen != len) return -EIO; return 0; }
/* * find_boot_record: Find the INFTL Media Header and its Spare copy which * contains the various device information of the INFTL partition and * Bad Unit Table. Update the PUtable[] table according to the Bad * Unit Table. PUtable[] is used for management of Erase Unit in * other routines in inftlcore.c and inftlmount.c. */ static int find_boot_record(struct INFTLrecord *inftl) { struct inftl_unittail h1; //struct inftl_oob oob; unsigned int i, block; u8 buf[SECTORSIZE]; struct INFTLMediaHeader *mh = &inftl->MediaHdr; struct INFTLPartition *ip; size_t retlen; DEBUG(MTD_DEBUG_LEVEL3, "INFTL: find_boot_record(inftl=%p)\n", inftl); /* * Assume logical EraseSize == physical erasesize for starting the * scan. We'll sort it out later if we find a MediaHeader which says * otherwise. */ inftl->EraseSize = inftl->mbd.mtd->erasesize; inftl->nb_blocks = inftl->mbd.mtd->size / inftl->EraseSize; inftl->MediaUnit = BLOCK_NIL; /* Search for a valid boot record */ for (block = 0; block < inftl->nb_blocks; block++) { int ret; /* * Check for BNAND header first. Then whinge if it's found * but later checks fail. */ ret = MTD_READ(inftl->mbd.mtd, block * inftl->EraseSize, SECTORSIZE, &retlen, buf); /* We ignore ret in case the ECC of the MediaHeader is invalid (which is apparently acceptable) */ if (retlen != SECTORSIZE) { static int warncount = 5; if (warncount) { printk(KERN_WARNING "INFTL: block read at 0x%x " "of mtd%d failed: %d\n", block * inftl->EraseSize, inftl->mbd.mtd->index, ret); if (!--warncount) printk(KERN_WARNING "INFTL: further " "failures for this block will " "not be printed\n"); } continue; } if (retlen < 6 || memcmp(buf, "BNAND", 6)) { /* BNAND\0 not found. Continue */ continue; } /* To be safer with BIOS, also use erase mark as discriminant */ if ((ret = MTD_READOOB(inftl->mbd.mtd, block * inftl->EraseSize + SECTORSIZE + 8, 8, &retlen, (char *)&h1) < 0)) { printk(KERN_WARNING "INFTL: ANAND header found at " "0x%x in mtd%d, but OOB data read failed " "(err %d)\n", block * inftl->EraseSize, inftl->mbd.mtd->index, ret); continue; } /* * This is the first we've seen. * Copy the media header structure into place. */ memcpy(mh, buf, sizeof(struct INFTLMediaHeader)); /* Read the spare media header at offset 4096 */ MTD_READ(inftl->mbd.mtd, block * inftl->EraseSize + 4096, SECTORSIZE, &retlen, buf); if (retlen != SECTORSIZE) { printk(KERN_WARNING "INFTL: Unable to read spare " "Media Header\n"); return -1; } /* Check if this one is the same as the first one we found. */ if (memcmp(mh, buf, sizeof(struct INFTLMediaHeader))) { printk(KERN_WARNING "INFTL: Primary and spare Media " "Headers disagree.\n"); return -1; } mh->NoOfBootImageBlocks = le32_to_cpu(mh->NoOfBootImageBlocks); mh->NoOfBinaryPartitions = le32_to_cpu(mh->NoOfBinaryPartitions); mh->NoOfBDTLPartitions = le32_to_cpu(mh->NoOfBDTLPartitions); mh->BlockMultiplierBits = le32_to_cpu(mh->BlockMultiplierBits); mh->FormatFlags = le32_to_cpu(mh->FormatFlags); mh->PercentUsed = le32_to_cpu(mh->PercentUsed); #ifdef CONFIG_MTD_DEBUG_VERBOSE if (CONFIG_MTD_DEBUG_VERBOSE >= 2) { printk("INFTL: Media Header ->\n" " bootRecordID = %s\n" " NoOfBootImageBlocks = %d\n" " NoOfBinaryPartitions = %d\n" " NoOfBDTLPartitions = %d\n" " BlockMultiplerBits = %d\n" " FormatFlgs = %d\n" " OsakVersion = 0x%x\n" " PercentUsed = %d\n", mh->bootRecordID, mh->NoOfBootImageBlocks, mh->NoOfBinaryPartitions, mh->NoOfBDTLPartitions, mh->BlockMultiplierBits, mh->FormatFlags, mh->OsakVersion, mh->PercentUsed); } #endif if (mh->NoOfBDTLPartitions == 0) { printk(KERN_WARNING "INFTL: Media Header sanity check " "failed: NoOfBDTLPartitions (%d) == 0, " "must be at least 1\n", mh->NoOfBDTLPartitions); return -1; } if ((mh->NoOfBDTLPartitions + mh->NoOfBinaryPartitions) > 4) { printk(KERN_WARNING "INFTL: Media Header sanity check " "failed: Total Partitions (%d) > 4, " "BDTL=%d Binary=%d\n", mh->NoOfBDTLPartitions + mh->NoOfBinaryPartitions, mh->NoOfBDTLPartitions, mh->NoOfBinaryPartitions); return -1; } if (mh->BlockMultiplierBits > 1) { printk(KERN_WARNING "INFTL: sorry, we don't support " "UnitSizeFactor 0x%02x\n", mh->BlockMultiplierBits); return -1; } else if (mh->BlockMultiplierBits == 1) { printk(KERN_WARNING "INFTL: support for INFTL with " "UnitSizeFactor 0x%02x is experimental\n", mh->BlockMultiplierBits); inftl->EraseSize = inftl->mbd.mtd->erasesize << mh->BlockMultiplierBits; inftl->nb_blocks = inftl->mbd.mtd->size / inftl->EraseSize; block >>= mh->BlockMultiplierBits; } /* Scan the partitions */ for (i = 0; (i < 4); i++) { ip = &mh->Partitions[i]; ip->virtualUnits = le32_to_cpu(ip->virtualUnits); ip->firstUnit = le32_to_cpu(ip->firstUnit); ip->lastUnit = le32_to_cpu(ip->lastUnit); ip->flags = le32_to_cpu(ip->flags); ip->spareUnits = le32_to_cpu(ip->spareUnits); ip->Reserved0 = le32_to_cpu(ip->Reserved0); #ifdef CONFIG_MTD_DEBUG_VERBOSE if (CONFIG_MTD_DEBUG_VERBOSE >= 2) { printk(" PARTITION[%d] ->\n" " virtualUnits = %d\n" " firstUnit = %d\n" " lastUnit = %d\n" " flags = 0x%x\n" " spareUnits = %d\n", i, ip->virtualUnits, ip->firstUnit, ip->lastUnit, ip->flags, ip->spareUnits); } #endif if (ip->Reserved0 != ip->firstUnit) { struct erase_info *instr = &inftl->instr; instr->mtd = inftl->mbd.mtd; /* * Most likely this is using the * undocumented qiuck mount feature. * We don't support that, we will need * to erase the hidden block for full * compatibility. */ instr->addr = ip->Reserved0 * inftl->EraseSize; instr->len = inftl->EraseSize; MTD_ERASE(inftl->mbd.mtd, instr); } if ((ip->lastUnit - ip->firstUnit + 1) < ip->virtualUnits) { printk(KERN_WARNING "INFTL: Media Header " "Partition %d sanity check failed\n" " firstUnit %d : lastUnit %d > " "virtualUnits %d\n", i, ip->lastUnit, ip->firstUnit, ip->Reserved0); return -1; } if (ip->Reserved1 != 0) { printk(KERN_WARNING "INFTL: Media Header " "Partition %d sanity check failed: " "Reserved1 %d != 0\n", i, ip->Reserved1); return -1; } if (ip->flags & INFTL_BDTL) break; } if (i >= 4) { printk(KERN_WARNING "INFTL: Media Header Partition " "sanity check failed:\n No partition " "marked as Disk Partition\n"); return -1; } inftl->nb_boot_blocks = ip->firstUnit; inftl->numvunits = ip->virtualUnits; if (inftl->numvunits > (inftl->nb_blocks - inftl->nb_boot_blocks - 2)) { printk(KERN_WARNING "INFTL: Media Header sanity check " "failed:\n numvunits (%d) > nb_blocks " "(%d) - nb_boot_blocks(%d) - 2\n", inftl->numvunits, inftl->nb_blocks, inftl->nb_boot_blocks); return -1; } inftl->mbd.size = inftl->numvunits * (inftl->EraseSize / SECTORSIZE); /* * Block count is set to last used EUN (we won't need to keep * any meta-data past that point). */ inftl->firstEUN = ip->firstUnit; inftl->lastEUN = ip->lastUnit; inftl->nb_blocks = ip->lastUnit + 1; /* Memory alloc */ inftl->PUtable = kmalloc(inftl->nb_blocks * sizeof(u16), GFP_KERNEL); if (!inftl->PUtable) { printk(KERN_WARNING "INFTL: allocation of PUtable " "failed (%zd bytes)\n", inftl->nb_blocks * sizeof(u16)); return -ENOMEM; } inftl->VUtable = kmalloc(inftl->nb_blocks * sizeof(u16), GFP_KERNEL); if (!inftl->VUtable) { kfree(inftl->PUtable); printk(KERN_WARNING "INFTL: allocation of VUtable " "failed (%zd bytes)\n", inftl->nb_blocks * sizeof(u16)); return -ENOMEM; } /* Mark the blocks before INFTL MediaHeader as reserved */ for (i = 0; i < inftl->nb_boot_blocks; i++) inftl->PUtable[i] = BLOCK_RESERVED; /* Mark all remaining blocks as potentially containing data */ for (; i < inftl->nb_blocks; i++) inftl->PUtable[i] = BLOCK_NOTEXPLORED; /* Mark this boot record (NFTL MediaHeader) block as reserved */ inftl->PUtable[block] = BLOCK_RESERVED; /* Read Bad Erase Unit Table and modify PUtable[] accordingly */ for (i = 0; i < inftl->nb_blocks; i++) { int physblock; /* If any of the physical eraseblocks are bad, don't use the unit. */ for (physblock = 0; physblock < inftl->EraseSize; physblock += inftl->mbd.mtd->erasesize) { if (inftl->mbd.mtd->block_isbad(inftl->mbd.mtd, i * inftl->EraseSize + physblock)) inftl->PUtable[i] = BLOCK_RESERVED; } } inftl->MediaUnit = block; return 0; }
int nvram_commit(void) { char *buf; size_t erasesize, len, magic_len; unsigned int i; int ret; struct nvram_header *header; unsigned long flags; u_int32_t offset; DECLARE_WAITQUEUE(wait, current); wait_queue_head_t wait_q; struct erase_info erase; u_int32_t magic_offset = 0; /* Offset for writing MAGIC # */ if (!nvram_mtd) { printk("nvram_commit: NVRAM not found\n"); return -ENODEV; } if (in_interrupt()) { printk("nvram_commit: not committing in interrupt\n"); return -EINVAL; } /* Backup sector blocks to be erased */ erasesize = ROUNDUP(NVRAM_SPACE, nvram_mtd->erasesize); if (!(buf = kmalloc(erasesize, GFP_KERNEL))) { printk("nvram_commit: out of memory\n"); return -ENOMEM; } down(&nvram_sem); if ((i = erasesize - NVRAM_SPACE) > 0) { offset = nvram_mtd->size - erasesize; len = 0; ret = MTD_READ(nvram_mtd, offset, i, &len, buf); if (ret || len != i) { printk("nvram_commit: read error ret = %d, len = %d/%d\n", ret, len, i); ret = -EIO; goto done; } header = (struct nvram_header *)(buf + i); magic_offset = i + ((void *)&header->magic - (void *)header); } else { offset = nvram_mtd->size - NVRAM_SPACE; magic_offset = ((void *)&header->magic - (void *)header); header = (struct nvram_header *)buf; } /* clear the existing magic # to mark the NVRAM as unusable we can pull MAGIC bits low without erase */ header->magic = NVRAM_CLEAR_MAGIC; /* All zeros magic */ /* Unlock sector blocks (for Intel 28F320C3B flash) , 20060309 */ if(nvram_mtd->unlock) nvram_mtd->unlock(nvram_mtd, offset, nvram_mtd->erasesize); ret = MTD_WRITE(nvram_mtd, offset + magic_offset, sizeof(header->magic), &magic_len, (char *)&header->magic); if (ret || magic_len != sizeof(header->magic)) { printk("nvram_commit: clear MAGIC error\n"); ret = -EIO; goto done; } header->magic = NVRAM_MAGIC; /* reset MAGIC before we regenerate the NVRAM, otherwise we'll have an incorrect CRC */ /* Regenerate NVRAM */ spin_lock_irqsave(&nvram_lock, flags); ret = _nvram_commit(header); spin_unlock_irqrestore(&nvram_lock, flags); if (ret) goto done; /* Erase sector blocks */ init_waitqueue_head(&wait_q); for (; offset < nvram_mtd->size - NVRAM_SPACE + header->len; offset += nvram_mtd->erasesize) { erase.mtd = nvram_mtd; erase.addr = offset; erase.len = nvram_mtd->erasesize; erase.callback = erase_callback; erase.priv = (u_long) &wait_q; set_current_state(TASK_INTERRUPTIBLE); add_wait_queue(&wait_q, &wait); /* Unlock sector blocks */ if (nvram_mtd->unlock) nvram_mtd->unlock(nvram_mtd, offset, nvram_mtd->erasesize); if ((ret = MTD_ERASE(nvram_mtd, &erase))) { set_current_state(TASK_RUNNING); remove_wait_queue(&wait_q, &wait); printk("nvram_commit: erase error\n"); goto done; } /* Wait for erase to finish */ schedule(); remove_wait_queue(&wait_q, &wait); } /* Write partition up to end of data area */ header->magic = NVRAM_INVALID_MAGIC; /* All ones magic */ offset = nvram_mtd->size - erasesize; i = erasesize - NVRAM_SPACE + header->len; ret = MTD_WRITE(nvram_mtd, offset, i, &len, buf); if (ret || len != i) { printk("nvram_commit: write error\n"); ret = -EIO; goto done; } /* Now mark the NVRAM in flash as "valid" by setting the correct MAGIC # */ header->magic = NVRAM_MAGIC; ret = MTD_WRITE(nvram_mtd, offset + magic_offset, sizeof(header->magic), &magic_len, (char *)&header->magic); if (ret || magic_len != sizeof(header->magic)) { printk("nvram_commit: write MAGIC error\n"); ret = -EIO; goto done; } /* * Reading a few bytes back here will put the device * back to the correct mode on certain flashes */ offset = nvram_mtd->size - erasesize; ret = MTD_READ(nvram_mtd, offset, 4, &len, buf); done: up(&nvram_sem); kfree(buf); return ret; }
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 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; }