www.pudn.com > SMSC USB2.0.zip > map.c
/*============================================================================
____________________________________________________________________________
______________________________________________
SSSS M M CCCC Standard Microsystems Corporation
S MM MM SSSS C Austin Design Center
SSS M M M S C 11000 N. Mopac Expressway
S M M SSS C Stonelake Bldg. 6, Suite 500
SSSS M M S CCCC Austin, Texas 78759
SSSS ______________________________________________
____________________________________________________________________________
Copyright(C) 1999, Standard Microsystems Corporation
All Rights Reserved.
This program code listing is proprietary to SMSC and may not be copied,
distributed, or used without a license to do so. Such license may have
Limited or Restricted Rights. Please refer to the license for further
clarification.
____________________________________________________________________________
Notice: The program contained in this listing is a proprietary trade
secret of SMSC, Hauppauge, New York, and is copyrighted
under the United States Copyright Act of 1976 as an unpublished work,
pursuant to Section 104 and Section 408 of Title XVII of the United
States code. Unauthorized copying, adaption, distribution, use, or
display is prohibited by this law.
____________________________________________________________________________
Use, duplication, or disclosure by the Government is subject to
restrictions as set forth in subparagraph(c)(1)(ii) of the Rights
in Technical Data and Computer Software clause at DFARS 52.227-7013.
Contractor/Manufacturer is Standard Microsystems Corporation,
80 Arkay Drive, Hauppauge, New York, 1178-8847.
____________________________________________________________________________
____________________________________________________________________________
map.c - implements common sector mapping algorithms
____________________________________________________________________________
comments tbd
____________________________________________________________________________
Revision History
Date Who Comment
________ ___ _____________________________________________________________
03/11/02 cds initial version - moved from former media.c location
04/18/02 cds tweaked the conflict-resolution logic to comform to SMART MEDIA's
recovery. The log2phy conflict-resolution code is soon to be
moved into the media so that memory stick and smart media
can handle 'em differently. for now this code WILL LIKELY BREAK
MEMORY STICK, should a conflicting log2phy situation occur!
04/19/02 cds map_build_sector_map() now calls media specific routine to
resolve conflicting log2phy bindings.
04/24/02 cds updated map_lba2addr_rd & map_build_sector_map() to try to
eliminate some stack variables which were causing
stack overflows. More may be necessary
07/10/02 cds added (but #if 0'd out) code that would return a blank block
address (but valid) instead of k_block_free.
07/29/02 cds - removed g_media_err_code references (old smil leftover)
- removed (uint8) cast of g_addr_sector calculation in build sector map
08/06/02 tbh enabled dormant code to reclaim inconsistent blocks. this is
part of the solution for br308 (though it is not required for
that bug fix... so if it causes a problem it can be disabled here
in both locations and not affect he SanDisk issue as resolved in
ms.c and ms_media.c)
08/20/02 cds the above br308 fix did infact cause problems, but the fix was
introduced a new bug where when paging in a new zone (for any
mapped media), if there was no logical binding for the first
block during a write split, the mapper would select the same
block for both the rd and wr phy block, thus erasing the block
immediately after writing valid data to it. To fix this,
the block selected for reading is given a temporary binding and
marked as used.
08/27/02 cds updated map_erase_rd_blk to reflect the accurate recovery
processing for smart media. the new code attempts retry the
erase command on failure. If it fails a second time, the
block is marked "bad" by calls to the media functions, but
the erase reports success. The non-erased block should no
longer be in the working set, even when the map is reconstructed.
09/04/02 cds address name & usage change-over
g_addr_sector => g_addr_page,
g_addr_page => g_addr_segment,
_media_data(sectors_per_block) => _media_data(pages_per_block)
_media_data(cis_phy_block) => _media_data(boot_block)
_media_data(cis_sector) => _media_data(boot_sector)
09/19/02 cds - initial erase caching code. added new functions:
map_erase_block()
map_erase_block_cache()
map_erase_cache_flush_one()
map_erase_cache_flush_all()
- call to map_erase_rd_blk() calls the erase cache if enabled
for the media
09/21/02 cds - fixed bug that was double-incrementing the erase_cache_head_ptr
when adding blocks to the cache.
- write_caching implemented. The caching appears to do what it's
supposed to, but the xfer halts w/o error or timeout when flushing
a write on a read command. Have a feeling it's to do with
sharing the fmdu...
09/22/02 cds - All tables and erase cache variables have been moved into the
media_data(...) structure for per-instance erase caching.
- Added support for one erase_cache map for each zone mapped into
the media_data() so that it gets updated whenever the zone maps
paged-in/paged-out.
- redefined a few of teh erase_cache functions to take advantage
of the existing code that does the 'usage' tracking for free blocks.
Instead of 'used' meaning 'written', used means 'cached'.
- cleaned up unused caching experiments
- #if'd out write-caching for code-space
09/23/02 cds - optimized mapper to not erase read-blocks that had no logical
binding. (improves efficiency of large > 1/2-zone file streams)
09/24/02 cds - added k_erase_cnt which can be adjusted to tune how many blocks
are erased per call to flush single... this boosts the degraded
performance up to 711K/sec hs.
10/01/02 cds - fixed _xxx_map() functions to use reentrant stack
- updated erase cache code to reduce stack size by storing addresses in xdata
instead of on the stack (makes 'em non-reentrant tho)
10/05/02 cds - protected all erase cache code with #ifdef k_opt_erase_cache to reduce 210 code space
- defined k_opt_erase_cache to be defined for 211 and 242
10/08/02 cds - updated soft logical binding code (in lba2addr_rd & erase_rd_blk) to tag soft bound (ram only)\
log2phy entries with a bit indicating that phy blk is not written. On erase_rd_blk, this bit
is checked, and if set, only marks the rd_phy blk free. If the bit is NOT set, then the block
is erased or added to the erase cache.
- fixed erase_cache_flush_one() to ensure that only valid phy blocks are erased.
10/23/02 cds - added feature to mark source block "bad" when a read (copy_src) error occurs, but pass the copy
- added feature to erase dest block when copy write (copy_dst) error occurs, and fail the copy
10/29/02 cds - added feature to precheck for bad data pages in blocks. memories like memory stick should be unaffected
because the default media_block_has_bad_data() function returns false always.
- added hook for sm.c, nand.c, and sm_media.c to check if a logical block has a bad data block in it.
( map_log_blk_has_bad_data() )
10/30/02 cds - fixed copy_tail to use bitwise OR rather than logical OR when updating log2phy table
- fixed copy_tail to return a result explicity.
11/02/02 cds - updated map_build_sector_map() to always call the virtual _media_resolve_conflict() when 2 phy blocks
report the same logical binding. this allows greater flexibility in handling differences between
sm, nand and ms compliance issues.
11/03/02 cds - updated map_build_sector_map() to call _media_is_phyblock_reserved() instead of assuming which blocks are OK
- updated map_build_sector_map() to check if a block is good before checking if it is blank
==============================================================================*/
#include "project.h"
#define k_erase_cnt 4
#if defined(k_mcu_97211) || defined(k_mcu_97242)
#define k_opt_erase_cache
#endif
//-----------------------------------------------------------------------------
// bit control macros
//-----------------------------------------------------------------------------
static code char k_tbl_bitdata[] = { 0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80} ;
#define _setbit(__buffer,__bitaddr) (__buffer[(uint8)((__bitaddr)/8)] |= k_tbl_bitdata[(__bitaddr)%8])
#define _clrbit(__buffer,__bitaddr) (__buffer[(uint8)((__bitaddr)/8)] &= ~k_tbl_bitdata[(__bitaddr)%8])
#define _chkbit(__buffer,__bitaddr) (__buffer[(uint8)((__bitaddr)/8)] & k_tbl_bitdata[(__bitaddr)%8])
#define _flpbit(__buffer,__bitaddr) (__buffer[(uint8)((__bitaddr)/8)] ^= k_tbl_bitdata[(__bitaddr)%8])
//-----------------------------------------------------------------------------
// more clearly defined macros to wrap set/clr/flip bit macros
//-----------------------------------------------------------------------------
#define _map_phy_blk_used( __map, __blk ) ( _setbit( __map, __blk ) )
#define _map_phy_blk_free( __map, __blk ) ( _clrbit( __map, __blk ) )
#define _map_is_phy_blk_free( __map, __blk ) (!(_chkbit( __map, __blk)))
#define _map_is_phy_blk_used( __map, __blk ) ( _chkbit( __map, __blk))
//+-----------------------------------------------------------------------------
// obtain pointer to log2phy map for the given zone from the media data
//------------------------------------------------------------------------------
// $$$ => will leave this alone, as long as logical-blocks-per-boot_zone is < every other zone, we'll be ok
// $$$ converted this to a function to save code space...
t_log2phy_map_ref _log_map(uint8 __zone) reentrant
{
return ((_media_data(log2phy_map) + ((__zone?1:0)*_media_data(logical_blocks_per_zone))));
}
//+-----------------------------------------------------------------------------
// obtain pointer to assignment map for the given zone from the media data
//------------------------------------------------------------------------------
t_assign_map_ref _assign_map( uint8 __zone ) reentrant
{
return ((_media_data(assign_map)+((__zone?1:0)*_media_data(physical_blocks_per_zone)/8)));
}
#ifdef k_opt_erase_cache
//+-----------------------------------------------------------------------------
// obtain pointer to cached-erase map for the given zone from the media data
//------------------------------------------------------------------------------
t_erase_cache_ref _erase_cache(uint8 __zone) reentrant
{
return ((_media_data(erase_cache)+((__zone?1:0)*_media_data(physical_blocks_per_zone)/8)));
}
#endif
#define kbm_map_soft_l2p_binding (0x8000)
#define kbm_map_blk_has_bad_data (0x4000)
#define kbm_map_l2p_bits (0x0FFF)
// cached pointers to the log2phy and assignment map for a single zone
static xdata t_log2phy_map_ref _l2p_map;
static xdata t_assign_map_ref _a_map;
static xdata t_erase_cache_ref _e_map;
#ifdef k_opt_erase_cache
// cache status macros
#define _is_erase_cache_empty(__zone) ( _media_data(erase_start)[(__zone)?1:0]==k_block_free)
extern void erase_cache_flush_one() reentrant;
#endif
//------------------------------------------------------------------------------
// write caching data (don't argue with it)
//------------------------------------------------------------------------------
xdata uint16 g_wr_cache_tail_log;
xdata uint16 g_wr_cache_tail_src;
xdata uint16 g_wr_cache_tail_dst;
xdata uint16 g_wr_cache_tail_page;
xdata uint16 g_wr_cache_tail_zone;
// #define _write_cache_is_empty() ((g_wr_cache_head_log==k_block_free)&&(g_wr_cache_tail_log==k_block_free))
#define _write_cache_is_empty() (g_wr_cache_tail_log==k_block_free)
#define _write_cache_is_full() (!_write_cache_empty())
//+-----------------------------------------------------------------------------
// Name:
// map_resolve_conflict()
//
// Declaration:
// t_result sm_media_resolve_conflict(void) reentrant
//
// Purpose:
// resolve a log2phy binding in a media-specific algorithm
//
// Arguments:
// - see Notes
//
// Return:
// k_success always
//
// Notes:
// when called,
// g_addr_rd_phy_blk contains one of the bindings,
// g_addr_wr_phy_blk contains the original mapping
//
// before returning, ensure:
// g_addr_rd_phy_blk should contain the correct mapping
// g_addr_wr_phy_blk should contain the block to be erased (or not used)
//
// Since:
// fmc-1.0
//------------------------------------------------------------------------------
static xdata uint16 _save_rd_pb ;
static xdata uint16 _save_wr_pb ;
static xdata uint16 _save_lb ;
static xdata uint16 _pb1 ;
static xdata uint16 _lb1_first ;
static xdata uint16 _lb1_last ;
static xdata uint16 _pb2 ;
static xdata uint16 _lb2_first ;
static xdata uint16 _lb2_last ;
t_result map_resolve_conflict(void) reentrant
{
trace0(0, map, 0, "map_resolve_conflict()") ;
// store the rd/wr/log blks in case we can't resolve the conflict here, and need to
// defer to the media-specific method.
_save_rd_pb = g_addr_rd_phy_blk ;
_save_wr_pb = g_addr_wr_phy_blk ;
_save_lb = g_addr_log_blk ;
// set-up vars
_pb1 = g_addr_rd_phy_blk ;
_pb2 = g_addr_wr_phy_blk ;
_lb1_first = _lb2_first = g_addr_log_blk ;
// read last-sector extra-data for first pb
g_addr_page = (_media_data(pages_per_block)-1);
if ( k_success != _media_read_extra_data() )
{
trace2(0, map, 0, "resolve conflict case 1: result: %d. reason: could not read extra data for pb %d.", _pb2, _pb1) ;
// conflict resolved: pb2 is the winner
g_addr_rd_phy_blk = _pb2 ;
g_addr_wr_phy_blk = _pb1 ;
g_addr_log_blk = _save_lb ;
return k_success ;
}
if ( k_success != _media_phy2log() )
{
trace2(0, map, 0, "resolve conflict case 2: result: %d. reason: could not read log binding for last sector of pb %d.", _pb2, _pb1) ;
// conflict resolved: pb2 is winner
g_addr_rd_phy_blk = _pb2 ;
g_addr_wr_phy_blk = _pb1 ;
g_addr_log_blk = _save_lb ;
return k_success ;
}
_lb1_last = g_addr_log_blk ;
if( _lb1_first != _lb1_last)
{
trace2(0, map, 0, "resolve conflict case 3: result: %d. reason: pb %d first and last sectors report different logical bindings.", _pb2, _pb1) ;
// conflict resolved: pb1 has conflicting bindings between
// first and last sector, so pb2 is chosen
// conflict resolved: pb2 is winner
g_addr_rd_phy_blk = _pb2 ;
g_addr_wr_phy_blk = _pb1 ;
g_addr_log_blk = _save_lb ;
return k_success ;
}
// read last-sector extra-data for second pb
g_addr_rd_phy_blk = _pb2 ;
if ( k_success != _media_read_extra_data() )
{
// conflict resolved: pb1 is the winner
trace2(0, map, 0, "resolve conflict case 4: result: %d. reason: could not read extra data for pb %d.", _pb1, _pb2) ;
g_addr_rd_phy_blk = _pb1 ;
g_addr_wr_phy_blk = _pb2 ;
g_addr_log_blk = _save_lb ;
return k_success ;
}
if ( k_success != _media_phy2log() )
{
// conflict resolved: pb1 is winner
trace2(0, map, 0, "resolve conflict case 5: result: %d. reason: could not read log binding for last sector of pb %d.", _pb1, _pb2) ;
g_addr_rd_phy_blk = _pb1 ;
g_addr_wr_phy_blk = _pb2 ;
g_addr_log_blk = _save_lb ;
return k_success ;
}
_lb2_last = g_addr_log_blk ;
if( _lb2_first != _lb2_last)
{
trace2(0, map, 0, "resolve conflict case 6: result: %d. reason: pb %d first and last sectors report different logical bindings.", _pb1, _pb2) ;
// conflict resolved: pb2 has conflicting logical bindings between
// first and last sector, so pb1 is chosen
g_addr_rd_phy_blk = _pb1 ;
g_addr_wr_phy_blk = _pb2 ;
g_addr_log_blk = _save_lb ;
return k_success ;
}
// TRACE0(244, map, 0, "resolve conflict case 7: result: unresolved. calling media specific method.") ;
// at this point we know that pb1 and pb2 both have the same log block,
// and the first and last sectors both report the same log block, so
// we don't know which to choose, so we have to call the media-specific
// arbitrator.
// restore the g_addrs
g_addr_rd_phy_blk = _save_rd_pb ;
g_addr_wr_phy_blk = _save_wr_pb ;
g_addr_log_blk = _save_lb ;
return k_error ;
// $$$ took this out because of a stack overflow
// return _media_resolve_conflict() ;
}
//+-----------------------------------------------------------------------------
// Name:
// map_build_sector_map()
//
// Declaration:
// t_result sm_build_sector_map(void) reentrant
//
// Purpose:
//
// Arguments:
//
// Return:
//
// Notes:
//
// Since:
// fmc-1.0
//------------------------------------------------------------------------------
t_result map_build_sector_map(void) reentrant
{
uint16 max_lb;
uint16 pb;
uint16 lb;
t_bool bad_data;
trace0(0, map, 0, "map_build_sector_map()") ;
//_stack_dump() ;
g_addr_segment=0;
g_addr_page=0;
trace2(0, map, 0, "mapping zone %d into map index %d", g_addr_zone, (g_addr_zone?1:0)) ;
_l2p_map = _log_map(g_addr_zone);
_a_map = _assign_map(g_addr_zone);
trace1(0, n2k, 0, "&_l2p_map:%04x", ((uint16)_l2p_map));
trace1(0, n2k, 0, "&_a_map :%04x", ((uint16)_a_map));
#ifdef k_opt_erase_cache
if( _media_data(options)&kbm_media_data_opt_erase_cache)
{
_e_map = _erase_cache(g_addr_zone);
}
else
#endif
{
// this simplifies the clearing of the data below
_e_map = _a_map;
}
trace1(0, cache, 0, "&_e_map :%04x", ((uint16)_e_map));
// erase_start cache always begins cleared to k_block_free
_media_data(erase_start)[(g_addr_zone?1:0)]=k_block_free;
// clear the logical2physical table
for( lb=0 ; lb < _media_data(logical_blocks_per_zone) ; lb++ )
_l2p_map[lb] = k_block_free ;
// clear out the physical block assignment table
for ( pb=0; pb < ( _media_data(physical_blocks_per_zone)/8); pb++ )
{
_a_map[pb]=0x00;
_e_map[pb]=0x00;
}
max_lb = g_addr_zone?_media_data(logical_blocks_per_zone):_media_data(logical_blocks_per_boot_zone);
trace1(0, map, 0, "max log_block this zone:%d", max_lb) ;
// populate the assignment map
for (g_addr_rd_phy_blk=0;g_addr_rd_phy_blk<_media_data(physical_blocks_per_zone);g_addr_rd_phy_blk++)
{
if( _media_is_phyblock_reserved() )
{
trace1(0, map, 0, "phy_blk %d: reserved", g_addr_rd_phy_blk) ;
// first zone, before CIS struct... in sm terms, nothing comes there
_map_phy_blk_used( _a_map, g_addr_rd_phy_blk ) ;
continue;
}
if(k_error == _media_read_extra_data() )
{
trace1(0, map, 0, "phy_blk %d: corrupt or bad extra data", g_addr_rd_phy_blk) ;
_map_phy_blk_used( _a_map, g_addr_rd_phy_blk ) ;
continue;
}
// skip bad block
if( !_media_is_phyblock_ok() )
{
trace1(0, map, 0, "phy_blk %d: user block not usable", g_addr_rd_phy_blk) ;
// treat block as if it is reserved... (i.e. mark it used, but do not add it to l2p table)
_map_phy_blk_used( _a_map, g_addr_rd_phy_blk ) ;
continue;
}
if( _media_is_phyblock_blank() )
{
trace1(0, map, 0, "phy_blk %d: blank", g_addr_rd_phy_blk) ;
continue;
}
// set the block used
_map_phy_blk_used( _a_map, g_addr_rd_phy_blk ) ;
// check for bad data status
bad_data=_media_block_has_bad_data();
if(bad_data)
{
trace1(0, map, 0, "phy_blk %d: contains pages with invalid data", g_addr_rd_phy_blk);
}
// call media to retrieve lb of current pb, or return a reason why it can't be done
if(k_error == _media_phy2log())
{
trace1(0, map, 0, "phy_blk %d: invalid log binding", g_addr_rd_phy_blk) ;
continue;
}
// block can be loaded. is block beyond max logical blocks?
if ( g_addr_log_blk >= max_lb )
{
trace1(0, map, 0, "phy_blk %d: log binding out of range", g_addr_log_blk) ;
continue;
}
// does log_block have a previously detected phy_block?
if (_l2p_map[g_addr_log_blk]==k_block_free)
{
trace2(0, map, 0, "phy_blk %d: log_blk %d", g_addr_rd_phy_blk, g_addr_log_blk);
_l2p_map[g_addr_log_blk] = g_addr_rd_phy_blk|(bad_data?kbm_map_blk_has_bad_data:0);
continue;
}
trace3(0, map, 0, "phy_blk %d: log_blk binding conflict. log_blk %d already mapped to phy_blk %d", g_addr_rd_phy_blk, g_addr_log_blk, (_l2p_map[g_addr_log_blk])) ;
// $$$ cds to do -> potential bug which could drop bad_data info here
// store the rd_phy_blk, and set up the resolver
pb = g_addr_rd_phy_blk ;
g_addr_wr_phy_blk = _l2p_map[g_addr_log_blk] ;
// g_addr_rd_phy_blk contains one of the bindings,
// g_addr_wr_phy_blk contains the original mapping
// call media specific resolver
_media_resolve_conflict();
// g_addr_rd_phy_blk should contain the correct mapping
// g_addr_wr_phy_blk should contain the block to be erased (or not used)
_l2p_map[g_addr_log_blk]=g_addr_rd_phy_blk ;
trace2(0, map, 0, "log_blk %d now mapped to phy_blk:%d", g_addr_log_blk, (_l2p_map[g_addr_log_blk])) ;
trace1(0, map, 0, "*** conflict resolution: Erase Block %d", g_addr_wr_phy_blk) ;
if( k_success == _media_erase_block() )
{
_clrbit( _a_map, g_addr_wr_phy_blk ) ;
}
// restore rd_phy_blk & sector
g_addr_rd_phy_blk = pb ;
g_addr_page=0;
g_addr_wr_phy_blk=k_block_free;
}
_media_data(assign_start)[g_addr_zone?1:0]=0;
TRACE0(303, media, 0, "sector map built") ;
return k_success;
}
//+-----------------------------------------------------------------------------
// Name:
// map_alloc_wr_blk()
//
// Declaration:
// t_result map_alloc_wr_blk(void) reentrant ;
//
// Purpose:
// Physical Block Assign/Release Subroutine, ported from SMIL
//
// Arguments:
//
// Return:
//
// Notes:
//
// Since:
// fmc-1.0
//------------------------------------------------------------------------------
t_result map_alloc_wr_blk(void) reentrant
{
uint8 zone_idx ;
t_bool retry ;
trace0(0, map, 0, "map_alloc_wr_blk()") ;
// _stack_dump() ;
zone_idx = g_addr_zone?1:0 ;
_a_map = _assign_map(g_addr_zone) ;
retry=k_true;
while(retry)
{
trace1(0, map, 0, " starting search at phyblock %d", _media_data(assign_start)[zone_idx]) ;
for (g_addr_wr_phy_blk = _media_data(assign_start)[zone_idx] ;
g_addr_wr_phy_blk < _media_data(physical_blocks_per_zone) ;
g_addr_wr_phy_blk++ )
{
if ( _map_is_phy_blk_free(_a_map, g_addr_wr_phy_blk) )
{
_map_phy_blk_used(_a_map, g_addr_wr_phy_blk) ;
_media_data(assign_start)[zone_idx] = g_addr_wr_phy_blk + 1;
trace2(0, map, 0, "zone %d phy_blk %d allocated for writing", g_addr_zone, g_addr_wr_phy_blk) ;
return k_success ;
}
}
for (g_addr_wr_phy_blk = 0 ;
g_addr_wr_phy_blk < _media_data(assign_start)[zone_idx] ;
g_addr_wr_phy_blk++ )
{
if( _map_is_phy_blk_free(_a_map, g_addr_wr_phy_blk) )
{
_map_phy_blk_used(_a_map, g_addr_wr_phy_blk) ;
_media_data(assign_start)[zone_idx]=g_addr_wr_phy_blk+1;
trace2(0, map, 0, "zone %d phy_blk %d allocated for writing", g_addr_zone, g_addr_wr_phy_blk) ;
return k_success ;
}
}
trace0(0, map, 0, "error: no more available phyblocks in this zone! try to reclaim some 'bad' blocks") ;
#ifdef k_opt_erase_cache
if( _media_data(options)&kbm_media_data_opt_erase_cache)
{
if(!map_erase_cache_flush_one())
{
trace0(0, map, 0, "erase cache is empty... no blocks available in this zone!") ;
retry=k_false;
}
}
else
#endif
{
trace0(0, map, 0, "no blocks available in this zone!");
retry=k_false;
}
}
g_addr_wr_phy_blk = k_block_free ;
trace1(0, map, 0, "error: k_media_err_write_fault - no more phyblocks available in zone %d", g_addr_zone) ;
return k_error ;
}
//+-----------------------------------------------------------------------------
// Name:
// map_bind_wr_blk()
//
// Declaration:
// t_result map_release_read_block(void) reentrant
//
// Purpose:
//
// Arguments:
//
// Return:
//
// Notes:
//
// Since:
// fmc-1.0
//------------------------------------------------------------------------------
t_result map_bind_wr_blk(void) reentrant
{
trace0(0, map, 0, "map_bind_wr_blk()") ;
_l2p_map = _log_map(g_addr_zone) ;
_l2p_map[g_addr_log_blk] = g_addr_wr_phy_blk ;
trace3(0, map, 0, "zone:%d log_blk:%d bound to phy_blk:%d", g_addr_zone, g_addr_log_blk, g_addr_wr_phy_blk ) ;
// release the wr_phy_blk... it is no longer the write-block after the binding.
g_addr_wr_phy_blk = k_block_free ;
return k_success ;
}
//+-----------------------------------------------------------------------------
// Name:
// map_erase_rd_block()
//
// Declaration:
// t_result map_erase_block(void) reentrant ;
//
// Purpose:
//
// Arguments:
//
// Return:
//
// Notes:
//
// Since:
// fmc-1.0
//------------------------------------------------------------------------------
t_result map_erase_rd_blk(void) reentrant
{
uint16 old_wr_blk ;
trace0(0, map, 0, "map_erase_rd_blk()") ;
// get log2phy mapping for the zone
_l2p_map = _log_map(g_addr_zone) ;
if(g_addr_rd_phy_blk==k_block_free)
{
trace0(0, map, 0, "rd_blk blank. not erasing") ;
// ensure that the log2phy map table is correctly marked 'free'
_l2p_map[g_addr_log_blk] = k_block_free ;
trace1(0, map, 0, "log block %d unbound", g_addr_log_blk) ;
}
else
{
trace1(0, map, 0, "unbinding rd phy block %d.", g_addr_rd_phy_blk);
// save wr_phy_block
old_wr_blk = g_addr_wr_phy_blk ;
// aim the wr_phy_block to the rd target
g_addr_wr_phy_blk = g_addr_rd_phy_blk&kbm_map_l2p_bits;
// check for soft binding.
if( !(_l2p_map[g_addr_log_blk]&kbm_map_soft_l2p_binding))
{
trace0(0, map, 0, "full binding: phy block needs erasing");
// to cache or not to cache
#ifdef k_opt_erase_cache
if(_media_data(options)&kbm_media_data_opt_erase_cache)
map_erase_block_cache();
else
#endif
map_erase_block();
}
else
{
trace1(0, map, 0, "rd phy blk %d has soft binding. not erasing", g_addr_rd_phy_blk) ;
_a_map=_assign_map(g_addr_zone);
_map_phy_blk_free(_a_map,g_addr_rd_phy_blk);
}
// block is erased, or will be. unbind it
_l2p_map[g_addr_log_blk] = k_block_free ;
g_addr_rd_phy_blk = k_block_free ;
// restore wr_phy_blk
g_addr_wr_phy_blk = old_wr_blk ;
}
return k_success ;
}
#if 0 // $$$ cds -> 242 code reduction
//+-----------------------------------------------------------------------------
// Name:
// map_erase_block()
//
// Declaration:
// t_result map_erase_block(void) reentrant ;
//
// Purpose:
//
// Arguments:
//
// Return:
//
// Notes:
//
// Since:
// fmc-1.0
//------------------------------------------------------------------------------
t_result map_erase_wr_blk(void) reentrant
{
trace0(0, map, 0, "map_erase_wr_blk()") ;
if(g_addr_wr_phy_blk == k_block_free)
return k_success ;
_a_map = _assign_map(g_addr_zone) ;
_media_erase_block() ;
trace1(0, map, 0, "phy block %d erased", g_addr_wr_phy_blk) ;
_map_phy_blk_free( _a_map, g_addr_wr_phy_blk) ;
trace1(0, map, 0, "phy block %d set to 'free' in assign table", g_addr_wr_phy_blk) ;
g_addr_wr_phy_blk = k_block_free ;
return k_success ;
}
#endif // $$$ cds -> 242 code reduction
#if 0 // $$$ cds -> 242 code reduction
//+-----------------------------------------------------------------------------
// Name:
// map_reassign_block_head()
//
// Declaration:
// t_result map_reassign_block_head(void) reentrant ;
//
// Purpose:
//
// Arguments:
//
// Return:
//
// Notes:
//
// Since:
// fmc-1.0
//------------------------------------------------------------------------------
t_result map_reassign_block_head(void) reentrant
{
#if 1
trace0(0, map, 0, "+map_reassign_block_head() - not implemented yet") ;
return k_error ;
#else
uint16 block;
uint8 page;
block=g_addr_rd_phy_blk ;
page=g_addr_page;
if (k_success != map_alloc_wr_blk())
return k_error ;
for (g_addr_page=0; g_addr_page 242 code reduction
//+-----------------------------------------------------------------------------
// Name:
// map_lba2addr_rd()
//
// Declaration:
// t_result sm_map_lba2phy(unsigned long addr) reentrant
//
// Purpose:
//
// Arguments:
//
// Return:
//
// Notes:
//
// Since:
// fmc-1.0
//------------------------------------------------------------------------------
static xdata uint32 _log ;
static xdata uint16 _block ;
static xdata uint16 _page ;
static xdata uint16 _sectors_per_block ;
t_result map_lba2addr_rd(uint32 addr) reentrant
{
// uint32 _log;
// uint16 _block;
// uint8 _sect;
// map lba -> logical block / logical sector
g_addr_segment = 0 ;
if(_media_data(segments_per_page) == 1)
{
_log = addr / (uint32) _media_data(pages_per_block) ;
g_addr_page = addr% _media_data(pages_per_block);
}
else
{
_sectors_per_block = (uint16) _media_data(pages_per_block) * (uint16) _media_data(segments_per_page) ;
_log = addr / (uint32) _sectors_per_block ;
g_addr_page = (addr % _sectors_per_block) / _media_data(segments_per_page) ;
// new: segment computes lba offset within a page for pages larger than 2k
g_addr_segment= (addr % _sectors_per_block) % _media_data(segments_per_page) ;
}
// map logical block -> zone
if( _log >= _media_data(logical_blocks_per_boot_zone) )
{
_log -= _media_data(logical_blocks_per_boot_zone) ;
g_addr_log_blk = (uint16)(_log%_media_data(logical_blocks_per_zone));
g_addr_zone = (uint8) (_log/_media_data(logical_blocks_per_zone)) + 1 ;
_log += _media_data(logical_blocks_per_boot_zone) ;
}
else
{
g_addr_log_blk = _log ;
g_addr_zone = 0 ;
}
trace3(0, map, 0, "absolute log_blk:%d ==> zone:%d log_blk:%d", _log, g_addr_zone, g_addr_log_blk) ;
// validate the mapping as an addressable sector
if (g_addr_zone>=_media_data(num_zones))
{
trace2(0, map, 0, "zone %d out of range. should be between 0 and %d", g_addr_zone, (_media_data(num_zones))) ;
return k_error ;
}
// map zone, logblock to phyblock through mapping table
if (g_addr_zone!=0 && g_addr_zone != _media_data(assign_zone) )
{
// log2phy map not available for current zone... ditch one of the zones and create a map for the given zone
trace2(0, map, 0, "paging in new zone! old zone:%d new zone:%d ", (_media_data(assign_zone)), g_addr_zone) ;
#ifdef k_opt_erase_cache
if( _media_data(options)&kbm_media_data_opt_erase_cache)
{
// flush only non-boot zones
if(_media_data(assign_zone))
{
TRACE0(304, stk, 0, "about to call map_erase_cache_flush_zone") ;
// $$$ get the above call, but die in the zone call...
// $$$ _stack_dump() ;
map_erase_cache_flush_zone(_media_data(assign_zone));
// don't get this one
TRACE0(305, stk, 0, "map_erase_cache_flush_zone() returned") ;
}
}
#endif
#ifdef k_enable_write_caching
if( _media_data(options)&kbm_media_data_opt_write_cache)
{
map_write_cache_flush_all();
}
#endif
_media_data(assign_zone)=0;
_block=g_addr_log_blk;
_page=g_addr_page;
if (k_success != map_build_sector_map())
{
trace0(0, map, 0, "error building sector map!") ;
return k_error ;
}
_media_data(assign_zone)=g_addr_zone;
g_addr_log_blk=_block;
g_addr_page=_page;
}
// map_dump_log2phy_table() ;
// perform the physical block mapping
_l2p_map = _log_map(g_addr_zone) ;
g_addr_rd_phy_blk=_l2p_map[g_addr_log_blk]&kbm_map_l2p_bits;
// enabled this block for br308
if(g_addr_rd_phy_blk == k_block_free)
{
uint16 old_wr_blk;
trace0(0, map, 0, "logical block not bound... find free block to use in its place") ;
old_wr_blk=g_addr_wr_phy_blk ;
map_alloc_wr_blk() ;
g_addr_rd_phy_blk=g_addr_wr_phy_blk;
g_addr_wr_phy_blk=old_wr_blk;
trace1(0, map, 0, "soft binding block %d", g_addr_rd_phy_blk) ;
// set soft binding bit in log2phy table
_l2p_map[g_addr_log_blk]=g_addr_rd_phy_blk|kbm_map_soft_l2p_binding ;
}
trace3(0, map, 0, "zone:%d log_blk:%d ==> rd_phy_blk:%d", g_addr_zone, g_addr_log_blk, g_addr_rd_phy_blk) ;
// bind current log block to the current 'extra data' buffer
_media_clear_extra_data() ;
_media_bind_log2phy();
return k_success;
}
//+-----------------------------------------------------------------------------
// Name:
// map_is_addr_first_in_block()
//
// Declaration:
// t_bool map_is_addr_first_in_block(void) reentrant
//
// Purpose:
//
// Arguments:
//
// Return:
//
// Notes:
//
// Since:
// fmc-1.0
//------------------------------------------------------------------------------
t_bool map_is_addr_first_in_block(void) reentrant
{
return (g_addr_page?k_false:k_true) ;
}
//+-----------------------------------------------------------------------------
// Name:
// map_is_addr_last_in_block()
//
// Declaration:
// t_bool map_is_addr_last_in_block(void) reentrant
//
// Purpose:
//
// Arguments:
//
// Return:
//
// Notes:
//
// Since:
// fmc-1.0
//------------------------------------------------------------------------------
t_bool map_is_addr_last_in_block(void) reentrant
{
return (g_addr_page < (_media_data(pages_per_block)-1)?k_false:k_true) ;
}
//+-----------------------------------------------------------------------------
// Name:
// map_write_begin()
//
// Declaration:
// t_result map_write_begin(unsigned long start,uint8 *buf) reentrant
//
// Purpose:
//
// Arguments:
//
// Return:
//
// Notes:
//
// Since:
// fmc-1.0
//------------------------------------------------------------------------------
t_result map_write_begin(uint32 lba) reentrant
{
t_result result;
trace1(0, map, 0, "map_write_begin(lba:%04X)", lba) ;
if (k_success != map_lba2addr_rd(lba))
return( k_error );
TRACE0(306, stk, 0, "returned from lba2addr_rd from map_write_begin()") ;
// copy the head sectors to new block
result=k_success;
if(g_addr_page)
{
#ifdef k_enable_write_caching
/*
if (_media_data(options)&kbm_media_data_opt_write_cache)
{
trace0(0, map, 0, "beginning write in mid block... caching copy_head()") ;
if (k_success != map_copy_block_head_cache())
return k_error;
}
else
{
trace0(0, map, 0, "using non-cached copy_block_head()") ;
if (k_success != map_alloc_wr_blk())
return k_error;
if (k_success != media_copy_block_head())
return k_error ;
}
*/
#else
trace0(0, map, 0, "using non-cached copy_block_head()") ;
if (k_success != map_alloc_wr_blk())
return k_error;
trace4(0, nand_int, 0, "inside mapper() - page:%d phy_blk:%d log:%d segment:%d", g_addr_page, g_addr_rd_phy_blk, g_addr_log_blk, g_addr_segment) ;
result=media_copy_block_head();
if (k_success != result)
{
if(k_media_copy_error_src==result)
{
uint16 old_wr_phy_blk;
trace2(0, map, 0, "failure reading from source block %d:%d. setting phyblock failed", g_addr_zone, g_addr_rd_phy_blk) ;
old_wr_phy_blk=g_addr_wr_phy_blk;
g_addr_wr_phy_blk=g_addr_rd_phy_blk;
_media_set_phyblock_failed();
g_addr_wr_phy_blk=old_wr_phy_blk;
// switch the logical binding on the read block to "soft-binding" so that it's not erased
_l2p_map = _log_map(g_addr_zone);
_l2p_map[g_addr_log_blk]|=kbm_map_soft_l2p_binding|kbm_map_blk_has_bad_data;
trace2(0, map, 0, "block %d:%d now soft bound, and marked as having bad data", g_addr_zone, g_addr_rd_phy_blk);
result=k_success;
}
else
{
trace0(0, map, 0, "unrecoverable copy error. erasing write block reporting error") ;
map_erase_block() ;
}
}
#endif
}
else
{
trace0(0, map, 0, " first sector in block. skipping copy block_head") ;
if (k_success != map_alloc_wr_blk())
return k_error;
}
trace0(0, map, 0, "ready to begin");
// ready to begin writing new sectors
return result ;
}
//+-----------------------------------------------------------------------------
// Name:
// map_write_flush()
//
// Declaration:
// t_result map_write_flush(void) reentrant
//
// Purpose:
//
// Arguments:
//
// Return:
//
// Notes:
//
// Since:
// fmc-1.0
//------------------------------------------------------------------------------
t_result map_write_flush(void) reentrant
{
TRACE0(307, stk, 0, "map_write_flush()") ;
// this works because the write-cache is always cleared at the beginning
// of a cached write, even if we are continuing one.
if(map_is_addr_last_in_block())
{
return k_success;
}
g_addr_page++;
// if we were NOT the last in the block, then incrementing the
// sector counter will NOT force a new zone/block mapping, so we
// can safely increment the sector count.
#ifdef k_enable_write_caching
if(_media_data(options)&kbm_media_data_opt_write_cache)
{
// keep track of what's next, and tell host we've got idle processing...
/// eww wanted to keep the lun stuff away from here.
return map_copy_block_tail_cache() ;
}
else
#endif
{
return map_copy_block_tail() ;
}
}
//+-----------------------------------------------------------------------------
// Name:
// map_log2phy()
//
// Declaration:
// t_result map_log2phy(void) reentrant
//
// Purpose:
//
// Arguments:
//
// Return:
//
// Notes:
//
// Since:
// fmc-1.0
//------------------------------------------------------------------------------
uint16 map_log2phy(uint16 log) reentrant
{
return _log_map(g_addr_zone)[log];
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void map_erase_block() reentrant
{
t_result result;
trace2(0, map, 0, "map_erase_block(): phy %d:%d", g_addr_zone, g_addr_wr_phy_blk) ;
// _stack_dump();
_a_map=_assign_map(g_addr_zone) ;
//check result. if failure, then try again.
//fails twice, mark the block bad, and don't free it in the map table.
result=_media_erase_block();
if(k_success != result)
{
if(result!=k_media_error_wp)
{
trace0(0, map, 0, "warning: erase block failed! attempting to retry") ;
if(k_success != _media_erase_block())
{
uint8 page ;
page=g_addr_page ;
g_addr_page=0 ;
trace0(0, map, 0, "error: block %d erase failure. marking block bad") ;
_media_set_phyblock_failed() ;
_media_write_extra_data() ;
trace0(0, cache, 0, "block removed from usage.") ;
g_addr_page=page ;
}
else
{
_map_phy_blk_free( _a_map, g_addr_wr_phy_blk ) ;
trace1(0, map, 0, "retry successful. phy block %d available for writing", g_addr_wr_phy_blk) ;
}
}
else
{
trace0(0, map, 0, "media write protected... cannot erase block! something MAY be amiss");
}
}
else
{
trace1(0, map, 0, "erase success. phy block %d available for writing", g_addr_wr_phy_blk) ;
_map_phy_blk_free( _a_map, g_addr_wr_phy_blk ) ;
}
}
#ifdef k_opt_erase_cache
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void map_erase_cache_init() reentrant
{
#if 0
g_erase_cache_head_ptr=0;
g_erase_cache_tail_ptr=0;
g_erase_cache_zone=0;
#else
#endif
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
t_result map_erase_block_cache() reentrant
{
uint8 zone_idx;
trace2(0, cache, 0, "map_erase_block_cache(): caching phy %d:%d", g_addr_zone, g_addr_wr_phy_blk);
_e_map=_erase_cache(g_addr_zone);
zone_idx=g_addr_zone?1:0;
// do not need to check for 'full' because there are exactly the same number of cache bits as there
// phy blocks for a zone, so it's impossible to become full. The cache will be flushed either one
// at a time when alloc_wr_blk detects no more phy_blks, or when idle-time processing comes around.
if(_media_data(erase_start)[zone_idx]==k_block_free)
{
trace1(0, cache, 0, "erase cache zone %d: empty->not_empty", g_addr_zone) ;
_media_data(erase_start)[zone_idx]=g_addr_wr_phy_blk;
}
_map_phy_blk_used(_e_map, g_addr_wr_phy_blk) ;
g_addr_wr_phy_blk=k_block_free;
return k_true;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// should only be called by internal functions...
void erase_cache_flush_one() reentrant
{
uint8 zone_idx ;
trace1(0, cache, 0, "erase_cache_flush_one(). zone:%d", g_addr_zone) ;
// _stack_dump();
zone_idx= g_addr_zone?1:0 ;
if(_media_data(erase_start)[zone_idx]==k_block_free)
{
trace0(0, cache, 0, "erase cache is empty... scroll through again, just in case, though") ;
_media_data(erase_start)[zone_idx]=0;
}
_e_map=_erase_cache(g_addr_zone) ;
trace1(0, map, 0, " searching cache... starting at %d", _media_data(erase_start)[zone_idx]) ;
for (g_addr_wr_phy_blk=_media_data(erase_start)[zone_idx];g_addr_wr_phy_blk<_media_data(physical_blocks_per_zone);g_addr_wr_phy_blk++)
{
if ( _map_is_phy_blk_used(_e_map, g_addr_wr_phy_blk) )
{
trace2(0, cache, 0, "erase cache: erasing phy %d:%d", g_addr_zone, g_addr_wr_phy_blk) ;
map_erase_block();
_map_phy_blk_free(_e_map, g_addr_wr_phy_blk) ;
_media_data(erase_start)[zone_idx]=g_addr_wr_phy_blk+1;
return ;
}
}
for (g_addr_wr_phy_blk=0;g_addr_wr_phy_blk<_media_data(erase_start)[zone_idx];g_addr_wr_phy_blk++ )
{
if( _map_is_phy_blk_used(_e_map, g_addr_wr_phy_blk) )
{
trace2(0, cache, 0, "erase cache: erasing phy %d:%d", g_addr_zone, g_addr_wr_phy_blk) ;
map_erase_block();
_map_phy_blk_free(_e_map, g_addr_wr_phy_blk) ;
_media_data(erase_start)[zone_idx]=g_addr_wr_phy_blk+1;
return ;
}
}
trace1(0, map, 0, "erase cache zone %d: not_empty->empty detected", g_addr_zone) ;
_media_data(erase_start)[zone_idx]=k_block_free;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// can be called from anywhere
xdata uint8 _ec_prev_zone;
xdata uint16 _ec_prev_blk;
t_result map_erase_cache_flush_one() reentrant
{
uint8 i=k_erase_cnt;
_ec_prev_blk=g_addr_wr_phy_blk;
// _stack_dump();
// zone_idx=g_addr_zone?1:0;
if( _media_data(erase_start)[(g_addr_zone?1:0)]==k_block_free)
{
trace0(0, cache, 0, "erase cache: zone %d erase cache is empty.") ;
return k_false;
}
while(i--)
{
erase_cache_flush_one();
}
g_addr_wr_phy_blk=_ec_prev_blk;
return k_true;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
t_result map_erase_cache_flush_zone(uint8 zone) reentrant
{
_ec_prev_zone = g_addr_zone;
_ec_prev_blk = g_addr_wr_phy_blk;
g_addr_zone=zone?_media_data(assign_zone):0;
trace1(0, cache, 0, "map_erase_cache_flush_zone(zone:%d)", g_addr_zone)
while(!_is_erase_cache_empty(g_addr_zone))
erase_cache_flush_one();
g_addr_zone=_ec_prev_zone;
g_addr_wr_phy_blk=_ec_prev_blk;
return k_success;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// assumes g_addr_zone has been set now
t_result map_erase_cache_flush_all() reentrant
{
trace0(0, cache, 0, "map_erase_cache_flush_all()");
//_stack_dump();
// flush boot zone
map_erase_cache_flush_zone(0) ;
// flush paged zone
if(_media_data(assign_zone))
map_erase_cache_flush_zone(_media_data(assign_zone));
return k_success;
}
#endif
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
t_result map_rebind_log_blk() reentrant
{
uint8 bad_data;
uint16 blk;
trace4(0, map, 0, "map_rebind_log_blk(). updating log binding %d:%d. old phy_blk:%d new phy_blk:%d", g_addr_zone, g_addr_log_blk, g_addr_rd_phy_blk, g_addr_wr_phy_blk);
bad_data=map_log_blk_has_bad_data();
if (k_success != map_erase_rd_blk() )
{
trace0(0, map, 0, "mapper error erasing rd block") ;
return k_error;
}
blk=g_addr_wr_phy_blk;
if(k_success != map_bind_wr_blk())
{
trace0(0, map, 0, "mapper error binding wr block") ;
return k_error;
}
if(bad_data)
{
trace0(0, map, 0, "old binding had a bad data page. check the new binding to see if it carried over");
_l2p_map=_log_map(g_addr_zone);
g_addr_rd_phy_blk=_l2p_map[g_addr_log_blk]&kbm_map_l2p_bits;
trace2(0, map, 0, "checking read phyblk %d (old wr_phy_blk:%d)", g_addr_rd_phy_blk, g_addr_wr_phy_blk);
// there was a bad block on the old phy blk. check to see if there is one there now
if(_media_block_has_bad_data())
{
trace2(0, map, 0, "log blk %d (phy blk %d) has bad data page", g_addr_log_blk, g_addr_rd_phy_blk);
_l2p_map[g_addr_log_blk]|=kbm_map_blk_has_bad_data;
}
g_addr_rd_phy_blk=k_block_free;
}
return k_success;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
t_result map_copy_block_tail() reentrant
{
t_result result;
trace0(0, map, 0, "map_copy_block_tail()");
result=media_copy_block_tail();
if (k_success != result)
{
if(k_media_copy_error_src==result)
{
uint16 old_wr_phy_blk;
trace2(0, map, 0, "failure reading from source block %d:%d. setting phyblock failed", g_addr_zone, g_addr_rd_phy_blk) ;
old_wr_phy_blk=g_addr_wr_phy_blk;
g_addr_wr_phy_blk=g_addr_rd_phy_blk;
_media_set_phyblock_failed();
g_addr_wr_phy_blk=old_wr_phy_blk;
// switch the logical binding on the read block to "soft-binding" so that it's not erased
_l2p_map = _log_map(g_addr_zone);
_l2p_map[g_addr_log_blk]|=kbm_map_soft_l2p_binding|kbm_map_blk_has_bad_data;
trace2(0, map, 0, "block %d:%d now soft bound, and marked having bad binding", g_addr_zone, g_addr_rd_phy_blk);
// no need to report a write failure, since the error occured on non-user data.
}
else
{
trace0(0, map, 0, "unrecoverable copy error. erasing write block reporting error") ;
map_erase_block() ;
return k_error;
}
}
// else status was good
result=map_rebind_log_blk();
if (k_success!=result)
{
trace0(0, map, 0, "mapper error when rebinding log blk from rd_blk to wr_blk") ;
return k_error;
}
return result;
}
#ifdef k_enable_write_caching
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void map_write_cache_get_tail() reentrant
{
g_addr_log_blk =g_wr_cache_tail_log;
g_addr_rd_phy_blk=g_wr_cache_tail_src;
g_addr_wr_phy_blk=g_wr_cache_tail_dst;
g_addr_page =g_wr_cache_tail_page;
g_addr_zone =g_wr_cache_tail_zone;
trace5(0, map, 0, "write cache: get tail from cache: zone:%d log:%d src:%d dst:%d page:%d", g_wr_cache_tail_zone, g_wr_cache_tail_log, g_wr_cache_tail_src, g_wr_cache_tail_dst, g_wr_cache_tail_page) ;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void map_write_cache_set_tail() reentrant
{
g_wr_cache_tail_log=g_addr_log_blk;
g_wr_cache_tail_src=g_addr_rd_phy_blk;
g_wr_cache_tail_dst=g_addr_wr_phy_blk;
g_wr_cache_tail_page=g_addr_page;
g_wr_cache_tail_page=g_addr_zone;
trace5(0, cache, 0, "write cache: deferring copy tail. zone:%d log:%d src:%d dst:%d page:%d", g_wr_cache_tail_zone, g_wr_cache_tail_log, g_wr_cache_tail_src, g_wr_cache_tail_dst, g_wr_cache_tail_page) ;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void map_write_cache_init() reentrant
{
trace0(0, cache, 0, "map_write_cache_init()") ;
// g_wr_cache_head_log=k_block_free;
g_wr_cache_tail_log=k_block_free;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void map_write_cache_free() reentrant
{
trace0(0, cache, 0, "map_write_cache_free(). write is now empty") ;
// g_wr_cache_head_log=k_block_free;
g_wr_cache_tail_log=k_block_free;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
t_result map_copy_block_head_cache(void) reentrant
{
uint8 page ;
page=g_addr_page;
trace0(0, map, 0, "map_copy_block_head_cache()") ;
// check to see if there is a cached head or tail already present... if so,
// decide whether we can add this request to the tail, or if we need to flush
// the cached copy....
if(!_write_cache_is_empty())
{
if( (g_wr_cache_tail_log==g_addr_log_blk) && (g_wr_cache_tail_page == g_addr_page))
{
trace0(0, map, 0, "copy cache: copy head begins at cached copy_tail. no need to do a new copy head") ;
g_addr_wr_phy_blk=g_wr_cache_tail_dst;
// clear the tail cache... the rest of the parameters should be the same, but will be re-set at the
// end of the write
g_wr_cache_tail_log=k_block_free;
trace5(0, map, 0, "copy cache: resuming cached write transfer at log %d:%d src:%d dst:%d pg:%d", g_addr_zone, g_addr_log_blk, g_addr_rd_phy_blk, g_addr_wr_phy_blk, g_addr_page) ;
return k_success;
}
else
{
trace0(0, map, 0, "copy cache: cannot resume write transfer. flush cache and start new xfer") ;
map_write_cache_flush_one() ;
// fall through to empty cache processing
}
}
trace0(0, map, 0, "copy cache: begin new cached write transfer.") ;
if (k_success != map_alloc_wr_blk())
return k_error ;
media_copy_block_head() ;
#if 0
/*
map_write_cache_set_head();
return k_success;
*/
#endif
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
t_result map_copy_block_tail_cache(void) reentrant
{
// only work on one cache entry at a time, so it should already be available
trace4(0, cache, 0, "map_copy_block_tail_cache(): log:%d, dst:%d, src:%d, page:%d", g_addr_log_blk, g_addr_wr_phy_blk, g_addr_rd_phy_blk, g_addr_page) ;
map_write_cache_set_tail() ;
return k_success;
}
xdata uint16 prev_log;
xdata uint16 prev_src;
xdata uint16 prev_dst;
xdata uint16 prev_page;
xdata uint16 prev_zone;
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
t_result map_write_cache_flush_one() reentrant
{
t_result result ;
trace0(0, cache, 0, "map_write_cache_flush_one()") ;
prev_log=g_addr_log_blk;
prev_src=g_addr_rd_phy_blk;
prev_dst=g_addr_wr_phy_blk;
prev_page=g_addr_page;
prev_zone=g_addr_zone;
result=k_success;
#if 0
/*
// bind the phy block(s) to the log2phy table
// note: this assertion must be tested, and when working, can be taken out of the code
if(g_wr_cache_head_log == g_wr_cache_tail_log)
{
if( g_wr_cache_head_log != k_block_free)
{
if(g_wr_cache_head_src != g_wr_cache_tail_src)
{
trace0(0, cache, 0, "write cache error: copy-head/copy-tail entry has same logical binding, but different src blocks!") ;
return k_error ;
}
if(g_wr_cache_head_dst != g_wr_cache_tail_dst)
{
trace0(0, cache, 0, "write cache error: copy-head/copy-tail entry has same logical binding, but different dst blocks!") ;
return k_error;
}
if(g_wr_cache_head_page >= g_wr_cache_tail_page)
{
trace0(0, cache, 0, "write cache error: copy-head/copy-tail entry has same logical binding, but head page > tail page.") ;
return k_error;
}
}
}
// cached copy head operation
map_write_cache_get_head();
if( g_addr_log_blk != k_block_free )
{
if (k_success!=media_copy_block_head())
{
trace0(0, cache, 0, "error copying block head... abort") ;
return k_error;
}
trace0(0, cache, 0, "write cache copy_head() complete.") ;
}
// update map bindings for the head, only if the block isn't teh same as the tail
if(g_addr_log_blk != g_wr_cache_tail_log)
{
trace0(0, cache, 0, "binding block head in table") ;
map_rebind_log_blk() ;
}
*/
#endif
// cached copy tail operation
map_write_cache_get_tail();
if( g_addr_log_blk!=k_block_free )
{
if( k_success != map_copy_block_tail())
{
trace0(0, cache, 0, "error coyping block head... no logical block specified in cached copy_tail!") ;
result=k_error;
}
}
map_write_cache_free();
trace0(0, cache, 0, "write cache: copy complete") ;
g_addr_log_blk=prev_log;
g_addr_rd_phy_blk=prev_src;
g_addr_wr_phy_blk=prev_dst;
g_addr_page=prev_page;
g_addr_zone=prev_zone;
return result;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
t_result map_write_cache_flush_all() reentrant
{
// TRACE0(200, cache, 0, "map_write_cache_flush_all() - flushing write cache") ;
while(!_write_cache_is_empty())
map_write_cache_flush_one() ;
trace0(0, cache, 0, "write cache empty") ;
return k_success;
}
#endif // k_enable_write_cache
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
t_bool map_log_blk_has_bad_data() reentrant
{
_l2p_map = _log_map(g_addr_zone);
trace2(0, map, 0, "map_log_blk_has_bad_data(%d) - %c", g_addr_log_blk, ((_l2p_map[g_addr_log_blk]&kbm_map_blk_has_bad_data)?'Y':'N'));
return (_l2p_map[g_addr_log_blk]&kbm_map_blk_has_bad_data)?k_true:k_false;
}
//---eof------------------------------------------------------------------------