| /**************************************************************************** |
| * fs/nxffs/nxffs_reformat.c |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. The |
| * ASF licenses this file to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance with the |
| * License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations |
| * under the License. |
| * |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <nuttx/config.h> |
| |
| #include <stdint.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <debug.h> |
| |
| #include <nuttx/mtd/mtd.h> |
| |
| #include "nxffs.h" |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: nxffs_format |
| * |
| * Description: |
| * Erase and reformat the entire volume. |
| * |
| * Input Parameters: |
| * volume - Describes the NXFFS volume to be reformatted. |
| * |
| * Returned Value: |
| * Zero on success or a negated errno on a failure. Failures will be |
| * returned n the case of MTD reported failures o. |
| * Nothing in the volume data itself will generate errors. |
| * |
| ****************************************************************************/ |
| |
| 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 */ |
| |
| for (blkptr = volume->pack, i = 0; |
| i < volume->blkper; |
| blkptr += volume->geo.blocksize, i++) |
| { |
| nxffs_blkinit(volume, blkptr, 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) |
| { |
| ferr("ERROR: Erase block %jd failed: %d\n", (intmax_t)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) |
| { |
| ferr("ERROR: Write erase block %jd failed: %zd\n", |
| (intmax_t)lblock, nxfrd); |
| return -EIO; |
| } |
| } |
| |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: nxffs_badblocks |
| * |
| * Description: |
| * Verify each block and mark improperly erased blocks as bad. |
| * |
| * Input Parameters: |
| * volume - Describes the NXFFS volume to be reformatted. |
| * |
| * Returned Value: |
| * Zero on success or a negated errno on a failure. Failures will be |
| * returned n the case of MTD reported failures o. |
| * Nothing in the volume data itself will generate errors. |
| * |
| ****************************************************************************/ |
| |
| 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 of the erase block */ |
| #ifdef CONFIG_NXFFS_NAND |
| off_t block; /* Working block number */ |
| #endif |
| 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++) |
| { |
| /* Get the logical block number of the erase block */ |
| |
| lblock = eblock * volume->blkper; |
| |
| #ifndef CONFIG_NXFFS_NAND |
| /* Read the entire erase block */ |
| |
| nxfrd = MTD_BREAD(volume->mtd, lblock, volume->blkper, volume->pack); |
| if (nxfrd != volume->blkper) |
| { |
| ferr("ERROR: Read erase block %jd failed: %zd\n", |
| (intmax_t)lblock, nxfrd); |
| return -EIO; |
| } |
| #endif |
| |
| /* Keep track if any part of the erase block gets modified */ |
| |
| modified = false; |
| |
| /* Process each logical block */ |
| |
| #ifndef CONFIG_NXFFS_NAND |
| for (blkptr = volume->pack, i = 0; |
| i < volume->blkper; |
| blkptr += volume->geo.blocksize, i++) |
| #else |
| for (i = 0, block = lblock, blkptr = volume->pack; |
| i < volume->blkper; |
| i++, block++, blkptr += volume->geo.blocksize) |
| #endif |
| { |
| FAR struct nxffs_block_s *blkhdr = |
| (FAR struct nxffs_block_s *)blkptr; |
| |
| /* Assume that this is a good block until we learn otherwise */ |
| |
| good = true; |
| |
| #ifdef CONFIG_NXFFS_NAND |
| /* Read the next block in the erase block */ |
| |
| nxfrd = MTD_BREAD(volume->mtd, block, 1, blkptr); |
| if (nxfrd < 0) |
| { |
| /* Failed to read the block. This should never happen with |
| * most FLASH. However, for NAND this probably means that we |
| * read a block with uncorrectable bit errors. |
| */ |
| |
| ferr("ERROR: Failed to read block %d: %d\n", |
| block, (int)nxfrd); |
| |
| good = false; |
| } |
| else |
| #endif |
| /* Check block header */ |
| |
| if (memcmp(blkhdr->magic, g_blockmagic, NXFFS_MAGICSIZE) != 0 || |
| blkhdr->state != BLOCK_STATE_GOOD) |
| { |
| /* The block is not formatted with the NXFFS magic bytes or |
| * else the block is specifically marked bad. |
| */ |
| |
| good = false; |
| } |
| |
| /* This is a properly formatted, good NXFFS block. Check that the |
| * block data payload 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) |
| { |
| nxffs_blkinit(volume, blkptr, 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) |
| { |
| ferr("ERROR: Write erase block %jd failed: %zd\n", |
| (intmax_t)lblock, nxfrd); |
| return -EIO; |
| } |
| } |
| } |
| |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: nxffs_reformat |
| * |
| * Description: |
| * Erase and reformat the entire volume. Verify each block and mark |
| * improperly erased blocks as bad. |
| * |
| * Input Parameters: |
| * volume - Describes the NXFFS volume to be reformatted. |
| * |
| * Returned Value: |
| * Zero on success or a negated errno on a failure. Failures will be |
| * returned n the case of MTD reported failures o. |
| * Nothing in the volume data itself will generate errors. |
| * |
| ****************************************************************************/ |
| |
| int nxffs_reformat(FAR struct nxffs_volume_s *volume) |
| { |
| int ret; |
| |
| /* Erase and reformat the entire volume */ |
| |
| ret = nxffs_format(volume); |
| if (ret < 0) |
| { |
| ferr("ERROR: Failed to reformat the volume: %d\n", -ret); |
| return ret; |
| } |
| |
| /* Check for bad blocks */ |
| |
| ret = nxffs_badblocks(volume); |
| if (ret < 0) |
| { |
| ferr("ERROR: Bad block check failed: %d\n", -ret); |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: nxffs_blkinit |
| * |
| * Description: |
| * Initialize an NXFFS block to the erased state with the specified block |
| * status. |
| * |
| * Input Parameters: |
| * volume - Describes the NXFFS volume (needed for the blocksize). |
| * blkptr - Pointer to the logic block to initialize. |
| * state - Either BLOCK_STATE_GOOD or BLOCK_STATE_BAD. |
| * |
| * Returned Value: |
| * None. |
| * |
| ****************************************************************************/ |
| |
| void nxffs_blkinit(FAR struct nxffs_volume_s *volume, FAR uint8_t *blkptr, |
| uint8_t state) |
| { |
| FAR struct nxffs_block_s *blkhdr = (FAR struct nxffs_block_s *)blkptr; |
| |
| memset(blkptr, CONFIG_NXFFS_ERASEDSTATE, volume->geo.blocksize); |
| memcpy(blkhdr->magic, g_blockmagic, NXFFS_MAGICSIZE); |
| blkhdr->state = state; |
| } |