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------------------------------------------------------------------------