www.pudn.com > SMSC USB2.0.zip > nand.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. 
  ____________________________________________________________________________ 
  ____________________________________________________________________________ 
  nand.c - nand flash fixed media mass storage class implementation 
  ____________________________________________________________________________ 
  comments tbd 
  ____________________________________________________________________________ 
  Revision History 
  Date      Who  Comment 
  ________  ___  _____________________________________________________________ 
  06/21/02  cds  initial version - implements the methods for a raw nand-flash 
                 fixed media lun.  The NAND lun is derived from the removable 
                 smart media lun, but should be able to co-exist with smart 
                 media as a separate physical lun. 
  06/25/02  cds  incorporated boot block reading/writing for device usb config 
                 info 
  06/26/02  cds  fixed multi-card reads/writes by adjusting when the card select 
                 is called in the fmc xfers, and fixed the chip select to 
                 multiply the # of chips by the max zone for the particular type 
                 of chip detected, instead of assuming that it was 8 zones per chip. 
  06/27/02  cds  - implemented nand-specific set_read/write/erase addr functions 
                 that can be called vcalled from the smart media base class.  these 
                 functions calculate the chip-based zone from the global g_addr_zone. 
                 - fixed select_chip equation to correctly select a chip from g_addr_zone 
                 - added new variables g_nand_num_chips and g_nand_max_zones_per_chip 
                   to allow future chip auto-detection. 
  07/02/02  cds  added auto-detection for 1-chip (no or-gate)/n-chip configurations. 
                 After detecting # of chips, detects flash configuration from 
                 smart media card-select code. 
  07/08/02  cds  updated nand_dfa_write to include write-protect updates that 
                 affected smart media. 
  07/09/02  cds  - renamed WorkBuf to g_sector_buffer 
                 - during read_begin_split, waited until after busy before 
                   enabling ecc detection 
  07/10/02  cds  updated nand_read_begin_split() to use mcu_register_wr(...) 
                 instead of mcu_register_set_bits(...) on the smc_stat register 
                 to clear the ecc status bits. 
  07/19/02  cds  - protected password-specific code with #ifdef k_opt_password 
                 - added definition of g_ix_media_nand, instead of using an array 
                 to swap media instances. (similar to sd/mmc lun dynamic subclassing) 
  07/19/02  baw  Added initialization of lockable bit in nand_initialize_controller 
  07/23/02  baw  moved initialization of removable media from nand_initialize_controller 
                 to dev_validate_nvstore 
  07/29/02  cds  - added nand_select_media() to switch between nand_media and n2k_media 
                   objects based on what was detected 
                 - updated nand_identify_media_format() to select appropriate media class 
                   before setting any media instance variables 
                 - updated card id 0xf1 with Toshiba parameters.  (this will have to 
                   be updated again to determine the capacity of the chips) 
                 - updated 2k map parameters to be a single zone of 1024 blocks. 
                 - updated nand_read_begin_xfer to only do single block read xfers, 
                   to prevent sequential reads from freezing the fmc_xfer (to be debugged) 
                 - updated nand_read_begin_split to begin reads at the appropriate 
                   column offset for 2k page sized chips. 
                 - added entire new dfa_write to handle fmc xfers to 2k paged media 
                 - removed bogus init_unit() 
  07/31/02  cds  called media_set_active(...) instead of setting g_active_media 
                 to select a new media object. 
  08/06/02  cds  - fixed a bug in the n2k_write_begin_xfer() by doing a chip select 
                   before calling the copy_head function. 
                 - added extra smarts to distinguish between Toshiba & Samsung 
                   2k page nand chips. 
  08/09/02  cds  - used attribute flag to determine whether to force Smart Media 
                   compatible rw cycle timing for a data xfer. 
  08/13/02  cds  - identify Samsung vs Toshiba chips. 
                 - identify & setup 2 Mbit chips 
                 - removed some debug code which erased cards on boot-up...  whoops 
  08/20/02  cds  added functions to test read cache functions of 2k page media types 
  08/21/02  cds  altered nand select chip to use detect the mcu and pick correct gpiob scheme 
  08/27/02  cds  - fixed a glitch where 2k page nands were not correctly initializing 
                   the read cycle speed bit. 
                 - added code to report media change whenever a test-unit-ready or 
                   write command comes down from the host and the write-protect bit 
                   has changed.  This forces the host to detect the new state of the bit 
                 - added attribute bit to determine how the device should enumerate. 
                   if set, always enumerate as a removable-media type drive 
                   if clear & write protected, enumerate as removable media drive 
                   if clear & not write protected, enumerate as normal fixed media drive 
  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) 
                     (new item)                      => _media_data(segments_per_page) 
                 - nand_select_card moved to sm.c to allow sm to switch between 
                   nand cards and smart media slot when necessary 
  09/05/02  cds  - n2k read & writes working again (but code bloated up to 50K) 
                 - added password check to determine if lun reports itself as removable or fixed 
                   before the inquiry data. 
  09/10/02  cds  combined duplicated calculation code to one spot to save code space 
  09/12/02  tbh  replaced strncpy() with memcpy() to save data space. 
  09/19/02  cds  - unified n2k & nand fmc write code 
                 - set location to enable media optimization options (erase caching... currently off) 
                 fixed nand corruption (same place as sm corruption) 
  09/22/02  cds  - added idle_process hook to flush cache on an idle message, 
                 - added call to flush cache on nand_dfa_read, and on idle processing 
                 - added flag to enable/disable whether write-caching (independently of erase-caching) 
                 - enabled erase caching by default for nand devices 
                 - defined buffer for use by the erase cache code for this lun 
                 - #if'd out calls to write-caching functions to save code space 
  10/01/02  cds  - surrounded nand_erase_block() with critical section to avoid 
                   an intervening isr from causing a stack overrun. 
  10/11/02  cds  - fixed check_wp() to only look at write-protect bit when determining if the wp has changed. 
                   (this fixes a bug in WinME/Win98 that was failed to unlock the media after validating a password) 
  10/15/02  cds  - added new code for Samsung 2GB chips so that they are properly identified. 
  10/17/02  cds  - project-wide lun data & vtbl paging to reduce code space.            
                 - removed g_active_media from _lun_data, _lun_() virtual functions     
                 - added _lun_data_rd() and _lun_data_wr() macros to bypass lun paging  
                 - added lun_set_active(log_lun) function to switch luns                
  10/29/02  cds  - checked data status bits to see if data is good on all lbas before starting a split                  
  11/13/02  ds   intialized g_addr_segment to zero in order to select the right chip for interleave demo.                    
  11/19/02  ds   fixed the corner case with reads crossing block boundaries. 
  11/19/02  cds  created polling loop in read_begin_split to quickly start fmc_xfers w/o fmc loop overhead for interleaving 
                 created polling loop in write_begin_split to quickly start fmc_xfers w/o fmc loop overhead for interleaving 
  11/20/02  ds   Fixed a bug in read_begin_split where the wrong chip was selected on a zone change.                
  11/20/02  ds   Fixed a minor bug in read_begin_split where it does not return immediately on a abort conditions. 
  11/20/02  ds   Ensured proper chip select for nand_int. 
                 Enabled interrupts around data xfer loop. 
  ============================================================================*/ 
#define __nand_dot_c__ 
#include "project.h" 
#include "dev.h" 
 
// provide vtable definition 'nand' 
code _vtbl_defn(nand) ; 
 
//----------------------------------------------------------------------------- 
//----------------------------------------------------------------------------- 
xdata uint8 g_ix_media_nand ; 
 
//----------------------------------------------------------------------------- 
//----------------------------------------------------------------------------- 
static xdata uint8 _nand_media_status ; 
 
//----------------------------------------------------------------------------- 
//----------------------------------------------------------------------------- 
void n2k_test_read_sequential() reentrant; 
 
//----------------------------------------------------------------------------- 
// smart media 'media layer" routines 
//----------------------------------------------------------------------------- 
t_result sm_media_check_format(void)reentrant; 
t_result sm_media_seek_cis(void) reentrant; 
t_result sm_read_cis_sect(uint8 *)reentrant; 
t_result sm_validate_cis(uint8 *) reentrant ; 
t_result sm_media_erase_block(void)reentrant; 
t_result sm_media_erase_card(void)reentrant; 
t_result sm_wait_rdy_with_timeout(uint16 ) reentrant; 
 
 
 
//----------------------------------------------------------------------------- 
// read fmc xfer callbacks 
//----------------------------------------------------------------------------- 
t_result sm_read_begin_xfer(void) reentrant; 
t_result sm_read_end_xfer(void) reentrant ; 
t_result sm_read_end_split(void) reentrant ; 
t_result sm_read_end_burst(void) reentrant ; 
 
//----------------------------------------------------------------------------- 
// write fmc xfer callbacks 
//----------------------------------------------------------------------------- 
t_result sm_write_begin_xfer(void) reentrant; 
t_result sm_write_end_xfer(void) reentrant ; 
t_result sm_write_begin_split(void) reentrant; 
t_result sm_write_begin_first_split() reentrant ; 
t_result sm_write_end_split(void) reentrant ; 
t_result sm_write_begin_burst(void) reentrant; 
t_result sm_write_end_burst(void) reentrant ; 
 
//------------------------------------------------------------------------- 
//------------------------------------------------------------------------- 
void util_trace_media() reentrant; 
void util_trace_map_media() reentrant; 
void util_trace_phy_block() reentrant; 
void util_trace_log_media() reentrant; 
void util_safe_erase_media() reentrant; 
void util_test_hardware_ecc() reentrant; 
void util_test_copy_double_bit_ecc(uint32 lba) reentrant; 
//----------------------------------------------------------------------------- 
//----------------------------------------------------------------------------- 
xdata uint16 _nand_log2phy[k_nand_max_zones_per_table*k_nand_max_lb_per_zone] ; 
xdata uint8  _nand_assign_map[k_nand_max_zones_per_table*k_nand_max_pb_per_zone/8] ; 
xdata uint8  _nand_erase_cache_map[k_nand_max_zones_per_table*k_nand_max_pb_per_zone/8] ; 
 
 
 
//----------------------------------------------------------------------------- 
//----------------------------------------------------------------------------- 
static void nand_select_media(t_ix_media sel) 
{ 
  TRACE1(361, nand, 0, "selecting new nand media media class:%d", sel) ; 
  g_ix_media_nand = sel ; 
  media_set_active(g_ix_media_nand) ; 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   nand_identify_media_format() 
// 
// Declaration: 
//   t_result nand_erase_all(void) reentrant 
// 
// Purpose: 
// 
// Arguments: 
// 
// Return: 
// 
// Notes: 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
t_result nand_identify_media_format(void) reentrant 
{ 
  uint8 id ; 
  uint8 id_test ; 
  uint8 addr_segment; 
  TRACE0(362, nand, 0, "nand_identify_media_format()") ; 
 
  g_addr_zone=0 ; 
  g_nand_zones_per_chip = 1 ; 
  //ds set to segment 0 to start up 
  g_addr_segment = 0; 
  nand_int_select_chip(g_addr_segment); 
  id = sm_read_id() ; 
 
  // check to see if this is a one-chip, which no chip-select hanging off gpiob 
  // if we disable all chips, then issue the command on a one-chip board, and 
  // match the first id, either we see none, or one. 
  // read-id command, 
//#ifndef k_interleave_demo 
  if (_mcu_register_rd(x_chip_id) == 0x18) 
  { 
    //this has to change depending on chip!! for 242 $$$ 
    _mcu_register_wr(x_gpiob_out, 0x00) ; 
  } 
  else 
  { 
    //this has to change depending on chip!! for 211/210 $$$ 
    _mcu_register_wr(x_gpiob_out, 0xff) ; 
  } 
//#endif 
  id_test=sm_read_id() ; 
  if (id == id_test) 
  { 
    TRACE0(363, nand, 0, "Single NAND Flash version detected.") ; 
    g_nand_num_chips=1 ; 
  } 
  else 
  { 
    // loop through cards until we don't have a match for the ID 
    for (g_addr_zone=1;g_addr_zone<8;g_addr_zone++) 
    { 
      nand_select_card() ; 
      id_test=sm_read_id() ; 
      TRACE2(364, nand, 0, "chip %d id: %02x", g_addr_zone, id_test) ; 
      if ( id_test != id ) 
        break; 
    } 
    g_nand_num_chips=g_addr_zone ; 
    /*  for (addr_segment=1;addr_segment<3;addr_segment++) 
      { 
        nand_int_select_chip(addr_segment); 
        id_test=sm_read_id() ; 
        TRACE2(365, nand, 0, "chip %d id: %02x", addr_segment, id_test) ; 
        if ( id_test != id ) 
          break; 
      } 
      g_nand_num_chips=addr_segment;*/ 
 
  } 
 
  TRACE1(366, nand, 0, "detected %d chips", g_nand_num_chips) ; 
 
  g_addr_zone=0 ; 
  nand_int_select_chip(0); 
  sm_read_id() ; 
  switch (nand_id_maker) 
  { 
    case 0x98: 
      { 
        TRACE0(367, nand, 0, "Toshiba NAND Flash Chip") ; 
        switch (nand_id_device) 
        { 
          case 0xf1: 
            { 
              nand_select_media(k_ix_media_nand_2k) ; 
              TRACE0(368, nand, 0, "128MB Capacity - Zones:1, PB/Zone:1024, LB/Zone:1000, Pages/Blk:64 Bytes/Page:2112") ; 
              _media_data(options)=kbm_media_data_opt_none; 
              _media_data(num_zones)                    = 1; 
              _media_data(physical_blocks_per_zone)     = 1024; 
              _media_data(logical_blocks_per_zone)      = 1000; 
              _media_data(logical_blocks_per_boot_zone) = 1000; 
              _media_data(pages_per_block)              = 64; 
              _media_data(segments_per_page)            = 4 ; 
              _mcu_register_clr_bits(x_media_sts, kbm_media_sts_sm_256_page) ; 
            } 
            break; 
 
          case 0xAA: 
          case 0xDA: 
            { 
              TRACE0(369, nand, 0, "256MB Capacity - Zones:2, PB/Zone:1024, LB/Zone:1000, Pages/Blk:64 Bytes/Page:2112") ; 
              nand_select_media(k_ix_media_nand_2k) ; 
              _media_data(options)=kbm_media_data_opt_none; 
              _media_data(num_zones)                    = 2; 
              _media_data(physical_blocks_per_zone)     = 1024; 
              _media_data(logical_blocks_per_zone)      = 1000; 
              _media_data(logical_blocks_per_boot_zone) = 1000; 
              _media_data(pages_per_block)              = 64; 
              _media_data(segments_per_page)            = 4 ; 
              _mcu_register_clr_bits(x_media_sts, kbm_media_sts_sm_256_page) ; 
            } 
            break; 
 
          default: 
            { 
              nand_select_media(k_ix_media_nand) ; 
              sm_identify_media_format() ; 
            } 
            break; 
        } 
      } 
      break; 
 
    case 0xec: 
      { 
        TRACE0(370, nand, 0, "Samsung NAND Flash Chip") ; 
        switch (nand_id_device) 
        { 
          case 0xf1: 
            { 
              nand_select_media(k_ix_media_nand_2k) ; 
              TRACE0(371, nand, 0, "128MB Capacity - Zones:1, PB/Zone:1024, LB/Zone:1000, Pages/Blk:64 Seg/Page:4") ; 
              _media_data(options)=kbm_media_data_opt_none; 
              _media_data(num_zones)                    = 1; 
              _media_data(physical_blocks_per_zone)     = 1024; 
              _media_data(logical_blocks_per_zone)      = 1000; 
              _media_data(logical_blocks_per_boot_zone) = 1000; 
              _media_data(pages_per_block)              = 64; 
              _media_data(segments_per_page)            = 4; 
              _mcu_register_clr_bits(x_media_sts, kbm_media_sts_sm_256_page) ; 
              break; 
            } 
          case 0xAA: 
          case 0xDA: 
            { 
              nand_select_media(k_ix_media_nand_2k) ; 
              TRACE0(372, nand, 0, "256MB Capacity - Zones:2, PB/Zone:1024, LB/Zone:1000, Pages/Blk:64 Seg/Page:4") ; 
              _media_data(options)=kbm_media_data_opt_none; 
              _media_data(num_zones)                    = 2; 
              _media_data(physical_blocks_per_zone)     = 1024; 
              _media_data(logical_blocks_per_zone)      = 1000; 
              _media_data(logical_blocks_per_boot_zone) = 1000; 
              _media_data(pages_per_block)              = 64; 
              _media_data(segments_per_page)            = 4; 
              _mcu_register_clr_bits(x_media_sts, kbm_media_sts_sm_256_page) ; 
              break; 
            } 
          default: 
            { 
              nand_select_media(k_ix_media_nand_int) ; 
              sm_identify_media_format() ; 
              break; 
            } 
        } 
      } 
      break; 
    default: 
      nand_select_media(k_ix_media_nand_int) ; 
      sm_identify_media_format() ; 
      break; 
  } 
 
 
  if (x_media_sts & kbm_media_sts_sm_wp ) 
    _lun_data(media) |= kbm_lun_media_wrprot ; 
  else 
    _lun_data(media) &= ~kbm_lun_media_wrprot ; 
 
// #ifdef k_interleave_demo 
  _media_data(segments_per_page) = g_nand_num_chips; 
  g_nand_num_chips = 1; 
// #else 
  // update the # of zones, assuming all chips are the same at this point 
  g_nand_zones_per_chip = _media_data(num_zones) ; 
  _media_data(num_zones) *= g_nand_num_chips ; 
// #endif 
  // setup the map tables (now that the media, and it's instance are known) 
  _media_data(log2phy_map) = (t_log2phy_map_ref) _nand_log2phy ; 
  _media_data(assign_map)  = (t_assign_map_ref)  _nand_assign_map ; 
  _media_data(erase_cache) = (t_erase_cache_ref) _nand_erase_cache_map; 
 
  // enable & init caching mechanisms on per-media basis here 
  _media_data(options) |= kbm_media_data_opt_erase_cache; 
  // _media_data(options) |= kbm_media_data_opt_write_cache; 
 
  TRACE1(373, nand, 0, "flash chips:    %d", g_nand_num_chips) ; 
  TRACE1(374, nand, 0, "zones per chip: %d", g_nand_zones_per_chip) ; 
  TRACE1(375, nand, 0, "total zones:    %d", (_media_data(num_zones))) ; 
  nand_cmd_reset_device(); 
  TRACE0(376, nand, 0, "Got here") ; 
 
  return(k_success); 
 
} 
 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   nand_check_format() 
// 
// Declaration: 
//   t_result nand_check_format(void) reentrant ; 
// 
// Purpose: 
// 
// Arguments: 
// 
// Return: 
// 
// Notes: 
//   this is a function 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
t_result nand_check_format(void) reentrant 
{ 
  TRACE0(377, nand, 0, "nand_check_format()") ; 
 
  #if 0 
  TRACE0(378, nand, 0, "ERASING ALL FLASH CARDS") ; 
  util_safe_erase_media() ; 
  #endif 
 
 
  if ( _lun_is_media_known(g_active_lun) ) 
    return(k_success); 
 
  // media already identified, but sector map not built yet 
  g_addr_rd_phy_blk=0; 
  g_addr_page=0; 
  _media_data(boot_block)=0; 
  _media_data(boot_page)=0; 
  _media_data(assign_zone)=0; 
  g_addr_zone=0; 
 
  // note:  building a sector map could take 100s of ms 
  if ( k_success != map_build_sector_map()) 
  { 
    TRACE0(379, nand, 0, "failed to build sector map") ; 
    return(k_error ); 
  } 
 
  // induce an error bit here under cbw protection, after a sector map has been built 
  // util_test_copy_double_bit_ecc(33) ; 
  nand_cmd_reset_device() ; 
  TRACE0(380, nand, 0, "media identified") ; 
  _lun_data(media) &= ~kbm_lun_media_unknown ; 
 
  return(k_success); 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   nand_cmd_check_status() 
// 
// Declaration: 
//   t_result nand_cmd_check_status(void) 
// 
// Purpose: 
// 
// Arguments: 
// 
// Return: 
// 
// Notes: 
// 
// Since: 
//   fmc-2.0 
//------------------------------------------------------------------------------ 
t_result nand_cmd_check_status(void) reentrant 
{ 
  uint8 status ; 
 
  _sm_set_rd_cmd(k_sm_read_status); 
  status = _sm_data_rd() ; 
  trace1(0, sm, 0, "nand_cmd_check_status(): %02x", status ) ; 
  if (status&kbm_sm_status_write_failed ) 
    return(k_error ); 
  return(k_success ); 
} 
//+----------------------------------------------------------------------------- 
// Name: 
//   nand_cmd_check_status() 
// 
// Declaration: 
//   t_result nand_cmd_check_status(void) 
// 
// Purpose: 
// 
// Arguments: 
// 
// Return: 
// 
// Notes: 
// 
// Since: 
//   fmc-2.0 
//------------------------------------------------------------------------------ 
t_result nand_cmd_check_status_new(void) reentrant 
{ 
  uint8 status ; 
 
  _sm_set_wr_cmd(k_sm_read_status); 
  status = _sm_data_rd() ; 
  trace1(0, sm, 0, "nand_cmd_check_status(): %02x", status ) ; 
  if (status&kbm_sm_status_write_failed ) 
    return(k_error ); 
  return(k_success ); 
} 
 
 
 
 
 
 
 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   sm_media_erase_all() 
// 
// Declaration: 
//   t_result sm_media_erase_all(void) reentrant 
// 
// Purpose: 
//   erase all physical blocks on media card, then 
//   rebuild sector mapping table 
// 
// Arguments: 
//   none 
// 
// Return: 
//   k_success  - on successful completion 
// 
// Notes: 
//   currently, not a dfa 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
t_result nand_cmd_reset_device(void) reentrant 
{ 
  // reset the chip 
  trace0(0, sm, 0, "nand_cmd_reset_device()") ; 
  _sm_rd_cmd_begin(k_nand_cmd_reset) ; 
  sm_wait_rdy_with_timeout(k_nand_reset_timeout) ; 
  _sm_hw_set_rd_standby(); 
  return(k_success); 
} 
 
 
//+----------------------------------------------------------------------------- 
//------------------------------------------------------------------------------ 
t_result nand_cmd_read_id(void) reentrant 
{ 
  TRACE0(381, nand, 0, "nand_cmd_read_id()"); 
  _sm_hw_set_rd_cmd(); 
  _sm_data_wr(k_sm_read_id); 
  _sm_hw_set_rd_addr(); 
  _sm_data_wr(0x00); 
  _sm_hw_set_rd_data(0); 
  return(k_success); 
} 
 
//+----------------------------------------------------------------------------- 
//------------------------------------------------------------------------------ 
t_result nand_cmd_read_id_ex(void) reentrant 
{ 
  TRACE0(382, nand, 0, "nand_cmd_read_id_ex()"); 
  _sm_hw_set_rd_cmd(); 
  _sm_data_wr(k_nand_cmd_read_id_ex); 
  _sm_hw_set_rd_addr(); 
  _sm_data_wr(0x00); 
  _sm_hw_set_rd_data(0); 
  return(k_success); 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   nand_erase_block() 
// 
// Declaration: 
//   t_result nand_erase_block(void) reentrant 
// 
// Purpose: 
// 
// Arguments: 
// 
// Return: 
// 
// Notes: 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
t_result nand_erase_block(void) reentrant 
{ 
  TRACE4(383, nand, 0, "nand_erase_block() - zone:%d wr_phy:%d log:%d page:%d", g_addr_zone, g_addr_wr_phy_blk, g_addr_log_blk, g_addr_page) ; 
  nand_select_card() ; 
  return(sm_media_erase_block()); 
} 
 
 
//------------------------------------------------------------------------------ 
// read for serial-input. 
//------------------------------------------------------------------------------ 
t_result  nand_page_read_for_data(void) reentrant 
{ 
  TRACE4(384, nand, 0, "nand_page_read_for_data() - zone:%d blk:%d log_blk:%d page:%d", g_addr_zone, g_addr_rd_phy_blk, g_addr_log_blk, g_addr_page) ; 
  _mcu_register_wr(x_smc_stat, kbm_smc_stat_rdy) ; 
  _sm_set_rd_cmd(k_sm_read) ; 
  _media_set_read_addr(); 
  _sm_set_rd_cmd(k_nand_cmd_read_for_data) ; 
  if (k_success != sm_wait_rdy_with_timeout(k_sm_busy_read_timeout)) 
  { 
    TRACE0(385, nand, 0, "error waiting for data") ; 
    nand_cmd_reset_device(); 
    return(k_error ); 
  } 
  return(k_success ); 
 
} 
 
//------------------------------------------------------------------------------ 
// read for copyback 
//------------------------------------------------------------------------------ 
t_result n2k_page_read_for_copy() reentrant 
{ 
  trace4(0, n2k, 0, "n2k_page_read_for_copy() - zone:%d blk:%d log_blk:%d page:%d", g_addr_zone, g_addr_rd_phy_blk, g_addr_log_blk, g_addr_page) ; 
 
  _mcu_register_wr(x_smc_stat, kbm_smc_stat_rdy) ; 
  _sm_set_rd_cmd(k_sm_read) ; 
  _media_set_read_addr(); 
  _sm_set_rd_cmd(k_nand_cmd_read_for_copy) ; 
  if (k_success != sm_wait_rdy_with_timeout(k_sm_busy_read_timeout)) 
  { 
    trace0(0, n2k, 0, "error waiting for data") ; 
    nand_cmd_reset_device(); 
    return(k_error ); 
  } 
  // g_addr_rd_phy_blk is now in the chip's page cache register 
  return(k_success ); 
} 
 
 
//------------------------------------------------------------------------------ 
// jump to offset within page in chip cache for data source... page_rd_for_data 
// must have previously been called. 
//------------------------------------------------------------------------------ 
t_result n2k_page_set_rd_offset( uint16 offset ) reentrant 
{ 
  trace1(0, n2k, 0, "n2k_page_set_rd_offset() - column:%04X", offset) ; 
  _sm_set_rd_cmd(k_nand_cmd_read_offset_addr) ; 
  _sm_hw_set_rd_addr(); 
  _sm_data_wr( _l(offset) ) ; 
  _sm_data_wr( _h(offset) ) ; 
  _sm_set_rd_cmd(k_nand_cmd_read_offset_data) ; 
  _sm_hw_set_rd_data(g_nand_rw_speed); 
  return(k_success ); 
} 
 
//------------------------------------------------------------------------------ 
// jump to offset within page in chip cache for data dest 
//------------------------------------------------------------------------------ 
t_result n2k_page_set_wr_offset( uint16 offset )  reentrant 
{ 
  trace1(0, n2k, 0, "n2k_page_set_wr_offset() - column:%02X", offset) ; 
  _sm_set_wr_cmd(k_nand_cmd_write_cache_data) ; 
  _sm_hw_set_wr_addr() ; 
  _sm_data_wr( _l(offset) ) ; 
  _sm_data_wr( _h(offset) ) ; 
  _sm_hw_set_wr_data(g_nand_rw_speed); 
  return(k_success ); 
} 
 
//------------------------------------------------------------------------------ 
// program page with serial data input 
//------------------------------------------------------------------------------ 
t_result nand_cmd_page_write_cache_data() reentrant 
{ 
  _sm_set_wr_cmd(k_nand_cmd_write_cache_data) ; 
  nand_2k_set_write_addr() ; 
  return(k_success ); 
} 
 
 
//------------------------------------------------------------------------------ 
// program page with serial data input 
//------------------------------------------------------------------------------ 
t_result nand_cmd_page_program() reentrant 
{ 
  trace0(0, n2k, 0, "nand_cmd_page_program()") ; 
  _mcu_register_wr(x_smc_stat, kbm_smc_stat_rdy) ; 
  _sm_set_wr_cmd(k_nand_cmd_write_page) ; 
  if (k_success != sm_wait_rdy_with_timeout(k_nand_write_page_timeout)) 
  { 
    trace0(0, n2k, 0, "error writing data") ; 
    nand_cmd_reset_device(); 
    return(k_error ); 
  } 
 
  if (k_success != nand_cmd_check_status()) 
  { 
    trace0(0, n2k, 0, "error failed status check") ; 
    nand_cmd_reset_device() ; 
    return(k_error ); 
  } 
 
  trace0(0, n2k, 0, "success") ; 
  return(k_success ); 
} 
 
//------------------------------------------------------------------------------ 
//------------------------------------------------------------------------------ 
t_result nand_cmd_cache_program() reentrant 
{ 
  trace0(0, n2k, 0, "nand_cmd_cache_program()") ; 
  _mcu_register_wr(x_smc_stat, kbm_smc_stat_rdy) ; 
  _sm_set_wr_cmd(k_nand_cmd_write_cache) ; 
  if (k_success != sm_wait_rdy_with_timeout(k_nand_write_page_timeout)) 
  { 
    trace0(0, n2k, 0, "error waiting for device busy") ; 
    nand_cmd_reset_device(); 
    return(k_error ); 
  } 
  trace0(0, n2k, 0, "success") ; 
  return(k_success ); 
} 
 
//------------------------------------------------------------------------------ 
//------------------------------------------------------------------------------ 
t_result n2k_read_cache_next() reentrant 
{ 
  trace0(0, n2k, 0, "n2k_read_cache_next()") ; 
  _mcu_register_wr(x_smc_stat, kbm_smc_stat_rdy) ; 
  _sm_set_rd_cmd(k_nand_cmd_read_cache_next) ; 
  if (k_success!=sm_wait_rdy_with_timeout(k_sm_busy_read_timeout)) 
  { 
    trace0(0, n2k, 0, "error waiting for data") ; 
    nand_cmd_reset_device(); 
    return(k_error ); 
  } 
  return(k_success ); 
} 
//------------------------------------------------------------------------------ 
//------------------------------------------------------------------------------ 
t_result n2k_read_cache_final() reentrant 
{ 
  trace0(0, n2k, 0, "n2k_read_cache_final()") ; 
  _mcu_register_wr(x_smc_stat, kbm_smc_stat_rdy) ; 
  _sm_set_rd_cmd(k_nand_cmd_read_cache_final) ; 
  if (k_success!=sm_wait_rdy_with_timeout(k_sm_busy_read_timeout)) 
  { 
    trace0(0, n2k, 0, "error waiting for data") ; 
    nand_cmd_reset_device(); 
    return(k_error ); 
  } 
  return(k_success ); 
} 
 
//----------------------------------------------------------------------------- 
// read fmc xfer callbacks 
//----------------------------------------------------------------------------- 
extern t_result nand_read_begin_split(void) reentrant; 
extern t_result nand_read_end_first_split(void) reentrant ; 
 
 
//+----------------------------------------------------------------------------- 
//------------------------------------------------------------------------------ 
 
t_result nand_read_begin_xfer() reentrant 
{ 
  t_result result; 
  TRACE4(386, nand, 10, "nand_read_begin_xfer() - start:0x%04X%04X count:0x%04X%04X" , _hw(_fmc_get_start_lb_32()), _lw(_fmc_get_start_lb_32()), _hw(_fmc_get_lb_count_32()), _lw(_fmc_get_lb_count_32())); 
  _lun_data(sensep) = &sense_read_error; 
 
  if (g_ix_media_nand == k_ix_media_nand) 
  { 
    return(sm_read_begin_xfer() ); 
  } 
  //nand interleaved  
  if (g_ix_media_nand == k_ix_media_nand_int) 
  { 
    result = sm_read_begin_xfer() ; 
    if (!g_addr_page) 
    { 
      _lun_data(max_lb_per_split) = _min((_media_data(segments_per_page)*_media_data(pages_per_block)), _fmc_get_lb_count_32()); 
      trace5(0, sm, 10, "-----read begin xfer - zone:%02d phy:0x%04X log:0x%04X page:%02d count:%02d - first in block", g_addr_zone, g_addr_rd_phy_blk, g_addr_log_blk, g_addr_page, _lun_data(max_lb_per_split) ); 
    } 
    else 
    { 
      _lun_data(max_lb_per_split) = _min((((_media_data(pages_per_block) - g_addr_page)*_media_data(segments_per_page)) - g_addr_segment), _fmc_get_lb_count_32()); 
      trace5(0, sm, 10, "-----read begin xfer - zone:%02d phy:0x%04X log:0x%04X page:%02d count:%02d", g_addr_zone, g_addr_rd_phy_blk, g_addr_log_blk, g_addr_page, _lun_data(max_lb_per_split)); 
    } 
 
    _lun_data(max_lb_per_burst) = 1 ;  //just one burst at a time 
    return(result); 
  } 
  if (k_success != map_lba2addr_rd( _fmc_get_start_lb_32() ) ) 
  { 
    _lun_data(sensep)=&sense_illegal_address ; 
    trace0(0, sm, 0, "error in lba2addr") ; 
    return(k_error ); 
  } 
  _lun_data(max_lb_per_split) = 4-g_addr_segment ; 
  _lun_data(max_lb_per_burst) = 4-g_addr_segment ; 
  return(k_success ); 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   sm_read_begin_next_split 
// 
// Declaration: 
//   t_result sm_read_begin_next_split(void); 
// 
// Purpose: 
//   Issue the read command to subsequent splits after the first. 
// 
// Arguments: 
//   None. 
// 
// Return: 
//   A t_result indicating: 
//     k_success - command completed. 
// 
// Notes: 
//   This is a FUNCTION, not a DFA. 
//   Do not yeild, or run a DFA, from within this callback. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
t_result nand_read_begin_split(void) reentrant 
{ 
  t_result result ; 
  t_sync sync; 
  uint8 addr_segment = g_addr_segment; 
  uint8 cnt; 
  TRACE0(387, nand, 1, "nand_read_begin_split()") ; 
 
  if (k_success != map_lba2addr_rd( _fmc_get_start_lb_32() ) ) 
  { 
    _lun_data(sensep)=&sense_illegal_address ; 
    return(k_error ); 
  } 
 
  addr_segment = g_addr_segment; 
  // start the read 
  result=k_success; 
  nand_int_select_chip(addr_segment); 
  if (map_log_blk_has_bad_data()) 
  { 
    if (sm_check_data_status(g_addr_page, (uint8) _fmc_get_lb_count_32())) 
    { 
      trace0(0, sm, 0, "read error detected in lba range") ; 
      nand_cmd_reset_device(); 
      return(k_error); 
    } 
    nand_cmd_reset_device(); 
    trace0(0, sm, 0, "read in log blk that has bad data is OK"); 
  } 
 
  TRACE5(388, nand, 0, "beginning split - zone:%d phy:%d log:%d page:%d segment:%d", g_addr_zone, g_addr_rd_phy_blk, g_addr_log_blk, g_addr_page, g_addr_segment) ; 
  nand_rd_va2pa() ; 
  if (g_ix_media_nand == k_ix_media_nand_2k) 
  { 
    TRACE0(389, nand, 0, "using large nand flash chips") ; 
    // g_fmc_begin_burst_callback=(t_fmc_callback) nand_read_begin_burst ; 
    result = nand_page_read_for_data() ; 
    if (g_addr_segment) 
      n2k_page_set_rd_offset( _n2k_data_offset(g_addr_segment)) ; 
    // return k_success; 
  } 
  else 
  { 
    // issue the read commands to all chips to be used (could be < number in set) 
    cnt = _min(_lun_data(max_lb_per_split), _media_data(segments_per_page)); 
    while (cnt) 
    { 
      nand_int_select_chip(addr_segment); 
      _sm_rd_cmd_begin(k_sm_read) ; 
      _media_set_read_addr() ; 
 
      // update counters here, while device is "busy" 
      --cnt; 
      ++addr_segment; 
      if (addr_segment >= _media_data(segments_per_page)) 
      { 
        addr_segment=0; 
        nand_incr_addr(); 
      } 
      // now wait for rdy... should be done by now... only takes a few usec 
      result = sm_wait_rdy_with_timeout(k_sm_busy_read_timeout); 
 
      if (k_success != result) 
      { 
        TRACE1(390, nand, 0, "timeout waiting for busy - smc_stat:%02x", x_smc_stat) ; 
        nand_cmd_reset_device(); 
        return(k_error ); 
      } 
    } 
 
    // do single lba bursting here in w/ a poller for use by the interleaved read/write code 
    cnt=(uint8) _fmc_get_lb_count_32(); 
    addr_segment=g_addr_segment; 
 
    // enable ecc hardware 
    _mcu_register_wr(x_smc_stat, (kbm_smc_stat_ecc_err_d_a|kbm_smc_stat_ecc_err_d_b|kbm_smc_stat_ecc_err_c_a|kbm_smc_stat_ecc_err_c_b)); 
    _mcu_register_clr_bits(x_smc_stat_msk, (kbm_smc_stat_ecc_err_d_a|kbm_smc_stat_ecc_err_d_b|kbm_smc_stat_ecc_err_c_a|kbm_smc_stat_ecc_err_c_b)); 
    _mcu_register_clr_bits(x_imr0, kbm_isr0_fmc_irq); 
    _mcu_register_set_bits(sm_mode_ctl, kbm_sm_mode_ecc_blk_xfer_en) ; 
 
    // clear blk_xfer_complete irq. 
    _mcu_register_wr(x_isr0, kbm_isr0_blk_xfer_complete); 
 
    // write the count registers which won't change 
    _mcu_register_wr(x_fmc_cnt3, 0); 
    _mcu_register_wr(x_fmc_cnt2, 0); 
    _mcu_register_wr(x_fmc_cnt0, 0); 
 
    // prime the loop to "resume" transfer 
    result = k_success; 
    ///////////////////////////////////////////////// 
    while (cnt) 
    { 
      nand_int_select_chip(addr_segment); 
      // set cnt1 to 0x02 to get a 512 byte xfer 
      _mcu_register_wr(x_fmc_cnt1, 0x02); 
      // enable blk xfer 
      _mcu_register_set_bits(x_fmc_ctl, kbm_fmc_ctl_blk_xfer_en); 
 
      // do loop calculations 
      cnt--; 
      addr_segment=(addr_segment+1)%_media_data(segments_per_page); 
      thread_set_timer(g_fmc_timeout); 
      do 
      { 
        sync = thread_got_sync(kbm_sync_abort |kbm_sync_usbrst |kbm_sync_timer|kbm_sync_fmc_irq); 
        _thread_clr_sync(sync); 
        if (sync & kbm_sync_usbrst) 
        { 
          trace0(0, sm, 1, "optimized fmc_wait_blk_irq_with_timeout() - error: kbm_sync_usbrst"); 
          result = k_usbreset; 
          break; 
        } 
        if (sync & kbm_sync_abort) 
        { 
          trace0(0, sm, 1, "optimized fmc_wait_blk_irq_with_timeout() - error: kbm_sync_abort"); 
          result = k_aborted; 
          break; 
        } 
        if (sync & kbm_sync_timer) 
        { 
          trace0(0, sm, 1, "optimized fmc_wait_blk_irq_with_timeout() - error: timeout awaiting irq"); 
          result = k_timeout; 
          break; 
        } 
        if (sync & kbm_sync_fmc_irq) 
        { 
          trace0(0, sm, 1, "optimized fmc_wait_blk_irq_with_timeout() - error: kbm_sync_fmc_irq") ; 
          // set to success, since we don't know the cause of the error yet... the specific end-burst 
          // function should determine the actual status fot eh fmc xfer 
          result = k_success; 
          break; 
        } 
        // AAA: added this to poll instead of taking the interrupt 
        // cds: added isr bit fmc_irq to break out of loop for fmc error (ecc or otherwise) detection 
      } while (!(_mcu_register_rd(x_isr0) & (kbm_isr0_blk_xfer_complete|kbm_isr0_fmc_irq) )); 
 
      if (k_success!=result) 
      { 
        trace0(0, sm, 1, "optimized read begin split detected error... aborting") ; 
        return(result); 
      } 
 
      if (_mcu_register_rd(x_smc_stat)&(kbm_smc_stat_ecc_err_c_a|kbm_smc_stat_ecc_err_c_b|kbm_smc_stat_ecc_err_d_a|kbm_smc_stat_ecc_err_d_b)) 
      { 
        result=sm_read_end_burst(); 
        if (k_resume==result) 
        { 
          result=k_success; 
        } 
        if (k_success!=result) 
        { 
          trace0(0, sm, 1, "optimized read begin split detected error... aborting") ; 
          break; 
        } 
      } 
    } 
    // hack fmc registers to prevent fmc split loop from being entered upon our callback 
    g_start_lb_this_xfer.u32 += g_n_lb_this_split.u32; 
    // decrement the number of logical blocks remaining in the transfer 
    g_n_lb_this_xfer.u32 -= g_n_lb_this_split.u32; 
    // track the mscbot byte residue 
    _mscbot_decr_residue( (g_n_lb_this_split.u32*512L) ); 
    g_n_lb_this_split.u32 = 0; 
  } 
  return(result); 
} 
 
#if 0  
//------------------------------------------------------------------------------ 
// only for n2k media, that doesn't have read-sequential capabilities. 
//------------------------------------------------------------------------------ 
t_result nand_read_begin_burst() reentrant  
{ 
 
  if ( g_addr_page <= (_media_data(pages_per_block)-2)) 
  { 
    TRACE0(391, nand, 0, "issue read next cached page here") ; 
    n2k_read_cache_next() ; 
  } 
  else if ( g_addr_page == (_media_data(pages_per_block)-2)) 
  { 
    TRACE0(392, nand, 0, "issue read final cached page here") ; 
    n2k_read_cache_final() ; 
  } 
 
  _lun_data(max_lb_per_burst) = 4 ;  
 
 
  // setup hardware 
  _mcu_register_wr(x_smc_stat, kbm_smc_stat_ecc_err_d_a | kbm_smc_stat_ecc_err_d_b | kbm_smc_stat_ecc_err_c_a | kbm_smc_stat_ecc_err_c_b) ; 
  _mcu_register_clr_bits(x_smc_stat_msk, kbm_smc_stat_ecc_err_d_a | kbm_smc_stat_ecc_err_d_b | kbm_smc_stat_ecc_err_c_a | kbm_smc_stat_ecc_err_c_b) ; 
  _mcu_register_wr(x_isr0, kbm_isr0_fmc_irq) ; 
  _mcu_register_clr_bits(x_imr0, kbm_isr0_fmc_irq) ; 
  _mcu_register_set_bits(sm_mode_ctl,kbm_sm_mode_ecc_blk_xfer_en); 
  return(k_success ); 
} 
#endif 
 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   nand_read_end_first_split 
// 
// Declaration: 
//   t_result nand_read_end_first_split(void); 
// 
// Purpose: 
//    
// 
// Arguments: 
//   None. 
// 
// Return: 
//   A t_result indicating: 
//     k_success - command completed. 
// 
// Notes: 
//   This is a FUNCTION, not a DFA. 
//   Do not yeild, or run a DFA, from within this callback. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
t_result nand_read_end_first_split(void) reentrant 
{ 
  TRACE0(393, nand, 0, "nand_read_end_first_split()") ; 
 
  // go into overdrive 
  TRACE2(394, nand, 0, "adjusting split/burst size.  before: max_lb_per_split:%d  max_lb_per_burst: %d", _lun_data(max_lb_per_split), _lun_data(max_lb_per_burst)) ; 
  if (g_active_media==k_ix_media_nand) 
  { 
    _lun_data(max_lb_per_split) = _min( (_media_data(pages_per_block)*_media_data(segments_per_page)), g_n_lb_this_xfer.u32); 
    _lun_data(max_lb_per_burst) = _lun_data(max_lb_per_split) ; 
  } 
  else if (g_active_media==k_ix_media_nand_int) 
  { 
    _lun_data(max_lb_per_split) = _min( (_media_data(pages_per_block)*_media_data(segments_per_page)), g_n_lb_this_xfer.u32); 
    _lun_data(max_lb_per_burst) = 1 ; 
  } 
  else 
  { 
    _lun_data(max_lb_per_split) = _min( 4, g_n_lb_this_xfer.u32); 
    _lun_data(max_lb_per_burst) = _min( 4, g_n_lb_this_xfer.u32);  
  } 
  TRACE2(395, nand, 0, "adjusting split/burst size.  after:  max_lb_per_split:%d  max_lb_per_burst: %d", _lun_data(max_lb_per_split), _lun_data(max_lb_per_burst)) ; 
 
  // no need to callback here any more 
  g_fmc_end_split_callback = (t_fmc_callback) sm_read_end_split ; 
  _sm_hw_set_rd_standby() ; 
  return(k_success ); 
} 
 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   dfa_sm_read 
// 
// Declaration: 
//   void dfa_sm_read(void) reentrant 
// 
// Purpose: 
//   Read data from sm media 
// 
// Arguments: 
//   None. 
//   Uses _lun_data and g_bot_cbw to get its parameters. 
// 
// Return: 
//   No return value. 
//   However, on exit the DFA's argument pointer is written with a t_csw_status indicating: 
//     k_command_passed - command completed. 
//     k_command_failed - an error occurred. 
// 
// Notes: 
//   This is the start STATE of the dfa_ata_read DFA. 
//   It overrides dfa_lun_read. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
void nand_dfa_read(void) reentrant 
{ 
  TRACE0(396, nand, 0, "nand_dfa_read()"); 
  _fmc_set_options(0); 
  _fmc_set_timeout(10000); 
  if ( g_ix_media_nand == k_ix_media_nand_int) 
  { 
    _fmc_set_callback(nand_read_begin_xfer, sm_read_end_xfer, 
                      nand_read_begin_split, nand_read_end_first_split, 
                      fmc_dflt_callback, fmc_dflt_callback, nand_int_read_end_burst); 
  } 
  else 
  { 
    _fmc_set_callback(nand_read_begin_xfer, sm_read_end_xfer, 
                      nand_read_begin_split, nand_read_end_first_split, 
                      fmc_dflt_callback, fmc_dflt_callback, sm_read_end_burst); 
  } 
#ifdef k_enable_write_caching 
  // flush write cache 
  if (_media_data(options)&kbm_media_data_opt_write_cache) 
    map_write_cache_flush_all() ; 
#endif 
 
  dfa_lun_read(); 
} 
 
//------------------------------------------------------------------------------ 
//------------------------------------------------------------------------------ 
//------------------------------------------------------------------------------ 
//------------------------------------------------------------------------------ 
 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   nand_write_begin_first_split 
// 
// Declaration: 
//   t_result nand_write_begin_first_split(void); 
// 
// Purpose: 
//   Issue the write command to the smart media device. 
// 
// Arguments: 
//   None. 
// 
// Return: 
//   A t_result indicating: 
//     k_success - command completed. 
// 
// Notes: 
//   This is a FUNCTION, not a DFA. 
//   Do not yeild, or run a DFA, from within this callback. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
t_result nand_write_begin_first_split() reentrant 
{ 
  TRACE0(397, nand, 1, "nand_write_begin_first_split()"); 
  // compute phy addresses in case this is the first sector 
  if (k_ix_media_nand_int != g_active_media) 
    nand_select_card() ; 
  nand_wr_va2pa() ; 
  nand_rd_va2pa() ; 
 
  if (k_ix_media_nand==g_active_media) 
  { 
    g_fmc_begin_split_callback = (t_fmc_callback) sm_write_begin_split; 
    _sm_rd_cmd_begin(k_sm_reset_chip);  
    sm_wait_rdy_with_timeout(k_sm_busy_reset_timeout); 
  } 
  else if (k_ix_media_nand_int == g_active_media) 
  { 
 
    g_fmc_begin_split_callback = (t_fmc_callback) nand_int_write_begin_quick_split; 
    nand_int_select_chip(_media_data(segments_per_page)-1); 
    _sm_rd_cmd_begin(k_sm_reset_chip); 
    sm_wait_rdy_with_timeout(k_sm_busy_reset_timeout); 
    return(nand_int_write_begin_quick_split()); 
    trace2(0, nand_int, 0, "beginning split at page:%d segment:%d", g_addr_page, g_addr_segment) ; 
  } 
  else 
  { 
    g_fmc_begin_split_callback = (t_fmc_callback) n2k_write_begin_split; 
 
    trace2(0, n2k, 0, "beginning split at page:%d segment:%d", g_addr_page, g_addr_segment) ; 
    // check to see if we are starting in middle of a page 
    if (g_addr_segment) 
    { 
      // pre-read the old page into chip cache 
      n2k_page_read_for_copy() ; 
      // write the full destination address 
      nand_cmd_page_write_cache_data() ; 
      n2k_page_set_wr_offset( _n2k_data_offset( g_addr_segment ) ) ; 
    } 
 
    return(k_success); 
  } 
  return(k_success); 
} 
 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   n2k_write_begin_xfer 
// 
// Declaration: 
//   void n2k_write_begin_xfer(void) reentrant 
// 
// Purpose: 
// 
// Arguments: 
// 
// Return: 
// 
// Notes: 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
t_result n2k_write_begin_xfer() reentrant 
{ 
  t_result result ; 
  uint16   max_split_size ; 
 
  trace4(0, sm, 10, "n2k_write_begin_xfer() - start:0x%04X%04X count:0x%04X%04X" , _hw(_fmc_get_start_lb_32()), _lw(_fmc_get_start_lb_32()), _hw(_fmc_get_lb_count_32()), _lw(_fmc_get_lb_count_32())); 
 
  _lun_data(sensep) = &sense_write_error; 
 
  // get virtual addr 
  map_lba2addr_rd(_fmc_get_start_lb_32()) ; 
 
  // enable the correct card 
  nand_select_card() ; 
 
  // copy head 
  result = map_write_begin( _fmc_get_start_lb_32() ) ; 
 
  // make sure we're in the right mode - fixes a bug where 
  // the first burst of the first split of the xfer begins 
  // on a zone that needed to be paged in... 
  sm_set_write_mode_page_data(); 
 
  // precompute max split size 
  max_split_size = (_media_data(pages_per_block)-g_addr_page)*_media_data(segments_per_page)-g_addr_segment ; 
 
  if (!g_addr_page) 
  { 
    _lun_data(max_lb_per_split) = _min( max_split_size, _fmc_get_lb_count_32()); 
    trace5(0, n2k, 10, "-----write begin xfer - zone:%d phy:%d log:%d page:%d count:%d - first in block", g_addr_zone, g_addr_wr_phy_blk, g_addr_log_blk, g_addr_page, _lun_data(max_lb_per_split) ); 
  } 
  else 
  { 
    _lun_data(max_lb_per_split) = _min( max_split_size, _fmc_get_lb_count_32()); 
    trace5(0, n2k, 10, "-----write begin xfer - zone:%d phy:%d log:%d page:%d count:%d", g_addr_zone, g_addr_wr_phy_blk, g_addr_log_blk, g_addr_page, _lun_data(max_lb_per_split)); 
  } 
 
  // burst size needs to be 1, because the sm controller can't 
  // handle more than 512 bytes out at a time. 
  _lun_data(max_lb_per_burst) = 1; 
  return(result ); 
} 
 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   n2k_write_begin_split 
// 
// Declaration: 
//   t_result n2k_write_begin_split(void); 
// 
// Purpose: 
//   Issue the write command to the smart media device. 
// 
// Arguments: 
//   None. 
// 
// Return: 
//   A t_result indicating: 
//     k_success - command completed. 
// 
// Notes: 
//   This is a FUNCTION, not a DFA. 
//   Do not yeild, or run a DFA, from within this callback. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
t_result n2k_write_begin_split() reentrant 
{ 
  uint8 zone_before ; 
  trace0(0, n2k, 1, "sm_write_begin_split()"); 
  trace1(0, n2k, 0, "g_addr_rd_phy_blk: %d", g_addr_rd_phy_blk) ; 
 
  zone_before = g_addr_zone ; 
 
  // if a new physical block, tell the mapper to go git us one. 
  if (k_success != map_lba2addr_rd(_fmc_get_start_lb_32())) 
    return(k_error); 
 
  if (g_addr_zone != zone_before) 
  { 
    // we've crossed a boundry on a split, do a reset 
    trace0(0, n2k, 0, "zone-change detected.") ; 
    nand_select_card() ; 
    sm_set_write_mode_page_data() ; 
  } 
 
  if (!g_addr_page) 
  { 
    if (k_success != map_alloc_wr_blk()) 
      return(k_error); 
  } 
 
  // convert virtual address zone/block/sector into page/offsets 
  nand_rd_va2pa() ; 
  nand_wr_va2pa() ; 
 
 
  trace0(0, sm, 0, "ready to begin writing sectors") ; 
  return(k_success); 
} 
 
//+----------------------------------------------------------------------------- 
//------------------------------------------------------------------------------ 
t_result n2k_write_end_split(void) reentrant 
{ 
  trace0(0, n2k, 0, "n2k_write_end_split()") ; 
 
  trace2(0, n2k, 0, "split ended on to page %d segment %d", g_addr_page, g_addr_segment) ; 
  if ( g_addr_segment ) 
  { 
    trace0(0, n2k, 0, "last split.  need to flush current page cache") ; 
    if ( k_success != nand_cmd_page_program() ) 
    { 
      trace0(0, n2k, 0, "nand_cmd_cache_program failed") ; 
      return(k_error ); 
    } 
    g_addr_page++; 
    g_addr_segment=0; 
    nand_incr_addr() ; 
  } 
  return(sm_write_end_split() ); 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   sm_write_begin_burst 
// 
// Declaration: 
//   t_result sm_write_begin_burst(void); 
// 
// Purpose: 
//   Issue the write command to the smart media device. 
// 
// Arguments: 
//   None. 
// 
// Return: 
//   A t_result indicating: 
//     k_success - command completed. 
// 
// Notes: 
//   This is a FUNCTION, not a DFA. 
//   Do not yeild, or run a DFA, from within this callback. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
t_result n2k_write_begin_burst() reentrant 
{ 
  trace5(0, n2k, 0, "n2k_write_begin_burst() - zone:%d log:%d-phy:%d page:%d seg:%d", g_addr_zone, g_addr_log_blk, g_addr_wr_phy_blk, g_addr_page, g_addr_segment) ; 
  trace1(0, n2k, 0, "                        - n_lbs_this_xfer:%d", g_n_lb_this_xfer.u32) ; 
  // check to see if we are starting in middle of a page 
  if (!g_addr_segment) 
  { 
    // pre-read the old page into chip cache 
    n2k_page_read_for_copy() ; 
    // write data into the on-chip cache, starting at the beginning of the page 
    nand_cmd_page_write_cache_data() ; 
  } 
 
  _mcu_register_set_bits(sm_mode_ctl,kbm_sm_mode_ecc_blk_xfer_en) ; 
  _sm_hw_ecc_wr_start() ; 
  trace0(0, n2k, 0, "bursting") ; 
  return(k_success); 
} 
 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   sm_write_end_burst 
// 
// Declaration: 
//   t_result sm_write_end_burst(void); 
// 
// Purpose: 
//   TBD 
// 
// Arguments: 
//   None. 
// 
// Return: 
//   A t_result indicating: 
//     k_success - command completed. 
// 
// Notes: 
//   This is a FUNCTION, not a DFA. 
//   Do not yeild, or run a DFA, from within this callback. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
t_result n2k_write_end_burst(void) reentrant 
{ 
 
  trace0(0, sm, 0, "n2k_write_end_burst()") ; 
  _sm_hw_ecc_wr_stop() ; 
  sm_mode_ctl &= ~kbm_sm_mode_ecc_blk_xfer_en; 
  trace0(0, sm, 0, "ecc_en off") ; 
 
  g_addr_segment++ ; 
 
  if ( g_addr_segment==_media_data(segments_per_page) ) 
  { 
    trace0(0, n2k, 0, "last burst in the page.  program the page cache") ; 
    if ( k_success != nand_cmd_page_program() ) 
    { 
      trace0(0, sm, 0, "nand_cmd_cache_program failed") ; 
      return(k_error ); 
    } 
    g_addr_page++; 
    g_addr_segment=0; 
    nand_incr_addr() ; 
  } 
  return(k_success ); 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   dfa_sm_write 
// 
// Declaration: 
//   void dfa_sm_write(void) reentrant 
// 
// Purpose: 
//   Read data from sm media 
// 
// Arguments: 
//   None. 
//   Uses _lun_data and g_bot_cbw to get its parameters. 
// 
// Return: 
//   No return value. 
//   However, on exit the DFA's argument pointer is written with a t_csw_status indicating: 
//     k_command_passed - command completed. 
//     k_command_failed - an error occurred. 
// 
// Notes: 
//   This is the start STATE of the dfa_ata_write DFA. 
//   It overrides dfa_lun_write. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
void nand_dfa_write(void) reentrant 
{ 
  TRACE0(398, nand, 0, "nand_dfa_write()"); 
 
  // check for write protection change 
  if ( nand_check_wp() ) 
  { 
    TRACE0(399, nand, 0, "error:  write protection state changed") ; 
    __thread_return_dfa(k_error) ; 
  } 
 
  // check for write protection state 
  if ( _mcu_register_rd(x_media_sts)&kbm_media_sts_sm_wp) 
  { 
    TRACE0(400, nand, 0, "error:  write protection enabled") ; 
    _lun_data(sensep) = &sense_media_change ; 
    __thread_return_dfa(k_error) ; 
  } 
 
  _fmc_set_options(0); 
  _fmc_set_timeout(10000); 
 
  if (k_ix_media_nand == g_ix_media_nand) 
  { 
    TRACE0(401, nand, 0, "preparing for 512-byte per page writes") ; 
    _fmc_set_callback(sm_write_begin_xfer, sm_write_end_xfer, 
                      nand_write_begin_first_split, sm_write_end_split, 
                      sm_write_begin_burst, fmc_dflt_callback, sm_write_end_burst); 
  } 
  else if (k_ix_media_nand_int == g_ix_media_nand) 
  { 
    _fmc_set_callback(nand_int_write_begin_xfer, nand_int_write_end_xfer, 
                      nand_write_begin_first_split, nand_int_write_end_split, 
                      nand_int_write_begin_burst, fmc_dflt_callback, nand_int_write_end_burst); 
 
  } 
  else 
  { 
    trace0(0, n2k, 0, "preparing for 2048-byte-per-page writes") ; 
    _fmc_set_callback(n2k_write_begin_xfer, sm_write_end_xfer, 
                      nand_write_begin_first_split, n2k_write_end_split, 
                      n2k_write_begin_burst, fmc_dflt_callback, n2k_write_end_burst); 
  } 
  dfa_lun_write(); 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   sm_power_dn() 
// 
// Declaration: 
//   t_result sm_power_dn() reentrant 
// 
// Purpose: 
//   erase all physical blocks on media card, then 
//   rebuild sector mapping table 
// 
// Arguments: 
//   none 
// 
// Return: 
//   k_success  - on successful completion 
// 
// Notes: 
//   currently, not a dfa 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
t_result nand_power_dn() reentrant 
{ 
  TRACE0(402, nand, 0, "nand_power_dn()") ; 
  _sm_hw_set_rd_standby(); 
  return(k_success ); 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   sm_power_up() 
// 
// Declaration: 
//  t_result sm_power_up(void) reentrant 
// 
// Purpose: 
// 
// Arguments: 
// 
// Return: 
// 
// Notes: 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
t_result nand_power_up(void) reentrant 
{ 
  TRACE0(403, nand, 0, "nand_power_up()"); 
  return(k_success ); 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   sm_reset_controller() 
// 
// Declaration: 
//   void sm_reset_controller(void) reentrant 
// 
// Purpose: 
//   erase all physical blocks on media card, then 
//   rebuild sector mapping table 
// 
// Arguments: 
//   none 
// 
// Return: 
// 
// Notes: 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
void nand_reset_controller(void) reentrant 
{ 
  TRACE0(404, nand, 0, "nand_reset_controller()"); 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   dfa_sm_identify_media 
// 
// Declaration: 
//   void dfa_sm_identify_media(void); 
// 
// Purpose: 
//   determine if smart media is present the device and if so, 
//   determine max lba and lb size 
// 
// Arguments: 
//   none 
// 
// Return: 
//   none 
// 
// Notes: 
//   This is a DFA 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
void nand_dfa_identify_media(void) reentrant 
{ 
  TRACE0(405, nand, 0, "nand_dfa_identify_media()"); 
  _lun_data(media) |= kbm_lun_media_unknown ; 
 
  if (k_success != nand_check_format()) 
  { 
    TRACE0(406, nand, 0, "nand_check_format() returned an error!") ; 
    _thread_return_dfa(k_error) ; 
  } 
 
  _lun_data(capacity.lba_max.u32) = _media_data(num_zones) * (uint32) _media_data(logical_blocks_per_zone) * (uint32) _media_data(pages_per_block)* (uint32) _media_data(segments_per_page) ; 
  _lun_data(capacity.lba_sz.u32)  = 512 ; 
 
  if ( x_media_sts & kbm_media_sts_sm_256_page ) 
    _lun_data(capacity.lba_max.u32) /= 2 ; 
  if ( k_ix_media_nand_2k == g_active_media) 
    _lun_data(capacity.lba_max.u32) *= 4 ; 
 
  _lun_data(capacity.lba_max.u32) -= (uint32) 1 ; 
 
  // media is now known 
  _lun_data(media) &= ~kbm_lun_media_unknown; 
  _lun_data(sensep) = &sense_media_change; 
 
  if (x_media_sts & kbm_media_sts_sm_wp ) 
    _lun_data(media) |= kbm_lun_media_wrprot ; 
  else 
    _lun_data(media) &= ~kbm_lun_media_wrprot ; 
 
#if 1 
 
 
  // check attributes 
  if (g_dev_attr_lo&kbm_attr_lo_sm_timing) 
  { 
    TRACE0(407, nand, 0, "Forcing Smart Media-Compatible Cycle Timing on NAND flash") ; 
    g_nand_rw_speed=0; 
  } 
  else 
  { 
    TRACE0(408, nand, 0, "Enabling fast cycle time on NAND flash") ; 
    g_nand_rw_speed=kbm_sm_mode_fast_cycle_time; 
  } 
#else 
  g_nand_rw_speed=0; 
#endif 
  TRACE2(409, nand, 0, "nand capacity:  lba_max:%04x%04x lba_sz:00000200", _hw(_lun_data(capacity.lba_max.u32)), _lw(_lun_data(capacity.lba_max.u32))) ; 
 
 
  thread_return_dfa(k_success) ; 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   TBD 
// 
// Declaration: 
//   TBD 
// 
// Purpose: 
//   TBD 
// 
// Arguments: 
//   TBD 
// 
// Return: 
//   TBD 
// 
// Notes: 
//   This is a DFA, not a FUNCTION. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
void nand_dfa_initialize_media() reentrant 
{ 
  TRACE0(410, nand, 0, "nand_dfa_initialize_media()"); 
  _lun_data(media) &= ~kbm_lun_media_changed; 
  TRACE2(411, lun, 0, "_lun_data(%d, media)=%02x", g_active_media, _lun_data(media)); 
  _thread_return_dfa(k_success); 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   TBD 
// 
// Declaration: 
//   TBD 
// 
// Purpose: 
//   TBD 
// 
// Arguments: 
//   TBD 
// 
// Return: 
//   TBD 
// 
// Notes: 
//   This is a DFA, not a FUNCTION. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
void nand_dfa_reset_media() reentrant 
{ 
  TRACE0(412, nand, 0, "nand_dfa_reset_media()"); 
  _thread_return_dfa(k_success); 
} 
 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   sm_initialize_controller() 
// 
// Declaration: 
//   void sm_initialize_controller(void) reentrant 
// 
// Purpose: 
//   TBD 
// 
// Arguments: 
//   TBD 
// 
// Return: 
//   TBD 
// 
// Notes: 
//   TBD 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
void nand_initialize_controller(void) reentrant 
{ 
  uint16 i; 
  TRACE0(413, nand, 0, "nand_initialize_controller()") ; 
 
  // initialize _lun_data 
  memcpy(_lun_data(device_id), "HD", k_lun_max_devid_sz) ; 
  _lun_data(media) &=~ kbm_lun_media_unknown ; 
  g_nand_rw_speed=0 ; 
 
#ifdef k_opt_password 
  _lun_data(media) |= kbm_lun_media_lockable; 
#endif 
 
 
 
  // by default, it is thus.  However, we must check at run-time 
  // whether or not to keep it such.  The removable bit is first used 
  // checked on Inquiry processing 
  _lun_data(media) &=~ kbm_lun_media_removable; 
  _lun_data(device_type) = k_device_type_nand ; 
 
  // we have to make sure we wait at least 1 ms before accessing the device, 
  // but we don't have a timer running yet 
  i=1000 ; 
  while (i--) ; 
 
  // try to identify the media configuration 
  fmc_select_nand() ; 
  if ( k_success != nand_identify_media_format() ) 
  { 
    TRACE0(414, nand, 0, "failed to identify media format") ; 
    return; 
  } 
 
// $$$ intercepting 
  // util_trace_media() ; 
  // util_trace_map_media() ; 
 
  // read the boot block to see if out custom boot structure exists. 
  nand_boot_seek() ; 
 
#ifdef k_enable_write_caching 
 
 
  // initialize the write head/tail cache 
  map_write_cache_init() ; 
#endif 
 
 
  // safe erase the media here...  for utilitarian purposes 
  // util_safe_erase_media() ; 
  // util_trace_map_media(); 
 
/* 
  for(g_addr_rd_phy_blk=0;g_addr_rd_phy_blk<8;g_addr_rd_phy_blk++) 
  { 
    util_trace_phy_block(); 
  } 
*/ 
 
  // util_trace_log_media(); 
/* 
  g_addr_rd_phy_blk=0; 
  g_addr_page=0; 
  _media_data(boot_block)=0; 
  _media_data(boot_page)=0; 
  _media_data(assign_zone)=0; 
  g_addr_zone=0; 
  map_build_sector_map(); 
  util_test_copy_double_bit_ecc(33) ; 
*/ 
 
  // store initial state of the wp bit. 
  _nand_media_status = _mcu_register_rd(x_media_sts) ; 
} 
 
//------------------------------------------------------------------------------ 
void nand_dfa_inquiry(void) reentrant 
{ 
  TRACE0(415, nand, 0, "nand_dfa_inquiry()") ; 
 
  // check attribute bit... will be valid by this point 
  if (g_dev_attr_lo&kbm_attr_lo_hd_as_rm) 
  { 
    TRACE0(416, nand, 0, "kbm_attr_lo_hd_as_rm bit set.  Reporting lun as Removable") ; 
    _lun_data(media)|=kbm_lun_media_removable ; 
  } 
  else if (x_media_sts&kbm_media_sts_sm_wp ) 
  { 
    TRACE0(417, nand, 0, "nand write protected.  Reporting lun as Removable") ; 
    _lun_data(media)|=kbm_lun_media_removable ; 
  } 
  else if (!g_password_validated) 
  { 
    TRACE0(418, nand, 0, "Password exists, and is not validated.  Make removable") ; 
    _lun_data(media)|=kbm_lun_media_removable; 
  } 
  else 
  { 
    TRACE0(419, nand, 0, "reporting lun as Fixed Media") ; 
    _lun_data(media)&=~kbm_lun_media_removable ; 
  } 
 
  // call base class 
  dfa_lun_inquiry() ; 
} 
 
//+----------------------------------------------------------------------------- 
//------------------------------------------------------------------------------ 
t_result nand_check_wp() reentrant 
{ 
  uint8 cs ; 
  TRACE0(420, nand, 0, "nand_check_wp()") ; 
 
  if ( _lun_data(media)&kbm_lun_media_removable) 
  { 
    // check to see if there's been a change 
    cs=_mcu_register_rd(x_media_sts)&kbm_media_sts_sm_wp; 
    _nand_media_status&= kbm_media_sts_sm_wp; 
    if (_nand_media_status^cs) 
    { 
      TRACE2(421, nand, 0, "media_sts sm_wp bit change detected.  _nand_media_status:%02X x_media_sts:%02X", _nand_media_status, x_media_sts) ; 
      _lun_data(sensep) = &sense_media_change ; 
      _lun_data(media) |= kbm_lun_media_unknown; 
      _nand_media_status=cs; 
      // change 
      return(k_true ); 
    } 
  } 
  // no change in wp status 
  return(k_false ); 
 
} 
 
 
//------------------------------------------------------------------------------ 
//------------------------------------------------------------------------------ 
void nand_process_idle() reentrant 
{ 
  TRACE0(422, nand, 0, "nand_dfa_process_idle()") ; 
#if 0 
  if ( _media_data(options)&kbm_media_data_opt_write_cache) 
  { 
    TRACE0(423, nand, 0, "flushing write cache()") ; 
    map_write_cache_flush_all(); 
  } 
#endif 
  if ( _media_data(options)&kbm_media_data_opt_erase_cache) 
  { 
    TRACE0(424, nand, 0, "flushing erase cache()") ; 
    map_erase_cache_flush_all(); 
  } 
  _lun_data(media)&=~kbm_lun_media_process_idle; 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   nand_dfa_test_unit_ready 
// 
// Declaration: 
//   void dfa_lun_test_unit_ready(void); 
// 
// Purpose: 
// 
// Arguments: 
// 
// Return: 
// 
// Notes: 
//   This allows us to check if the wp status has 'changed' since previous cmd. 
//   if so, then report a media change. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
void nand_dfa_test_unit_ready(void)reentrant 
{ 
  TRACE0(425, nand, 0, "nand_dfa_test_unit_ready()"); 
 
  // if happy, check wp status.  this function will update sensep 
  if ( _lun_data(sensep) == &sense_none) 
    nand_check_wp() ; 
 
  // call base-class 
  dfa_lun_test_unit_ready() ; 
  // report sense as normal. 
  _thread_return_dfa((_lun_data(sensep) == &sense_none) ? k_command_passed : k_command_failed); 
} 
 
 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   sm_dfa_verify() 
// 
// Declaration: 
//   void sm_dfa_verify(void); 
// 
// Purpose: 
//   Verify that the data on the drive matches the data sent down. 
// 
// Arguments: 
//   None. 
//   Uses _lun_data and g_bot_cbw to get its parameters. 
// 
// Return: 
//   No return value. 
//   However, on exit the DFA's argument pointer is written with a t_csw_status indicating: 
//     k_command_passed - command completed. 
//     k_command_failed - an error occurred. 
// 
// Notes: 
//   This is the start STATE of the dfa_ata_verify DFA. 
//   It overrides dfa_lun_verify. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
void nand_dfa_verify() reentrant 
{ 
  TRACE0(426, nand, 0, "dfa_ata_verify"); 
  // _fmc_set_callback(sm_read_begin_xfer, sm_read_end_xfer, sm_read_begin_split, sm_read_end_split, sm_read_begin_burst, fmc_dflt_callback, sm_read_end_burst); 
  dfa_lun_verify(); 
}