www.pudn.com > SMSC USB2.0.zip > ms.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. 
  ____________________________________________________________________________ 
  ____________________________________________________________________________ 
  sm.c - memory stick mass storage class implementation file 
  ____________________________________________________________________________ 
  comments tbd 
  ____________________________________________________________________________ 
  Revision History 
  Date      Who  Comment 
  ________  ___  _____________________________________________________________ 
  11/##/01  tbh  initial version 
  02/26/02  tbh  changed strcpy to strncpy for safety 
  04/01/02  tbh  read_boot now finds 2 boot blocks on cards that have them 
  04/02/02  tbh  added ms_copy_page 
  04/17/02  tbh  override dfa_lun_mode_sense 
  04/18/02  tbh  removed that override.  superclass method handles it now. 
  05/07/02  cds  added a rw_intra_burst callback to actually perform the blk_xfer inner loop 
                 to get around xdata-access bug in the chip 
  05/09/02  cds  added call to _dev_soft_reset() when a usbreset occurs during rw_intra_bursts 
  06/03/02  cds  updated ms_write_end_xfer() to turn off auto-xfer so that 
                 the sector copy can use the fmdu. 
  06/19/02  tbh  converted to read boot into xdata buf instead of packet buf 
                 since bot layer is can now run concurrently with discovery 
  06/20/02  tbh  tweaked the intra-burst callback to stop clearing the isr bit 
                 to be compatible with the perf opto done in fmc_transfer 
  06/26/02  tbh  added nops in ms_wait_msc_stat_sif_with_timeout. 
                 two back to back reads of the alt_stat register causes failure. 
                 there is some latency issue causing intermittant failure... 
                 inserting nops fixes it. 
  07/01/02  tbh  write-protect check is now performed in the lun layer 
  07/09/02  cds  renamed WorkBuf to g_sector_buffer 
  07/12/02  tbh  lun string name change caused by mandate from on high 
  07/17/02  tbh  ms_write_end_xfer sets g_fmc_rslt if map_write_flush returns an error 
                 because fmc_transfer doesn't check the return code of the callback. 
                 I feared breaking all of the other luns by changing fmc... 
  07/18/02  tbh  fixed long standing (but until recently dormant) bug in 
                 ms_rw_intra_burst_callback where && was erroneously used 
                 in place of &. 
  07/23/02  tbh  the ms_wait_msc_stat_sif_with_timeout nop injection was insufficient, 
                 it seems.  i increased the number of ops and moved them into the do 
                 loop to delay betwean -every- read of the ms_alt_stat register. 
                 this appears to resolve the issue of the cards formatted by DoTop 
                 becoming unreadable in the 210. 
  07/31/02  cds  called media_set_active(...) instead of setting g_active_media 
                 to select a new media object. 
  08/06/02  tbh  added _force_one_burst_per_split to solve the SanDisk 64MB cards 
                 w/ date codes < J [bug report br308] 
  08/13/02  tbh  added status check after read error in read-begin-burst to 
                 distinguish uncorrectabel error from correctable error on last block. 
  08/28/02  tbh  added extra get-int after block-end command for read and write 
                 to ensure that no error happened issuing that command (shawn's suggestions). 
                 also changed interpretation of uncorrectable error bits from 
                 "all relevant bits set -> uncorrectable error" to 
                 "any relevant bit set -> uncorrectable error" to resolve the 
                 data corruption issue on sony cards (sorry, no bug reporter number 
                 was entered yet) 
  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) 
                                                  => _media_data(segments_per_page) 
  09/12/02  tbh  replaced strncpy() with memcpy() to save data space. 
  09/18/02  cds  initialized _media_data(options) field to kbm_media_data_opts_none 
  10/17/02  cds  updated for new lun vtbl and lun_data macros 
  11/01/02  tbh  made xbuf_rd() visible so ms_media.c can call it. 
                 ms_read_boot_block was using the wrong boot block when more than one 
                 exists.  changed to use the lowest-numbered one, per the spec. 
                 move write-protection test to occur before map_build_sector_map 
                 to avoid erroneous block erase on write protected disks. 
  11/02/02  tbh  added ms_is_this_blk_in_bad_blk_tbl(). 
                 added ms_find_map_block() to see if any exists, and compare contents. 
  11/03/02  cds  - updated ms_is_this_blk_in_bad_blk_tbl() to include info block 
                 - added new global g_ms_user_area_start_blk to help mapper know which blocks 
                   are to be used for mapping 
                 - updated bad-block table code to read data in big endian format 
============================================================================*/ 
#define __ms_dot_c__ 
#include "project.h" 
#include "dev.h" 
 
code _vtbl_defn(ms); 
 
void fmc_dbg_dump_sector(uint8* sector) reentrant; 
 
//#define k_memory_stick_eraser 
 
//------------------------------------------------------------------------------ 
// module globals 
 
// status 
xdata uint8 _factor; 
xdata uint8 _status0; 
xdata uint8 _status1; 
 
// extra data registers buffer 
xdata uint8 g_ms_extra[4]; 
 
// shadow registers 
xdata uint8 g_ms_blk_addr_msb; 
xdata uint8 g_ms_blk_addr_mid; 
xdata uint8 g_ms_blk_addr_lsb; 
xdata uint8 g_ms_page; 
 
// logical to physical sector mapping table 
//xdata uint16 _ms_map_table[512]; 
#define k_map_boot_blk    0x8000 
#define k_map_initial_err 0xfffd 
#define k_map_fatal_err   0xfffe 
 
// total number of blocks on the card 
xdata t_uw16 _ms_block_per_card; 
 
// block size - 8k or 16k bytes 
xdata t_uw16 _ms_byte_per_block; 
 
// segment size - number of card blocks in a card segment 
#define k_phy_block_per_segment      512L 
#define k_log_block_per_segment      496L 
#define k_log_block_per_boot_segment 494L 
 
// pages per block - a page is analogous to a mass storage class logical block, or disk sector 
xdata uint16 _ms_page_per_block; 
 
// tables used by the mapper/pager 
#define k_ms_max_zones_per_table 2 
xdata uint16 _ms_log2phy[k_ms_max_zones_per_table][k_phy_block_per_segment]; 
xdata uint8  _ms_assign_map[k_ms_max_zones_per_table][k_phy_block_per_segment/8]; 
 
xdata uint16 _ms_bad_blk_tbl[256]; 
 
xdata uint16 g_ms_user_area_start_blk; 
 
 
 
// br308 
// set to false when the boot blocks are read. 
// set to true if a block write fails with breq after block-end (i.e., exibits the bug). 
// provides the solution for the SanDisk 64MB cards w/ date codes < J 
static bit _force_one_burst_per_split; 
 
//------------------------------------------------------------------------------ 
//------------------------------------------------------------------------------ 
t_bool ms_is_this_blk_in_bad_blk_tbl(uint16 blk) reentrant 
{ 
  uint8 i; 
  for (i=0; i<255; i++) 
  { 
    // the zeroth entry in this table is the "information block" 
    // any non-FFFF blocks in the table that are off the end of the card 
    // are to be ignored. 
    if (_ms_bad_blk_tbl[i] == 0xFFFF) 
      return k_no; 
    if (_ms_bad_blk_tbl[i] == blk) 
    { 
      // this physical block appears in the bad block list 
      trace1(0, ms_media, 110, "alert: block:%d is in the bad block table!", _ms_bad_blk_tbl[i]); 
      if (!i) 
      { 
        trace0(0, ms_media, 110, "       and its the information block!"); 
      } 
      return k_yes; 
    } 
  } 
  return k_no; 
} 
 
//------------------------------------------------------------------------------ 
//------------------------------------------------------------------------------ 
t_bool ms_is_this_blk_the_information_block(uint16 blk) reentrant 
{ 
  // the zeroth entry in this table is the "information block" 
  if (_ms_bad_blk_tbl[0] == blk) 
  { 
    // this physical block appears in the bad block list 
    trace1(0, ms_media, 110, "alert: block:%d is in the information block!", blk); 
    return k_yes; 
  } 
  return k_no; 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   ms_wait_msc_stat_rdy_with_timeout 
// 
// Declaration: 
//   t_result ms_wait_msc_stat_rdy_with_timeout(uint8 ticks); 
// 
// Purpose: 
//   Wait for the ready bit in the msc status register to go hi indicating that 
//   the MS card can accept a TPC. 
// 
// Arguments: 
//   ticks - a uint8 representing the timeout period in milliseconds. 
// 
// Return: 
//   A t_result indicating: 
//     k_success - the bit was set, and this routine has cleared it. 
//     k_usbrst  - usb reset signalling was detected while waiting. 
//     k_aborted - traffic occurred on the control pipe while waiting (probably a mass storage reset). 
//     k_timeout - timeout limit exceeded. 
// 
// Notes: 
//   This is a FUNCTION, not a DFA. 
//   Clears the ready bit after seeing it get set (to rearm for the next one). 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
xdata uint8 status;  //!!! NEED PROPER NAMES IF STICK TO THIS SCHEME (AVOID STKOVF) 
xdata t_sync sync; 
t_result ms_wait_msc_stat_rdy_with_timeout(uint8 ticks) reentrant; 
t_result ms_wait_msc_stat_rdy_with_timeout(uint8 ticks) reentrant 
{ 
  //uint8 status; 
  //t_sync sync; 
 
  trace1(0, ms, 1, "ms_wait_msc_stat_rdy_with_timeout(ticks:%d)", ticks); 
  //_stack_dump(); 
  //_stack_check(); 
  thread_set_timer(ticks+1); 
  do 
  { 
    // clear the interrupt generator in the sony block 
    //status = _ms_register_rd(ms_alt_stat); 
    status = _mcu_register_rd(x_msc_stat); 
    sync = thread_got_sync(kbm_sync_usbrst |kbm_sync_abort |kbm_sync_timer); 
    _thread_clr_sync(sync); 
    if (sync & kbm_sync_abort) 
    { 
      trace0(0, err, 110, "ms_wait_msc_stat_rdy_with_timeout() - error: kbm_sync_abort"); 
      return k_aborted; 
    } 
    if (sync & kbm_sync_usbrst) 
    { 
      trace0(0, err, 110, "ms_wait_msc_stat_rdy_with_timeout() - error: kbm_sync_usbrst"); 
      return k_usbreset; 
    } 
    if (sync & kbm_sync_timer) 
    { 
      trace0(0, err, 110, "ms_wait_msc_stat_rdy_with_timeout() - error: kbm_sync_timer"); 
      return k_timeout; 
    } 
  } while ((status & kbm_msc_stat_rdy) != kbm_msc_stat_rdy); 
  _mcu_register_wr(x_msc_stat, kbm_msc_stat_rdy); 
  return k_success; 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   ms_wait_msc_stat_sif_with_timeout 
// 
// Declaration: 
//   t_result ms_wait_msc_stat_sif_with_timeout(uint8 ticks); 
// 
// Purpose: 
//   Wait for the sif bit in the msc alternate status register to go hi indicating 
//   that the getint command can be issued. 
// 
// Arguments: 
//   ticks - a uint8 representing the timeout period in milliseconds. 
// 
// Return: 
//   A t_result indicating: 
//     k_success - the bit was set, and this routine has cleared it. 
//     k_usbrst  - usb reset signalling was detected while waiting. 
//     k_aborted - traffic occurred on the control pipe while waiting (probably a mass storage reset). 
//     k_timeout - timeout limit exceeded. 
// 
// Notes: 
//   This is a FUNCTION, not a DFA. 
//   Clears the sif bit after seeing it get set (to rearm for the next one) 
//   by writing the msc staus register (causing alternate status register to clear also. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
t_result ms_wait_msc_stat_sif_with_timeout(uint8 ticks) reentrant; 
t_result ms_wait_msc_stat_sif_with_timeout(uint8 ticks) reentrant 
{ 
  //uint8 status; 
  //t_sync sync; 
 
  trace1(0, ms, 0, "ms_wait_msc_stat_sif_with_timeout(ticks:%d)", ticks); 
  //_stack_check(); 
  thread_set_timer(ticks+1); 
  do 
  { 
    // two back to back reads of the alt_stat register appears to cause failure. 
    // apparently there is some latency issue causing intermittant failure... 
    // note well that i have not yet invested the time to determine if this 
    // is really cause by a synchronization issue or not.  i merely opted 
    // for the 'bit ricochet' gambit and inserted a delay between every read of the 
    // ms_alt_status register.  emperical evidence suggests that this fixs the problem... 
    // but i am not willing to assert that as a proof... 
    // the actual cause of death could not be determined from the forensic evidence. 
    _nop_();_nop_();_nop_();_nop_();_nop_();_nop_(); 
    _nop_();_nop_();_nop_();_nop_();_nop_();_nop_(); 
    _nop_();_nop_();_nop_();_nop_();_nop_();_nop_(); 
    // clear the interrupt generator in the sony block 
    status = _ms_register_rd(ms_alt_stat); 
    //status = _mcu_register_rd(x_msc_stat); 
    trace1(0, ms, 0, "status:0x%02X", status); 
    sync = thread_got_sync(kbm_sync_usbrst |kbm_sync_abort |kbm_sync_timer); 
    _thread_clr_sync(sync); 
    if (sync & kbm_sync_abort) 
    { 
      trace0(0, err, 110, "ms_wait_msc_stat_sif_with_timeout() - error: kbm_sync_abort"); 
      return k_aborted; 
    } 
    if (sync & kbm_sync_usbrst) 
    { 
      trace0(0, err, 110, "ms_wait_msc_stat_sif_with_timeout() - error: kbm_sync_usbrst"); 
      return k_usbreset; 
    } 
    if (sync & kbm_sync_timer) 
    { 
      trace0(0, err, 110, "ms_wait_msc_stat_sif_with_timeout() - error: kbm_sync_timer"); 
      return k_timeout; 
    } 
  } while ((status & kbm_msc_stat_sif) != kbm_msc_stat_sif); 
  _mcu_register_wr(x_msc_stat, kbm_msc_stat_sif); 
  return k_success; 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   ms_wait_fifo_with_timeout 
// 
// Declaration: 
//   t_result ms_wait_fifo_with_timeout(uint8 msk, uint8 val, uint8 ticks); 
// 
// Purpose: 
//   Wait for the a bit patern in the msc fifo status register to go hi indicating 
//   that the fifo has emptied, filled, etc.  (Waiting for status & mask == value.) 
// 
// Arguments: 
//   msk   - a uint8 bitmask to be logically and-ed with the msc fifo status register. 
//   val   - a uint8 indicating the desired bit pattern. 
//   ticks - a uint8 representing the timeout period in milliseconds. 
// 
// Return: 
//   A t_result indicating: 
//     k_success - status & mask == value. 
//     k_usbrst  - usb reset signalling was detected while waiting. 
//     k_aborted - traffic occurred on the control pipe while waiting (probably a mass storage reset). 
//     k_timeout - timeout limit exceeded. 
// 
// Notes: 
//   This is a FUNCTION, not a DFA. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
t_result ms_wait_fifo_with_timeout(uint8 msk, uint8 val, uint8 ticks) reentrant; 
t_result ms_wait_fifo_with_timeout(uint8 msk, uint8 val, uint8 ticks) reentrant 
{ 
  //uint8 status; 
  //t_sync sync; 
 
  trace3(0, ms, 1, "ms_wait_fifo_with_timeout(mask:%02X val:%02X ticks:%d)", msk, val, ticks); 
  //_stack_check(); 
  thread_set_timer(ticks+1); 
  do 
  { 
    status = _ms_register_rd(ms_fifo_stat); 
    sync = thread_got_sync(kbm_sync_usbrst |kbm_sync_abort |kbm_sync_timer); 
    _thread_clr_sync(sync); 
    if (sync & kbm_sync_abort) 
    { 
      trace0(0, err, 110, "ms_wait_fifo_with_timeout() - error: kbm_sync_abort"); 
      return k_aborted; 
    } 
    if (sync & kbm_sync_usbrst) 
    { 
      trace0(0, err, 110, "ms_wait_fifo_with_timeout() - error: kbm_sync_usbrst"); 
      return k_usbreset; 
    } 
    if (sync & kbm_sync_timer) 
    { 
      trace0(0, err, 110, "ms_wait_fifo_with_timeout() - error: kbm_sync_timer"); 
      return k_timeout; 
    } 
  } while ((status & msk) != val); 
  return k_success; 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   ms_set_tpc 
// 
// Declaration: 
//   t_result ms_set_tpc(uint8 tpc, uint8 count); 
// 
// Purpose: 
//   Send a transfer protocol command (TPC) to the MS card. 
// 
// Arguments: 
//   tpc - a uint8 indicating the tpc. 
//   count - a uint8 indicating the number of bytes to move during this command. 
// 
// Return: 
//   A t_result indicating: 
//     k_success - tpc issued. 
//     k_error   - failed to issue tpc. 
// 
// Notes: 
//   This is a FUNCTION, not a DFA. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
t_result ms_set_tpc(uint8 tpc, uint8 count) reentrant; 
t_result ms_set_tpc(uint8 tpc, uint8 count) reentrant 
{ 
  // profile: 
  //  during read: 
  //  ms_set_tpc     48usec to 76usec 
  //    wait rdy bit 46usec to 74usec 
 
  //_profile_on(5); 
  trace2(0, ms, 0, "ms_set_tpc() tpc:%02X count:%02X", tpc, count); 
  //_stack_check(); 
  // wait for the rdy bit in msc_stat to go hi 
  //_profile_on(4); 
  if (k_success != ms_wait_msc_stat_rdy_with_timeout(10)) 
    return k_error; 
  //_profile_off(4); 
  // write the byte count 
  _ms_register_wr(ms_bc, count); 
  // write the tpc 
  _ms_register_wr(ms_tpc_cmd, tpc); 
  //_profile_off(5); 
  return k_success; 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   ms_get_int 
// 
// Declaration: 
//   t_result ms_get_int(uint8 ticks); 
// 
// Purpose: 
//   Issue a getint command to solicit the result of the previous command. 
// 
// Arguments: 
//   ticks - a uint8 representing the timeout period in milliseconds. 
// 
// Return: 
//   A t_result indicating: 
//     k_success - the module global variable _factor contains the results. 
//     k_error   - either cannot issue the command or read back the results. 
//                 contents of _factor undefined. 
// 
// Notes: 
//   This is a FUNCTION, not a DFA. 
//   The module global variable _factor is written with the results as a side effect. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
t_result ms_get_int(uint8 ticks) reentrant; 
t_result ms_get_int(uint8 ticks) reentrant 
{ 
  // profile: 
  //  during read: 
  //  ms_get_int                108 to 133usec 
  //  wait sif                  52usec 
  //  ms_set_tpc                52usec to 76usec 
  //  wait fifo empty           2usec 
 
  //_profile_on(6); 
  trace0(0, ms, 0, "ms_get_int()"); 
  //_stack_check(); 
  // if you dont have this in here sisoft sandra fails. tbh... 
  // the problem may well be a stack overflow... 
  #if 1 
  //_profile_on(6); 
  // new - dont wait if its already there 
  if ((_ms_register_rd(ms_alt_stat) & kbm_msc_stat_sif) == kbm_msc_stat_sif) 
    {_mcu_register_wr(x_msc_stat, kbm_msc_stat_sif);} 
  else 
  // end of new 
  #endif 
    if (k_success != ms_wait_msc_stat_sif_with_timeout(ticks)) 
      return k_error; 
  //_profile_off(6); 
  // write the tpc 
  //_profile_on(5); 
  ms_set_tpc(k_ms_tpc_get_int, 1); 
  //_profile_off(5); 
  //_profile_on(4); 
  // wait rx fifo not empty 
  if (_ms_rx_data_fifo_is_empty()) 
    if (k_success != ms_wait_fifo_with_timeout(ms_fifo_stat_r_buf_e, 0, 3)) 
      return k_error; 
  //_profile_off(4); 
  // read the interrupt data 
  _factor = _ms_register_rd(ms_rx_db_lsb); 
  g_tmp = _ms_register_rd(ms_rx_db_msb);  // throw away 
  trace1(0, ms, 0, "_factor:%02X", _factor); 
  //_profile_off(6); 
  return k_success; 
} 
 
// so far i haven't got the acd code to work correctly... 
// but since it doesn't improve performance anyhow, there's not much point... 
//#define k_use_acd 
#ifdef k_use_acd 
//+----------------------------------------------------------------------------- 
// Name: 
//   ms_get_int_acd 
// 
// Declaration: 
//   t_result ms_get_int_acd(uint8 ticks); 
// 
// Purpose: 
//   Issue a getint command to solicit the result of the previous command. 
//   Only to be used with ACD mode. 
//   Allows skipping the wait for SIF, and the set-tpc for getint. 
//   The goal is to use it to slightly improve the read performance. 
// 
// Arguments: 
//   None. 
// 
// Return: 
//   A t_result indicating: 
//     k_success - the module global variable _factor contains the results. 
//     k_error   - either cannot issue the command or read back the results. 
//                 contents of _factor undefined. 
// 
// Notes: 
//   This is a FUNCTION, not a DFA. 
//   The module global variable _factor is written with the results as a side effect. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
t_result ms_get_int_acd(void) reentrant; 
t_result ms_get_int_acd(void) reentrant 
{ 
  trace0(0, ms, 0, "ms_get_int_acd()"); 
  //_stack_check(); 
  // wait rx fifo not empty 
  if (_ms_rx_data_fifo_is_empty()) 
    if (k_success != ms_wait_fifo_with_timeout(ms_fifo_stat_r_buf_e, 0, 3)) 
      return k_error; 
    // read the interrupt data 
  _factor = _ms_register_rd(ms_rx_db_lsb); 
  g_tmp = _ms_register_rd(ms_rx_db_msb);  // throw away 
  trace1(0, ms, 0, "_factor:%02X", _factor); 
  return k_success; 
} 
#endif 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   ms_set_cmd 
// 
// Declaration: 
//   t_result ms_set_cmd(uint8 cmd); 
// 
// Purpose: 
//   Send a flash command to the MS card. 
// 
// Arguments: 
//   cmd - a uint8 representing the command id. 
// 
// Return: 
//   A t_result indicating: 
//     k_success - command issued. 
//     k_error   - failed to issue command. 
// 
// Notes: 
//   This is a FUNCTION, not a DFA. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
t_result ms_set_cmd(uint8 cmd) reentrant; 
t_result ms_set_cmd(uint8 cmd) reentrant 
{ 
  trace1(0, ms, 0, "ms_set_cmd() cmd:%02X", cmd); 
  //_stack_check(); 
  // write the tpc 
  if (k_success != ms_set_tpc(k_ms_tpc_set_cmd, 1)) 
    return k_error; 
  // write the command 
  _ms_register_wr(ms_tx_db_msb, 0); 
  _ms_register_wr(ms_tx_db_lsb, cmd); 
  return k_success; 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   ms_set_read_reg_addrs 
// 
// Declaration: 
//   t_result ms_set_read_reg_addrs(uint8 start, uint8 count); 
// 
// Purpose: 
//   Tell the MS card that its registers are to be read by specifying the starting 
//   register offset and the number of registers to be read. 
// 
// Arguments: 
//   start - the offset of the first register to read. 
//   count - the number of consecutive registers to read. 
// 
// Return: 
//   A t_result indicating: 
//     k_success - read register start/count are delivered to the card. 
//     k_error   - failed to configure the card for a register read. 
// 
// Notes: 
//   This is a FUNCTION, not a DFA. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
t_result ms_set_read_reg_addrs(uint8 start, uint8 count) reentrant; 
t_result ms_set_read_reg_addrs(uint8 start, uint8 count) reentrant 
{ 
  trace2(0, ms, 0, "ms_set_read_reg_addrs() start:0x%02X count:0x%02X", start, count); 
  //_stack_check(); 
  // write the tpc 
  if (k_success != ms_set_tpc(k_ms_tpc_set_rw_reg, 4)) 
    return k_error; 
  // write the command 
  _ms_register_wr(ms_tx_db_msb, count); 
  _ms_register_wr(ms_tx_db_lsb, start); 
  if (!_ms_tx_data_fifo_is_empty()) 
    if (k_success != ms_wait_fifo_with_timeout(ms_fifo_stat_t_buf_e, ms_fifo_stat_t_buf_e, 3)) 
      return k_error; 
  _ms_register_wr(ms_tx_db_msb, 0); 
  _ms_register_wr(ms_tx_db_lsb, k_ms_parm_system); 
  if (!_ms_tx_data_fifo_is_empty()) 
    if (k_success != ms_wait_fifo_with_timeout(ms_fifo_stat_t_buf_e, ms_fifo_stat_t_buf_e, 3)) 
      return k_error; 
  return k_success; 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   ms_set_write_reg_addrs 
// 
// Declaration: 
//   t_result ms_set_write_reg_addrs(uint8 start, uint8 count); 
// 
// Purpose: 
//   Tell the MS card that its registers are to be written by specifying the starting 
//   register offset and the number of registers to be written. 
// 
// Arguments: 
//   start - the offset of the first register to write. 
//   count - the number of consecutive registers to write. 
// 
// Return: 
//   A t_result indicating: 
//     k_success - write register start/count are delivered to the card. 
//     k_error   - failed to configure the card for a register write. 
// 
// Notes: 
//   This is a FUNCTION, not a DFA. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
t_result ms_set_write_reg_addrs(uint8 start, uint8 count) reentrant; 
t_result ms_set_write_reg_addrs(uint8 start, uint8 count) reentrant 
{ 
  //_profile_on(6); 
  trace2(0, ms, 0, "ms_set_write_reg_addrs() start:%02X count:0x%02X", start, count); 
  //_stack_check(); 
  // write the tpc 
  if (k_success != ms_set_tpc(k_ms_tpc_set_rw_reg, 4)) 
    return k_error; 
  // write the command 
  _ms_register_wr(ms_tx_db_msb, k_ms_ext_ovwr_flg); 
  _ms_register_wr(ms_tx_db_lsb, 0); 
  if (!_ms_tx_data_fifo_is_empty()) 
    if (k_success != ms_wait_fifo_with_timeout(ms_fifo_stat_t_buf_e, ms_fifo_stat_t_buf_e, 3)) 
      return k_error; 
  _ms_register_wr(ms_tx_db_msb, count); 
  _ms_register_wr(ms_tx_db_lsb, start); 
  if (!_ms_tx_data_fifo_is_empty()) 
    if (k_success != ms_wait_fifo_with_timeout(ms_fifo_stat_t_buf_e, ms_fifo_stat_t_buf_e, 3)) 
      return k_error; 
  //_profile_off(6); 
  return k_success; 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   ms_read_status 
// 
// Declaration: 
//   t_result ms_read_status(void); 
// 
// Purpose: 
//   After an error read the status registers to determine the cause. 
// 
// Arguments: 
//   None. 
// 
// Return: 
//   A t_result indicating: 
//     k_success - the module global variables _status0 and _status1 contain the status. 
//     k_error   - failed to obtain the status. 
//                 contents of _status0 and _status1 undefined. 
// 
// Notes: 
//   This is a FUNCTION, not a DFA. 
//   The module global variables _status0 and _status1are written with the results 
//   as a side effect. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
t_result ms_read_status() reentrant 
{ 
  trace0(0, ms, 0, "ms_read_status()"); 
  //_stack_check(); 
  // set the address 
  if (k_success != ms_set_read_reg_addrs(k_ms_reg_status0, 2)) 
    return k_error; 
  // write the tpc 
  if (k_success != ms_set_tpc(k_ms_tpc_rd_reg, 2)) 
    return k_error; 
  // read the value 
  if (_ms_rx_data_fifo_is_empty()) 
    if (k_success != ms_wait_fifo_with_timeout(ms_fifo_stat_r_buf_e, 0, 3)) 
      return k_error; 
  _status0 = _ms_register_rd(ms_rx_db_lsb); 
  _status1 = _ms_register_rd(ms_rx_db_msb); 
  trace2(0, ms, 110, "_status0:%02X _status1:%02X", _status0, _status1); 
  return k_success; 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   ms_write_parm_reg 
// 
// Declaration: 
//   t_result ms_write_parm_reg(uint8 sys_parm, uint8 cmd_parm); 
// 
// Purpose: 
//   Write the parameter registers of the MS card in preparation for issuing a 
//   flash command. 
// 
// Arguments: 
//   sys_parm - specific to the flash command about to be issued. 
//   cmd_parm - specific to the flash command about to be issued. 
// 
//   The following global variables are accessed and delivered to the MS card: 
//     g_ms_blk_addr_msb 
//     g_ms_blk_addr_mid 
//     g_ms_blk_addr_lsb 
//     g_ms_page 
// 
// Return: 
//   A t_result indicating: 
//     k_success - parameter registers are written. 
//     k_error   - failed write parameter registers.  actual contents unknown. 
// 
// Notes: 
//   This is a FUNCTION, not a DFA. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
t_result ms_write_parm_reg(uint8 sys_parm, 
                           uint8 cmd_parm) reentrant; 
t_result ms_write_parm_reg(uint8 sys_parm, 
                           uint8 cmd_parm) reentrant 
{ 
  trace0(0, ms, 0, "ms_write_parm_reg()"); 
  //_stack_check(); 
  trace3(0, ms, 0, " mwpr - addr:0x00%02X%02X%02X", g_ms_blk_addr_msb, g_ms_blk_addr_mid, g_ms_blk_addr_lsb); 
  trace3(0, ms, 0, " mwpr - page:0x%02X sys_parm:0x%02X cmd_parm:0x%02X", g_ms_page, sys_parm, cmd_parm); 
  if (k_success != ms_set_write_reg_addrs(k_ms_parm_system, 6)) 
    return k_error; 
  // write the tpc 
  if (k_success != ms_set_tpc(k_ms_tpc_wr_reg, 6)) 
    return k_error; 
  // write the parm regs 
  _ms_register_wr(ms_tx_db_msb, g_ms_blk_addr_msb); 
  _ms_register_wr(ms_tx_db_lsb, sys_parm); 
  if (!_ms_tx_data_fifo_is_empty()) 
    if (k_success != ms_wait_fifo_with_timeout(ms_fifo_stat_t_buf_e, ms_fifo_stat_t_buf_e, 3)) 
      return k_error; 
  _ms_register_wr(ms_tx_db_msb, g_ms_blk_addr_lsb); 
  _ms_register_wr(ms_tx_db_lsb, g_ms_blk_addr_mid); 
  if (!_ms_tx_data_fifo_is_empty()) 
    if (k_success != ms_wait_fifo_with_timeout(ms_fifo_stat_t_buf_e, ms_fifo_stat_t_buf_e, 3)) 
      return k_error; 
  _ms_register_wr(ms_tx_db_msb, g_ms_page); 
  _ms_register_wr(ms_tx_db_lsb, cmd_parm); 
  if (!_ms_tx_data_fifo_is_empty()) 
    if (k_success != ms_wait_fifo_with_timeout(ms_fifo_stat_t_buf_e, ms_fifo_stat_t_buf_e, 3)) 
      return k_error; 
  return k_success; 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   ms_read_extra_data_reg 
// 
// Declaration: 
//   t_result ms_read_extra_data_reg(void); 
// 
// Purpose: 
//   Read the extra data registers of the active block of the MS card into 
//   the global variable g_ms_extra.  This is the lowest level, after the command 
//   has been issued.  (Several command handlers must access the extra data). 
// 
// Arguments: 
//   None. 
// 
// Return: 
//   A t_result indicating: 
//     k_success - the global variable g_ms_extra contains the results. 
//     k_error   - cannot obtain the extra data. 
//                 contents of g_ms_extra undefined. 
// 
// Notes: 
//   This is a FUNCTION, not a DFA. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
t_result ms_read_extra_data_reg(void) reentrant; 
t_result ms_read_extra_data_reg() reentrant 
{ 
  trace0(0, ms, 0, "ms_read_extra_data_reg()"); 
  //_stack_check(); 
  // set the address 
  if (k_success != ms_set_read_reg_addrs(k_ms_ext_ovwr_flg, 4)) 
    return k_error; 
  // write the tpc 
  if (k_success != ms_set_tpc(k_ms_tpc_rd_reg, 4)) 
    return k_error; 
  // read the value 
  if (_ms_rx_data_fifo_is_empty()) 
    if (k_success != ms_wait_fifo_with_timeout(ms_fifo_stat_r_buf_e, 0, 3)) 
      return k_error; 
  g_ms_extra[0] = _ms_register_rd(ms_rx_db_lsb); 
  g_ms_extra[1] = _ms_register_rd(ms_rx_db_msb); 
  if (_ms_rx_data_fifo_is_empty()) 
    if (k_success != ms_wait_fifo_with_timeout(ms_fifo_stat_r_buf_e, 0, 3)) 
      return k_error; 
  g_ms_extra[2] = _ms_register_rd(ms_rx_db_lsb); 
  g_ms_extra[3] = _ms_register_rd(ms_rx_db_msb); 
  trace4(0, ms, 0, "rd xtra[0..3]:%02X%02X%02X%02X", g_ms_extra[0], g_ms_extra[1], g_ms_extra[2], g_ms_extra[3]); 
  return k_success; 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   ms_read_extra_data 
// 
// Declaration: 
//   t_result ms_read_extra_data(void); 
// 
// Purpose: 
//   Read the extra data registers of the active block of the MS card into 
//   the global variable g_ms_extra. 
// 
// Arguments: 
//   None. 
// 
// Return: 
//   A t_result indicating: 
//     k_success - the global variable g_ms_extra contains the results. 
//     k_error   - cannot obtain the extra data. 
//                 contents of g_ms_extra undefined. 
// 
// Notes: 
//   This is a FUNCTION, not a DFA. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
t_result ms_read_extra_data() reentrant 
{ 
  trace4(0, ms, 0, "ms_read_extra_data(addr:0x00%02X%02X%02X page:%d)", g_ms_blk_addr_msb, g_ms_blk_addr_mid, g_ms_blk_addr_lsb, g_ms_page); 
  //_stack_check(); 
  if (k_success != ms_write_parm_reg(0x80, 0x40)) 
    return k_error; 
  if (k_success != ms_set_cmd(k_ms_block_read)) 
    return k_error; 
  if (k_success != ms_get_int(5)) 
    return k_error; 
  if (_factor & kbm_ms_reg_int_cmdnk) 
  { 
    trace0(0, err, 110, "error: cmdnk (ms_read_extra_data)"); 
    return k_error; 
  } 
  if (!(_factor & kbm_ms_reg_int_ced)) 
  { 
    trace0(0, err, 110, "error: ced not set (ms_read_extra_data)"); 
    return k_error; 
  } 
  if (_factor & kbm_ms_reg_int_err) 
  { 
    trace0(0, err, 110, "error: err (ms_read_extra_data)"); 
    if (k_success != ms_read_status()) 
      return k_error; 
//    if ((_status1 & (kbm_ms_reg_status1_ucex |kbm_ms_reg_status1_ucfg)) == 
//        (kbm_ms_reg_status1_ucex |kbm_ms_reg_status1_ucfg)) 
    if (_status1 & (kbm_ms_reg_status1_ucex |kbm_ms_reg_status1_ucfg)) 
    { 
      trace0(0, err, 110, "error: ucex|ucfg - uncorrectable flash read error (ms_read_extra_data)"); 
      //ms_media_set_phyblock_failed(); 
      return k_error; 
    } 
  } 
  // read the extra data registers 
  if (k_success != ms_read_extra_data_reg()) 
    return k_error; 
  return k_success; 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   ms_read_page_data 
// 
// Declaration: 
//   t_result ms_read_page_data(uint8 pnr); 
// 
// Purpose: 
//   Read a page (512 byte disk sector) from the MS card via pio and store the data 
//   into either a packet buffer or an xdata buffer. (Lowest level, callable from 
//   multple command handlers, as necessary.) 
// 
// Arguments: 
//   pnr - the packet buffer number.  any value greater than or equal to k_max_pnr 
//   will cause the data to be stored into xdata at _media_data(sector_buffer). 
// 
// Return: 
//   A t_result indicating: 
//     k_success - data read and stored. 
//     k_error   - cannot read data. 
//                 contents of target buffer undefined. 
// 
// Notes: 
//   This is a FUNCTION, not a DFA. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
t_result ms_read_page_data(uint8 pnr) reentrant; 
t_result ms_read_page_data(uint8 pnr) reentrant 
{ 
  uint16 i; 
 
  trace1(0, ms, 0, "ms_read_page_data(pnr:%d)", pnr); 
  //_stack_check(); 
  // write the tpc 
  if (k_success != ms_set_tpc(k_ms_tpc_rd_page_data |0x02, 0)) 
    return k_error; 
  // read the data 
  if (pnr < k_max_pnr) 
  { 
    _mcu_register_wr(x_sram_addr_lo, k_pkt_addrlo[pnr]); 
    _mcu_register_wr(x_sram_addr_hi, k_pkt_addrhi[pnr]); 
    // there are always 256 words of device data 
#if 1 // opt for improved code density over execution speed since only called on insertion 
    for (i=0; i<256; i++) 
    { 
      if (_ms_rx_data_fifo_is_empty()) 
        if (k_success != ms_wait_fifo_with_timeout(ms_fifo_stat_r_buf_e, 0, 3)) 
          return k_error; 
      x_sram_data = _ms_register_rd(ms_rx_db_lsb); 
      x_sram_data = _ms_register_rd(ms_rx_db_msb); 
    } 
#else // unrolled loop is a little faster, but eats more code space 
    for (i=0; i<64; i++) 
    { 
      if (!_ms_rx_data_fifo_is_full()) 
        if (k_success != ms_wait_fifo_with_timeout(ms_fifo_stat_r_buf_f, ms_fifo_stat_r_buf_f, 10)) 
          return k_error; 
      x_sram_data = _ms_register_rd(ms_rx_db_lsb); 
      x_sram_data = _ms_register_rd(ms_rx_db_msb); 
      x_sram_data = _ms_register_rd(ms_rx_db_lsb); 
      x_sram_data = _ms_register_rd(ms_rx_db_msb); 
      x_sram_data = _ms_register_rd(ms_rx_db_lsb); 
      x_sram_data = _ms_register_rd(ms_rx_db_msb); 
      x_sram_data = _ms_register_rd(ms_rx_db_lsb); 
      x_sram_data = _ms_register_rd(ms_rx_db_msb); 
    } 
#endif 
  } 
  else 
  { 
    uint8 xdata* bufp = _media_data(sector_buffer); 
 
    // there are always 256 words of device data 
#if 1 // opt for improved code density over execution speed since only called on insertion 
    for (i=0; i<256; i++) 
    { 
      if (_ms_rx_data_fifo_is_empty()) 
        if (k_success != ms_wait_fifo_with_timeout(ms_fifo_stat_r_buf_e, 0, 3)) 
          return k_error; 
      *bufp++ = _ms_register_rd(ms_rx_db_lsb); 
      *bufp++ = _ms_register_rd(ms_rx_db_msb); 
    } 
#else // unrolled loop is a little faster, but eats more code space 
    #if 0 
    for (i=0; i<64; i++) 
    { 
      if (!_ms_rx_data_fifo_is_full()) 
        if (k_success != ms_wait_fifo_with_timeout(ms_fifo_stat_r_buf_f, ms_fifo_stat_r_buf_f, 10)) 
          return k_error; 
      *bufp++ = _ms_register_rd(ms_rx_db_lsb); 
      *bufp++ = _ms_register_rd(ms_rx_db_msb); 
      *bufp++ = _ms_register_rd(ms_rx_db_lsb); 
      *bufp++ = _ms_register_rd(ms_rx_db_msb); 
      *bufp++ = _ms_register_rd(ms_rx_db_lsb); 
      *bufp++ = _ms_register_rd(ms_rx_db_msb); 
      *bufp++ = _ms_register_rd(ms_rx_db_lsb); 
      *bufp++ = _ms_register_rd(ms_rx_db_msb); 
    } 
    #else // unroll it more 
    for (i=0; i<32; i++) 
    { 
      if (!_ms_rx_data_fifo_is_full()) 
        if (k_success != ms_wait_fifo_with_timeout(ms_fifo_stat_r_buf_f, ms_fifo_stat_r_buf_f, 10)) 
          return k_error; 
      *bufp++ = _ms_register_rd(ms_rx_db_lsb); 
      *bufp++ = _ms_register_rd(ms_rx_db_msb); 
      *bufp++ = _ms_register_rd(ms_rx_db_lsb); 
      *bufp++ = _ms_register_rd(ms_rx_db_msb); 
      *bufp++ = _ms_register_rd(ms_rx_db_lsb); 
      *bufp++ = _ms_register_rd(ms_rx_db_msb); 
      *bufp++ = _ms_register_rd(ms_rx_db_lsb); 
      *bufp++ = _ms_register_rd(ms_rx_db_msb); 
      *bufp++ = _ms_register_rd(ms_rx_db_lsb); 
      *bufp++ = _ms_register_rd(ms_rx_db_msb); 
      *bufp++ = _ms_register_rd(ms_rx_db_lsb); 
      *bufp++ = _ms_register_rd(ms_rx_db_msb); 
      *bufp++ = _ms_register_rd(ms_rx_db_lsb); 
      *bufp++ = _ms_register_rd(ms_rx_db_msb); 
      *bufp++ = _ms_register_rd(ms_rx_db_lsb); 
      *bufp++ = _ms_register_rd(ms_rx_db_msb); 
    } 
    #endif 
#endif 
  } 
  return k_success; 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   ms_pio_read_page 
// 
// Declaration: 
//   t_result ms_pio_read_page(uint8 pnr); 
// 
// Purpose: 
//   Read a page (512 byte disk sector) from the MS card via pio and store the data 
//   into either a packet buffer or an xdata buffer. 
// 
// Arguments: 
//   pnr - the packet buffer number.  any value greater than or equal to k_max_pnr 
//   will cause the data to be stored into xdata at _media_data(sector_buffer). 
// 
// Return: 
//   A t_result indicating: 
//     k_success - data read and stored. 
//     k_error   - cannot read data. 
//                 contents of target buffer undefined. 
// 
// Notes: 
//   This is a FUNCTION, not a DFA. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
t_result ms_pio_read_page(uint8 pnr) reentrant 
{ 
  trace1(0, ms, 0, "ms_pio_read_page():%d", g_ms_page); 
  //_stack_check(); 
  if (k_success != ms_write_parm_reg(0x80, 0x20)) 
    return k_error; 
  if (k_success != ms_set_cmd(k_ms_block_read)) 
    return k_error; 
  if (k_success != ms_get_int(5)) 
    return k_error; 
  if (_factor & kbm_ms_reg_int_cmdnk) 
  { 
    trace0(0, err, 110, "error: cmdnk (ms_pio_read_page)"); 
    return k_error; 
  } 
  if (!(_factor & kbm_ms_reg_int_ced)) 
  { 
    trace0(0, err, 110, "error: ced not set (ms_pio_read_page)"); 
    return k_error; 
  } 
  if (_factor & kbm_ms_reg_int_breq) 
  { 
    trace0(0, ms, 110, "breq (ms_pio_read_page)"); 
    if (_factor & kbm_ms_reg_int_err) 
    { 
      trace0(0, err, 110, "error: err (ms_pio_read_page)"); 
      if (k_success != ms_read_status()) 
        return k_error; 
//      if ((_status1 & (kbm_ms_reg_status1_ucdt |kbm_ms_reg_status1_ucex |kbm_ms_reg_status1_ucfg)) == 
//          (kbm_ms_reg_status1_ucdt |kbm_ms_reg_status1_ucex |kbm_ms_reg_status1_ucfg)) 
      if (_status1 & (kbm_ms_reg_status1_ucdt |kbm_ms_reg_status1_ucex |kbm_ms_reg_status1_ucfg)) 
      { 
        trace0(0, err, 110, "error: ucdt&ucex&ucfg - uncorrectable flash read error (ms_pio_read_page)"); 
        ms_media_set_phyblock_failed(); 
        return k_error; 
      } 
    } 
    if (k_success != ms_read_extra_data_reg()) 
      return k_error; 
    if (k_success != ms_read_page_data(pnr)) 
      return k_error; 
    return k_success; 
  } 
  trace0(0, err, 110, "error: unrecognized interrupt factor (ms_pio_read_page)"); 
  return k_error; 
} 
 
//------------------------------------------------------------------------------ 
// Name: 
//   ms_write_all_reg 
// 
// Declaration: 
//   t_result ms_write_all_reg(uint8 sys_parm, uint8 cmd_parm); 
// 
// Purpose: 
//   Write the all of the of the MS card in preparation for issuing a 
//   write flash command.  Writes the address, sys_parm, cmd_parm, page, 
//   and extra data. 
// 
// Arguments: 
//   sys_parm - specific to the flash command about to be issued. 
//   cmd_parm - specific to the flash command about to be issued. 
// 
//   The following global variables are accessed and delivered to the MS card: 
//     g_ms_blk_addr_msb 
//     g_ms_blk_addr_mid 
//     g_ms_blk_addr_lsb 
//     g_ms_page 
//     g_ms_extra 
// 
// Return: 
//   A t_result indicating: 
//     k_success - parameter registers are written. 
//     k_error   - failed write parameter registers.  actual contents unknown. 
// 
// Notes: 
//   This is a FUNCTION, not a DFA. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
t_result ms_write_all_reg(uint8 sys_parm, 
                          uint8 cmd_parm) reentrant; 
t_result ms_write_all_reg(uint8 sys_parm, 
                          uint8 cmd_parm) reentrant 
{ 
  trace0(0, ms, 0, "ms_write_all_reg()"); 
  //_stack_check(); 
  trace3(0, ms, 10, " addr:0x00%02X%02X%02X", g_ms_blk_addr_msb, g_ms_blk_addr_mid, g_ms_blk_addr_lsb); 
  trace3(0, ms, 10, " page:0x%02X sys_parm:0x%02X cmd_parm:0x%02X", g_ms_page, sys_parm, cmd_parm); 
  if (k_success != ms_set_write_reg_addrs(k_ms_parm_system, 10)) 
    return k_error; 
  // write the tpc 
  if (k_success != ms_set_tpc(k_ms_tpc_wr_reg, 10)) 
    return k_error; 
  // write the parm regs 
  _ms_register_wr(ms_tx_db_msb, g_ms_blk_addr_msb); 
  _ms_register_wr(ms_tx_db_lsb, sys_parm); 
  if (!_ms_tx_data_fifo_is_empty()) 
    if (k_success != ms_wait_fifo_with_timeout(ms_fifo_stat_t_buf_e, ms_fifo_stat_t_buf_e, 3)) 
      return k_error; 
  _ms_register_wr(ms_tx_db_msb, g_ms_blk_addr_lsb); 
  _ms_register_wr(ms_tx_db_lsb, g_ms_blk_addr_mid); 
  if (!_ms_tx_data_fifo_is_empty()) 
    if (k_success != ms_wait_fifo_with_timeout(ms_fifo_stat_t_buf_e, ms_fifo_stat_t_buf_e, 3)) 
      return k_error; 
  _ms_register_wr(ms_tx_db_msb, g_ms_page); 
  _ms_register_wr(ms_tx_db_lsb, cmd_parm); 
  if (!_ms_tx_data_fifo_is_empty()) 
    if (k_success != ms_wait_fifo_with_timeout(ms_fifo_stat_t_buf_e, ms_fifo_stat_t_buf_e, 3)) 
      return k_error; 
  // write the extra data 
  trace4(0, ms, 10, "wr xtra[0..3]:%02X%02X%02X%02X", g_ms_extra[0], g_ms_extra[1], g_ms_extra[2], g_ms_extra[3]); 
  _ms_register_wr(ms_tx_db_msb, g_ms_extra[1]); 
  _ms_register_wr(ms_tx_db_lsb, g_ms_extra[0]); 
  if (!_ms_tx_data_fifo_is_empty()) 
    if (k_success != ms_wait_fifo_with_timeout(ms_fifo_stat_t_buf_e, ms_fifo_stat_t_buf_e, 3)) 
      return k_error; 
  _ms_register_wr(ms_tx_db_msb, g_ms_extra[3]); 
  _ms_register_wr(ms_tx_db_lsb, g_ms_extra[2]); 
  if (!_ms_tx_data_fifo_is_empty()) 
    if (k_success != ms_wait_fifo_with_timeout(ms_fifo_stat_t_buf_e, ms_fifo_stat_t_buf_e, 3)) 
      return k_error; 
  return k_success; 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   ms_write_extra_data 
// 
// Declaration: 
//   t_result ms_write_extra_data(void); 
// 
// Purpose: 
//   Write the extra data registers of the active block of the MS card from 
//   the global variable g_ms_extra. 
// 
// Arguments: 
//   None. 
// 
// Return: 
//   A t_result indicating: 
//     k_success - contents of the global variable g_ms_extra written to card. 
//     k_error   - cannot write the extra data. 
// 
// Notes: 
//   This is a FUNCTION, not a DFA. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
t_result ms_write_extra_data(void) reentrant; 
t_result ms_write_extra_data() reentrant 
{ 
  trace4(0, ms, 0, "ms_write_extra_data(addr:0x00%02X%02X%02X page:%d)", g_ms_blk_addr_msb, g_ms_blk_addr_mid, g_ms_blk_addr_lsb, g_ms_page); 
  //_stack_check(); 
  if (k_success != ms_write_all_reg(0x80, 0x40)) 
    return k_error; 
  if (k_success != ms_set_cmd(k_ms_block_write)) 
    return k_error; 
  if (k_success != ms_get_int(10)) 
    return k_error; 
  if (_factor & kbm_ms_reg_int_cmdnk) 
  { 
    trace0(0, err, 110, "error: cmdnk (ms_write_extra_data)"); 
    return k_error; 
  } 
  if (!(_factor & kbm_ms_reg_int_ced)) 
  { 
    trace0(0, err, 110, "error: ced not set (ms_write_extra_data)"); 
    return k_error; 
  } 
  if (_factor & kbm_ms_reg_int_err) 
  { 
    trace0(0, err, 110, "error: !ced&err (ms_write_extra_data)"); 
    return k_error; 
  } 
  return k_success; 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   ms_erase_block 
// 
// Declaration: 
//   t_result ms_erase_block(void); 
// 
// Purpose: 
//   Erase he active block in the MS card. 
// 
// Arguments: 
//   None. 
// 
// Return: 
//   A t_result indicating: 
//     k_success - block is erased. 
//     k_error   - cannot erase block.  contents undefined. 
// 
// Notes: 
//   This is a FUNCTION, not a DFA. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
t_result ms_erase_block() reentrant 
{ 
  //_profile_on(7); 
  trace0(0, ms, 80, "ms_erase_block()"); 
  //_stack_check(); 
  //_profile_on(6); 
  if (k_success != ms_write_parm_reg(0x80, 0x00)) 
    return k_error; 
  //_profile_off(6); 
  //_profile_on(5); 
  if (k_success != ms_set_cmd(k_ms_block_erase)) 
    return k_error; 
  //_profile_off(5); 
  //_profile_on(4); 
  if (k_success != ms_get_int(100)) 
    return k_error; 
  //_profile_off(4); 
  if (_factor & kbm_ms_reg_int_cmdnk) 
  { 
    trace0(0, err, 110, "error: cmdnk (ms_erase_block)"); 
    return k_error; 
  } 
  if (!(_factor & kbm_ms_reg_int_ced)) 
  { 
    trace0(0, err, 110, "error: ced not set (ms_erase_block)"); 
    return k_error; 
  } 
  if (_factor & kbm_ms_reg_int_err) 
  { 
    trace0(0, err, 110, "error: err (ms_erase_block)"); 
    return k_error; 
  } 
  //_profile_off(7); 
  return k_success; 
} 
 
 
//------------------------------------------------------------------------------ 
// steal a buffer from media.c temporarily (its ok, media.c only uses it for copy-sector). 
// actually now that copy sector is done via fmc its probably not used for that purpose 
// on the other luns anymore either. 
//------------------------------------------------------------------------------ 
extern xdata uint8  g_sector_buffer[]; 
void xbuf_rd(uint16 offset, uint16 len, t_memory_ref bufp) reentrant 
{ 
  t_xdata_ref srcbufp; 
  mcu_begin_critical_section(); 
  trace0(0, hal, 0, "mmu_rd()"); 
  srcbufp = g_sector_buffer;  // shouldnt this be _media_data(sector_buffer) ??? 
  srcbufp += offset; 
  for (; len; len--, *bufp++=*srcbufp++); 
  mcu_end_critical_section(); 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//  ms_read_boot_block 
// 
// Declaration: 
//   t_result ms_read_boot_block(void); 
// 
// Purpose: 
//   Determine the parameters (capacity etc) of the card by examining the contents 
//   of the boot block.  Configures the _media_data() settings for the media based 
//   of the contents of the boot block. 
// 
// Arguments: 
//   None. 
// 
// Return: 
//   A t_result indicating: 
//     k_success - boot block read, _media_data() configured. 
//     k_error   - cannot read boot block.  contents of _media_data() undefined. 
// 
// Notes: 
//   This is a FUNCTION, not a DFA. 
// 
//   Boot Block Format: 
//    page 0: header (368 bytes) 
//            system entry (48 bytes) 
//            boot and attribute info (96 bytes) 
//    page 1: disabled block data 
//    page 2: card info struct (256 byets) 
//            identification info (256 bytes) 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
 
t_result ms_read_boot_block(void) reentrant; 
t_result ms_read_boot_block() reentrant 
{ 
  uint8 blk; 
  //uint8 pnr; 
  uint8 boot_count; 
  t_result rslt; 
 
  trace0(0, ms, 80, "ms_read_boot_block()"); 
  _force_one_burst_per_split = k_no;  // br308 
  //_stack_check(); 
  //pnr = mmu_allocate(); 
 
  // no media/mapper optimizations at this time 
  _media_data(options) = kbm_media_data_opt_none; 
  _media_data(sector_buffer) = g_sector_buffer;  // sneakily share media.c's buffer 
  boot_count = 0; 
  _media_data(boot_block) = 0xFF; 
  for (blk=0; blk<11; blk++) 
  { 
    trace1(0, ms, 80, "checking block %d", blk); 
    // read page 0 of the block 
    g_ms_blk_addr_msb = 0; 
    g_ms_blk_addr_mid = 0; 
    g_ms_blk_addr_lsb = blk; 
    g_ms_page = 0; 
    if (k_success != (rslt = ms_pio_read_page(k_max_pnr))) 
    { 
      trace0(0, err, 80, "error: cant read page zero of this block"); 
      continue; 
      //goto exit; 
    } 
    // block status must be set; e.g., 'ok' 
    if (!(g_ms_extra[k_ovwr_flg_offset] & kbm_ms_ext_ovwr_bkst)) 
    { 
      trace2(0, ms, 80, "blk:%d bkst:%02X", blk, g_ms_extra[k_ovwr_flg_offset] & kbm_ms_ext_ovwr_bkst); 
      continue; 
    } 
    // system bit must be clear; e.g., 'boot' 
    if (g_ms_extra[k_mgmt_flg_offset] & kbm_ms_ext_mgmt_sysflg) 
    { 
      trace2(0, ms, 80, "blk:%d sysflg:%02X", blk, g_ms_extra[k_mgmt_flg_offset] & kbm_ms_ext_mgmt_sysflg); 
      continue; 
    } 
    // top word of page 0 must be 0x0001 
    xbuf_rd(0, 2, (uint8*)&g_wtmp); 
    if (0x0001 != g_wtmp) 
    { 
      trace2(0, ms, 80, "blk:%d top word:%04X", blk, g_wtmp); 
      //dbg_dumpram(k_pkt_addrlo[pnr], k_pkt_addrhi[pnr], 512); 
      continue; 
    } 
    // found the boot block 
    boot_count++; 
    trace1(0, ms, 80, "BOOT BLOCK:%d", blk); 
    if (_media_data(boot_block) == 0xFF) 
    { 
      // if more than one boot block is found then choose the lowest numbered 
      trace0(0, ms, 80, "INSTALLING BOOT BLOCK INTO TABLE"); 
      _media_data(boot_block) = blk; 
    } 
    trace1(0, ms, 80, "BOOT AREA MAX BLK NOW:%d", blk); 
    g_ms_user_area_start_blk=blk+1; 
 
    trace0(0, ms, 80, "PAGE 0 (HEADER)"); 
    // block size? 
    xbuf_rd(418, 1, &(_ms_byte_per_block.u8.hi)); 
    xbuf_rd(419, 1, &(_ms_byte_per_block.u8.lo)); 
    _ms_byte_per_block.u16 *= 1024; 
    trace1(0, ms, 80, "_ms_byte_per_block:%d", _ms_byte_per_block.u16); 
    // number of blocks? 
    xbuf_rd(420, 1, &(_ms_block_per_card.u8.hi)); 
    xbuf_rd(421, 1, &(_ms_block_per_card.u8.lo)); 
    trace1(0, ms, 80, "_ms_block_per_card:%d", _ms_block_per_card.u16); 
    _media_data(num_zones) = _ms_block_per_card.u16 / k_phy_block_per_segment; 
    trace1(0, ms, 80, "_ms_zone_per_card:%d", _media_data(num_zones)); 
    _media_data(physical_blocks_per_zone) = k_phy_block_per_segment; 
    _media_data(logical_blocks_per_zone) = k_log_block_per_segment; 
    _media_data(logical_blocks_per_boot_zone) = k_log_block_per_boot_segment; 
    // pages per block? 
    _ms_page_per_block = _ms_byte_per_block.u16 / 512; 
    trace1(0, ms, 80, "_ms_page_per_block:%d", _ms_page_per_block); 
    _media_data(pages_per_block) =  _ms_page_per_block; 
    // 1 segment (sector) per page 
    _media_data(segments_per_page) = 1 ; 
 
    // ASSEMBLER: 
    //  SanDisk 01 (sampled 15 cards) 
    //  Lexar   02, 03 (sampled 15 cards) 
    //  Sony    01 (sampled 4 cards) 
    //  Apacer  01 (sampled 1 card) 
#if 0 
    { 
      uint8 assembler; 
      t_udw32 assembly; 
      xbuf_rd(440, 1, &(assembler)); 
      trace1(0, ms, 80, "assembler code:%d", assembler); 
      xbuf_rd(441, 80, &(assembly.u8.lh)); 
      xbuf_rd(442, 80, &(assembly.u8.hl)); 
      xbuf_rd(443, 80, &(assembly.u8.lo)); 
      trace3(0, ms, 80, "assembly model code:00%02X%02X%02X", assembly.u8.lh, assembly.u8.hl, assembly.u8.lo); 
    } 
#endif 
 
// this block is only enabled to facilitate debugging the SanDisk 64MB issue... 
#if 1  // this is currently unused by the mapper 
 
    // found the disabled block data 
    g_ms_page = 1; 
    if (k_success != (rslt = ms_pio_read_page(k_max_pnr))) 
    { 
      trace0(0, err, 80, "error: cant read page one of this boot block"); 
      continue; 
      //goto exit; 
    } 
    trace0(0, ms, 80, "PAGE 1 (DISABLED BLOCK DATA)"); 
    { 
      t_uw16 x; 
      uint8 i; 
      for (i=0; i<255; i++) 
      { 
#if 0 
        xbuf_rd(i*2, 1, &x.u8.lo);             // big endian 
        xbuf_rd(i*2+1, 1, &x.u8.hi); 
#else 
        xbuf_rd(i*2, 1, &x.u8.hi);             // big endian 
        xbuf_rd(i*2+1, 1, &x.u8.lo); 
#endif 
        // stop at the end of the list 
        _ms_bad_blk_tbl[i] = x.u16; 
        if (x.u16 == 0xFFFF) 
          break; 
        if (i) 
        { 
          trace1(0, ms, 80, "bad block:%d", x.u16); 
        } 
        else 
        { 
          trace1(0, ms, 80, "information block:%d", x.u16); 
        } 
        trace2(0, ms, 80, "  (zone:%d phy:%d)", x.u16/k_phy_block_per_segment, x.u16%512); 
        if (x.u16/k_phy_block_per_segment > _media_data(num_zones)) 
        { 
          trace0(0, ms, 80, "  (off the end of the card - meaningless)"); 
          continue; 
        } 
        g_ms_blk_addr_msb = 0; 
        g_ms_blk_addr_mid = 0; 
        g_ms_blk_addr_lsb = x.u16; 
        g_ms_page = 0; 
        if (k_success != ms_read_extra_data_reg()) 
        { 
          trace0(0, err, 80, "  error: can't read xtra data"); 
        } 
        else 
        { 
          trace4(0, ms, 80, "  xtra[0..3]:%02X%02X%02X%02X", g_ms_extra[0], g_ms_extra[1], g_ms_extra[2], g_ms_extra[3]); 
          // block status of 0 is bad 
          if (!(g_ms_extra[k_ovwr_flg_offset] & kbm_ms_ext_ovwr_bkst)) 
          { 
            trace0(0, ms, 80, "  bad block by bkst - mapper will detect"); 
          } 
          else 
          { 
            // if this is seen for VALID bad blocks then ms_media_is_phyblock_ok 
            // needs to refer to the bad block table before rendering its decision 
            trace0(0, ms, 80, "  good block by bkst - mapper needs assist to detect"); 
          } 
        } 
      } 
      //fmc_dbg_dump_sector((uint8*)_ms_bad_blk_tbl); 
    } 
    g_ms_blk_addr_msb = 0; 
    g_ms_blk_addr_mid = 0; 
    g_ms_blk_addr_lsb = blk; 
#endif 
 
    // found the cis/idi page 
    g_ms_page = 2; 
    if (k_success != (rslt = ms_pio_read_page(k_max_pnr))) 
    { 
      trace0(0, err, 80, "error: cant read page two of this boot block"); 
      continue; 
      //goto exit; 
    } 
    trace0(0, ms, 80, "PAGE 2 (CIS/IDI)"); 
#if 0  // this is debug stuff 
    //dbg_dumpram(k_pkt_addrlo[pnr], k_pkt_addrhi[pnr], 512); 
    { 
      t_uw16 x, y; 
      trace0(0, ms, 0, "=====STUFF FROM THE IDI====="); 
      xbuf_rd(258, 1, &x.u8.lo); 
      xbuf_rd(259, 1, &x.u8.hi); 
      trace1(0, ms, 80, "cylinders per head:%d", x.u16); 
 
      xbuf_rd(262, 1, &x.u8.lo); 
      xbuf_rd(263, 1, &x.u8.hi); 
      trace1(0, ms, 80, "heads per disk:%d", x.u16); 
 
      xbuf_rd(264, 1, &x.u8.lo); 
      xbuf_rd(265, 1, &x.u8.hi); 
      trace1(0, ms, 80, "bytes per track:%d", x.u16); 
 
      xbuf_rd(266, 1, &x.u8.lo); 
      xbuf_rd(267, 1, &x.u8.hi); 
      trace1(0, ms, 80, "bytes per sector:%d", x.u16); 
 
      xbuf_rd(268, 1, &x.u8.lo); 
      xbuf_rd(269, 1, &x.u8.hi); 
      trace1(0, ms, 80, "sectors per track:%d", x.u16); 
 
      xbuf_rd(364, 1, &x.u8.lo); 
      xbuf_rd(365, 1, &x.u8.hi); 
      trace1(0, ms, 80, "current cylinders per head:%d", x.u16); 
 
      xbuf_rd(366, 1, &x.u8.lo); 
      xbuf_rd(367, 1, &x.u8.hi); 
      trace1(0, ms, 80, "current heads per disk:%d", x.u16); 
 
      xbuf_rd(368, 1, &x.u8.lo); 
      xbuf_rd(369, 1, &x.u8.hi); 
      trace1(0, ms, 80, "sectors per track:%d", x.u16); 
 
      xbuf_rd(264, 1, &x.u8.lo); 
      xbuf_rd(265, 1, &x.u8.hi); 
      xbuf_rd(264, 1, &y.u8.lo); 
      xbuf_rd(265, 1, &y.u8.hi); 
      trace2(0, ms, 80, "current capacity in sectors:0x%04X%04X", x.u16, y.u16); 
    } 
#endif 
 
#if 0 
    // firmware version 
    { 
      t_udw32 fwlo; 
      t_udw32 fwhi; 
      xbuf_rd(302, 1, &(fwhi.u8.hi)); 
      xbuf_rd(303, 1, &(fwhi.u8.lh)); 
      xbuf_rd(304, 1, &(fwhi.u8.hl)); 
      xbuf_rd(305, 1, &(fwhi.u8.lo)); 
      xbuf_rd(306, 1, &(fwlo.u8.hi)); 
      xbuf_rd(307, 1, &(fwlo.u8.lh)); 
      xbuf_rd(308, 1, &(fwlo.u8.hl)); 
      xbuf_rd(309, 1, &(fwlo.u8.lo)); 
      trace4(0, ms, 80, "fwver:%02X%02X%02X%02X", fwhi.u8.hi, fwhi.u8.lh, fwhi.u8.hl, fwhi.u8.lo); 
      trace4(0, ms, 80, "      %02X%02X%02X%02X", fwlo.u8.hi, fwlo.u8.lh, fwlo.u8.hl, fwlo.u8.lo); 
    } 
#endif 
 
    // total logical sectors? 
    xbuf_rd(270, 1, &(_lun_data(capacity).lba_max.u8.lh)); 
    xbuf_rd(271, 1, &(_lun_data(capacity).lba_max.u8.hi)); 
    xbuf_rd(272, 1, &(_lun_data(capacity).lba_max.u8.lo)); 
    xbuf_rd(273, 1, &(_lun_data(capacity).lba_max.u8.hl)); 
    _lun_data(capacity).lba_max.u32 -= 1; 
    trace2(0, ms, 80, "Total User-addressable Sectors: %04X%04X", _hw(_lun_data(capacity).lba_max.u32), _lw(_lun_data(capacity).lba_max.u32)); 
    _lun_data(capacity).lba_sz.u32 = 512; 
  } 
  //get_out: 
  rslt = k_error; 
  if (boot_count) 
  { 
    g_addr_zone = 0; 
    rslt = k_success; 
  } 
  //exit: 
  trace1(0, ms, 80, "found %d boot blocks", boot_count); 
  trace1(0, ms, 80, "user area starts at block %d", g_ms_user_area_start_blk); 
  return rslt; 
} 
 
//------------------------------------------------------------------------------ 
t_result ms_find_map_block(void) reentrant; 
t_result ms_find_map_block() reentrant 
{ 
  uint16 blk; 
  uint8 zone; 
  uint8 old_zone; 
  t_result rslt; 
 
  trace0(0, ms, 80, "ms_find_map_block()"); 
  trace1(0, ms, 80, "current zone:%d", g_addr_zone); 
  rslt = k_error; 
  zone = _media_data(num_zones) - 1; 
  old_zone = g_addr_zone; 
  g_addr_zone = zone; 
  set_rd_phyblk(); 
  g_ms_page = 0; 
  for (blk=0; blk<512; blk++) 
  { 
    trace2(0, ms, 80, "checking block %d of zone:%d", blk, zone); 
    // read page 0 of the block 
    if (k_success != (rslt = ms_pio_read_page(k_max_pnr))) 
    { 
      trace1(0, err, 80, "error: cant read page zero of block:%d", blk); 
      continue; 
    } 
    // is it the log2phy map table block?  if yes it should get whacked. 
    if (g_ms_extra[k_mgmt_flg_offset] & kbm_ms_ext_mgmt_atflg) 
    { 
      continue; 
    } 
    // found the map block 
    trace0(0, ms_media, 110, "alert: ms_media_is_phyblock_ok() - table block found"); 
    trace1(0, ms, 80, "atflg:%02X", g_ms_extra[k_mgmt_flg_offset] & kbm_ms_ext_mgmt_atflg); 
    trace1(0, ms, 80, "MAP BLOCK:%d", blk); 
    fmc_dbg_dump_sector((uint8*)g_sector_buffer); 
    rslt = k_success; 
  } 
  g_addr_zone = old_zone; 
  trace1(0, ms, 80, "restored zone:%d", g_addr_zone); 
  return rslt; 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   ms_reset_controller 
// 
// Declaration: 
//   void ms_reset_controller(void); 
// 
// Purpose: 
//   Reset the MSC block of the 210. 
// 
// Arguments: 
//   None. 
// 
// Return: 
//   None. 
// 
// Notes: 
//   This is a FUNCTION, not a DFA. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
void ms_reset_controller() reentrant 
{ 
  uint8 loop; 
 
  trace0(0, ms, 0, "ms_reset_controller()"); 
  //_stack_check(); 
  ms_mode_ctl |= kbm_ms_mode_ctl_msc_rst; 
  loop = 0xff; 
  while (loop--); 
  ms_mode_ctl &= ~kbm_ms_mode_ctl_msc_rst; 
  _mcu_register_set_bits(x_fmc_clk_ctl, k_fmc_clk_ctl_ms_20mhz); 
  _ms_register_set_bits(ms_mode_ctl, kbm_ms_mode_ctl_sien); 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   ms_initialize_controller 
// 
// Declaration: 
//   void ms_initialize_controller(void); 
// 
// Purpose: 
//   Initialize the MSC block of the 210. 
// 
// Arguments: 
//   None. 
// 
// Return: 
//   None. 
// 
// Notes: 
//   This is a FUNCTION, not a DFA. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
void ms_initialize_controller() reentrant 
{ 
  trace0(0, ms, 1, "ms_initialize_controller()"); 
  //_stack_check(); 
  //strncpy(_lun_data(k_lun_ms, device_id), "MEMORY STICK", k_lun_max_devid_sz); 
  memcpy(_lun_data(device_id), "MS", k_lun_max_devid_sz); 
  _lun_data(device_type) = k_device_type_ms; 
  _lun_data(max_lb_per_burst) = 1; 
  _mcu_register_set_bits(x_fmc_clk_ctl, k_fmc_clk_ctl_ms_20mhz); 
  _ms_register_set_bits(ms_mode_ctl, kbm_ms_mode_ctl_sien); 
  media_set_active(k_ix_media_ms); 
  _media_data(log2phy_map) = (t_log2phy_map_ref)_ms_log2phy; 
  _media_data(assign_map)  = (t_assign_map_ref)_ms_assign_map; 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   dfa_ms_reset_media 
// 
// Declaration: 
//   void dfa_ms_reset_media(void); 
// 
// Purpose: 
//   Deliver a reset command to the MS card. 
// 
// Arguments: 
//   None. 
// 
// Return: 
//   None. 
// 
// Notes: 
//   This is a DFA, not a FUNCTION. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
void dfa_ms_reset_media() reentrant 
{ 
  trace0(0, ms, 0, "dfa_ms_reset_media()"); 
  //_stack_check(); 
  // must wait at least 1 msec after por before issuing commands to ms 
  thread_set_timer(2); 
  while (!thread_got_sync(kbm_sync_timer)); 
  // issue the reset 
  ms_set_cmd(k_ms_reset); 
  _thread_return_dfa(k_success); 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   ms_initialize_media2 
// 
// Declaration: 
//   void ms_initialize_media2(void); 
// 
// Purpose: 
//   After the media has received a reset command, and then at least 1msec has 
//   expired, clear the media changed flag so that the device layer will not reast 
//   and initialize the card an more and will instead attempt to identify the media. 
// 
// Arguments: 
//   None 
// 
// 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 a state in a DFA, not a FUNCTION. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
void ms_initialize_media2(void) reentrant; 
void ms_initialize_media2() reentrant 
{ 
  trace0(0, ms, 1, "ms_initialize_media2()"); 
  //_stack_check(); 
  _lun_data(media) &= ~kbm_lun_media_changed; 
  // house keeping 
  _thread_return_dfa(k_success); 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   dfa_ms_initialize_media 
// 
// Declaration: 
//   void dfa_ms_initialize_media(void); 
// 
// Purpose: 
//   After the media has received a reset command wait at least 1msec. 
// 
// Arguments: 
//   None 
// 
// 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 entry state of a DFA, not a FUNCTION. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
void dfa_ms_initialize_media() reentrant 
{ 
  trace0(0, ms, 1, "dfa_ms_initialize_media()"); 
  //_stack_check(); 
  // wait at least 1 millisecond before accessing the device 
  thread_set_timer(2); 
  thread_yield_ex(kbm_sync_none, kbm_sync_timer, ms_initialize_media2); 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   dfa_ms_identify_media 
// 
// Declaration: 
//   void dfa_ms_identify_media(void); 
// 
// Purpose: 
//   Determine the geometry of the MS card that has just been inserted, reset, 
//   and initialized.  Also checks the write rotect status of the card. 
// 
// Arguments: 
//   None. 
// 
//   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 entry state of a DFA, not a FUNCTION. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
extern t_result ms_media_erase_card() reentrant; 
extern void util_safe_erase_media() reentrant; 
 
void dfa_ms_identify_media() reentrant 
{ 
  trace0(0, ms, 1, "dfa_ms_identify_media()"); 
  //_stack_check(); 
  // locate the boot block and extract relevant parameters 
  if (k_success != ms_read_boot_block()) 
  { 
    trace0(0, err, 110, "error: boot blocks not found (dfa_ms_identify_media)"); 
    _thread_return_dfa(k_error); 
  } 
 
  // ms_find_map_block(); 
 
#ifdef k_memory_stick_eraser 
  util_safe_erase_media() ; 
#endif 
 
  // check the write protect status 
  if (k_success != ms_read_status()) 
  { 
    trace0(0, err, 110, "warning: read_status failed"); 
  } 
  if (_status0 & kbm_ms_reg_status0_wp) 
  { 
    trace0(0, ms, 10, "write protected"); 
    _lun_data(media) |= kbm_lun_media_wrprot; 
  } 
  else 
  { 
    trace0(0, ms, 10, "writeable"); 
    _lun_data(media) &= ~kbm_lun_media_wrprot; 
  } 
  if (k_success != map_build_sector_map()) 
  { 
    _thread_return_dfa(k_error); 
  } 
  // media is now known 
  _lun_data(media) &= ~kbm_lun_media_unknown; 
  _lun_data(sensep) = &sense_media_change; 
  _thread_return_dfa(k_success); 
} 
 
//------------------------------------------------------------------------------ 
//------------------------------------------------------------------------------ 
// the fmc transfer callbacks 
//------------------------------------------------------------------------------ 
//------------------------------------------------------------------------------ 
t_result ms_read_begin_xfer(void) reentrant; 
t_result ms_read_begin_split(void) reentrant; 
t_result ms_read_begin_burst(void) reentrant; 
t_result ms_read_end_burst(void) reentrant; 
t_result ms_read_end_split(void) reentrant; 
t_result ms_read_end_xfer(void) reentrant; 
 
//#define k_dbg 
#ifdef k_dbg 
xdata uint8 dbg_split; 
xdata uint8 dbg_burst; 
#endif 
 
//+----------------------------------------------------------------------------- 
//------------------------------------------------------------------------------ 
// profile: 
//   ms_read_begin_xfer 335usec to 560usec 
//   mapping            298usec to 522usec 
t_result ms_read_begin_xfer() reentrant 
{ 
  uint32 split_sz; 
 
  //_profile_on(7); 
  //TRACE0(142, ms, 10, "ms_read_begin_xfer()"); 
  trace4(0, ms, 10, "ms_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())); 
  //_stack_check(); 
  _lun_data(sensep) = &sense_read_error; 
  _fmc_set_options(0); 
  //_profile_on(6); 
  if (k_success != map_lba2addr_rd(_fmc_get_start_lb_32())) 
    return k_error; 
  set_rd_phyblk(); 
  //_profile_off(6); 
  //if (map_is_addr_first_in_block()) 
  if (!g_addr_page) 
    split_sz = _min(_ms_page_per_block, _fmc_get_lb_count_32()); 
  else 
    split_sz = _min(_ms_page_per_block - g_addr_page, _fmc_get_lb_count_32()); 
  _lun_data(max_lb_per_split) = split_sz; 
 
#ifdef k_dbg 
  dbg_split = 0; 
  dbg_burst = 0; 
#endif 
  //_profile_off(7); 
  return k_success; 
} 
 
//+----------------------------------------------------------------------------- 
//------------------------------------------------------------------------------ 
t_result ms_read_begin_first_split(void) reentrant; 
t_result ms_read_begin_first_split() reentrant 
{ 
  trace0(0, ms, 1, "ms_read_begin_first_split()"); 
  //_stack_check(); 
  // cheat for speed 
  //_fmc_set_callback(ms_read_begin_xfer, ms_read_end_xfer, ms_read_begin_split, ms_read_end_split, ms_read_begin_burst, NULL, ms_read_end_burst); 
  g_fmc_begin_split_callback = (t_fmc_callback)(ms_read_begin_split); 
  //return ms_read_issue_command(); 
  if (k_success != ms_write_parm_reg(0x80, 0x00)) 
    return k_error; 
 
#ifdef k_use_acd 
  //_mcu_register_wr(x_isr0, kbm_isr0_fmc_irq); 
  _ms_register_wr(ms_acd_bc, 2); 
  _ms_register_wr(ms_acd_ctl, kbm_ms_acd_ctl_adc); 
#endif 
 
  if (k_success != ms_set_cmd(k_ms_block_read)) 
    return k_error; 
  return k_success; 
} 
 
//+----------------------------------------------------------------------------- 
//------------------------------------------------------------------------------ 
// profile: 
//   ~490usec to ~750usec 
//   ~298usec to 565usec 
//   ~131usec 
//   ~58usec 
 
t_result ms_read_begin_split() reentrant 
{ 
  //_profile_on(7); 
  trace0(0, ms, 1, "ms_read_begin_split()"); 
  //_stack_check(); 
  //_profile_on(6); 
  if (k_success != map_lba2addr_rd(_fmc_get_start_lb_32())) 
    return k_error; 
  set_rd_phyblk(); 
  //_profile_off(6); 
  //return ms_read_issue_command(); 
  //_profile_on(5); 
  if (k_success != ms_write_parm_reg(0x80, 0x00)) 
    return k_error; 
  //_profile_off(5); 
#ifdef k_use_acd 
  //_mcu_register_wr(x_isr0, kbm_isr0_fmc_irq); 
  _ms_register_wr(ms_acd_bc, 2); 
  _ms_register_wr(ms_acd_ctl, kbm_ms_acd_ctl_adc); 
#endif 
 
  //_profile_on(4); 
  if (k_success != ms_set_cmd(k_ms_block_read)) 
    return k_error; 
  //_profile_off(4); 
  //_profile_off(7); 
  return k_success; 
} 
 
//+----------------------------------------------------------------------------- 
//------------------------------------------------------------------------------ 
// profile: 
//      ms_rd_begin_burst             201usec normal, 235usec w/ block_end 
//        ms_get_int                  133usec 
//        ms_set_tpc                  52usec 
//        block_end                   58usec 
 
t_result ms_read_begin_burst() reentrant 
{ 
  //_profile_on(7); 
  trace0(0, ms, 1, "ms_read_begin_burst()"); 
#ifdef k_dbg 
  trace3(0, ms, 1, "ms_read_begin_burst() - split:%d burst:%d page:%d", dbg_split, dbg_burst, g_ms_page); 
#endif 
  //_stack_check(); 
#ifdef k_use_acd 
  //while (!_thread_got_sync(kbm_sync_fmc_irq)); 
  //while (!_mcu_register_rd(x_isr0) & kbm_isr0_fmc_irq); 
  if (k_success != ms_get_int_acd()) 
    return k_error; 
#else 
  //_profile_on(6); 
  if (k_success != ms_get_int(5)) 
    return k_error; 
  //_profile_off(6); 
#endif 
 
  if (_factor & kbm_ms_reg_int_cmdnk) 
  { 
    trace0(0, err, 110, "error: cmdnk (ms_read_begin_burst)"); 
    return k_error; 
  } 
  if (_factor & (kbm_ms_reg_int_ced |kbm_ms_reg_int_breq |kbm_ms_reg_int_err) == 
      (kbm_ms_reg_int_ced |kbm_ms_reg_int_breq |kbm_ms_reg_int_err)) 
  { 
    trace0(0, err, 110, "alert: ced&breq&err"); 
    if (k_success != ms_read_status()) 
      return k_error; 
//    if ((_status1 & (kbm_ms_reg_status1_ucdt |kbm_ms_reg_status1_ucex |kbm_ms_reg_status1_ucfg)) == 
//        (kbm_ms_reg_status1_ucdt |kbm_ms_reg_status1_ucex |kbm_ms_reg_status1_ucfg)) 
    if (_status1 & (kbm_ms_reg_status1_ucdt |kbm_ms_reg_status1_ucex |kbm_ms_reg_status1_ucfg)) 
    { 
      trace0(0, err, 110, "error: ucdt&ucex&ucfg - uncorrectable flash read error (ms_read_begin_burst)"); 
      ms_media_set_phyblock_failed(); 
      return k_error; 
    } 
    trace0(0, err, 110, "warning: correctable flash read error (ms_read_begin_burst)"); 
  } 
 
  //------------------------------------------------------------------------------ 
  // removed to try and get reads a bit faster... why test for it if all you do is log trace? 
  //------------------------------------------------------------------------------ 
  //if (_factor & (kbm_ms_reg_int_breq |kbm_ms_reg_int_err) == 
  //    (kbm_ms_reg_int_breq |kbm_ms_reg_int_err)) 
  //{ 
  //  trace0(0, err, 0, "warning: correctable flash read error"); 
  //} 
  //------------------------------------------------------------------------------ 
 
  if (!(_factor & kbm_ms_reg_int_breq)) 
  { 
    trace0(0, err, 110, "error: breq not set (ms_read_begin_burst)"); 
    return k_error; 
  } 
  trace0(0, ms, 0, "breq"); 
 
  //------------------------------------------------------------------------------ 
  // this does not need to be done for every page even though the flowcharts in the 
  // ms spec indicate that it does.  removing this speeds up reads. 
  //------------------------------------------------------------------------------ 
  //if (k_success != ms_read_extra_data_reg()) 
  //  return k_error; 
  //------------------------------------------------------------------------------ 
 
  //TRACE2(329, ms, 0, "n_lb_this_split: %04x%04x" , _hw(g_n_lb_this_split.u32), _lw(g_n_lb_this_split.u32)); 
  if (!(_factor & kbm_ms_reg_int_ced)) 
  { 
    if (_fmc_get_lb_count_32() == 1L) 
    { 
      trace0(0, ms, 110, "issue the block end command"); 
      //_profile_on(5); 
      if (k_success != ms_set_cmd(k_ms_block_end)) 
        return k_error; 
 
// shawn pointed out to me that there should be a get_int after the block_end 
// and no get int after reading the final page.  this mod test's that theory. 
#if 1 
      if (k_success != ms_get_int(5)) 
        return k_error; 
      if (!(_factor & kbm_ms_reg_int_ced)) 
      { 
        trace0(0, err, 110, "error: missing ced after block end (ms_read_begin_burst)"); 
        return k_error; 
      } 
      if (!(_factor & kbm_ms_reg_int_breq)) 
      { 
        trace0(0, err, 110, "error: missing breq after block end (ms_read_begin_burst)"); 
        return k_error; 
      } 
#endif 
      //_profile_off(5); 
    } 
  } 
  else 
  { 
    trace0(0, ms, 0, "ced"); 
  } 
  // daken must be set during dma transfers. 
  // only wr-page-data and rd-page-data can be issued with daken set. 
  _ms_register_set_bits(ms_mode_ctl, kbm_ms_mode_ctl_daken); 
  //_profile_on(5); 
  if (k_success != ms_set_tpc(k_ms_tpc_rd_page_data |0x02, 0)) 
  { 
    _ms_register_clr_bits(ms_mode_ctl, kbm_ms_mode_ctl_daken); 
    return k_error; 
  } 
  //_profile_off(5); 
  // IMPORTANT!  This begin_critical_section() is ended in the 'end_burst' function 
  // to prevent xdata access while blk_xfer bit is enabled 
  _mcu_begin_critical_section(); 
  //_profile_off(7); 
  return k_success; 
} 
 
//------------------------------------------------------------------------------ 
// new ms_intra_burst_callback to prevent access to xdata during a transfer. 
// this hack exists only to work around a hardware bug. 
// if *any* xdata address is read or written during fmdu xfers to ms 
// then the data passing through the fifo will be corrupted. 
// this happens often enough during actual usage that it is apparent, 
// but is rare enough that it is non obvious. 
// a new version of the chip is expected to fix it. 
// until then the intra-burst handler ensure no access to xdata until 
// the xfer has completed, avoiding the corruption of data by hdw. 
// note: xdata access *includes* logging trace... handy, yes? 
//------------------------------------------------------------------------------ 
t_result ms_rw_intra_burst() reentrant 
{ 
  // profile: ~200-220usec 
  //_profile_on(6); 
  while (1) 
  { 
    // poll isr's to check on events that should cause us to break, 
    // and on those events, set the mask and get out. 
    if ((_mcu_register_rd(x_isr0) & kbm_isr0_blk_xfer_complete)) 
    { 
      //irq_control(k_irq_blk_xfer_complete, kbm_irqctl_clear| kbm_irqctl_mask); 
      // dont clear it here because the irq is no longer used, and the sync is no longer 
      // delivered, and the fmc layer polls the bit 
      //_mcu_register_wr(x_isr0, kbm_isr0_blk_xfer_complete); 
      _mcu_register_set_bits(x_imr0, kbm_isr0_blk_xfer_complete); 
      //thread_set_sync(g_tid, kbm_sync_fmc_blk); 
      break; 
    } 
    // check for a usb reset during the xfer 
    else 
    if (_mcu_register_rd(x_wu_src1) & kbm_wk1_usb_reset) 
    { 
      _dev_soft_reset(); 
      thread_set_sync(g_tid, kbm_sync_usbrst); 
      // you can't get here from there... soft reset puts everything back to por state 
      break; 
    } 
    // abort on status change of memory stick 
    else 
    if (_mcu_register_rd(x_wu_src1) & kbm_wk1_crd_sts_chg) 
    { 
      if (_mcu_register_rd(x_crd_stat) & kbm_crd_stat_ms_out) 
      { 
        _ejected(g_active_lun); 
        thread_set_sync(g_tid, kbm_sync_abort); 
        break; 
      } 
    } 
  } 
  _mcu_end_critical_section(); 
  // always return success and let the fmc_xfer handle error conditions based on the 
  // synchronizer that was set. 
  //_profile_off(6); 
  _profile_off(7); 
  return k_success; 
} 
 
//------------------------------------------------------------------------------ 
//------------------------------------------------------------------------------ 
t_result ms_read_end_burst() reentrant 
{ 
  // profile: ~3usec 
  //_profile_on(6); 
  trace0(0, ms, 99, "ms_read_end_burst()"); 
 
#ifdef k_dbg 
  dbg_burst++; 
#endif 
 
  //_stack_check(); 
  #if 0 
  if (!_ms_rx_data_fifo_is_empty()) 
  { 
    trace0(0, err, 10, "alert - entered read_end_burst before rx fifo emptied - wait before stopping dma"); 
    if (k_success != ms_wait_fifo_with_timeout(ms_fifo_stat_r_buf_e, ms_fifo_stat_r_buf_e, 3))  //rcc test for MS bug tbh!!! 
      return k_error; 
  } 
  #endif 
  // clear daken before issuing command other than wr-page-data and rd-page-data 
  _ms_register_clr_bits(ms_mode_ctl, kbm_ms_mode_ctl_daken); 
  g_ms_page++; 
  //_profile_off(6); 
  return k_success; 
} 
 
//+----------------------------------------------------------------------------- 
//------------------------------------------------------------------------------ 
t_result ms_read_end_split() reentrant 
{ 
  // progile: ~20usec 
  //_profile_on(7); 
  trace0(0, ms, 99, "ms_read_end_split()"); 
 
#ifdef k_dbg 
  dbg_split++; 
#endif 
 
  //_stack_check(); 
  // adjust for the next split 
  _lun_data(max_lb_per_split) = _min(_ms_page_per_block, g_n_lb_this_xfer.u32); 
  //_profile_off(7); 
  return k_success; 
} 
 
//+----------------------------------------------------------------------------- 
//------------------------------------------------------------------------------ 
// profile: ~7usec 
t_result ms_read_end_xfer() reentrant 
{ 
  //_profile_on(6); 
  trace0(0, ms, 99, "ms_read_end_xfer()"); 
  //_stack_check(); 
  // clear daken before issuing command other than wr-page-data and rd-page-data 
  // need to do this again here in case end burst skiped on error 
  _ms_register_clr_bits(ms_mode_ctl, kbm_ms_mode_ctl_daken); 
  if (k_success != _fmc_get_result()) 
    return k_error; 
  _lun_data(sensep) = &sense_none; 
  //_profile_off(6); 
  return k_success; 
} 
 
//------------------------------------------------------------------------------ 
//------------------------------------------------------------------------------ 
t_result ms_write_begin_split(void) reentrant; 
t_result ms_write_begin_burst(void) reentrant; 
t_result ms_write_end_burst(void) reentrant; 
t_result ms_write_end_split(void) reentrant; 
t_result ms_write_end_xfer(void) reentrant; 
 
//------------------------------------------------------------------------------ 
//------------------------------------------------------------------------------ 
t_result ms_write_begin_xfer(void) reentrant; 
t_result ms_write_begin_xfer() reentrant 
{ 
  uint32 split_sz; 
 
  //_profile_on(7); 
  //TRACE0(162, ms, 1, "ms_write_begin_xfer()"); 
  trace4(0, ms, 10, "ms_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())); 
  //_stack_check(); 
 
  _lun_data(sensep) = &sense_write_error; 
 
  _fmc_set_options(0); 
 
  //_profile_on(6); 
  if (k_success != map_write_begin(_fmc_get_start_lb_32())) 
    return k_error; 
  set_wr_phyblk(); 
  //_profile_off(6); 
  //if (map_is_addr_first_in_block()) 
  if (!g_addr_page) 
  { 
    split_sz = _min(_ms_page_per_block, _fmc_get_lb_count_32()); 
  } 
  else 
  { 
    split_sz = _min(_ms_page_per_block - g_addr_page, _fmc_get_lb_count_32()); 
  } 
 
  if (_force_one_burst_per_split)  // br308 
  { 
    trace0(0, ms, 200, "_force_one_burst_per_split: YES"); 
    _lun_data(max_lb_per_split) = 1; 
  } 
  else 
  { 
    trace0(0, ms, 200, "_force_one_burst_per_split: NO"); 
    _lun_data(max_lb_per_split) = split_sz; 
  } 
 
#ifdef k_dbg 
  dbg_split = 0; 
  dbg_burst = 0; 
#endif 
 
  //_profile_off(7); 
  return k_success; 
} 
 
//------------------------------------------------------------------------------ 
//------------------------------------------------------------------------------ 
t_result ms_write_begin_first_split(void) reentrant; 
t_result ms_write_begin_first_split() reentrant 
{ 
  trace0(0, ms, 10, "ms_write_begin_first_split()"); 
  //_stack_check(); 
  //_fmc_set_callback(NULL, ms_write_end_xfer, ms_write_begin_split, ms_write_end_split, ms_write_begin_burst, NULL, ms_write_end_burst); 
  g_fmc_begin_split_callback = (t_fmc_callback)(ms_write_begin_split); 
  //return ms_write_issue_command(); 
  if (k_success != ms_write_all_reg(0x80, _force_one_burst_per_split ? 0x20 : 0x00))  // br308 
    return k_error; 
  if (k_success != ms_set_cmd(k_ms_block_write)) 
    return k_error; 
  return k_success; 
} 
 
//+----------------------------------------------------------------------------- 
//------------------------------------------------------------------------------ 
t_result ms_write_begin_split() reentrant 
{ 
// profile: 
//  ms_wr_begin_split             640usec to 820usec  to 1.64msec 
//    mapping                     420usec to 620 usec  to 1.46msec 
//    ms_write_all_reg            140usec 
//    ms_set_cmd                  80usec 
 
  //_profile_on(7); 
  trace0(0, ms, 10, "ms_write_begin_split()"); 
  //_stack_check(); 
  //_profile_on(6); 
  if (k_success != map_lba2addr_rd(_fmc_get_start_lb_32())) 
    return k_error; 
  if (map_is_addr_first_in_block()) 
  { 
    if (k_success != map_alloc_wr_blk()) 
      return k_error; 
  } 
  set_wr_phyblk(); 
  //_profile_off(6); 
  //return ms_write_issue_command(); 
  //_profile_on(5); 
  if (k_success != ms_write_all_reg(0x80, _force_one_burst_per_split ? 0x20 : 0x00))  // br308 
    return k_error; 
  //_profile_off(5); 
  //_profile_on(4); 
  if (k_success != ms_set_cmd(k_ms_block_write)) 
    return k_error; 
  //_profile_off(4); 
  //_profile_off(7); 
  return k_success; 
} 
 
//------------------------------------------------------------------------------ 
//------------------------------------------------------------------------------ 
t_result ms_write_begin_burst() reentrant 
{ 
  _profile_on(7); 
  trace0(0, ms, 10, "ms_write_begin_burst()"); 
#ifdef k_dbg 
  trace3(0, ms, 10, "ms_write_begin_burst() - split:%d burst:%d page:%d", dbg_split, dbg_burst, g_ms_page); 
#endif 
  //_stack_check(); 
  _profile_on(6); 
  if (k_success != ms_get_int(10)) 
    return k_error; 
  _profile_off(6); 
  if (_factor & kbm_ms_reg_int_cmdnk) 
  { 
    trace0(0, err, 110, "error: cmdnk (ms_write_begin_burst)"); 
    return k_error; 
  } 
  if (_factor & kbm_ms_reg_int_ced) 
  { 
    trace0(0, err, 110, "error: ced set before wr-page-data (ms_write_begin_burst)"); 
    return k_error; 
  } 
  if (_factor & kbm_ms_reg_int_err) 
  { 
    trace0(0, err, 110, "error: ced&err uncorrectable flash write error (ms_write_begin_burst)"); 
    //ms_media_set_phyblock_failed(); 
    return k_error; 
  } 
  if (!(_factor & kbm_ms_reg_int_breq)) 
  { 
    trace0(0, err, 110, "error: breq not set (ms_write_begin_burst)"); 
    return k_error; 
  } 
  // daken must be set during dma transfers. 
  // only wr-page-data and rd-page-data can be issued with daken set. 
  _ms_register_set_bits(ms_mode_ctl, kbm_ms_mode_ctl_daken); 
  _profile_on(5); 
  if (k_success != ms_set_tpc(k_ms_tpc_wr_page_data |0x02, 0)) 
  { 
    _ms_register_clr_bits(ms_mode_ctl, kbm_ms_mode_ctl_daken); 
    return k_error; 
  } 
  _profile_off(5); 
  // IMPORTANT!  This begin_critical_section() is ended in the 'rw_intra_burst' function 
  // to prevent xdata access while blk_xfer bit is enabled 
  _mcu_begin_critical_section(); 
  _profile_off(7); 
  return k_success; 
} 
 
//------------------------------------------------------------------------------ 
//------------------------------------------------------------------------------ 
t_result ms_write_end_burst() reentrant 
{ 
  //_profile_on(5); 
  trace0(0, ms, 10, "ms_write_end_burst()"); 
 
#ifdef k_dbg 
  dbg_burst++; 
#endif 
 
  //_stack_check(); 
  if (!_ms_tx_data_fifo_is_empty()) 
  { 
    trace0(0, err, 10, "alert - entered write_end_burst before tx fifo emptied - wait before stopping dma"); 
    if (k_success != ms_wait_fifo_with_timeout(ms_fifo_stat_t_buf_e, ms_fifo_stat_t_buf_e, 3)) 
    { 
      // clear daken before issuing command other than wr-page-data and rd-page-data 
      _ms_register_clr_bits(ms_mode_ctl, kbm_ms_mode_ctl_daken); 
      return k_error; 
    } 
  } 
  // clear daken before issuing command other than wr-page-data and rd-page-data 
  _ms_register_clr_bits(ms_mode_ctl, kbm_ms_mode_ctl_daken); 
  g_ms_page++; 
  //_profile_off(5); 
  return k_success; 
} 
 
//+----------------------------------------------------------------------------- 
//------------------------------------------------------------------------------ 
t_result ms_write_end_split() reentrant 
{ 
  //_profile_on(7); 
  trace0(0, ms, 10, "ms_write_end_split()"); 
  //_stack_check(); 
 
#ifdef k_dbg 
  dbg_split++; 
#endif 
 
  // NEED get_int for final page of data 
  // THEN block_end withis its OWN get_int 
 
// shawn pointed out to me that there should be a get_int after the block_end 
// and no get int after reading the final page.  this mod test's that theory. 
#if 0 
  if (!_force_one_burst_per_split && (g_ms_page < _ms_page_per_block))  // br308 
  { 
    trace1(0, ms, 10, "issue the block end command, page:%d", g_ms_page); 
    //_profile_on(6); 
    if (k_success != ms_set_cmd(k_ms_block_end)) 
      return k_error; 
    //_profile_off(6); 
  } 
#else 
  // get int for the final burst 
  if (k_success != ms_get_int(10)) 
    return k_error; 
  _profile_off(6); 
  if (_factor & kbm_ms_reg_int_cmdnk) 
  { 
    trace0(0, err, 110, "error: cmdnk (final burst) (ms_write_end_split)"); 
    return k_error; 
  } 
  if (_factor & kbm_ms_reg_int_err) 
  { 
    trace0(0, err, 110, "error: ced&err uncorrectable flash write error (final burst) (ms_write_end_split)"); 
    //ms_media_set_phyblock_failed(); 
    return k_error; 
  } 
  if (!_force_one_burst_per_split && (g_ms_page < _ms_page_per_block))  // br308 
  { 
    if (_factor & kbm_ms_reg_int_ced) 
    { 
      trace0(0, err, 110, "error: ced set before block end (final burst) (ms_write_end_split)"); 
      return k_error; 
    } 
    if (!(_factor & kbm_ms_reg_int_breq)) 
    { 
      trace0(0, err, 110, "error: breq not set (final burst) (ms_write_end_split)"); 
      return k_error; 
    } 
    trace1(0, ms, 10, "issue the block end command, page:%d", g_ms_page); 
    if (k_success != ms_set_cmd(k_ms_block_end)) 
      return k_error; 
    if (k_success != ms_get_int(10)) 
      return k_error; 
    if (!(_factor & kbm_ms_reg_int_ced)) 
    { 
      trace0(0, err, 110, "error: missing ced after block end (ms_write_end_split)"); 
      return k_error; 
    } 
    if (_factor & kbm_ms_reg_int_breq) 
    { 
      trace0(0, err, 110, "error: breq set after block-end delivered (ms_write_end_split)"); 
      #if 0 
      return k_error; 
      #else 
      _force_one_burst_per_split = k_yes;  // br308 
      //return k_error; 
      _ms_pwr_off(); 
      _factor = kbm_ms_reg_int_ced; 
      _ms_pwr_on(); 
      thread_set_timer(2); 
      while(!thread_got_sync(kbm_sync_timer)); 
      #endif 
    } 
  } 
#if 0 
// only useful during debugging... 
  else 
  { 
    if (_factor & kbm_ms_reg_int_ced) 
    { 
      trace0(0, err, 110, "alert: ced set before block end (final burst) (ms_write_end_split)"); 
    } 
    if (!(_factor & kbm_ms_reg_int_breq)) 
    { 
      trace0(0, err, 110, "alert: breq not set (final burst) (ms_write_end_split)"); 
    } 
  } 
#endif 
#endif 
 
#if 0 
  //_profile_on(7); 
  if (k_success != ms_get_int(10)) 
    return k_error; 
  //_profile_off(7); 
  if (_factor & kbm_ms_reg_int_cmdnk) 
  { 
    trace0(0, err, 10, "error: cmdnk"); 
    return k_error; 
  } 
  if (_factor & kbm_ms_reg_int_breq) 
  { 
    //TRACE0(417, err, 0, "alert: breq set after block-end (presumably) delivered"); 
    trace0(0, err, 0, "error: breq set after block-end (presumably) delivered"); 
    _force_one_burst_per_split = k_yes;  // br308 
    //return k_error; 
    _ms_pwr_off(); 
    _factor = kbm_ms_reg_int_ced; 
    _ms_pwr_on(); 
    thread_set_timer(2); 
    while(!thread_got_sync(kbm_sync_timer)); 
  } 
  if (!(_factor & kbm_ms_reg_int_ced)) 
  { 
    trace0(0, err, 0, "error: ced not set after block-end delivered"); 
    return k_success; 
  } 
  if (_factor & kbm_ms_reg_int_err) 
  { 
    trace0(0, err, 10, "error: ced&err uncorrectable flash write error"); 
    //ms_media_set_phyblock_failed(); 
    return k_error; 
  } 
#endif 
 
  // see if that finished the phyblock 
  g_addr_page = g_ms_page - 1; 
  if (map_is_addr_last_in_block()) 
  { 
    //_profile_on(5); 
    if (k_success != map_erase_rd_blk()) 
    { 
      trace0(0, err, 110, "error: problem erasing rd block (ms_write_end_split)"); 
      return k_error; 
    } 
    //_profile_off(5); 
    //_profile_on(4); 
    if (k_success != map_bind_wr_blk()) 
    { 
      trace0(0, err, 110, "error: problem binding wr block (ms_write_end_split)"); 
      return k_error; 
    } 
    //_profile_off(4); 
  } 
  // adjust for the next split 
  if (_force_one_burst_per_split)  // br308 
  { 
    trace0(0, ms, 110, "_force_one_burst_per_split: YES"); 
    _lun_data(max_lb_per_split) = 1; 
  } 
  else 
  { 
    trace0(0, ms, 110, "_force_one_burst_per_split: NO"); 
    _lun_data(max_lb_per_split) = _min(_ms_page_per_block, g_n_lb_this_xfer.u32); 
  } 
  //_profile_off(7); 
  return k_success; 
} 
 
//+----------------------------------------------------------------------------- 
//------------------------------------------------------------------------------ 
t_result ms_write_end_xfer() reentrant 
{ 
  //_profile_on(5); 
  trace0(0, ms, 10, "ms_write_end_xfer()"); 
  //_stack_check(); 
  // clear daken before issuing command other than wr-page-data and rd-page-data 
  // need to do this again here in case end burst skiped on error 
  _ms_register_clr_bits(ms_mode_ctl, kbm_ms_mode_ctl_daken); 
  if (k_success != _fmc_get_result()) 
    return k_error; 
  // cds - turning off auto-xfer so that write_flush() can use 
  // the fmdu for the sector_copy 
  _mcu_register_clr_bits(x_fmc_ctl, kbm_fmc_ctl_auto_trans); 
  trace0(0, fmc, 0, "auto-transfer bit ==> OFF"); 
  //_profile_on(4); 
  if (k_success != map_write_flush()) 
    return g_fmc_rslt = k_error; 
  //_profile_off(4); 
  _lun_data(sensep) = &sense_none; 
  //_profile_off(5); 
  return k_success; 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   dfa_ms_read 
// 
// Declaration: 
//   void dfa_ms_read(void); 
// 
// Purpose: 
//   Read data from the MS card. 
// 
// Arguments: 
//   None. 
// 
//   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 entry state of a DFA, not a FUNCTION. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
void dfa_ms_read(void) reentrant 
{ 
  // profile: ~13usec 
  //_profile_on(6); 
  trace0(0, ms, 0, "dfa_ms_read()"); 
  //_stack_check(); 
  // _fmc_set_callback(ms_read_begin_xfer, ms_read_end_xfer, ms_read_begin_first_split, ms_read_end_split, ms_read_begin_burst, NULL, ms_read_end_burst); 
  _fmc_set_callback(ms_read_begin_xfer, ms_read_end_xfer, ms_read_begin_first_split, ms_read_end_split, ms_read_begin_burst, ms_rw_intra_burst, ms_read_end_burst); 
  //_profile_off(6); 
  dfa_lun_read(); 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   dfa_ms_write 
// 
// Declaration: 
//   void dfa_ms_write(void); 
// 
// Purpose: 
//   Write data to the MS card. 
// 
// Arguments: 
//   None. 
// 
//   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 entry state of a DFA, not a FUNCTION. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
void dfa_ms_write(void) reentrant 
{ 
  trace0(0, ms, 10, "dfa_ms_write()"); 
  // _fmc_set_callback(ms_write_begin_xfer, ms_write_end_xfer, ms_write_begin_first_split, ms_write_end_split, ms_write_begin_burst, NULL, ms_write_end_burst); 
  _fmc_set_callback(ms_write_begin_xfer, ms_write_end_xfer, ms_write_begin_first_split, ms_write_end_split, ms_write_begin_burst, ms_rw_intra_burst, ms_write_end_burst); 
  dfa_lun_write(); 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   dfa_ms_verify 
// 
// Declaration: 
//   void dfa_ms_verify(void); 
// 
// Purpose: 
//   Verify the data on the MS card. 
// 
// Arguments: 
//   None. 
// 
//   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 entry state of a DFA, not a FUNCTION. 
//   Should be overridden.  Derived method should set callbacks, then call this. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
void dfa_ms_verify(void) reentrant 
{ 
  trace0(0, ms, 0, "dfa_ms_verify()"); 
  //_stack_check(); 
  // !!! callbacks not set - verify with data will CRASH!!! 
  _fmc_set_callback(ms_write_begin_xfer, ms_write_end_xfer, ms_write_begin_first_split, ms_write_end_split, ms_write_begin_burst, ms_rw_intra_burst, ms_write_end_burst); 
  dfa_lun_verify(); 
} 
 
//---eof------------------------------------------------------------------------