www.pudn.com > SMSC USB2.0.zip > sd.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. 
  ____________________________________________________________________________ 
  ____________________________________________________________________________ 
  sd.c - secure digital mass storage class implementation file 
  ____________________________________________________________________________ 
  comments tbd 
  ____________________________________________________________________________ 
  Revision History 
  Date      Who  Comment 
  ________  ___  _____________________________________________________________ 
  11/##/01  cds initial version - stubs for a few overrides. 
  04/28/02  tbh xdata is not initialized to zero on power up so set fmc_options to 
                zero at start of every transfer. 
  05/06/02  ds  It has been noted that the power-cycling of the card is not sufficient to reset the card. 
                So,we roll back the change and issue the reset command along with power-cycle. 
  05/06/02  ds  If the media is write-protected we should pass the Verify command, to ensure proper operation 
                in Win2k 
  05/09/02  ds  Enabled "Emergency Exit" with setjmp when waiting for tx and rx interrupts during FS xfers 
  05/28/02  ds  In an attempt to speed up SD, I have made the code to poll for the tx and rx bits instead of 
                taking an interrupt. Note that the changes are reflected in the irq_timeout fn. Also, implemented 
                pre-erase which speeds-up 'certain SD cards'. 
  06/17/02  ds  The pre-erase is applicable only to SD and not MMC cards. 
  06/18/02  DS  More error detection code added to the Read/Write parts. On a media removal made sure we are 
                reporting the right sense code 
  06/18/02  ds  Added comments to explain the timeout value. Also added a margin to the timeout. This reflects 
                the current 1.1 xfer speed of 200 KBPS 
  06/19/02  ds  Fixed win2k 1.1 surprise removal bug. _sd_data_loaded is reinitialized on a surprise removal 
  06/19/02  ds  Changed the fmc_set_callback to use fmc_dflt_callback instead of NULL, BOT optimization. 
  06/20/02  ds  Set the minimum timeout to 50 ms in g_fmc_timeout, to care of the device overhead 
  06/20/02  ds  Since the Upper layer sets the sense code for no-media, it has been removed from SD layer. 
  06/25/02  ds  Now that we receive a sync_abort on sd_blk_ovrrun error and crc_error, they are handled accordingly 
                in the SD layer. 
  06/25/02  ds  Based on measurements set the "card programming " timeout to 1sec 
  06/27/02  ds  USB97c210 exibited the latency issue ( a la 201). This appeared as a format issue in Mac OS 10.1.5 
                Thus added NOPs to bur some time after we set the bits in ep2_ctl register. 
  07/01/02  ds  In sync with the optimization of reads and writes, the read and write dfas are now functions. 
                So, they shall use __thread_return_dfa a new macro which simply returns to the caller. 
                Also, took out the check for wr_protection on writes. This should be done in the generic lun layer. 
  07/11/02  ds  Changed the device ID name to be consistent with recent change (by Trenton), to the drive names. 
  07/16/02  tbh added k_pfm_led code to de/activate led on card discovery/absence so led 
                operates correctly with old-n-busted OSs that don't send periodic TURs. 
  07/30/02  ds  On a high speed read we now wait for an innak before issuing the read command. This is 
                to take care of slow hosts ( some wait for about 2ms after the read command to send the first IN token). 
                Due to lack of flow control in the SDC hardware, the sd_blk_ovrrun error occurs, forcing us to send 
                a read_error and fail the transfer. It has been noted that the Wincomp software does not retry a read. 
                This causes it to fail. This does not happen in Full speed as we take a lot of time to hand-fill the buffers. 
  09/11/02  ds  The firing of the timer1 interrupt in the middle of 2.0 data xfer (FMDU), causes data loss and 
                makes it appear to the host like there is data corruption, even though the file itself is not corrupted. 
                So, turned off the timer for now, if we know it is an SD 2.0 read. So, the light will not blink to the desired interval on SD. 
  09/12/02  ds  To be safe turn of timer1 even on 2.0 writes. 
  09/12/02  ds  Manage the state of the gpio0 led, during the xfer. 
  09/12/02  tbh replaced strncpy() with memcpy() to save data space. 
  09/12/02  tbh/ds timer1 disabled during sd transfers to avoid data corruption issue.  bug report #343 
                added comments (including this one) 
  09/17/02  tbh fixed improperly coded accesses to x_fmc_clk_ctl. 
  09/18/02  tbh bug report 343, sd data corruption.  initial workaround was to 
                disable timer1 interrupts during sd transfers.  later examination 
                revealed that issuing the read to the sd card *before* enabling 
                the block transfer is problematic due to a window of vulnerability 
                during which the following can occur: 
                if the transfer is off for too long bu the sd is reading data then 
                the sd card will dump its data into the bit bucket.  once the 
                block transfer is enabled the sd card provides the desired quantity 
                of data, but starting from the incorrect offset.  thus the data 
                appears corrupt.  today's modification was to postpone issuing 
                the read command until after the block transfer is enabled. 
  09/19/02  tbh  typo - disable was diable 
  10/02/02  ds  Set the timeout value for data transfers to 10 seconds, following the other luns. 
                This value is safer than then the empirical value we had before. 
  10/17/02  cds updated lun_data macros to use the new vtbl and lun_data formats.  
  10/24/02  ds  Implemented Retry of 2.0 reads, when an sd_blk_ovrrun error occurs. The new address is  
                computed and a multiple-block-read is issued.                
============================================================================*/ 
#define __sd_dot_c__ 
#include "project.h" 
#include "dev.h" 
 
// provide vtable definition '_vtbl_cf' 
code _vtbl_defn(sd); 
 
#define sd_1point1_included 
 
 
//#define Debug_On //A local directive to turn on certain debug features 
//#define sd_fast_1point1 
 
#define FS_pktsz 64 
uint8 xdata _sd_card_active = k_true;  //This is a flag to know if the card is not responding 
uint8 xdata _sd_data_loaded = k_false;  //Tells me if the buffers are already loaded 
 
//------------------------------------------------------------------------------ 
// sd/mmc controller (sdc) interface registers 
unsigned char volatile xdata sdc_data             at_sd(F6);  // rw     SDC Data Register 
unsigned char volatile xdata sdc_mode_ctl        at_sd(F8);  // rw     SDC Model Control Register 
unsigned char volatile xdata sdc_ctl              at_sd(F9);  // rw     SDC Control Register 
unsigned char volatile xdata sdc_cmd_rsp_buf[17]  _at_ 0x3640; 
 
//RCA bytes 
uint8 xdata  g_sdc_rca_h = 0x00; 
uint8 xdata g_sdc_rca_l = 0x00; 
uint8 xdata _sd_state = 0x00;     //Tells us the current _sd_state of the card 
uint8 xdata _sd_data_xfer_cmd;    //Instead of passing a parameter this tells what cmd to issue 
uint8 xdata _sd_write_protected = k_false;  //The media could be Write Protected both in Soft and Hard way 
jmp_buf xdata _sd_context;        // This the context to which timeout fns will jump to on error 
xdata t_udw32 xdata _sd_start_addr_this_xfer;  //The device takes byte address instead of block address. This variable stores that. 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   TBD 
// 
// Declaration: 
//   TBD 
// 
// Purpose: 
//   TBD 
// 
// Arguments: 
//   TBD 
// 
// Return: 
//   TBD 
// 
// Notes: 
//   This is a FUNCTION, not a DFA. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
void sdc_read_rsp(uint8 length) reentrant 
{ 
  uint8 i,temp; 
  for(i=0;i<=length;i++) 
  { 
    temp = XBYTE[(0X3640+i)]; 
  } 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   sdc_wait_status_with_timeout 
// 
// Declaration: 
//   t_result sdc_wait_status_with_timeout(uint8 mask,uint16 ticks); 
// 
// Purpose: 
//   Poll the sdc status register to watch for specific bits being set. 
// 
// Arguments: 
//   mask    - a uint8 with 1's in the bits to be compared 
//   ticks   - a uint16 representing the timeout limit 
// 
// Return: 
//   A t_result indicating: 
//     k_success - the sdc status register logically anded with the mask 
//     k_usbreset  - 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. 
//   Why does this not return a result? and Why does this set a global instead? This is because this fn is 
//   is called several levels down from the main caller, and requires a lot of code to transmit the result up 
//   the hierarchy. So, now we just check for the flag and return, so the highest caller may take appropriate 
//   action. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
void sdc_wait_status_with_timeout(uint8 mask, uint16 ticks) reentrant 
{ 
  uint8 status; 
  t_sync sync = 0; 
  t_sync sync1 = 0; 
  _stack_check(); 
  //_stack_dump(); 
  //TRACE2(295, sd, 1, "sdc_wait_status_with_timeout(mask:%02X ticks:%d)", mask, ticks); 
  // do this to get kbm_sync_abort delivered of there is traffic on the control pipe 
  _isr_bind_dma_owner(g_tid, kbm_sync_none); 
  // wait until the device is no longer busy 
  thread_set_timer(ticks); 
  _mcu_begin_critical_section(); 
  sync = _thread_got_sync(kbm_sync_abort |kbm_sync_usbrst |kbm_sync_timer); 
  _mcu_end_critical_section(); 
  status = x_sdc_stat; 
  //TRACE1(296, sd, 1, "The value of status %02x ",status); 
  while((status & mask) != mask) 
  { 
    _mcu_begin_critical_section(); 
    sync = _thread_got_sync(kbm_sync_abort |kbm_sync_usbrst |kbm_sync_timer); 
    _mcu_end_critical_section(); 
    _stack_check(); 
    if(sync & kbm_sync_usbrst) 
    { 
      trace0(0, sd, 1, "sdc_wait_status_with_timeout() - error: kbm_sync_usbrst"); 
      break; 
    } 
    if(sync & kbm_sync_abort) 
    { 
      trace0(0, sd, 1, "sdc_wait_status_with_timeout() - error: kbm_sync_abort"); 
      break; 
    } 
    if(sync & kbm_sync_timer) 
    { 
      trace0(0, sd, 1, "sdc_wait_status_with_timeout() - error: hung busy"); 
      //when a command fails init stage it could be because of a mmc card 
      _sd_card_active = k_false; 
      break; 
    } 
    status = x_sdc_stat; 
    // trace1(0, sd, 1, "The value of status %02x ",status); 
  } 
  if(sync)                       //did we get out on a failure? 
  { 
    //Reset the media here 
    trace0(0, sd, 1, "Resetting the SDC Block"); 
    sdc_ctl = 0x20; 
    thread_set_timer(10); 
    while(!(sync1 & kbm_sync_timer)) 
    { 
      _mcu_begin_critical_section(); 
      sync1 = _thread_got_sync(kbm_sync_timer); 
      _mcu_end_critical_section(); 
    } 
    _thread_clr_sync(kbm_sync_timer); 
  } 
  _thread_clr_sync(sync);         //clear the synchronizers 
  if(sync & (kbm_sync_usbrst |kbm_sync_abort)) 
    longjmp(_sd_context, 1);      //Go to the caller dfa and return failed 
  //_stack_dump(); 
  //TRACE1(301, sd, 1, "The value of status %02x ",status); 
  _stack_check(); 
  //_stack_dump(); 
  return; 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   sdc_wait_irq_with_timeout 
// 
// Declaration: 
//   t_result sdc_wait_irq_with_timeout(uint16 ticks); 
// 
// Purpose: 
//   Poll for any synchronizer . 
// 
// Arguments: 
//   ticks   - a uint16 representing the timeout limit 
// 
// Return: 
//   A t_result indicating: 
//     k_success - the sync was spotted. 
//     k_usbreset  - 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 
//------------------------------------------------------------------------------ 
#ifndef sd_fast_1point1 
t_result sdc_wait_irq_with_timeout(uint8 irq_name, uint16 ticks) reentrant 
{ 
  t_sync sync; 
  //TRACE2(302, sd, 1, "sd_wait_irq_with_timeout(ticks:%d) for %02x", ticks, irq_name); 
  // do this to get kbm_sync_abort delivered of there is traffic on the control pipe 
  _isr_bind_dma_owner(g_tid, kbm_sync_none); 
  thread_set_timer(ticks); 
  sync = kbm_sync_none; 
  //while(!(sync & irq_name)) 
  while(!(_mcu_register_rd(x_isr0) & irq_name)) 
  { 
    sync = thread_got_sync(kbm_sync_abort |kbm_sync_usbrst |kbm_sync_timer); 
    _thread_clr_sync(sync); 
    if(sync & kbm_sync_usbrst) 
    { 
      trace0(0, sd, 1, "sd_wait_irq_with_timeout() - error: kbm_sync_usbrst"); 
    } 
    if(sync & kbm_sync_abort) 
    { 
      trace0(0, sd, 1, "sd_wait_irq_with_timeout() - error: kbm_sync_abort"); 
    } 
    if(sync & kbm_sync_timer) 
    { 
      trace0(0, sd, 1, "sd_wait_irq_with_timeout() - error: timeout awaiting irq"); 
      return k_timeout; 
    } 
    if(sync & (kbm_sync_usbrst |kbm_sync_abort)) 
      longjmp(_sd_context, 1);    //Go to the caller dfa and return failed 
  } 
  trace0(0, sd, 1, "sd_wait_irq_with_timeout() - got the irq"); 
  return k_success; 
} 
#endif 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   sd_pre_cmd 
// 
// Declaration: 
//   void sd_pre_cmd() reentrant 
// 
// Purpose: 
//   Prepare the device before issuing a command 
// 
// Arguments: 
//   None 
// 
// Return: 
//   None 
// 
// Notes: 
//   This is a FUNCTION, not a DFA. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
void sd_pre_cmd() reentrant 
{ 
  trace0(0, sd, 1, "+sd_pre_cmd"); 
  _mcu_register_wr(x_sdc_stat, kbm_sdc_stat_rsp_rdy); 
  _mcu_register_wr(x_sdc_stat, kbm_sdc_stat_crc_err); 
  sdc_ctl &= ~kbm_sdc_id_data; 
  sdc_wait_status_with_timeout(kbm_sdc_stat_crd_rdy,300); 
  sdc_wait_status_with_timeout(kbm_sdc_stat_cmd_rdy,300); 
  trace0(0, sd, 1, "-sd_pre_cmd"); 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   sd_display_rsp_buf 
// 
// Declaration: 
//   void sd_display_rsp_buf() reentrant 
// 
// Purpose: 
//  Dumps out the contents of the cmd_rsp buffer 
// 
// Arguments: 
//   None 
// 
// Return: 
//   None 
// 
// Notes: 
//   This is a FUNCTION, not a DFA. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
void sd_display_rsp_buf() reentrant 
{ 
  trace1(0, sd, 2, "Response Buffer[0] = %02x",sdc_cmd_rsp_buf[0]); 
  trace1(0, sd, 2, "Response Buffer[1] = %02x",sdc_cmd_rsp_buf[1]); 
  trace1(0, sd, 2, "Response Buffer[2] = %02x",sdc_cmd_rsp_buf[2]); 
  trace1(0, sd, 2, "Response Buffer[3] = %02x",sdc_cmd_rsp_buf[3]); 
  trace1(0, sd, 2, "Response Buffer[4] = %02x",sdc_cmd_rsp_buf[4]); 
  trace1(0, sd, 2, "Response Buffer[5] = %02x",sdc_cmd_rsp_buf[5]); 
  trace1(0, sd, 2, "Response Buffer[6] = %02x",sdc_cmd_rsp_buf[6]); 
  trace1(0, sd, 2, "Response Buffer[7] = %02x",sdc_cmd_rsp_buf[7]); 
  trace1(0, sd, 2, "Response Buffer[8] = %02x",sdc_cmd_rsp_buf[8]); 
  trace1(0, sd, 2, "Response Buffer[9] = %02x",sdc_cmd_rsp_buf[9]); 
  trace1(0, sd, 2, "Response Buffer[10] = %02x",sdc_cmd_rsp_buf[10]); 
  trace1(0, sd, 2, "Response Buffer[11] = %02x",sdc_cmd_rsp_buf[11]); 
  trace1(0, sd, 2, "Response Buffer[12] = %02x",sdc_cmd_rsp_buf[12]); 
  trace1(0, sd, 2, "Response Buffer[13] = %02x",sdc_cmd_rsp_buf[13]); 
  trace1(0, sd, 2, "Response Buffer[14] = %02x",sdc_cmd_rsp_buf[14]); 
  trace1(0, sd, 2, "Response Buffer[15] = %02x",sdc_cmd_rsp_buf[15]); 
  trace1(0, sd, 2, "Response Buffer[16] = %02x",sdc_cmd_rsp_buf[16]); 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   sd_app_cmd 
// 
// Declaration: 
//   void sd_app_cmd() reentrant 
// 
// Purpose: 
//   Prepare the device before issuing an app specific command 
// 
// Arguments: 
//   None 
// 
// Return: 
//   None 
// 
// Notes: 
//   This is a FUNCTION, not a DFA. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
void sd_app_cmd() reentrant 
{ 
  trace0(0, sd, 1, "+sd_app_cmd"); 
  sd_pre_cmd(); 
  //fill the command buffer 
  sdc_cmd_rsp_buf[0] = 0x77; 
  sdc_cmd_rsp_buf[1] = g_sdc_rca_h; 
  sdc_cmd_rsp_buf[2] = g_sdc_rca_l; 
  sdc_cmd_rsp_buf[3] = 0x00; 
  sdc_cmd_rsp_buf[4] = 0x00; 
  _sd_wait_48b_resp(); 
  sd_display_rsp_buf(); 
  sdc_read_rsp(5); 
  trace0(0, sd, 1, "-sd_app_cmd"); 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   sd_get_status 
// 
// Declaration: 
//   void sd_get_status() reentrant 
// 
// Purpose: 
//  Get the current State of the SD Device 
// 
// Arguments: 
//   None 
// 
// Return: 
// 
// 
// Notes: 
//   This is a FUNCTION, not a DFA. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
void sd_get_status() reentrant 
{ 
  trace0(0, sd, 1, "+ sd_get_status()"); 
  _stack_check(); 
  sd_pre_cmd(); 
  _stack_check(); 
  //fill the command buffer 
  sdc_cmd_rsp_buf[0] = 0x4d; 
  sdc_cmd_rsp_buf[1] = g_sdc_rca_h; 
  sdc_cmd_rsp_buf[2] = g_sdc_rca_l; 
  sdc_cmd_rsp_buf[3] = 0x00; 
  sdc_cmd_rsp_buf[4] = 0x00; 
  _sd_wait_48b_resp(); 
  //sd_display_rsp_buf(); 
  _sd_state = sdc_cmd_rsp_buf[3] & 0x1e; 
  _sd_state = _sd_state >> 1; 
#ifdef Debug_On 
  switch(_sd_state) 
  { 
  case k_sdc_state_stby: 
    trace0(0, sd, 1, "Standby state"); 
    break; 
  case k_sdc_state_tran: 
    trace0(0, sd, 1, "Transfer state"); 
    break; 
  case k_sdc_state_data: 
    trace0(0, sd, 1, "Data state"); 
    break; 
  case k_sdc_state_rcv: 
    trace0(0, sd, 1, "Receive state"); 
    break; 
  case k_sdc_state_prg: 
    trace0(0, sd, 1, "Program state"); 
    break; 
  case k_sdc_state_dis: 
    trace0(0, sd, 1, "Disable state"); 
    break; 
  default: 
    trace1(0, sd, 1, "Unknown state %d",_sd_state); 
    break; 
  } 
  if(sdc_cmd_rsp_buf[3] & 0x01) 
  { 
    trace0(0, sd, 0, "Ready for data!!"); 
  } else 
  { 
    trace0(0, sd, 0, "Not Ready for data!!"); 
  } 
#endif 
  sdc_read_rsp(5); 
  trace0(0, sd, 1, "- sd_get_status()"); 
  return; 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   sd_set_bus_width 
// 
// Declaration: 
//   void sd_set_bus_width(uint8 BusWidth) reentrant 
// 
// Purpose: 
//  Set the data bus width to either single line or four data lines. 
// 
// Arguments: 
//   None 
// 
// Return: 
// 
// 
// Notes: 
//   This is a FUNCTION, not a DFA. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
void sd_set_bus_width(uint8 BusWidth) reentrant 
{ 
  trace1(0, sd, 1, "Set Bus width to %d ", BusWidth); 
  sd_app_cmd(); 
  sd_pre_cmd(); 
  sdc_cmd_rsp_buf[0] = 0x46; 
  sdc_cmd_rsp_buf[1] = 0x00; 
  sdc_cmd_rsp_buf[2] = 0x00; 
  sdc_cmd_rsp_buf[3] = 0x00; 
  sdc_cmd_rsp_buf[4] = BusWidth; 
  _sd_wait_48b_resp(); 
  sd_display_rsp_buf(); 
  sdc_read_rsp(5); 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   sd_stop_transfer 
// 
// Declaration: 
//   void sd_stop_transfer() reentrant 
// 
// Purpose: 
//  Stop the data xfer 
// 
// Arguments: 
//   None 
// 
// Return: 
// 
// 
// Notes: 
//   This is a FUNCTION, not a DFA. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
void sd_stop_transfer() reentrant 
{ 
  trace0(0, sd, 1, "Stop the transfer"); 
  sd_pre_cmd(); 
  sdc_cmd_rsp_buf[0] = 0x4c; 
  sdc_cmd_rsp_buf[1] = 0x00; 
  sdc_cmd_rsp_buf[2] = 0x00; 
  sdc_cmd_rsp_buf[3] = 0x00; 
  sdc_cmd_rsp_buf[4] = 0x00; 
  _sd_wait_48b_resp(); 
  sd_display_rsp_buf(); 
  sd_get_status(); 
  sdc_read_rsp(5); 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   sd_init_controller 
// 
// Declaration: 
//   void sd_init_controller(void) reentrant 
// 
// Purpose: 
//  tbd 
// 
// Arguments: 
//   None 
// 
// Return: 
// 
// 
// Notes: 
//   tbd 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
void sd_init_controller(void) reentrant 
{ 
  trace0(0, cf, 0, "cf_init_controller()"); 
  // copy string name into the controller device ID 
  memcpy(_lun_data_rd(k_lun_sd, device_id), "SD/MMC", k_lun_max_devid_sz); 
  memcpy(_lun_data_rd(k_lun_mmc, device_id), "SD/MMC", k_lun_max_devid_sz); 
  _lun_data_wr(k_lun_sd, device_type, k_device_type_sd); 
  _lun_data_wr(k_lun_mmc, device_type, k_device_type_mmc); 
} 
 
//------------------------------------------------------------------------------ 
//------------------------------------------------------------------------------ 
#include "dev.h" 
//------------------------------------------------------------------------------ 
//------------------------------------------------------------------------------ 
 
//------------------------------------------------------------------------------ 
//------------------------------------------------------------------------------ 
// should be in sd.h with all other sd register bit definitions 
#define kbm_sdc_ctl_sdc_rst 0x20 
t_result tbh_wait_status_with_timeout(uint8 mask, uint16 ticks) reentrant; 
t_result tbh_wait_status_with_timeout(uint8 mask, uint16 ticks) reentrant 
{ 
  uint8 status; 
  t_sync sync; 
  trace0(0, sd, 10, "tbh_wait_status_with_timeout()"); 
  // do this to get kbm_sync_abort delivered of there is traffic on the control pipe 
  _isr_bind_dma_owner(g_tid, kbm_sync_none); 
  thread_set_timer(ticks+1); 
  do 
  { 
    _mcu_begin_critical_section(); 
    sync = _thread_got_sync(kbm_sync_abort |kbm_sync_usbrst |kbm_sync_timer); 
    _mcu_end_critical_section(); 
    thread_clr_sync(sync); 
    status = _sd_register_rd(x_sdc_stat); 
    if(sync & kbm_sync_abort) 
    { 
      trace0(0, sd, 10, "tbh_wait_status_with_timeout() - error: kbm_sync_abort"); 
      // must delay some after this, but unknown how long, assume a few instruction cycles... 
      _sd_register_wr(sdc_ctl, kbm_sdc_ctl_sdc_rst); 
      return k_aborted; 
    } 
    if(sync & kbm_sync_usbrst) 
    { 
      trace0(0, sd, 10, "tbh_wait_status_with_timeout() - error: kbm_sync_usbrst"); 
      // must delay some after this, but unknown how long, assume a few instruction cycles... 
      _sd_register_wr(sdc_ctl, kbm_sdc_ctl_sdc_rst); 
      return k_usbreset; 
    } 
    if(sync & kbm_sync_timer) 
    { 
      trace0(0, sd, 10, "tbh_wait_status_with_timeout() - error: kbm_sync_timer"); 
      // must delay some after this, but unknown how long, assume a few instruction cycles... 
      _sd_register_wr(sdc_ctl, kbm_sdc_ctl_sdc_rst); 
      return k_timeout; 
    } 
  } while((status & mask) != mask); 
  return k_success; 
} 
 
//------------------------------------------------------------------------------ 
// issuing CMD0 - go idle - is the same as power on reset 
// power cycling the card is easier and faster 
//------------------------------------------------------------------------------ 
void dfa_sd_reset() reentrant 
{ 
  trace0(0, sd, 99, "sd_reset()"); 
  //Jump to this location on an "Emergency Exit" due to sync_abort or sync_usbreset 
  if(setjmp(_sd_context)) 
  { 
    trace0(0, sd, 99, "EMERGENCY EXIT!!"); 
    thread_return_dfa(k_error); 
  } 
  _mcu_register_clr_bits(x_fmc_clk_ctl, k_fmc_clk_ctl_sd_disable); 
  _mcu_register_set_bits(x_fmc_clk_ctl, k_fmc_clk_ctl_sd_200khz); 
  _sd_register_set_bits(sdc_ctl, kbm_sdc_bus_type); 
  g_sdc_rca_h = 0x00; 
  g_sdc_rca_l = 0x00; 
  _sd_write_protected = k_false; 
  _sd_card_active = k_true; 
  _sd_data_loaded = k_false; 
  _sd_state = 0x00; 
  _sd_pwr_on(); 
  // can afford a 2msec blocking loop here, hopefully 
  thread_set_timer(2); 
  while(!thread_got_sync(kbm_sync_timer)); 
  //Now issue the reset command as well 
  trace1(0, sd, 1, "The sdc_ctl register %02x",sdc_ctl); 
  trace1(0, sd, 1, "The crd_ps register %02x",_mcu_register_rd(x_crd_ps)); 
  sd_pre_cmd(); 
  trace1(0, sd, 1, "The stat register %02x",_mcu_register_rd(x_sdc_stat)); 
  //fill the command buffer 
  sdc_cmd_rsp_buf[0] = 0x40; 
  sdc_cmd_rsp_buf[1] = 0x00; 
  sdc_cmd_rsp_buf[2] = 0x00; 
  sdc_cmd_rsp_buf[3] = 0x00; 
  sdc_cmd_rsp_buf[4] = 0x00; 
  sd_display_rsp_buf(); 
  //reset mode control register 
  sdc_mode_ctl = 0; 
  sdc_mode_ctl |= (kbm_sdc_cmd_no_rsp | kbm_sdc_rsp_crc_ds);  // Disable Crc 7 
  thread_set_timer(1); 
  while(!(thread_got_sync(kbm_sync_timer))); 
  thread_clr_sync(kbm_sync_timer); 
  thread_return_dfa(k_success); 
} 
 
//------------------------------------------------------------------------------ 
//------------------------------------------------------------------------------ 
void dfa_sd_initialize() reentrant 
{ 
  t_result rslt; 
  //Jump to this location on an "Emergency Exit" due to sync_abort or sync_usbreset 
  trace0(0, sd, 99, "dfa_sd_initialize()"); 
  // assmume no media 
  _lun_data_wr(k_lun_sd, media, (_lun_data_rd(k_lun_sd, media)&~(kbm_lun_media_present |kbm_lun_media_unknown |kbm_lun_media_changed))); 
  _lun_data_wr(k_lun_mmc, media, (_lun_data_rd(k_lun_mmc, media)&~(kbm_lun_media_present |kbm_lun_media_unknown |kbm_lun_media_changed))); 
  // clear the rsp buf byte 1 so we don't see remnant of previous operation 
  sdc_cmd_rsp_buf[1] = 0; 
  // monitor the card's busy bit - $$$need a symbol instead of a hex constant... 
  while(!(sdc_cmd_rsp_buf[1] & 0x80)) 
  { 
    trace1(0, sd, 0, "top of while loop, _sd_card_active:%d", _sd_card_active); 
    // probe for SD card.  if not found probe for MMC card. 
    if(k_lun_sd == g_active_lun) 
    { 
      _mcu_register_wr(x_sdc_stat, kbm_sdc_stat_rsp_rdy); 
      _mcu_register_wr(x_sdc_stat, kbm_sdc_stat_crc_err); 
      _sd_register_clr_bits(sdc_ctl, kbm_sdc_id_data); 
      if(k_success != tbh_wait_status_with_timeout(kbm_sdc_stat_crd_rdy, 5)) 
        goto SWITCH_TO_MMC; 
      if(k_success != tbh_wait_status_with_timeout(kbm_sdc_stat_cmd_rdy, 5)) 
        goto SWITCH_TO_MMC; 
      sdc_cmd_rsp_buf[0] = 0x77; 
      sdc_cmd_rsp_buf[1] = g_sdc_rca_h; 
      sdc_cmd_rsp_buf[2] = g_sdc_rca_l; 
      sdc_cmd_rsp_buf[3] = 0x00; 
      sdc_cmd_rsp_buf[4] = 0x00; 
      sdc_mode_ctl = 0; 
      sdc_mode_ctl |= kbm_sdc_cmd_48b_rsp; 
      rslt = tbh_wait_status_with_timeout(kbm_sdc_stat_rsp_rdy, 5); 
      sdc_read_rsp(5); 
      if(k_timeout == rslt) 
      { 
        SWITCH_TO_MMC: 
        trace0(0, sd, 0, "try mmc..."); 
        _lun_map_log2phy(k_log_lun_sd, k_lun_mmc); 
        //g_active_lun = k_lun_mmc; 
        lun_set_active(k_lun_mmc); 
        _sd_card_active = k_true; 
      } else 
        if(k_success != rslt) 
        goto EXIT_NO_MEDIA; 
    } 
    // issue ACMD41 
    trace0(0, sd, 1, "Issuing ACMD41"); 
    _mcu_register_wr(x_sdc_stat, kbm_sdc_stat_rsp_rdy); 
    _mcu_register_wr(x_sdc_stat, kbm_sdc_stat_crc_err); 
    _sd_register_clr_bits(sdc_ctl, kbm_sdc_id_data); 
    if(k_success != tbh_wait_status_with_timeout(kbm_sdc_stat_crd_rdy, 5)) 
      goto EXIT_NO_MEDIA; 
    if(k_success != tbh_wait_status_with_timeout(kbm_sdc_stat_cmd_rdy, 5)) 
      goto EXIT_NO_MEDIA; 
    if(k_lun_mmc == g_active_lun) 
      sdc_cmd_rsp_buf[0] = 0x41; 
    else 
      sdc_cmd_rsp_buf[0] = 0x69; 
    sdc_cmd_rsp_buf[1] = 0x80;    // Voltage range 2.0 - 2.1 
    sdc_cmd_rsp_buf[2] = 0xFF; 
    sdc_cmd_rsp_buf[3] = 0x80; 
    sdc_cmd_rsp_buf[4] = 0x00; 
    sd_display_rsp_buf(); 
    sdc_mode_ctl = 0; 
    sdc_mode_ctl |= (kbm_sdc_cmd_48b_rsp | kbm_sdc_rsp_crc_ds); 
    if(k_success != tbh_wait_status_with_timeout(kbm_sdc_stat_rsp_rdy, 5)) 
      goto EXIT_NO_MEDIA; 
    sdc_read_rsp(5); 
    _mcu_register_wr(x_sdc_stat, kbm_sdc_stat_rsp_rdy); 
  } 
  // was there a card in the slot?  if so g_active_lun is set to the correct phy lun index. 
  if(_sd_card_active) 
  { 
    // found a card 
    // clear the media changed flag so the framework will move on and establish the geometry 
    _lun_data(media)  |= (kbm_lun_media_present |kbm_lun_media_unknown); 
    // need to set sense code here to get it reported (different from other luns) 
    _lun_data(sensep) = &sense_media_change; 
    switch(g_active_lun) 
    { 
    case k_lun_sd: 
      trace0(0, sd, 99, "media is SD"); 
      _mcu_register_set_bits(x_crd_ps, kbm_crd_ps_sd); 
      break; 
    case k_lun_mmc: 
      trace0(0, sd, 99, "media is MMC"); 
      _mcu_register_set_bits(x_crd_ps, kbm_crd_ps_mmc); 
      break; 
    } 
  } 
  irq_control(k_irq_sd_insert_eject, kbm_irqctl_clear |kbm_irqctl_unmask); 
  trace0(0, sd, 99, "media"); 
#ifdef k_pfm_led 
  _mcu_register_set_bits(x_gpiob_out,  kbm_gpio15);  //on 
#endif 
  thread_return_dfa(k_success); 
  EXIT_NO_MEDIA: 
  trace0(0, sd, 99, "no media"); 
  _sd_pwr_off(); 
  _lun_map_log2phy(k_log_lun_sd, k_lun_sd); 
  irq_control(k_irq_sd_insert_eject, kbm_irqctl_clear |kbm_irqctl_unmask); 
#ifdef k_pfm_led 
  _mcu_register_clr_bits(x_gpiob_out,  kbm_gpio15);  //off 
#endif 
  thread_return_dfa(k_success); 
} 
 
//------------------------------------------------------------------------------ 
//------------------------------------------------------------------------------ 
void dfa_sd_identify_device() reentrant 
{ 
  uint8 i =0; 
  //local variables for calculating the size 
  uint8 read_bl_len, c_size_mult; 
  uint16 c_size, block_len, mult; 
  uint32 blocknr; 
  uint8 Result = 0; 
  //Jump to this location on an "Emergency Exit" due to sync_abort or sync_usbreset 
  if(setjmp(_sd_context)) 
  { 
    trace0(0, sd, 0, "EMERGENCY EXIT!!"); 
    thread_return_dfa(k_error); 
  } 
  trace0(0, sd, 1, "+sd_identify_device()"); 
  //This will det the identification data from the sd device 
  trace1(0, sd, 1, "The sdc_ctl register %02x",sdc_ctl); 
  sd_pre_cmd(); 
  trace1(0, sd, 1, "The stat register %02x",_mcu_register_rd(x_sdc_stat)); 
  sdc_cmd_rsp_buf[0] = 0x42; 
  sdc_cmd_rsp_buf[1] = 0x00; 
  sdc_cmd_rsp_buf[2] = 0x00; 
  sdc_cmd_rsp_buf[3] = 0x00; 
  sdc_cmd_rsp_buf[4] = 0x00; 
  sdc_cmd_rsp_buf[5] = 0x00; 
  //sd_display_rsp_buf(); 
  //reset mode control register 
  sdc_mode_ctl = 0; 
  //ready to receive the response 
  sdc_mode_ctl |= (kbm_sdc_cmd_136b_rsp ); 
  trace1(0, sd, 1, "The sdc_mode_ctl register %02x",sdc_mode_ctl); 
  trace1(0, sd, 1, "The stat register %02x",_mcu_register_rd(x_sdc_stat)); 
  sdc_wait_status_with_timeout(kbm_sdc_stat_rsp_rdy,300); 
  trace1(0, sd, 1, "The stat register %02x",_mcu_register_rd(x_sdc_stat)); 
  sdc_read_rsp(17); 
  //sd_display_rsp_buf(); 
  _mcu_register_wr(x_sdc_stat, kbm_sdc_stat_rsp_rdy); 
  //Issue CMD3 Or GetRCA 
  trace1(0, sd, 1, "The sdc_ctl register %02x",sdc_ctl); 
  sd_pre_cmd(); 
  trace1(0, sd, 1, "The stat register %02x",_mcu_register_rd(x_sdc_stat)); 
  //If the device is mmc then we need to preset the rca 
  if(g_active_lun == k_lun_mmc) 
  { 
    g_sdc_rca_h = 0x01; 
    g_sdc_rca_l = 0x02; 
  } 
  sdc_cmd_rsp_buf[0] = 0x43; 
  sdc_cmd_rsp_buf[1] = g_sdc_rca_h; 
  sdc_cmd_rsp_buf[2] = g_sdc_rca_l; 
  sdc_cmd_rsp_buf[3] = 0x00; 
  sdc_cmd_rsp_buf[4] = 0x00; 
  sdc_cmd_rsp_buf[5] = 0x00; 
  //sd_display_rsp_buf(); 
  _sd_wait_48b_resp(); 
  if(g_active_lun == k_lun_sd) 
  { 
    g_sdc_rca_h = sdc_cmd_rsp_buf[1]; 
    g_sdc_rca_l = sdc_cmd_rsp_buf[2]; 
  } 
  //sd_display_rsp_buf(); 
  sdc_read_rsp(5); 
  _mcu_register_wr(x_sdc_stat, kbm_sdc_stat_rsp_rdy); 
  //sd_get_status(); 
  //sdc_read_rsp(17); 
  //SEND_CSD or CMD9 
  trace0(0, sd, 1, "Issue SEND_CSD"); 
  sd_pre_cmd(); 
  trace1(0, sd, 1, "The stat register %02x",_mcu_register_rd(x_sdc_stat)); 
  sdc_cmd_rsp_buf[0] = 0x49; 
  sdc_cmd_rsp_buf[1] = g_sdc_rca_h; 
  sdc_cmd_rsp_buf[2] = g_sdc_rca_l; 
  sdc_cmd_rsp_buf[3] = 0x00; 
  sdc_cmd_rsp_buf[4] = 0x00; 
  sdc_cmd_rsp_buf[5] = 0x00; 
  //reset mode control register 
  sdc_mode_ctl = 0; 
  //ready to receive the response 
  sdc_mode_ctl |= (kbm_sdc_cmd_136b_rsp); 
  trace1(0, sd, 1, "The sdc_mode_ctl register %02x",sdc_mode_ctl); 
  trace1(0, sd, 1, "The stat register %02x",_mcu_register_rd(x_sdc_stat)); 
  sdc_wait_status_with_timeout(kbm_sdc_stat_rsp_rdy,300); 
  trace1(0, sd, 1, "The stat register %02x",_mcu_register_rd(x_sdc_stat)); 
  sd_display_rsp_buf(); 
  //Calculate LBA max 
  read_bl_len = (sdc_cmd_rsp_buf[6] & 0x0f); 
  c_size = (sdc_cmd_rsp_buf[7] & 0x03); 
  c_size = (c_size << 10); 
  c_size += ((uint16)sdc_cmd_rsp_buf[8] << 2); 
  c_size += ((sdc_cmd_rsp_buf[9] & 0xc0) >> 6); 
  c_size &= 0x0fff; 
  c_size_mult = (sdc_cmd_rsp_buf[10] & 0x03); 
  c_size_mult = (c_size_mult << 1); 
  c_size_mult += ((sdc_cmd_rsp_buf[11] & 0x80) >> 7); 
  trace3(0, sd, 1, " The read_bl_len is %d , c_size is %04x, c_size_mult : %d ", read_bl_len,c_size,c_size_mult); 
  block_len = (1 << read_bl_len); 
  mult = (1 << (c_size_mult + 2)); 
  trace2(0, sd, 1, " Block_len is %d, mult = %d",block_len, mult); 
  blocknr = (((uint32)c_size + (uint32)1) * (uint32)mult); 
  trace2(0, sd, 1, "the blocknr is %04x%04x", _hw(blocknr),_lw(blocknr)); 
  blocknr = blocknr - (uint32) 1; 
  trace2(0, sd, 1, "the blocknr is %04x%04x", _hw(blocknr),_lw(blocknr)); 
  _lun_data(capacity).lba_max.u32 = blocknr; 
  _lun_data(capacity).lba_sz.u32 = block_len; 
  trace2(0, sd, 1, "Total User-addressable Sectors: %04X%04X", _hw(_lun_data(capacity).lba_max.u32), _lw(_lun_data(capacity).lba_max.u32)); 
  //Is the media write protected? 
  //Check for hardware write protection... 
  if(_mcu_register_rd(x_media_sts) & 0x01) 
    _sd_write_protected = k_true; 
  //Check for software write protection 
  if((sdc_cmd_rsp_buf[15] & 0x04) || (sdc_cmd_rsp_buf[15] & 0x08)) 
    _sd_write_protected = k_true; 
  if(_sd_write_protected == k_true) 
  { 
    _lun_data(media) |= kbm_lun_media_wrprot; 
  } else 
  { 
    _lun_data(media) &= ~kbm_lun_media_wrprot; 
  } 
  sdc_read_rsp(17); 
  _mcu_register_wr(x_sdc_stat, kbm_sdc_stat_rsp_rdy); 
  //Put the device into transfer mode and get ready to roll 
  sd_get_status(); 
  //select the crd 
  sd_pre_cmd(); 
  sdc_cmd_rsp_buf[0] = 0x47; 
  sdc_cmd_rsp_buf[1] = g_sdc_rca_h; 
  sdc_cmd_rsp_buf[2] = g_sdc_rca_l; 
  sdc_cmd_rsp_buf[3] = 0x00; 
  sdc_cmd_rsp_buf[4] = 0x00; 
  sd_display_rsp_buf(); 
  trace1(0, sd, 1, "The stat register %02x",_mcu_register_rd(x_sdc_stat)); 
  _sd_wait_48b_resp(); 
  sd_display_rsp_buf(); 
  sd_get_status(); 
  sdc_read_rsp(5); 
  //********************************** 
  //Set the block length 
  trace0(0, sd, 1, "Set the block length to 512") 
  sd_pre_cmd(); 
  sdc_cmd_rsp_buf[0] = 0x50; 
  sdc_cmd_rsp_buf[1] = 0x00; 
  sdc_cmd_rsp_buf[2] = 0x00; 
  sdc_cmd_rsp_buf[3] = 0x02; 
  sdc_cmd_rsp_buf[4] = 0x00; 
  //sd_display_rsp_buf(); 
  trace1(0, sd, 0, "The stat register %02x",_mcu_register_rd(x_sdc_stat)); 
  _sd_wait_48b_resp(); 
  sd_display_rsp_buf(); 
  sd_get_status(); 
  sdc_read_rsp(5); 
  //******************************************** 
  //set the data bus to 4 bit wide 
  if(g_active_lun == k_lun_sd) 
  { 
    sd_set_bus_width(k_four_data_lines); 
  } 
  sd_get_status(); 
  _lun_data(media) &= ~kbm_lun_media_unknown; 
  _mcu_register_clr_bits(x_fmc_clk_ctl, k_fmc_clk_ctl_sd_disable); 
  _mcu_register_set_bits(x_fmc_clk_ctl, k_fmc_clk_ctl_sd_20mhz); 
  //Enough time to let the clk freq change... should be changed to more accurate time. 
  thread_set_timer(1); 
  while(!(thread_got_sync(kbm_sync_timer))); 
  thread_clr_sync(kbm_sync_timer); 
  thread_return_dfa(k_success); 
} 
 
//------------------------------------------------------------------------------ 
//------------------------------------------------------------------------------ 
void sd_pre_erase() reentrant 
{ 
  trace0(0, sd, 0, "+sd_pre_erase();"); 
  sd_app_cmd(); 
  sd_pre_cmd(); 
  sdc_cmd_rsp_buf[0] = 0x57;      //pre_ERASE 
  sdc_cmd_rsp_buf[1] = 0X00; 
  sdc_cmd_rsp_buf[2] = 0X00; 
  sdc_cmd_rsp_buf[3] = g_n_lb_this_split.u8.hl; 
  sdc_cmd_rsp_buf[4] = g_n_lb_this_split.u8.lo; 
  _sd_wait_48b_resp(); 
  sd_display_rsp_buf(); 
  sdc_read_rsp(5); 
  return; 
} 
 
//------------------------------------------------------------------------------ 
//------------------------------------------------------------------------------ 
t_result sd_read_write_begin_xfer() reentrant 
{ 
  uint32 n_bytes_this_xfer = 0; 
  //The max lbs we can transfer is however much the host asks for 
  if(sie_is_high_speed()) 
  { 
    // timer1 disabled during sd transfers to avoid data corruption issue.  bug report #343 
    #if 0 
    //Turn off the timer interrupt for 2.0 reads 
    irq_control(k_irq_cpu_t1,  kbm_irqctl_mask); 
    //Put the led in the opposite state of idle 
    if (g_led_stays_on)             //opposite of idle state 
      gpio_clrbit(0); 
    else 
      gpio_setbit(0); 
    #endif 
 
    _lun_data(max_lb_per_split) =_fmc_get_lb_count_32(); 
    _lun_data(max_lb_per_burst) = _fmc_get_lb_count_32(); 
  } else 
  { 
    if(_sd_data_xfer_cmd == k_sdc_cmd_write_mult_blocks)  // we can issue mult write cmd 
    { 
      _lun_data(max_lb_per_split) = _fmc_get_lb_count_32(); 
      _lun_data(max_lb_per_burst) = 1; 
      //Turn off Auto xfer  and exit on count zero 
      _fmc_set_options(kbm_fmc_disable_auto_xfer |kbm_fmc_end_burst_on_count_down |kbm_fmc_xfer_512_byte_pkt); 
    } else 
    {                             //1.1 reads 
      _lun_data(max_lb_per_split) = 1; 
      _lun_data(max_lb_per_burst) = 1; 
      //Turn off Auto xfer 
      _fmc_set_options(kbm_fmc_disable_auto_xfer |kbm_fmc_xfer_512_byte_pkt); 
      trace1(0, sd, 3, "Setting the 512 byte pkt bit.. %02x", g_fmc_options); 
    } 
  } 
  //Even though the timeout should be calculated based on the CSD register, we shall calculate based on 1.1 
  //transfer speed. Currently we are at 200kBPS and this is the slowest part of the equation. 
  //On future releases, when we have overcome the hardware limitations, we shall calculate the timeout using 
  //the media-dependant values from the CSD register. 
  //Since any 16 or 32 bit computation slows down the reads/writes.. this value is computed offline 
  //It takes 2.5 ms to xfer a block of data at 200 KBPS. Let us round it to 3 ms. The  multiply by 2 to add a margin. 
  //g_fmc_timeout = ( 6 * _fmc_get_lb_count_32()); 
  // if(g_fmc_timeout < 250) 
  //  g_fmc_timeout = 250;           // Set the minimum timeout to 50 ms, to care of the device overhead 
   
  //The speeds of cards vary. So the timeout value is being set to a big number 10 sec, following the  
  //other luns 
  _fmc_set_timeout(10000); 
  trace1(0, sd, 0, "Setting the timeout value to.. %04x", g_fmc_timeout); 
  //Clear and unmask the CRC error interrupt 
  irq_control(k_irq_fmc_sdc_crc_err, kbm_irqctl_clear |kbm_irqctl_unmask); 
  return k_success; 
} 
 
//------------------------------------------------------------------------------ 
//------------------------------------------------------------------------------ 
t_result sd_read_write_end_xfer() reentrant 
{ 
  t_result Result = 0; 
  trace0(0, sd, 3, "sd_read_write_end_xfer"); 
  //Did we get here on a failure? 
  if(k_success !=_fmc_get_result()) 
  { 
    //did the media get removed 
    if(!(_mcu_register_rd(x_crd_ps) & (kbm_crd_ps_sd |kbm_crd_ps_mmc))) 
      return  _fmc_get_result(); 
    //Issue a get status to see if the device is alive and kicking ? 
    sd_get_status(); 
    //Did a CRC Error occur? or  Did we exit on a sd_blk_ovrrun error Or the card is dead? 
    if((_mcu_register_rd(x_sdc_stat) & kbm_sdc_stat_crc_err) || (_mcu_register_rd(x_fmc_err) & kbm_fmc_err_sd_blk_ovrun) 
       || (_sd_card_active == k_false)) 
    { 
      if(_sd_data_xfer_cmd == k_sdc_cmd_write_mult_blocks) 
      { 
        _lun_data(sensep) = &sense_write_error; 
      } else 
      { 
        //if the sd_blk_ovrrun error occurred there are atleast 2 blocks that were not received by the host 
        //This has not been observed in 1.1 speeds as we hand fill the buffers 
        if((_mcu_register_rd(x_fmc_err) & kbm_fmc_err_sd_blk_ovrun) && sie_is_high_speed()) 
        { 
          _mscbot_incr_residue(0x400); 
        } 
        _lun_data(sensep) = &sense_read_error; 
 
      } 
    } 
 
  } 
  //We are done with the data transfer... so issue the stop command 
  //NB:- The SD spec says in  section 4.3.3 'Some cards may require long an unpredictable times to write a block of data" 
  // So, how long should we wait? On trial and error basis I have set the timeout to 1 sec. We have good 
  // reasons now why lesser values will not work. 
  sdc_wait_status_with_timeout(kbm_sdc_stat_crd_rdy,1000); 
  //Only on 2.0 speeds and in writes 
  if(sie_is_high_speed() || ( _sd_data_xfer_cmd == k_sdc_cmd_write_mult_blocks)) 
    sd_stop_transfer(); 
 
  // timer1 disabled during sd transfers to avoid data corruption issue.  bug report #343 
  #if 0 
  if(sie_is_high_speed()) 
  { 
    //Turn on the timer interrupt 
    irq_control(k_irq_cpu_t1,  kbm_irqctl_unmask); 
    if (g_led_stays_on)             //restore to idle state 
      gpio_setbit(0); 
    else 
      gpio_clrbit(0); 
  } 
  #endif 
 
  return _fmc_get_result(); 
} 
 
//------------------------------------------------------------------------------ 
//------------------------------------------------------------------------------ 
t_result sd_read_write_begin_split() reentrant 
{ 
  trace0(0, sd, 3, "sd_read_write_begin_split"); 
#ifdef Debug_On 
  gpio_bit_is_output(5); 
  gpio_setbit(5); 
#endif 
  //did the media get removed 
  if((_sd_card_active == k_false)||(!(_mcu_register_rd(x_crd_ps) & (kbm_crd_ps_sd |kbm_crd_ps_mmc)))) 
    return k_error; 
  //issue the cmd 
  //check to see if the device is done with previous transfer 
  if(!(x_sdc_stat & kbm_sdc_stat_crd_rdy)) 
    sdc_wait_status_with_timeout(kbm_sdc_stat_crd_rdy,1000); 
  //we have some data to start with ... so do the prep 
  trace1(0, tm, 0, "@%d Start wait state tran", g_cdm0); 
  sd_get_status(); 
  while((_sd_state != k_sdc_state_tran) && (_sd_card_active == k_true)) 
  { 
    sd_get_status(); 
  } 
  trace1(0, tm, 3, "@%d End wait state tran", g_cdm0); 
  //did the media get removed 
  if(_sd_card_active == k_false) 
    return k_error; 
  //Issue the Read/write  command  CMD18 
  trace1(0, sd, 3, "The stat register %02x",_mcu_register_rd(x_sdc_stat)); 
  //if it is a write issue pre erase 
  if((_sd_data_xfer_cmd == k_sdc_cmd_write_mult_blocks) && (g_active_lun == k_lun_sd)) 
    sd_pre_erase(); 
  _mcu_register_wr(x_sdc_stat, kbm_sdc_stat_rsp_rdy); 
  if(g_active_lun == k_lun_sd) 
    sdc_ctl |= (kbm_sdc_id_data | kbm_sdc_bus_type); 
  else 
    sdc_ctl |= (kbm_sdc_id_data); 
  trace1(0, sd, 3, "The fmc_clk_ctl register %02x",x_fmc_clk_ctl); 
  trace1(0, sd, 3, "The sdc_ctl register %02x",sdc_ctl); 
  //sdc_wait_status_with_timeout(kbm_sdc_stat_crd_rdy,1000); 
  if(!(x_sdc_stat & kbm_sdc_stat_cmd_rdy)) 
    sdc_wait_status_with_timeout(kbm_sdc_stat_cmd_rdy,1000); 
  _sd_start_addr_this_xfer.u32 = _fmc_get_start_lb_32(); 
  trace2(0, sd, 3, "read/write addr:%04X%04X ", _hw(_sd_start_addr_this_xfer.u32), _lw(_sd_start_addr_this_xfer.u32)); 
  _sd_start_addr_this_xfer.u32 =  _sd_start_addr_this_xfer.u32 * (uint32) 512; 
  trace2(0, sd, 3, "read/write addr:%04X%04X ", _hw(_sd_start_addr_this_xfer.u32), _lw(_sd_start_addr_this_xfer.u32)); 
  sdc_cmd_rsp_buf[0] = _sd_data_xfer_cmd; 
  sdc_cmd_rsp_buf[1] = _sd_start_addr_this_xfer.u8.hi; 
  sdc_cmd_rsp_buf[2] = _sd_start_addr_this_xfer.u8.lh; 
  sdc_cmd_rsp_buf[3] = _sd_start_addr_this_xfer.u8.hl; 
  sdc_cmd_rsp_buf[4] = _sd_start_addr_this_xfer.u8.lo; 
  sd_display_rsp_buf(); 
  trace1(0, sd, 3, "The stat register %02x",_mcu_register_rd(x_sdc_stat)); 
  trace0(0, sd, 0, "I am HERE!!!"); 
  //Not calling the macro coz we have to wait a little longer for the response 
  // _sd_wait_48b_resp(); 
  sdc_mode_ctl = 0; 
  trace1(0, sd, 0, "The stat register %02x",_mcu_register_rd(x_sdc_stat)); 
  sdc_mode_ctl |= kbm_sdc_cmd_48b_rsp; 
  trace1(0, sd, 0, "The sdc_mode_ctl register %02x",sdc_mode_ctl); 
  sdc_wait_status_with_timeout(kbm_sdc_stat_rsp_rdy,2000); 
  trace1(0, sd, 0, "The stat register %02x",_mcu_register_rd(x_sdc_stat)); 
  //did the media get removed 
  if(_sd_card_active == k_false) 
    return k_error; 
  trace1(0, sd, 3, "The stat register %02x",_mcu_register_rd(x_sdc_stat)); 
  _mcu_register_wr(x_sdc_stat, kbm_sdc_stat_rsp_rdy); 
  _mcu_register_wr(x_sdc_stat, kbm_sdc_stat_crc_err); 
  _mcu_register_clr_bits(x_sdc_stat_msk,kbm_sdc_stat_crc_err); 
  trace1(0, sd, 3, "The stat register %02x",_mcu_register_rd(x_sdc_stat)); 
  trace1(0, sd, 3, "The stat msk register %02x",x_sdc_stat_msk); 
#ifdef Debug_On 
  gpio_clrbit(5); 
#endif 
   return k_success; 
} 
 
//------------------------------------------------------------------------------ 
//------------------------------------------------------------------------------ 
xdata t_udw32 _current_byte_count; 
t_result sd_read_intra_burst() reentrant 
{ 
  //get the buffer ready to receive data 
  trace0(0, sd, 3, "sd_read_intra_burst"); 
  if (!sie_is_high_speed()) 
  { 
  #ifdef k_dbg_on 
    dbg_bufferstat(); 
  #endif 
    _mcu_register_clr_bits(x_ep2_ctl, kbm_ep2_ctl_ramwr_tog | kbm_ep2_ctl_rdtog_valid); 
    _mcu_register_set_bits(x_ep2_ctl, kbm_ep2_ctl_wrtog_valid); 
    _mcu_register_wr(x_isr0, kbm_isr0_ramwr_a); 
  #ifdef k_dbg_on 
    dbg_bufferstat(); 
  #endif 
  } 
  else 
  { 
    //make a copy of this register for read retries 
    _current_byte_count.u8.hi = _mcu_register_rd(x_fmc_cnt3); 
    _current_byte_count.u8.lh = _mcu_register_rd(x_fmc_cnt2); 
    _current_byte_count.u8.hl = _mcu_register_rd(x_fmc_cnt1); 
    _current_byte_count.u8.lo = _mcu_register_rd(x_fmc_cnt0); 
  } 
  return sd_read_write_begin_split(); 
} 
 
//------------------------------------------------------------------------------ 
//------------------------------------------------------------------------------ 
uint8 k; 
uint8 xdata * data data_ptr; 
uint8 xdata temp_buf[64]; 
t_result sd_read_end_burst() reentrant 
{ 
  //allocate 64 bytes of xdata space for the temporary holder 
  uint8  n, Result = 0; 
  uint16 offset = 0; 
  t_udw32 n_bytes; 
  trace0(0, sd, 3, "sd_read_end_burst"); 
  g_cdm0 = 0; 
  //Did a CRC Error occur? 
  if (_mcu_register_rd(x_sdc_stat) & kbm_sdc_stat_crc_err) 
  { 
    trace0(0, sd, 0, "A CRC Error occurred"); 
    return k_error; 
  } 
  //Just for High speed 
  if (sie_is_high_speed()) 
  { 
    if (_mcu_register_rd(x_fmc_err) & kbm_fmc_err_sd_blk_ovrun) 
    { 
      //stop the current transfer 
      sd_stop_transfer(); 
      n_bytes.u8.hi = _mcu_register_rd(x_fmc_cnt3); 
      n_bytes.u8.lh = _mcu_register_rd(x_fmc_cnt2); 
      n_bytes.u8.hl = _mcu_register_rd(x_fmc_cnt1); 
      n_bytes.u8.lo = _mcu_register_rd(x_fmc_cnt0); 
      trace4(0, sd, 0, "fmc_cnt#: %02x%02x%02x%02x", x_fmc_cnt3, x_fmc_cnt2, x_fmc_cnt1, x_fmc_cnt0); 
      //No. of bytes sent to the host 
      n_bytes.u32 = _current_byte_count.u32 - n_bytes.u32; 
      //New LBA for the retry 
      _fmc_set_start_lb_32(_fmc_get_start_lb_32() + (n_bytes.u32 / 512L));  
      trace4(0, sd, 0, "read lba:%04X%04X xfer_length:%04X%04X", _hw(_fmc_get_start_lb_32()), _lw(_fmc_get_start_lb_32()), _hw(g_n_lb_this_xfer.u32), _lw(g_n_lb_this_xfer.u32)); 
      //clear and unmask the sd_blk_ovrrun interrupt so that we will be warned if this occurs 
      irq_control(k_irq_fmc_err_sd_blk_ovrun, kbm_irqctl_clear); 
      irq_control(k_irq_fmc_err, kbm_irqctl_unmask); 
      //Check to see if the last two packets have gone out the door 
      trace4(0, sd, 0, "bufferstat - ramrd_b:%d ramrd_a:%d ramwr_b:%d ramwr_a:%d", (_mcu_register_rd(x_isr0) & kbm_isr0_ramrd_b) ? 1 : 0, (_mcu_register_rd(x_isr0) & kbm_isr0_ramrd_a) ? 1 : 0, (_mcu_register_rd(x_isr0) & kbm_isr0_ramwr_b) ? 1 : 0, (_mcu_register_rd(x_isr0) & kbm_isr0_ramwr_a) ? 1 : 0); 
      Result = sdc_wait_irq_with_timeout((kbm_isr0_ramrd_a |kbm_isr0_ramrd_b), 2000); 
      if(Result != k_success) 
        return Result; 
      trace4(0, sd, 0, "bufferstat - ramrd_b:%d ramrd_a:%d ramwr_b:%d ramwr_a:%d", (_mcu_register_rd(x_isr0) & kbm_isr0_ramrd_b) ? 1 : 0, (_mcu_register_rd(x_isr0) & kbm_isr0_ramrd_a) ? 1 : 0, (_mcu_register_rd(x_isr0) & kbm_isr0_ramwr_b) ? 1 : 0, (_mcu_register_rd(x_isr0) & kbm_isr0_ramwr_a) ? 1 : 0); 
      return (k_resume); 
    } 
    return (k_success); 
  } 
  for(n=0; n<8;n++) 
  { 
    _mcu_begin_critical_section(); 
    //Point to buffer A 
#ifdef k_dbg_on 
    dbg_dumpram(_l(addr_word),_h(addr_word), FS_pktsz); 
#endif 
    _mcu_register_wr(x_sram_addr_lo, k_pkt_addrlo[4] + _l(offset)); 
    _mcu_register_wr(x_sram_addr_hi, k_pkt_addrhi[4] + _h(offset)); 
    _nop_(); 
    _sd_copy_buf_to_xdata(); 
    //Point to buffer B 
    _mcu_register_wr(x_sram_addr_lo, k_pkt_addrlo[5]); 
    _mcu_register_wr(x_sram_addr_hi, k_pkt_addrhi[5]); 
    _nop_(); 
    _sd_copy_xdata_to_buf(); 
    //see what we have copied 
#ifdef k_dbg_on 
    dbg_dumpram(_l(addr_word),_h(addr_word), FS_pktsz); 
#endif 
 
 
    //tell how many bytes to xmit 
    _mcu_register_wr(x_ramrdbc_b2, FS_pktsz); 
    _mcu_register_wr(x_ramrdbc_b1, FS_pktsz); 
    //transmit the packet 
    _mcu_register_clr_bits(x_ep2_ctl, kbm_ep2_ctl_wrtog_valid); 
    // aim/set ramrd_tog at buffer B so it is ARMED for tx (clrs isr0 ramrd_b bit also) 
    _mcu_register_set_bits(x_ep2_ctl, kbm_ep2_ctl_rdtog_valid |kbm_ep2_ctl_ramrd_tog); 
    // _mcu_register_wr(x_isr0, kbm_isr0_ramrd_b); 
    _nop_();_nop_();_nop_();_nop_();_nop_(); 
    _mcu_end_critical_section(); 
#ifdef sd_fast_1point1 
    while(!(_mcu_register_rd(x_isr0) & kbm_isr0_ramrd_b)); 
#else 
    Result = sdc_wait_irq_with_timeout(kbm_isr0_ramrd_b, 2000); 
    if(Result != k_success) 
      return Result; 
#endif 
    offset = offset + FS_pktsz; 
  } 
#ifdef k_dbg_on 
  dbg_bufferstat(); 
#endif 
  return k_success; 
} 
 
//------------------------------------------------------------------------------ 
//------------------------------------------------------------------------------ 
void dfa_sd_read() reentrant 
{ 
  g_cdm0 = 0; 
  trace1(0, tm, 0, "@%d Start sd_read", g_cdm0); 
  trace0(0, sd, 3, "+sd_read()"); 
  //Jump to this location on an "Emergency Exit" due to sync_abort or sync_usbreset 
  if(setjmp(_sd_context)) 
    __thread_return_dfa(k_command_failed); 
  //clear and unmask the sd_blk_ovrrun interrupt so that we will be warned if this occurs 
  irq_control(k_irq_fmc_err_sd_blk_ovrun, kbm_irqctl_clear); 
  irq_control(k_irq_fmc_err, kbm_irqctl_unmask); 
  if(sie_is_high_speed()) 
  { 
    trace0(0, sd, 3, "Doing High Speed!"); 
    _sd_data_xfer_cmd = k_sdc_cmd_read_mult_blocks; 
    //set the callbacks 
    //_fmc_set_callback(sd_read_write_begin_xfer,sd_read_write_end_xfer,sd_read_write_begin_split, fmc_dflt_callback,fmc_dflt_callback,fmc_dflt_callback,fmc_dflt_callback); 
    _fmc_set_callback(sd_read_write_begin_xfer,sd_read_write_end_xfer,fmc_dflt_callback, fmc_dflt_callback,fmc_dflt_callback,sd_read_intra_burst,sd_read_end_burst); 
  } else 
  { 
    trace0(0, sd, 3, "Doing Full Speed!"); 
    _sd_data_xfer_cmd = k_sdc_cmd_read_sgl_block; 
    //set the callbacks 
    _fmc_set_callback(sd_read_write_begin_xfer,sd_read_write_end_xfer,fmc_dflt_callback,fmc_dflt_callback,fmc_dflt_callback,sd_read_intra_burst,sd_read_end_burst); 
  } 
  // 04/28/02 tbh/ds - xdata is not initialized to zero on power up. 
  // do not depend on the fmc options being cleared to zero at power on. 
  _fmc_set_options(0); 
  dfa_lun_read(); 
  trace1(0, tm, 0, "@%d End sd_read", g_cdm0); 
} 
 
//------------------------------------------------------------------------------ 
//------------------------------------------------------------------------------ 
t_result sd_write_copy_data() reentrant; 
t_result sd_write_copy_data() reentrant 
{ 
  //allocate 64 bytes of xdata space for the temporary holder 
  uint8  n, Result = 0; 
  uint16 offset = 0; 
  g_cdm0 = 0; 
  trace1(0, sd, 0, "@%d Start write copy data", g_cdm0); 
  mmu_deallocate(5);              // ready to get the next set of data 
  //mmu_deallocate(4); 
  _mcu_register_wr(x_isr0, kbm_isr0_ramwr_a); 
  //_buffer_rx_enable(4); 
  _mcu_register_wr(x_ep2_ctl, (_mcu_register_rd(x_ep2_ctl) & ~kbm_ep2_ctl_ramwr_tog) |kbm_ep2_ctl_wrtog_valid); 
  for(n=0; n<8;n++) 
  { 
    //get the buffer ready to receive data 
#ifdef k_dbg_on 
    dbg_bufferstat(); 
#endif 
#ifdef sd_fast_1point1 
    thread_set_timer(2000); 
    while(!thread_got_sync(kbm_sync_usbrx)) 
    { 
      if(thread_got_sync(kbm_sync_timer)) 
      { 
        return k_timeout; 
      } 
    } 
    thread_clr_sync(kbm_sync_usbrx); 
#else 
    Result = sdc_wait_irq_with_timeout(kbm_isr0_ramwr_a, 2000); 
    if(Result != k_success) 
      return Result; 
#endif 
 
 
    //Start the copying process 
    trace1(0, tm, 0, "@%d Start copy", g_cdm0); 
    mcu_begin_critical_section(); 
    //Point to buffer A 
#ifdef k_dbg_on 
    dbg_dumpram(k_pkt_addrlo[4],k_pkt_addrhi[4], FS_pktsz); 
#endif 
    _mcu_register_wr(x_sram_addr_lo, k_pkt_addrlo[4]); 
    _mcu_register_wr(x_sram_addr_hi,k_pkt_addrhi[4]); 
    _nop_(); 
    _sd_copy_buf_to_xdata(); 
    //let the next pkt come in 
    if(n != 7) 
    { 
      //mmu_deallocate(4); 
      _mcu_register_wr(x_isr0, kbm_isr0_ramwr_a); 
      //_buffer_rx_enable(4); 
      _mcu_register_wr(x_ep2_ctl, (_mcu_register_rd(x_ep2_ctl) & ~kbm_ep2_ctl_ramwr_tog) |kbm_ep2_ctl_wrtog_valid); 
    } 
    //Point to buffer B 
    _mcu_register_wr(x_sram_addr_lo, k_pkt_addrlo[5] + _l(offset)); 
    _mcu_register_wr(x_sram_addr_hi, k_pkt_addrhi[5] + _h(offset)); 
    _nop_(); 
    _sd_copy_xdata_to_buf(); 
    offset = offset + FS_pktsz; 
    mcu_end_critical_section(); 
    trace1(0, tm, 0, "@%d End copy", g_cdm0); 
    //see what we have copied 
#ifdef k_dbg_on 
    dbg_dumpram(_l(addr_word),_h(addr_word), FS_pktsz); 
#endif 
  } 
  trace0(0, sd, 0, "Setting Data Loaded to true"); 
  _sd_data_loaded = k_true; 
  return k_success; 
} 
 
//------------------------------------------------------------------------------ 
//------------------------------------------------------------------------------ 
t_result sd_write_begin_burst() reentrant 
{ 
  t_result Result = 0; 
  trace0(0, sd, 3, "sd_write_begin_burst"); 
  if(_sd_data_loaded == k_false) 
  { 
    trace0(0, sd, 0, "Data Not Loaded yet.."); 
    Result = sd_write_copy_data(); 
  } 
  //Now the 512 bytes from Buffer B needs to go to fmdu 
  //get the buffer B ready to transmit data 
#ifdef k_dbg_on 
  dbg_bufferstat(); 
#endif 
  mcu_begin_critical_section(); 
  thread_clr_sync(kbm_sync_usbtx); 
  _mcu_register_wr(x_isr0, kbm_isr0_ramrd_b); 
  _mcu_register_wr(x_ramrdbc_b2, _l(512)); 
  _mcu_register_wr(x_ramrdbc_b1, _h(512)); 
  // aim/set ramrd_tog at buffer B so it is ARMED for tx (clrs isr0 ramrd_b bit also) 
  _mcu_register_set_bits(x_ep2_ctl, kbm_ep2_ctl_rdtog_valid |kbm_ep2_ctl_ramrd_tog); 
#ifdef k_dbg_on 
  dbg_bufferstat(); 
#endif 
 
 
  // mask it back in to get the irq 
  _mcu_register_clr_bits(x_imr0, kbm_isr0_ramrd_b); 
  mcu_end_critical_section(); 
  _mcu_register_wr(x_sdc_stat, kbm_sdc_stat_crd_rdy); 
  trace1(0, sd, 0, "@%d End write burst", g_cdm0); 
  _sd_data_loaded = k_false; 
  return k_success; 
} 
 
//------------------------------------------------------------------------------ 
//------------------------------------------------------------------------------ 
t_result sd_write_end_burst() reentrant 
{ 
  t_result Result; 
  //if this is an aborted transfer get out immediately 
  if(k_success != g_fmc_rslt) 
  { 
    trace2(0, sd, 3, "Aborted transfer ... getting out of here! %d  %d",g_fmc_rslt, _fmc_get_result()); 
    return _fmc_get_result(); 
  } 
  trace1(0, sd, 1, "_sd_data_loaded : %02x",_sd_data_loaded); 
  //The data has been sent to fmdu. 
  if((_sd_data_loaded == k_false) && (_mscbot_residue_32() - (uint32)512)) 
  { 
    trace0(0, sd, 0, "Data Not Loaded yet.."); 
    Result = sd_write_copy_data(); 
  } 
  trace1(0, sd, 3, "The stat register %02x",_mcu_register_rd(x_sdc_stat)); 
  Result = fmc_wait_blk_irq_with_timeout(g_fmc_timeout); 
  if(Result == k_success) 
    if(!(x_sdc_stat & kbm_sdc_stat_crd_rdy)) 
      sdc_wait_status_with_timeout(kbm_sdc_stat_crd_rdy,1000); 
  return Result; 
} 
 
//------------------------------------------------------------------------------ 
//------------------------------------------------------------------------------ 
void dfa_sd_write(void) reentrant 
{ 
  //t_result result; 
  trace0(0, sd, 3, "+sd_write()"); 
  //Jump to this location on an "Emergency Exit" due to sync_abort or sync_usbreset 
  if(setjmp(_sd_context)) 
  { 
    trace0(0, sd, 0, "EMERGENCY EXIT!!"); 
    __thread_return_dfa(k_command_failed); 
  } 
  //Is the media write protected? 
  /*if(_sd_write_protected) 
  { 
    _lun_data(sensep) = &sense_write_protect; 
    __thread_return_dfa(k_command_failed) 
  } */ 
  if(sie_is_high_speed()) 
  { 
    trace0(0, sd, 3, "Doing High Speed!"); 
    _sd_data_xfer_cmd = k_sdc_cmd_write_mult_blocks; 
    //set the callbacks 
    _fmc_set_callback(sd_read_write_begin_xfer,sd_read_write_end_xfer,sd_read_write_begin_split, fmc_dflt_callback,fmc_dflt_callback,fmc_dflt_callback,fmc_dflt_callback); 
  } else 
  { 
    trace0(0, sd, 3, "Doing Full Speed!"); 
    _sd_data_xfer_cmd = k_sdc_cmd_write_mult_blocks; 
    //set the callbacks 
    _fmc_set_callback(sd_read_write_begin_xfer,sd_read_write_end_xfer,sd_read_write_begin_split, fmc_dflt_callback,sd_write_begin_burst,fmc_dflt_callback, sd_write_end_burst); 
  } 
  // 04/28/02 tbh/ds - xdata is not initialized to zero on power up. 
  // do not depend on the fmc options being cleared to zero at power on. 
  _fmc_set_options(0); 
  dfa_lun_write(); 
} 
 
//------------------------------------------------------------------------------ 
//------------------------------------------------------------------------------ 
void dfa_sd_verify(void) reentrant 
{ 
  trace0(0, sd, 0, "+sd_verify()"); 
  //Jump to this location on an "Emergency Exit" due to sync_abort or sync_usbreset 
  if(setjmp(_sd_context)) 
    thread_return_dfa(k_command_failed); 
  if(sie_is_high_speed()) 
  { 
    trace0(0, sd, 0, "Doing High Speed!"); 
    _sd_data_xfer_cmd = k_sdc_cmd_write_mult_blocks; 
    //set the callbacks 
    _fmc_set_callback(sd_read_write_begin_xfer,sd_read_write_end_xfer,sd_read_write_begin_split, fmc_dflt_callback,fmc_dflt_callback,fmc_dflt_callback,fmc_dflt_callback); 
  } else 
  { 
    trace0(0, sd, 0, "Doing Full Speed!"); 
    _sd_data_xfer_cmd = k_sdc_cmd_write_mult_blocks; 
    //set the callbacks 
    _fmc_set_callback(sd_read_write_begin_xfer,sd_read_write_end_xfer,sd_read_write_begin_split, fmc_dflt_callback, sd_write_begin_burst,fmc_dflt_callback,sd_write_end_burst); 
  } 
  // 04/28/02 tbh/ds - xdata is not initialized to zero on power up. 
  // do not depend on the fmc options being cleared to zero at power on. 
  _fmc_set_options(0); 
  dfa_lun_verify(); 
} 
 
//---eof------------------------------------------------------------------------