www.pudn.com > efs.rar > fs_ftl.c
/******************************************************************************
* fs_ftl.c
*
* This file implements the NAND Flash Translation Layer to present the NAND
* flash as logical sectors.
*
* Copyright (C) 2006 Qualcomm, Inc. All Rights Reserved
*
*****************************************************************************/
/*=============================================================================
EDIT HISTORY FOR MODULE
This section contains comments describing changes made to the module.
Notice that changes are listed in reverse chronological order.
$Header: //depot/asic/MSMSHARED/services/efs/MSM_EFS.01.02/fs_ftl.c#6 $
$DateTime: 2006/10/12 21:20:47 $
$Author: davidb $
when who what, where, why
-------- --- ----------------------------------------------------------
2006-10-09 yg Added device layer and counters support.
2006-10-02 yg Add Partition support to FTL.
2006-09-20 dlb Lint fixes.
2006-08-28 yg Added 2k page device and Cache layer support.
2006-06-12 yg Lint fixes.
2006-06-07 yg Removed block_count correction
2006-06-07 yg Removed MMC stub functions
2006-06-02 yg Rename var, release func clean, bad power down review fix
2006-06-02 sh Message on ftl_init()
2006-05-28 sh Cosmetic changes
2006-05-24 yg Changed to use unsigned integers.
2006-05-21 yg Cleanup
2006-04-10 yg Initial file for FTL layer
=============================================================================*/
#include "comdef.h"
#include "customer.h"
#ifdef FEATURE_EFS_FTL
#include
#include "flashi.h"
#include "fs_ftl.h"
#include "fs_ftl_i.h"
#include "flash_nand.h"
#include "crc.h"
#include "msg.h"
#include "err.h"
#include "jzap.h"
#include "fs_ftl_device.h"
// #include "tt_public.h"
#include "fs_timetest2.h"
#ifndef FS_UNIT_TEST
/* Some flash driver related data needed for probe */
static struct fsi_device_data ftl_priv_device;
#else
# include "utflash.h"
#endif
#define FTL_PARTITION_NAME "0:FTL"
/* This is the interface externally available from Flash layer */
fs_device_t ftl_flash_dev;
/* Buffer to hold the data to write */
static byte dev_page_buffer[MAX_DATA_PAGE_SIZE];
/* An extra page buffer to avoid doing memset everytime */
static byte erased_buffer[MAX_DATA_PAGE_SIZE];
/*
* This is the map to translate logical block to physical block,
* will also have 2 bit generation along with. So its a tradeoff
* between code complexity and memory usage to keep Generation
* in a seperate array.
*
* Storage format is:
* GG PPPPPP PPPPPPPP
* G(2) : 2 bit generation
* P(14) : 14 bit Physical block number
* Offset : LBN Logical block number
*/
static uint16 lbn_to_phy_blk_map[MAX_BLOCK_COUNT];
/*
* This is the state of the physical blocks to pick a free block.
* At any given point of time this gives the status of the physical
* block.
*/
static uint8 phy_blk_status[MAX_BLOCK_COUNT];
/* Just keep track of how many free blocks are available */
static uint32 free_count;
/* A running location pointer to pick next free block */
static uint32 free_blk_pos;
/* This is the maximum number of "logical sectors", which
* are actually physical flash PAGES for 512-byte flash. */
static uint32 max_logical_page_cnt;
/* Number of blocks in the FTL area of flash */
static uint32 ftl_block_count;
/* FTL page size in bytes */
static uint32 ftl_flash_page_size;
/* Number of pages (logical blocks) in one flash block */
static uint32 ftl_pages_per_block;
/* In case we want to erase the whole flash, just set this true from T32 */
uint32 ftl_fresh_start = 0;
/* This is the number of logical sub pages in a device page
* for a 2k page device we find 4, 512 byte logical pages.
*/
static uint32 ftl_logical_pages_per_dev_page;
/*
* These variables denote what is the mask for observing each entity
* given the sector number. Shift is used in the conversions. In case
* a device which has odd number which makes these masks not possible
* will have to use division method which will be slower than this.
*
*/
static uint32 logical_page_sector_mask;
static uint32 physical_page_sector_mask;
/*=============================================================================
F T L I m p l e m e n t a t i o n s t a r t
=============================================================================*/
/******************************************************************************
FUNCTION get_unique_free_block_search_pos
DESCRIPTION Use CRC 16 algorithm on the existing map and get a unique
(pseudo random) location to search for the free block. This
will make sure we don't start from a known physical block
always and wearing them out the first.
RETURN VALUE
Block number to start search for a free block.
******************************************************************************/
static uint32
get_unique_free_block_search_pos (void)
{
uint32 pseudo_random_pos, data_bit_len;
/* Number of bits to compute */
data_bit_len = ((max_logical_page_cnt /
(ftl_logical_pages_per_dev_page * ftl_pages_per_block))
* sizeof (*lbn_to_phy_blk_map) * BITS_PER_BYTE);
/* Correct it, if it overflows to more than CRC routine can handle */
if (data_bit_len > 0xFFF0)
data_bit_len = 0xFFF0;
pseudo_random_pos = crc_16_calc ((byte*)lbn_to_phy_blk_map, data_bit_len);
return pseudo_random_pos % ftl_block_count;
}
/******************************************************************************
FUNCTION erase_block_and_update_status
DESCRIPTION This function erases the given block.
Also attempts to mark the block bad on failure to erase.
RETURN VALUE Flash status
******************************************************************************/
static int
erase_block_and_update_status (uint32 block_num)
{
int result;
result = ftl_dev_erase_block (ftl_flash_dev, block_num);
if (result != FS_DEVICE_OK)
{
/* No use in checking error return for this function, we can't do much */
(void) ftl_dev_mark_block_bad (ftl_flash_dev, block_num);
phy_blk_status[block_num] = BLK_STATE_BAD;
}
else
{
phy_blk_status[block_num] = BLK_STATE_FREE;
++free_count;
}
return result;
}
/******************************************************************************
FUNCTION ftl_raw_init
DESCRIPTION Initialize the FTL layer
RETURN VALUE
0 : Success
Error code : Failure
******************************************************************************/
int
ftl_raw_init (void)
{
uint32 corrected_spare, pg_to_read, first_page_written, last_page_written;
uint32 stored_spare, block_num;
ZAP ("ftl_init()");
/* If we fail to initialize FTL layer, this count would make sure we
* return from other externally available API's without doing anything. */
max_logical_page_cnt = 0;
#ifndef FS_UNIT_TEST
/* Probe flash and get its parameters */
if (flash_nand_device_probe (&ftl_priv_device, 0) != FS_DEVICE_OK) {
ftl_flash_dev = (fs_device_t)FS_NO_DEVICE;
} else {
ftl_flash_dev = (fs_device_t)&ftl_priv_device;
}
#endif
if (ftl_flash_dev == (fs_device_t)FS_NO_DEVICE) {
return FTL_FAILURE;
}
/* Enable this feature in cust file to use partition support */
#ifdef FEATURE_FTL_USE_PARTITION
if (ftl_flash_dev->open_partition != 0)
{
if (ftl_flash_dev->open_partition (ftl_flash_dev, FTL_PARTITION_NAME)
!= FS_DEVICE_OK)
{
/* If the partition is not defined, then hotplug would know that
* the FTL device is not existing (since we return fail to init)
* so it would not allow FTL device to be mounted. To have FTL
* working, define the partion of needed size. */
return FTL_FAILURE;
}
}
#endif
/* Since we have small number of states, compiler should not allocate more
* than a byte for the enum. If it does we just waste a lot of memory space
* for no better use, so correct something to instruct compiler to allocate
* just 1 byte for this enum */
if (sizeof (phy_blk_status) > MAX_BLOCK_COUNT) {
ERR_FATAL("Compiler allocated size for enum bigger than a byte", 0, 0, 0);
}
/* Get basic flash device layout parameters */
ftl_block_count = ftl_dev_block_count (ftl_flash_dev);
ftl_flash_page_size = ftl_dev_page_size (ftl_flash_dev);
ftl_pages_per_block = ftl_dev_block_size (ftl_flash_dev);
/* The maximum block (sector) count is reduced by our reserved space
to allow spare pages for bad blocks. */
max_logical_page_cnt = (ftl_block_count
* ftl_pages_per_block
* (ftl_flash_page_size / DEFAULT_MS_PAGE_SIZE)
* (100 - PERCENT_BAD_BLOCK_BACKUP) / 100);
/* Limit the number to something which is rounded off to nearest 256 */
max_logical_page_cnt &= 0xFFFFFF00;
ftl_logical_pages_per_dev_page = ftl_flash_page_size / DEFAULT_MS_PAGE_SIZE;
physical_page_sector_mask =
(ftl_pages_per_block * ftl_logical_pages_per_dev_page) - 1;
logical_page_sector_mask = ftl_logical_pages_per_dev_page - 1;
if ((physical_page_sector_mask
& (ftl_pages_per_block * ftl_logical_pages_per_dev_page))
|| (ftl_logical_pages_per_dev_page & logical_page_sector_mask))
{
ERR_FATAL("Odd device configuration for this software to handle", 0, 0, 0);
}
/* If the device is bigger than we can handle just quit */
if ((ftl_block_count > MAX_BLOCK_COUNT)
|| (ftl_flash_page_size > MAX_DATA_PAGE_SIZE))
{
/* Here the arrays block map and page buffers are defined to be
* the smallest possible units for a given flash size, to save
* memory. If support for a bigger flash size is needed, then
* these sizes should be increased to accomodate these increased sizes. */
ERR_FATAL ("Mis configured size constants, Increase the size...", 0, 0, 0);
}
/* Check if we have enough Spare data bytes for storing FTL Meta data */
if (ftl_dev_get_spare_corrected_bytes (ftl_flash_dev) < 2)
{
ERR_FATAL("Flash Device doesn't have enough Spare bytes for FTL", 0, 0, 0);
}
/* Initialize the block map with invalid values */
memset (lbn_to_phy_blk_map, INVALID_GEN_PHY_BLK, sizeof(lbn_to_phy_blk_map));
/* Fill our erased buffer */
memset (erased_buffer, ERASED_BUFFER_VAL, sizeof(erased_buffer));
/* A count of free (erased and useable) blocks is maintained
* for debug purposes. */
free_count = 0;
corrected_spare = 0;
/* Walk through each physical block to assemble our map */
for (block_num = 0; block_num < ftl_block_count; ++block_num)
{
/* If this block is bad, skip it and never touch it again */
if (ftl_dev_bad_block_check (ftl_flash_dev, block_num)
!= FS_DEVICE_OK)
{
phy_blk_status[block_num] = BLK_STATE_BAD; /* Avoid */
continue;
}
/* If this was set TRUE for testing, erase each good block of the flash */
if (ftl_fresh_start) {
erase_block_and_update_status (block_num);
continue;
}
/* ------------------------------------------------------------
* Check the first and last page of this block to see if it has
* been written.
* ------------------------------------------------------------ */
/* Check if the first page of this block is written */
pg_to_read = BLOCK_TO_PAGE_NUM (block_num);
first_page_written =
ftl_dev_is_erased (ftl_flash_dev, pg_to_read) == FALSE;
/* Now read the last page of this block without ECC */
pg_to_read += (ftl_pages_per_block - 1);
last_page_written =
(ftl_dev_read_spare_udata (ftl_flash_dev, pg_to_read,
&corrected_spare, 2) == FS_DEVICE_OK);
/* If nothing is written in first and last page, then this block is free */
if (!first_page_written && !last_page_written)
{
phy_blk_status[block_num] = BLK_STATE_FREE;
free_count++;
continue;
}
/* If any one of the first or last page is not written, or ECC decode
* fails, then erase the block and continue, since power might have
* failed either while writing or during erase */
if ( (first_page_written && !last_page_written) ||
(!first_page_written && last_page_written) ||
((corrected_spare & LBN_MASK) >= ftl_block_count))
{
erase_block_and_update_status (block_num);
continue;
}
/* ------------------------------------------------------------
* This block has been fully written with valid data. Find out if
* it's the newest version of this logical block and update our
* maps accordingly.
* ------------------------------------------------------------ */
/* Check if this LBN has been seen in any other block */
stored_spare = lbn_to_phy_blk_map[corrected_spare & LBN_MASK];
if (stored_spare == 0xFFFF) {
/* Nothing is assigned, so just map this LBN to this physical block */
lbn_to_phy_blk_map[corrected_spare & LBN_MASK] =
block_num | (corrected_spare & GENERATION_MASK);
phy_blk_status[block_num] = BLK_STATE_IN_USE;
}
else
{
/* We found duplicate blocks claiming to be the same LBN.
* Decide which is newest and discard the old copy (erase it). */
/* Combine the two generations into a four-bit value for testing: */
#define GENERATION_PAIR(a, b) (((a) << 2) | b)
switch (GENERATION_PAIR (GET_GENERATION (stored_spare),
GET_GENERATION (corrected_spare)))
{
/* Left (previously-seen) generation is newer: */
case GENERATION_PAIR(1, 0):
case GENERATION_PAIR(2, 1):
case GENERATION_PAIR(3, 2):
case GENERATION_PAIR(0, 3):
/* Erase and forget the current block, which is outdated */
erase_block_and_update_status (block_num);
break;
/* Right (current block) generation is newer: */
case GENERATION_PAIR(0, 1):
case GENERATION_PAIR(1, 2):
case GENERATION_PAIR(2, 3):
case GENERATION_PAIR(3, 0):
/* Erase the previously-seen block that is outdated */
erase_block_and_update_status (stored_spare & LBN_MASK);
/* Note this one as the current block */
lbn_to_phy_blk_map[corrected_spare & LBN_MASK] =
block_num | (corrected_spare & GENERATION_MASK);
break;
/* Ambiguous cases: identical generations: */
case GENERATION_PAIR(0, 0):
case GENERATION_PAIR(1, 1):
case GENERATION_PAIR(2, 2):
case GENERATION_PAIR(3, 3):
/* Ambiguous cases: too far (2) apart: */
case GENERATION_PAIR(0, 2):
case GENERATION_PAIR(1, 3):
case GENERATION_PAIR(2, 0):
case GENERATION_PAIR(3, 1):
default:
/* The two generation values are ambiguous, and it is not
* possible to determine which one is newer. This should
* never happen. For now we can ERR_FATAL, but if it ever
* actually happens, we should probably just delete an
* arbitrary block (or even both blocks) to recover. */
ERR_FATAL("Generation fields are corrupted...", 0, 0, 0);
}
}
}
/* Compute the location where we will begin looking for a free
* block. While this doesn't have to be random, it should be
* well-distributed so that we don't unevenly wear flash by favoring
* some physical blocks over others. Once we pick a starting block,
* we will rotate through the blocks in a simple linear walk. */
free_blk_pos = get_unique_free_block_search_pos ();
return FTL_SUCCESS;
}
/******************************************************************************
FUNCTION ftl_raw_read
DESCRIPTION Read specified number of sectors from mentioned sector onwards.
Now with 2k page support we have too many variables in the picture.
Logical page : 512 byte page (or simply sector)
Logical unit : 2k page, its the actual page where logical page is present.
for 512 page device Logical page == Logical unit.
RETURN VALUE
0 : Success
Error code : Failure
******************************************************************************/
int
ftl_raw_read (uint16 drive_num, uint32 sector, byte* buffer, uint16 count)
{
uint32 phy_blk, phy_src_page, device_page, last_page_to_read;
byte* page_buff_ptr = erased_buffer;
(void)drive_num;
if (sector >= max_logical_page_cnt) {
return FTL_FAILURE; /* Out of range */
}
ftl_device_read_counter (sector, count, FTL_OP_RAW);
last_page_to_read = sector + count;
device_page = INVALID_PAGE;
/* Loop for as many pages we need to read */
for ( ; sector < last_page_to_read; ++sector)
{
if (device_page != LOGICAL_TO_DEVICE_PAGE(sector))
{
device_page = LOGICAL_TO_DEVICE_PAGE(sector);
phy_blk = lbn_to_phy_blk_map[BLOCK_NUM_OF_PAGE (device_page)];
phy_src_page = (BLOCK_TO_PAGE_NUM (phy_blk & LBN_MASK)
+ PAGE_OFFSET_IN_BLOCK (device_page));
page_buff_ptr = dev_page_buffer;
if ((phy_blk != INVALID_GEN_PHY_BLK)
&& (phy_blk_status[phy_blk & LBN_MASK] == BLK_STATE_BAD))
{
/* This should never happen, a bad block is assigned to
* a usable sector */
ERR_FATAL ("Bad block is assigned to a useable block: %X %d",
phy_blk, free_count, 0);
}
if ((phy_blk == INVALID_GEN_PHY_BLK)
|| (phy_blk_status[phy_blk & LBN_MASK] == BLK_STATE_FREE)
|| (ftl_dev_read_page (ftl_flash_dev, phy_src_page,
page_buff_ptr)
!= FS_DEVICE_OK))
{
page_buff_ptr = erased_buffer;
}
page_buff_ptr += (DEFAULT_MS_PAGE_SIZE * LOGICAL_PAGE_OFFSET(sector));
}
/* This double memory copy can be avoided later by adding a bit more
* complexity
*/
memcpy(buffer, page_buff_ptr, DEFAULT_MS_PAGE_SIZE);
/* Point to next page */
buffer += DEFAULT_MS_PAGE_SIZE;
page_buff_ptr += DEFAULT_MS_PAGE_SIZE;
}
return FTL_SUCCESS;
}
/******************************************************************************
FUNCTION get_free_block
DESCRIPTION Search for a free block in the free map and return block number.
Mark this block as "IN_USE" and decrement the free_count.
NOTE: this is physical Block number.
RETURN VALUE
0 : Failure
Free Blk# : Success
******************************************************************************/
uint32
get_free_block (void)
{
uint32 block_num;
/* At the most we have search ftl_block_count times */
for (block_num = 0; block_num < ftl_block_count; ++block_num)
{
if (phy_blk_status[free_blk_pos] == BLK_STATE_FREE)
{
phy_blk_status[free_blk_pos] = BLK_STATE_IN_USE;
--free_count;
return free_blk_pos;
}
++free_blk_pos;
if (free_blk_pos >= ftl_block_count)
free_blk_pos = 0;
}
return INVALID_GEN_PHY_BLK;
}
/******************************************************************************
FUNCTION get_spare_data_to_write
DESCRIPTION Calculate the spare data to write with new generation num.
RETURN VALUE Spare data to write into the last page spare area.
******************************************************************************/
uint32
get_spare_data_to_write (uint32 sector)
{
uint32 spare_val;
uint32 temp_lbn = BLOCK_NUM_OF_PAGE (sector);
/* Have new generation ready in the spare data */
spare_val = GET_GENERATION (lbn_to_phy_blk_map[temp_lbn]);
/* Increment the Generation value and mask off. After max it wraps around */
spare_val = ((spare_val + 1) << GENERATION_SHIFT) & GENERATION_MASK;
/* Get the LBN combined with Generation to write into the spare area */
spare_val |= temp_lbn;
return spare_val;
}
/******************************************************************************
FUNCTION convert_absolute_to_relative_sectors
DESCRIPTION Convert all absolute sector numbers to relative sector numbers
within physical block.
RETURN VALUE Success / Failure
******************************************************************************/
static void
convert_absolute_to_relative_sectors (cached_buffer_list_type* data_buffer_ary)
{
uint32 conversion_mask;
conversion_mask = ((ftl_logical_pages_per_dev_page * ftl_pages_per_block)
- 1);
/* Place only the offset numbers in the location, instead of sector
* number. This will help just make one calculation for mapping
* and rest of the code needs only the offsets
*/
while (data_buffer_ary->data_ptr != 0)
{
data_buffer_ary->sector_num
= conversion_mask & data_buffer_ary->sector_num;
data_buffer_ary++;
}
}
int
find_and_copy_new_data (uint32 dest_page,
cached_buffer_list_type * src_data_buffer,
byte * dest_page_buffer)
{
uint32 logical_page_start, logical_page_end;
uint32 matched_page_cnt = 0;
logical_page_start = dest_page * ftl_logical_pages_per_dev_page;
/* Remove the block component out of the sector values, since we
* don't have block information in the buffers. It has only the
* physical page and logical part in the page as sector number.
*/
logical_page_start = logical_page_start & physical_page_sector_mask;
logical_page_end = logical_page_start + ftl_logical_pages_per_dev_page;
while (src_data_buffer)
{
if ((src_data_buffer->data_ptr == 0)
|| (src_data_buffer->sector_num == INVALID_PAGE)
|| (src_data_buffer->sector_num >= logical_page_end))
{
break;
}
if ((src_data_buffer->sector_num >= logical_page_start)
&& (src_data_buffer->sector_num < logical_page_end))
{
++matched_page_cnt;
if (dest_page_buffer != 0)
{
byte* dst_ptr = dest_page_buffer;
dst_ptr += ((src_data_buffer->sector_num & logical_page_sector_mask)
* DEFAULT_MS_PAGE_SIZE);
memcpy (dst_ptr, src_data_buffer->data_ptr, DEFAULT_MS_PAGE_SIZE);
}
if (matched_page_cnt == ftl_logical_pages_per_dev_page)
{
break;
}
}
src_data_buffer++;
}
return matched_page_cnt;
}
int
write_device_page (uint32 dest_page,
uint32 old_src_page,
cached_buffer_list_type * src_data_buffer,
uint32 spare_val)
{
uint32 matched_page_cnt, read_source_page_needed;
uint8 * data_ptr;
uint32 spare_data = spare_val;
int result = FS_DEVICE_OK;
data_ptr = dev_page_buffer;
/* Just search how many new pages we got, Don't copy yet. */
matched_page_cnt = find_and_copy_new_data (dest_page, src_data_buffer, 0);
read_source_page_needed = 0;
/* If we don't have src data available, but this page is either first or
* last page then we have to write the page data, so need to read the
* old src page.
*/
if ((matched_page_cnt == 0) &&
((PAGE_OFFSET_IN_BLOCK (dest_page) == 0) ||
(PAGE_OFFSET_IN_BLOCK (dest_page) == (ftl_pages_per_block - 1))))
{
read_source_page_needed = 1;
}
else if ((matched_page_cnt != 0)
&& (matched_page_cnt != ftl_logical_pages_per_dev_page))
{
read_source_page_needed = 1;
}
if (read_source_page_needed)
{
if (PAGE_IS_NOT_VALID (old_src_page) ||
(ftl_dev_read_page (ftl_flash_dev, old_src_page, data_ptr)
!= FS_DEVICE_OK))
{
memset (data_ptr, ERASED_BUFFER_VAL, ftl_flash_page_size);
}
}
if (matched_page_cnt)
{
/* This will overwrite the buffer with new data if available */
find_and_copy_new_data (dest_page, src_data_buffer, data_ptr);
}
/* Now we have source data ready if we need to write, or copy page */
if (PAGE_OFFSET_IN_BLOCK (dest_page) == (ftl_pages_per_block - 1))
{
result = ftl_dev_write_page_and_udata (ftl_flash_dev, dest_page,
data_ptr, &spare_data, 2);
}
else if ((PAGE_OFFSET_IN_BLOCK (dest_page) == 0) || (matched_page_cnt != 0))
{
result = ftl_dev_write_page (ftl_flash_dev, dest_page, data_ptr);
}
else if (PAGE_IS_VALID (old_src_page))
{
result = ftl_dev_copy_page (ftl_flash_dev, old_src_page, dest_page);
}
return result;
/* DONE */
}
/******************************************************************************
FUNCTION single_small_block_write
DESCRIPTION Attempt to Write one block worth of data.
RETURN VALUE Success / Failure
******************************************************************************/
static int
single_block_write (uint32 sector, cached_buffer_list_type* data_buffer_ary,
uint32 free_blk_to_write)
{
uint32 old_phy_src_page;
uint32 dst_page, dst_end_page;
uint32 old_phy_blk, spare_data;
uint32 device_page_num;
int result;
convert_absolute_to_relative_sectors (data_buffer_ary);
device_page_num = LOGICAL_TO_DEVICE_PAGE (sector);
spare_data = get_spare_data_to_write(device_page_num);
/* Do we have old block assigned to this LBN? */
old_phy_blk = lbn_to_phy_blk_map[BLOCK_NUM_OF_PAGE (device_page_num)]
& LBN_MASK;
/* Have the source page number for old block ready, if one is available */
if ((old_phy_blk < ftl_block_count) && /* Within limit..?? */
(phy_blk_status[old_phy_blk] == BLK_STATE_IN_USE)) /* Available..?? */
old_phy_src_page = BLOCK_TO_PAGE_NUM (old_phy_blk);
else
old_phy_src_page = INVALID_PAGE;
/* Our range of physical destination pages to loop through.
* The physical destination's: First page to last page.
*/
dst_page = BLOCK_TO_PAGE_NUM (free_blk_to_write);
dst_end_page = LAST_PAGE_OF_BLOCK (free_blk_to_write);
/*
* Write this entire block page by page. Sequence to write the data
*/
while (dst_page <= dst_end_page)
{
/* Check if our current writing page, should write new data that we got */
result = write_device_page (dst_page, old_phy_src_page,
data_buffer_ary, spare_data);
if ((result != FS_DEVICE_OK) && (result != FS_DEVICE_CP_READ_FAIL))
{
return FTL_FAILURE;
}
/* Pick next page */
dst_page++;
if (PAGE_IS_VALID (old_phy_src_page))
++old_phy_src_page;
} /* Handling of one block is done now */
/* Delete the old block if we had it active */
if ((old_phy_blk < ftl_block_count) &&
(phy_blk_status[old_phy_blk] == BLK_STATE_IN_USE))
{
erase_block_and_update_status (old_phy_blk);
}
/* Update our main map, use the spare data which already has the value */
phy_blk_status[free_blk_to_write] = BLK_STATE_IN_USE;
lbn_to_phy_blk_map[spare_data & LBN_MASK] = free_blk_to_write |
(spare_data & GENERATION_MASK);
return FTL_SUCCESS;
}
/******************************************************************************
FUNCTION ftl_raw_write
DESCRIPTION Write multiple logical sectors starting from the specified one.
RETURN VALUE Success / Failure
******************************************************************************/
int
ftl_raw_write (uint16 drive_num, uint32 sector,
cached_buffer_list_type* buffer, uint16 count)
{
uint32 free_blk;
uint32 logical_device_page;
(void) drive_num;
if ((sector >= max_logical_page_cnt) ||
((sector + count) > max_logical_page_cnt)) {
return FTL_FAILURE;
}
ftl_device_write_counter (sector, count, FTL_OP_RAW);
/* We don't have a free block available handy now, later its possible */
free_blk = INVALID_GEN_PHY_BLK;
while (count > 0)
{
/* Get a free block if we don't have one handy already */
if (free_blk == INVALID_GEN_PHY_BLK)
{
free_blk = get_free_block ();
if (free_blk == INVALID_GEN_PHY_BLK)
{
ERR_FATAL ("Error, NO more free blocks available to write", 0, 0, 0);
}
}
/* Write 1 block with this new data in the free block that we got */
if (single_block_write (sector, buffer, free_blk) == FTL_SUCCESS)
{
uint32 pages_written, pg_of_next_blk;
logical_device_page = LOGICAL_TO_DEVICE_PAGE(sector);
/* Calculate how many pages of required data we have in this 1 block */
pg_of_next_blk =
BLOCK_TO_PAGE_NUM (BLOCK_NUM_OF_PAGE (logical_device_page) + 1);
pages_written = ((pg_of_next_blk - logical_device_page)
* ftl_logical_pages_per_dev_page);
pages_written -= LOGICAL_PAGE_OFFSET(sector);
pages_written = (pages_written > count) ? count : pages_written;
/* Update our params */
count -= pages_written;
sector += pages_written;
/* Get new block to write */
free_blk = INVALID_GEN_PHY_BLK;
} else {
if (erase_block_and_update_status (free_blk) != FS_DEVICE_OK) {
/* Retry writing the data by allocating new block */
free_blk = INVALID_GEN_PHY_BLK;
}
} /* Single block write */
} /* While */
return FTL_SUCCESS;
}
/******************************************************************************
FUNCTION ftl_raw_format
DESCRIPTION Low level formatting
RETURN VALUE 0
******************************************************************************/
int
ftl_raw_format (fs_device_t dev)
{
uint32 block_num;
if (dev == (fs_device_t)FS_NO_DEVICE)
{
return -1;
}
for (block_num = 0; block_num < ftl_block_count; ++block_num)
{
if (ftl_dev_bad_block_check (ftl_flash_dev, block_num)
!= FS_DEVICE_BAD_BLOCK)
{
(void) ftl_dev_erase_block (ftl_flash_dev, block_num);
phy_blk_status[block_num] = BLK_STATE_FREE;
++free_count;
}
else
{
phy_blk_status[block_num] = BLK_STATE_BAD;
}
}
memset (lbn_to_phy_blk_map, INVALID_GEN_PHY_BLK, sizeof(lbn_to_phy_blk_map));
return 0;
}
/******************************************************************************
FUNCTION ftl_get_raw_dev_params
DESCRIPTION Get physical flash device parameters.
RETURN VALUE 0
******************************************************************************/
int
ftl_get_raw_dev_params (uint32 * pblock_count, uint32 * ppg_per_block,
uint16 * pg_size, uint32 *psector_cnt)
{
if (ftl_flash_dev == (fs_device_t)FS_NO_DEVICE)
{
return FTL_FAILURE;
}
if (ppg_per_block)
{
*ppg_per_block = ftl_pages_per_block;
}
if (pblock_count)
{
*pblock_count = ftl_block_count;
}
if (pg_size)
{
*pg_size = ftl_flash_page_size;
}
if (psector_cnt)
{
*psector_cnt = max_logical_page_cnt;
}
return FTL_SUCCESS;
}
#ifdef FS_UNIT_TEST
#error code not present
#endif
#else
extern int __dont_complain_about_empty_file;
#endif /* FEATURE_FS_FTL */