www.pudn.com > SMSC USB2.0.zip > ata.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. 
  ____________________________________________________________________________ 
  ____________________________________________________________________________ 
   -  
  ____________________________________________________________________________ 
  comments tbd 
  ____________________________________________________________________________ 
  Revision History 
  Date      Who  Comment 
  ________  ___  _____________________________________________________________ 
  04/20/01  cds  initial version 
  09/##/01  tbh  optimized for speed 
  11/16/01  cds  - updated code to use lun instance data instead of globals. 
                 - tried global search & replace of thread_clr_sync to 
                   _thread_clr_sync but it turns out the bug is more devious 
                   and is attached to bits_got, not the thread_clr_sync. 
  11/26/01  cds  modified ata_pio_rd to include blk-xfer for 210 instead of 
                 calling pio_wr_pkt.  Tests whether high-speed or full-speed 
                 and only sets 512-byte-pkts for blk-xfer in hs connections. 
  11/28/01  rcc  modified ata_pio_rd Tests whether high-speed or full-speed 
                 and only sets 512-byte-pkts for blk-xfer in hs connections also 
                 CLEARS this bit if FS and does this before the blk-xfer bit it set. 
  12/04/01  tbh  tweaked identify_device for newish fmc style _lun_data access. 
  12/13/01  tbh  tweaked data xfers to use fmc_transfer() 
  02/26/02  tbh  added NULL placeholder in _fmc_set_callback() invocations 
  03/12/02  tbh  minor renaming to fit the fmc project's idiom 
  04/15/02  tbh  ensure fmc_options set to 0 before read/write 
  05/20/02  tbh  turn off multiple emulation in ata_read_end_xfer. 
                 added ata_write_end_xfer to ensure multiple emulation turned off 
                 in case of aborted transfer. 
  06/05/02  cds  added trace statement to dump the # of sectors per irq (burst) 
                 of the read/write multiple command from identify device data 
  06/10/02  tbh  performance enhancements using _ata_register_wr() macro to replace 
                 ata_register_wr() function.  other minor enhancements. 
  06/20/02  tbh  in ata_initialize_media2 set the CF into pio mode 4 (from default 0) 
  06/25/02  tbh  subtract 1 from lba_max to get correct capacity reported to host 
                 to fix the problem with quick format. 
  07/09/02  cds  replaced _work_buf with common g_sector_buffer, as used in 
                 sm, ms, and nand luns. 
  07/23/02  cds  added obsolete PIO Transfer Cycle Mode field (word 51) to 
                 identify device data.  Moved the setting of the cfc_ata_mode_ctl 
                 register into the identify device function so that as soon as 
                 the mode is determined, the register is set, but before then, 
                 PIO mode 0 is used (to obtain the ID data). 
  08/29/02  tbh  added rudimentary profiling of ata drives (to identify the 
                 edata 32mb cf card and drop it to mode 0) 
  09/10/02  tbh  forced reporting as removable media always because some CF 
                 cards seem to say they are fixed disks. 
  10/17/02  cds  - project-wide lun data & vtbl paging to reduce code space. 
                 - removed g_active_media from _lun_data, _lun_() virtual functions 
                 - added _lun_data_rd() and _lun_data_wr() macros to bypass lun paging 
                 - added lun_set_active(log_lun) function to switch luns 
                                   
============================================================================*/ 
#define __ata_dot_c__ 
#include "project.h" 
#include "dev.h" 
 
code _vtbl_defn(ata); 
 
#ifdef k_flash_family 
#define k_lun_ata k_lun_cf 
#else 
#define k_lun_ata 0 
#endif 
 
// to use this optimization you want fmc_select_cf() to set CKCON = 0x00 also... 
#define _ata_clock_slow() CKCON=0x02 
#define _ata_clock_fast() CKCON=0x00 
 
//------------------------------------------------------------------------------ 
// exported globals 
xdata uint8   g_dev_max_dma_mode;  // represents max DMA mode supported by the device 
 
// ata device characteristics 
bit           g_use_dma; 
bit           g_disable_iordy; 
xdata uint8   g_dma_mode_current; 
 
// ata device capabilities & properties 
// these are all relevant properties from ata identify device data. 
xdata uint16  g_ata_dev_cmdset_support_1; 
xdata uint16  g_ata_dev_cmdset_support_2; 
xdata uint16  g_ata_dev_cmdset_support_3; 
xdata uint16  g_ata_dev_cmdset_enabled_1; 
xdata uint16  g_ata_dev_cmdset_enabled_2; 
xdata uint16  g_ata_dev_cmdset_enabled_3; 
xdata uint16  g_ata_dev_num_log_cyl; 
xdata uint16  g_ata_dev_num_log_hds; 
xdata uint16  g_ata_dev_num_log_sec_per_trk; 
xdata uint16  g_ata_dev_caps_1; 
xdata uint16  g_ata_dev_caps_2; 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   ata_dump_build_options 
// 
// Declaration: 
//   void ata_dump_build_options(void); 
// 
// Purpose: 
//   Debugging.  Dumps the build options. 
// 
// Arguments: 
//   None. 
// 
// Return: 
//   None. 
// 
// Notes: 
//   None. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
void ata_dump_build_options(void) reentrant; 
void ata_dump_build_options() reentrant 
{ 
  trace0(0, ata, 0, "ata_dump_build_options()"); 
  trace1(0, ata, 0, "dma mode: %02x", k_dma_mode); 
  trace0(0, ata, 0, "Shadow PIO_COMPLETE: Y"); 
  trace0(0, ata, 0, "ATA Device Support:Y"); 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   ata_register_wr 
// 
// Declaration: 
//   void ata_register_wr(t_ata_register_ref r, uint8 d); 
// 
// Purpose: 
//   Write a value to an ata register. 
// 
// Arguments: 
//   r - the t_ata_register_ref identifying the register to be written. 
//   d - the uint8 to be written there. 
// 
// Return: 
//   None. 
// 
// Notes: 
//   None. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
void ata_register_wr(t_ata_register_ref r, uint8 d) reentrant 
{ 
  _ata_clock_slow(); 
  trace2(0, ata, 0, "ata_register_wr(%04x=%02x)", (uint16)r, d); 
  while( !(x_cfc_stat & kbm_cfc_stat_xrdy));  // <-- WARNING! WARNING! DANGER, WILL ROBINSON! 
  *r = d; 
  _ata_clock_fast(); 
} 
 
//------------------------------------------------------------------------------ 
// performance enhancement is to use a macro in ata_read/write_begin_burst 
// while issuing the command 
//------------------------------------------------------------------------------ 
#define _ata_register_wr(__r, __d)            \ 
{                                             \ 
  _ata_clock_slow();                          \ 
  while( !(x_cfc_stat & kbm_cfc_stat_xrdy));  \ 
  *(__r) = (__d);                             \ 
  _ata_clock_fast();                          \ 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   ata_register_rd 
// 
// Declaration: 
//   uint8 ata_register_rd(t_ata_register_ref r); 
// 
// Purpose: 
//   Read a value from an ata register. 
// 
// Arguments: 
//   r - the t_register_ref identifying the register to be read. 
// 
// Return: 
//   The uint8 to read from the ata register. 
// 
// Notes: 
//   None. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
uint8 ata_register_rd(t_ata_register_ref r) reentrant 
{ 
  uint8 val; 
  _mcu_begin_critical_section(); 
  _ata_clock_slow(); 
  // start read cycle, then get data from lsb_ata 
  val = *r; 
  _nop_(); _nop_(); _nop_(); 
  val = x_lsb_ata; 
  _ata_clock_fast(); 
  _mcu_end_critical_section(); 
  return val; 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   ata_read_status 
// 
// Declaration: 
//   uint8 ata_read_status(void); 
// 
// Purpose: 
//   Read the ata status register. 
// 
// Arguments: 
//   None. 
// 
// Return: 
//   The uint8 read from the ata status register. 
// 
// Notes: 
//   Reading the ata status register clears the ata interrupt (in the drive). 
//   This function also clears and unmasks the interrupts k_irq_ata and k_irq_fmc_cfc_intrq. 
// 
// Since: 
//   fmc-1.0 
// 
// History: 
//   06/18/02 tbh - modified to leave the ata interrupt masked. 
//                - modified this wait_irq_with_timeout to break out if ata irq 
//                  bit seen via polling. 
//                - this cut about 270usec off the begin burst, decreasing cbw time. 
//------------------------------------------------------------------------------ 
uint8 ata_read_status(void) reentrant; 
uint8 ata_read_status() reentrant 
{ 
  uint8 val; 
  _mcu_begin_critical_section(); 
  _ata_clock_slow(); 
  val = ata_status; 
  _nop_(); _nop_(); _nop_(); 
  val = x_lsb_ata; 
  _ata_clock_fast(); 
  // clear compact flash status as well 
  // could do this... but its slower... 
  //irq_control(k_irq_fmc_cfc_intrq, kbm_irqctl_clear |kbm_irqctl_unmask); 
  x_cfc_stat = kbm_cfc_stat_intrq; 
  x_cfc_stat_msk &= ~kbm_cfc_stat_intrq; 
  // could do this... but its slower... 
  //irq_control(k_irq_ata, kbm_irqctl_clear |kbm_irqctl_unmask); 
  x_isr0  = kbm_isr0_ata_irq; 
  // 06/18/02 tbh - ata_wait_irq_with_timeout() now polls this bit to avoid 
  // the overhead of having the kernel interrupt handler process all of the 
  // nested interrupts beneath the fmc/ata irq.  (it takes so long in the isr0 
  // handler to check every possible nested source for the ata/fmc irq that 
  // it is dramatically faster to just poll the register. 
  //x_imr0 &= ~kbm_isr0_ata_irq; 
  trace1(0, ata, 99, "status:%02x", val); 
  _mcu_end_critical_section(); 
  return val; 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   ata_reset 
// 
// Declaration: 
//   void ata_reset(void); 
// 
// Purpose: 
//   Perform a hard reset on the ata drive via the reset pin in the ata cable. 
// 
// Arguments: 
//   None. 
// 
// Return: 
//   None. 
// 
// Notes: 
//   This is a FUNCTION, not a DFA. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
#define kix_ata_reset_pin 0x05 
void ata_reset() reentrant 
{ 
  trace0(0, ata, 0, "ata_reset()"); 
  _mcu_register_set_bits(x_cfc_ata_mode_ctl, kbm_cfc_ata_mode_ctl_dev_rst); 
  thread_set_timer(1); 
  while(!thread_got_sync(kbm_sync_timer)); 
  _mcu_register_clr_bits(x_cfc_ata_mode_ctl, kbm_cfc_ata_mode_ctl_dev_rst); 
  // spec requires at least 2msec wait here... make sure other code induces wait... 
  thread_set_timer(3); 
  while(!thread_got_sync(kbm_sync_timer)); 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   dfa_ata_reset_media 
// 
// Declaration: 
//   void dfa_ata_reset_media(void); 
// 
// Purpose: 
//   Perform a hard reset on the ata drive via the reset pin in the ata cable. 
// 
// Arguments: 
//   None. 
// 
// Return: 
//   None. 
// 
// Notes: 
//   This is a DFA, not a FUNCTION. 
//   It overrides dfa_lun_reset. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
void dfa_ata_reset_media() reentrant 
{ 
  trace0(0, ata, 0, "dfa_ata_reset_media()"); 
  ata_reset(); 
  _thread_return_dfa(k_success); 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   ata_poll_status_not_busy 
// 
// Declaration: 
//   void ata_poll_status_not_busy(void); 
// 
// Purpose: 
//   Poll the ata status register to watch for BSY going inactive. 
//   When it does deliver kbm_sync_ata_rdy to the active thread. 
// 
// Arguments: 
//   None. 
// 
// Return: 
//   None. 
// 
// Notes: 
//   MinimOS installable thread poller. 
//   Watches for synchronizing events that don't have associated interrupts 
//   in situations where the thread must yield to avoid blocking other threads. 
//   Uninstalls itself when sync delivered to its thread. 
//   This is a FUNCTION, not a DFA. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
void ata_poll_status_not_busy(void) reentrant; 
void ata_poll_status_not_busy() reentrant 
{ 
  trace0(0, ata, 90, "ata_poll_status_not_busy()"); 
  if (!(ata_read_status() & kbm_ata_status_bsy)) 
  { 
    thread_set_sync(g_tid, kbm_sync_ata_rdy); 
    thread_set_poller((t_thd_entry)NULL); 
  } 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   ata_wait_status_with_timeout 
// 
// Declaration: 
//   t_result ata_wait_status_with_timeout(uint8 mask, uint8 val, uint16 ticks, uint8 *statusp); 
// 
// Purpose: 
//   Poll the ata status register to watch for specific bits matching specific values. 
// 
// Arguments: 
//   mask    - a uint8 with 1's in the bits to be compared 
//   val     - a uint8 representing the desired value 
//   ticks   - a uint16 representing the timeout limit 
//   statusp - a pointer to a uint8, where the value of the status register is returned. 
//             statusp can be NULL, in which case no value is returned. 
// 
// Return: 
//   A t_result indicating: 
//     k_success - the ata status register logically anded with the mask equals the 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 ata_wait_status_with_timeout(uint8 mask, uint8 val, uint16 ticks, uint8 *statusp) reentrant; 
t_result ata_wait_status_with_timeout(uint8 mask, uint8 val, uint16 ticks, uint8 *statusp) reentrant 
{ 
  uint8 status; 
  t_sync sync; 
 
  trace2(0, ata, 1, "ata_wait_status_with_timeout(mask:0x%02X ticks:%d)", mask, ticks); 
  _stack_check(); 
  // 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+1); 
  status = ata_read_status(); 
  do 
  { 
    sync = thread_got_sync(kbm_sync_abort |kbm_sync_usbrst |kbm_sync_timer); 
    _thread_clr_sync(sync); 
    if (sync & kbm_sync_usbrst) 
    { 
      trace0(0, ata, 1, "ata_wait_status_with_timeout() - error: kbm_sync_usbrst"); 
      return k_usbreset; 
    } 
    if (sync & kbm_sync_abort) 
    { 
      trace0(0, ata, 1, "ata_wait_status_with_timeout() - error: kbm_sync_abort"); 
      return k_aborted; 
    } 
    if (sync & kbm_sync_timer) 
    { 
      trace0(0, ata, 1, "ata_wait_status_with_timeout() - error: hung busy"); 
      return k_timeout; 
    } 
    status = ata_read_status(); 
    if (status & kbm_ata_status_err) 
    { 
      status = ata_register_rd(&ata_error); 
      trace1(0, ata, 1, "ata_wait_status_with_timeout() - error: 0x%02X", status); 
      return k_error; 
    } 
  } while ((status & mask) != val); 
  if (statusp) 
    *statusp = status; 
  return k_success; 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   ata_wait_irq_with_timeout 
// 
// Declaration: 
//   t_result ata_wait_irq_with_timeout(uint16 ticks); 
// 
// Purpose: 
//   Poll for the ata interrupt. 
// 
// Arguments: 
//   ticks   - a uint16 representing the timeout limit 
// 
// Return: 
//   A t_result indicating: 
//     k_success - the ata interrupt occurred. 
//     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 
// 
// History: 
//   06/18/02 tbh - modified ata_read_status() to leave the ata interrupt masked. 
//                - modified this routine to break out if ata irq bit seen via polling. 
//                - this cut about 270usec off the begin burst, decreasing cbw time. 
//------------------------------------------------------------------------------ 
t_result ata_wait_irq_with_timeout(uint16 ticks) reentrant; 
t_result ata_wait_irq_with_timeout(uint16 ticks) reentrant 
{ 
  t_sync sync; 
 
  trace1(0, ata, 1, "ata_wait_irq_with_timeout(ticks:%d)", ticks); 
  _stack_check(); 
  // 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 
  { 
    sync = thread_got_sync(kbm_sync_abort |kbm_sync_usbrst |kbm_sync_timer |kbm_sync_ata_irq); 
    _thread_clr_sync(sync); 
    if (sync & kbm_sync_usbrst) 
    { 
      trace0(0, ata, 1, "ata_wait_irq_with_timeout() - error: kbm_sync_usbrst"); 
      return k_usbreset; 
    } 
    if (sync & kbm_sync_abort) 
    { 
      trace0(0, ata, 1, "ata_wait_irq_with_timeout() - error: kbm_sync_abort"); 
      return k_aborted; 
    } 
    if (sync & kbm_sync_timer) 
    { 
      trace0(0, ata, 1, "ata_wait_irq_with_timeout() - error: timeout awaiting irq"); 
      return k_timeout; 
    } 
    if (_mcu_register_rd(x_isr0) & kbm_isr0_ata_irq) break; 
  } while (!(sync & kbm_sync_ata_irq)); 
  trace0(0, ata, 1, "ata_wait_irq_with_timeout() - got the ata irq"); 
  return k_success; 
} 
 
extern xdata uint8 g_sector_buffer[]; 
//+----------------------------------------------------------------------------- 
// Name: 
//   iddev_word 
// 
// Declaration: 
//   static uint16 iddev_word(uint16 word_offset); 
// 
// Purpose: 
//   Build a 16 bit word by extracting 2 bytee from pnr 4 at the specified offset. 
// 
// Arguments: 
//   word_offset - a uint16 specifying the offset into pnr 4 to get the result from. 
// 
// Return: 
//   A uint16 containing the 2 bytes extracted from pnr 4. 
// 
// Notes: 
//   This is a FUNCTION, not a DFA. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
static uint16 iddev_word(uint16 word_offset) reentrant; 
static uint16 iddev_word(uint16 word_offset) reentrant 
{ 
  uint16 byte_offset; 
  t_uw16 word; 
 
  byte_offset = 2 * word_offset; 
  #if 0 
  _mcu_register_wr(x_sram_addr_lo, k_pkt_addrlo[4] + _l(byte_offset)); 
  _mcu_register_wr(x_sram_addr_hi, k_pkt_addrhi[4] + _h(byte_offset)); 
  word.u8.lo = _mcu_register_rd(x_sram_data); 
  word.u8.hi = _mcu_register_rd(x_sram_data); 
  #else 
  word.u8.lo = g_sector_buffer[byte_offset++]; 
  word.u8.hi = g_sector_buffer[byte_offset]; 
  #endif 
  return word.u16; 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   ata_identify_device 
// 
// Declaration: 
//   void ata_identify_device(uint8 cmd); 
// 
// Purpose: 
//   Poll the ata status register to watch for specific bits matching specific values. 
// 
// Arguments: 
//   cmd - a uint8 representing the "identify" command to issue to the drive. 
// 
// Return: 
//   A t_result indicating: 
//     k_success - device successfully identified. 
//     k_error   - device failed to identify in a potentially recoverable way. 
//   OR 
//     No return - dfa ends on unrecoverable error. 
// 
// Notes: 
//   This is a FUNCTION, not a DFA, but must be called from within a DFA. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
t_result ata_identify_device(uint8 cmd) reentrant 
{ 
  uint8 status; 
  t_result result; 
  #if 0 
  uint8 i; 
  #else 
  uint16 i; 
  #endif 
  uint16 devword; 
  //uint8 mode_sel; 
  uint8 yes_no_flag; 
  trace0(0, ata, 1, "ata_identify_device()"); 
  _stack_check(); 
  _thread_clr_sync(kbm_sync_all); 
 
  // select master drive 
  ata_register_wr(&ata_device_head, kbm_ata_dev_0); 
  // select the identify-device / identify-packet_device function 
  ata_register_wr(&ata_command, cmd); 
  ata_read_status(); 
  result = k_error; 
  while (k_success != result)  // WARNING! WARNING! DANGER, WILL ROBINSON!!! 
  { 
    trace1(0, ata, 0, "-- imr0:0x%02X", x_imr0); 
    //result = ata_wait_irq_with_timeout(5000); 
    result = ata_wait_irq_with_timeout(1000); 
    if (k_success != result) 
    { 
      trace0(0, ata, 1, "ata_identify_device() - error: no irq"); 
      return k_error; 
    } 
  } 
  trace0(0, ata, 1, "ata_identify_device() - got the ata irq"); 
  // BSY=0 
  status = ata_read_status(); 
  if (!(status & kbm_ata_status_drq)) 
  { 
    // BSY=0, DRQ=0, therefore command is done 
    trace0(0, ata, 1, "ata_identify_device() - warning: drive has no data"); 
    return k_error; 
  } 
  // BSY=0, DRQ=1, therefore the drive wants to send more data 
  // read the data 
  _mcu_begin_critical_section(); 
  #if 0 // since the bot layer is now a high priority thread at irq level 
        // cannot use packet buffers here... switch to using xdata buffer 
  _mcu_register_wr(x_sram_addr_lo, k_pkt_addrlo[4]); 
  _mcu_register_wr(x_sram_addr_hi, k_pkt_addrhi[4]); 
  // there are always 256 words of device data 
  for (i=255; i; i--) 
  { 
    _mcu_register_wr(x_sram_data, ata_register_rd(&ata_data)); 
    _mcu_register_wr(x_sram_data, _mcu_register_rd(x_msb_ata)); 
  } 
  _mcu_register_wr(x_sram_data, ata_register_rd(&ata_data)); 
  _mcu_register_wr(x_sram_data, _mcu_register_rd(x_msb_ata)); 
  #else 
  // there are always 256 words of device data 
  for (i=0; i<512; i+=2) 
  { 
    g_sector_buffer[i] = ata_register_rd(&ata_data); 
    g_sector_buffer[i+1] = _mcu_register_rd(x_msb_ata); 
  } 
  #endif 
  _mcu_end_critical_section(); 
 
  //!!!$$$ check status for error - as per end of this funct 
  trace0(0, ata, 1, "ata_identify_device() - entire xfer complete"); 
 
  // examine the data 
  _mcu_begin_critical_section(); 
  // grab the product id string for non-210 devices. 
  #ifndef k_flash_family 
  for (i=0; i<8; i++) 
  { 
    // grab 2 chars 
    devword = iddev_word(i+27); 
    // store them in our sram 
    _lun_data(device_id)[2*i]   = _h(devword); 
    _lun_data(device_id)[2*i+1] = _l(devword); 
    trace1(0, dev_id, 0, "%c", _lun_data(device_id)[2*i]); 
    trace1(0, dev_id, 0, "%c", _lun_data(device_id)[2*i+1]); 
  } 
  #endif 
 
  //profile the edata card and drop it back to mode 1 
  for (i=0; i<8; i++) 
  { 
    // grab 2 chars 
    devword = iddev_word(i+27); 
    // store them in our sram 
    g_sector_buffer[2*i]   = _h(devword); 
    g_sector_buffer[2*i+1] = _l(devword); 
    trace1(0, ata, 0, "%c", _h(devword)); 
    trace1(0, ata, 0, "%c", _l(devword)); 
  } 
 
  g_ata_dev_caps_1 = iddev_word(k_ata_iddev_caps_l); 
  g_ata_dev_caps_2 = iddev_word(k_ata_iddev_caps_h); 
  // device capabilities 
  trace2(0, ata, 0, "dev_caps_1:%04X dev_caps_2:%04X", g_ata_dev_caps_1, g_ata_dev_caps_2); 
  // do atapi/ata specific interpretation here 
  switch (_lun_data(device_type)) 
  { 
    case k_device_type_ata: 
      // general config word 
      // set removable/present properties 
      if (iddev_word(k_ata_iddev_general_config) & kbm_ata_iddev_gc_removable) 
      { 
        trace0(0, ata, 0, "  GCFG:  Removable Media Device"); 
        // set removable property for Inquiry data 
        _lun_data(media) |= kbm_lun_media_removable; 
        // set media present property (because if we got here, the compact flash media is present) 
        _lun_data(media) |= kbm_lun_media_present; 
      } 
      else 
      { 
        // fixed media.  media is always present 
        _lun_data(media) |= kbm_lun_media_present; 
      } 
      if (iddev_word(k_ata_iddev_general_config) & kbm_ata_iddev_gc_fixed) 
      { 
        trace0(0, ata, 0, "  GCFG:  Controller/Device NOT removable"); 
        // not a removable device 
        //_lun_data(media) &= ~kbm_lun_media_removable; 
        trace0(0, ata, 0, "WARNING:  Controller/Device NOT removable? For CompactFlash?  Forcing the removable bit!"); 
        _lun_data(media) |= kbm_lun_media_removable; 
        _lun_data(media) |= kbm_lun_media_present; 
      } 
      // dev caps 
      if (g_ata_dev_caps_1& kbm_ata_iddev_caps_timer_values_per_spec) 
      { 
        trace0(0, ata, 0, "Standby timer values as specified in ATA spec are supported"); 
      } 
      else 
      { 
        trace0(0, ata, 0, "Standby timer values managed by device"); 
      } 
      #if k_dma_mode 
      g_use_dma = g_ata_dev_caps_1&kbm_ata_iddev_caps_support_dma?k_true:k_false; 
      #else 
      trace0(0, ata, 0, "** firmware dma support disabled ** "); 
      g_use_dma = k_false; 
      #endif 
      g_disable_iordy = (g_ata_dev_caps_1 & kbm_ata_iddev_caps_disable_iordy) ? k_true : k_false; 
      yes_no_flag=g_use_dma ? 'Y' : 'N'; 
      trace1(0, ata, 0, "  DMA Support:%c", yes_no_flag); 
      yes_no_flag=(devword & kbm_ata_iddev_caps_support_lba) ? 'Y' : 'N'; 
      trace1(0, ata, 0, "  LBA Support:%c", yes_no_flag); 
      yes_no_flag=(g_disable_iordy) ? 'Y' : 'N'; 
      trace1(0, ata, 0, "  Can Disable IORDY:%c", yes_no_flag); 
      yes_no_flag=(devword & kbm_ata_iddev_caps_support_iordy) ? 'Y' : 'N'; 
      trace1(0, ata, 0, "  IORDY Support:%c", yes_no_flag); 
      // ata-only info 
      g_ata_dev_num_log_cyl = iddev_word(k_ata_iddev_num_log_cyl); 
      g_ata_dev_num_log_hds = iddev_word(k_ata_iddev_num_log_hds); 
      g_ata_dev_num_log_sec_per_trk = iddev_word(k_ata_iddev_num_log_sec_per_log_track); 
      _lun_data(capacity).lba_max.u32 = (uint32)iddev_word(k_ata_iddev_total_user_addressable_sectors_h) << 16L; 
      _lun_data(capacity).lba_max.u32 += (uint32)iddev_word(k_ata_iddev_total_user_addressable_sectors_l); 
      _lun_data(capacity).lba_max.u32 -= 1; 
      trace2(0, ata, 0, "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; 
      _lun_data(max_lb_per_split) = 256; 
      _lun_data(max_lb_per_burst) = 256; 
      break; 
  } 
  // $$$ marked obsolete in ATA-6 spec, but, seems like CF cards use it 
  devword = iddev_word(k_ata_iddev_pio_mode) ; 
  trace1(0, ata, 0, "Device PIO Tranfer Cycle Mode:%d", (_h(devword))) ; 
  // RUDIMENTARY PROFILING OF ATA DRIVES 
  // throttle the edata card back to mode 1 because it says it runs at mode 2 3 but it 
  // fails to run at mode 3 or 2, and works in modes 1 and 0. 
  if (memcmp(g_sector_buffer, "HYPERSTONE FLASH", 16)) 
    _mcu_register_set_bits(x_cfc_ata_mode_ctl, _h(devword) ); 
  trace1(0, ata, 0, "x_cfc_ata_mode_ctl now: %02x", x_cfc_ata_mode_ctl) ; 
 
  if (iddev_word(k_ata_iddev_valid_fields) & kbm_ata_iddev_valid_fields_pio_mwdma_timing) 
  { 
    trace1(0, ata, 1, "PIO Modes Supported:%04X", iddev_word(k_ata_iddev_pio_mode_support)); 
    trace1(0, ata, 1, "Min MWDMA xfer cycle time: %d ns", iddev_word(k_ata_iddev_min_mwdma_cycles)); 
    trace1(0, ata, 1, "Manufacturer's recommended Multiword DMA transfer cycle time: %d ns", iddev_word(k_ata_iddev_mfr_recommened_mwdma_cycles)); 
    trace1(0, ata, 1, "Minimum PIO transfer cycle time without flow control: %d ns",  iddev_word(k_ata_iddev_min_pio_xfer_cycle_no_flow)); 
    trace1(0, ata, 1, "Minimum PIO transfer cycle time with IORDY flow control: %d ns", iddev_word(k_ata_iddev_min_pio_xfer_cycle_flow)); 
  } 
  if (iddev_word(k_ata_iddev_valid_fields) & kbm_ata_iddev_valid_fields_cur_log_chs_cap) 
  { 
    trace1(0, ata, 0, "Number of current logical cylinders: %d", iddev_word(k_ata_iddev_cur_log_cyl)); 
    trace1(0, ata, 0, "Number of current logical heads: %d", iddev_word(k_ata_iddev_cur_log_hds)); 
    trace1(0, ata, 0, "Number of current logical sectors per track: %d", iddev_word(k_ata_iddev_cur_log_sec_per_trk)); 
    trace2(0, ata, 0, "Current sector capacity: %04X%04X", iddev_word(k_ata_iddev_cur_sector_cap_h), iddev_word(k_ata_iddev_cur_sector_cap_l)); 
    //_lun_data(capacity).lba_max.u32 = (uint32)iddev_word(k_ata_iddev_cur_sector_cap_h) << 16L; 
    //_lun_data(capacity).lba_max.u32 += (uint32)iddev_word(k_ata_iddev_cur_sector_cap_l); 
    //TRACE2(389, ata, 0, "Total User-addressable Sectors: %04X%04X", _hw(_lun_data(capacity).lba_max.u32), _lw(_lun_data(capacity).lba_max.u32)); 
  } 
 
  trace1(0, ata, 0, "Number of sectors xferred per irq on READ/WRITE Multiple: %d", (iddev_word(k_ata_iddev_multi_burst_size)&0x00ff)) ; 
 
  #if 0 
  // dma info - already detected above if we support it 
  if (g_use_dma) 
  { 
    devword = iddev_word(k_ata_iddev_mwdma_mode_support); 
    mode_sel = (devword & kbm_ata_iddev_mwdma_mode_0_selected) ? 0 : 
    (devword & kbm_ata_iddev_mwdma_mode_1_selected) ? 1 : 
    (devword & kbm_ata_iddev_mwdma_mode_2_selected) ? 2 : 
    (0xff); 
    g_dev_max_dma_mode = (devword & kbm_ata_iddev_mwdma_mode_2_supported) ? k_dma_mode_multiword_2 : 
    (devword & kbm_ata_iddev_mwdma_mode_1_supported) ? k_dma_mode_multiword_1 : 
    (devword & kbm_ata_iddev_mwdma_mode_0_supported) ? k_dma_mode_multiword_0 : 
    (k_dma_mode_disabled); 
    if (mode_sel != 0xff) 
    { 
      trace1(0, ata, 0, "Multiword DMA Mode %d selected", mode_sel); 
    } 
    if (g_dev_max_dma_mode) 
    { 
      trace1(0, ata, 0, "Device Supports Multiword DMA up to mode %d", g_dev_max_dma_mode & 0x0f); 
    } 
    if (iddev_word(k_ata_iddev_valid_fields) & kbm_ata_iddev_valid_fields_udma_modes) 
    { 
      g_use_dma = k_true; 
      // Read the Ultra DMA Mode word (word 88) 
      devword = iddev_word(k_ata_iddev_udma_mode_support); 
      trace1(0, ata, 0, "Ultra DMA Mode Enabled/Supported: %04X", devword); 
      if (mode_sel == 0xff) 
      { 
        mode_sel = (devword & kbm_ata_iddev_udma_mode_0_selected) ? 0 : 
          (devword & kbm_ata_iddev_udma_mode_1_selected) ? 1 : 
          (devword & kbm_ata_iddev_udma_mode_2_selected) ? 2 : 
          (devword & kbm_ata_iddev_udma_mode_3_selected) ? 3 : 
          (devword & kbm_ata_iddev_udma_mode_4_selected) ? 4 : 
          (devword & kbm_ata_iddev_udma_mode_5_selected) ? 5 : 
          (0xff); 
        if (mode_sel != 0xff) 
        { 
          trace1(0, ata, 0, "Ultra DMA Mode %d currently selected", mode_sel); 
        } 
      } 
      g_dev_max_dma_mode = (devword & kbm_ata_iddev_udma_mode_5_supported) ? k_dma_mode_ultra_dma_5 : 
        (devword & kbm_ata_iddev_udma_mode_4_supported) ? k_dma_mode_ultra_dma_4 : 
        (devword & kbm_ata_iddev_udma_mode_3_supported) ? k_dma_mode_ultra_dma_3 : 
        (devword & kbm_ata_iddev_udma_mode_2_supported) ? k_dma_mode_ultra_dma_2 : 
        (devword & kbm_ata_iddev_udma_mode_1_supported) ? k_dma_mode_ultra_dma_1 : 
        (devword & kbm_ata_iddev_udma_mode_0_supported) ? k_dma_mode_ultra_dma_0 : 
        (g_dev_max_dma_mode); 
      trace1(0, ata, 0, "Device Supports up to Ultra DMA mode %d", g_dev_max_dma_mode & 0x0f); 
    } 
  } 
  #endif 
  // feature sets - surprisingly, identical for ata or atapi 
  // store local copy of device support/enabled values 
  g_ata_dev_cmdset_support_1 = iddev_word(k_ata_dev_cmdset_support_1); 
  g_ata_dev_cmdset_enabled_1 = iddev_word(k_ata_dev_cmdset_enabled_1); 
  g_ata_dev_cmdset_support_2 = iddev_word(k_ata_dev_cmdset_support_2); 
  g_ata_dev_cmdset_enabled_2 = iddev_word(k_ata_dev_cmdset_enabled_2); 
  g_ata_dev_cmdset_support_3 = iddev_word(k_ata_dev_cmdset_support_3); 
  g_ata_dev_cmdset_enabled_3 = iddev_word(k_ata_dev_cmdset_enabled_3); 
  // support_1 options 
  trace2(0, ata, 0, "command/feature set (1) support:%04X enabled:%04X", g_ata_dev_cmdset_support_1, g_ata_dev_cmdset_enabled_1); 
  trace2(0, ata, 0, "command/feature set (2) support:%04X enabled:%04X", g_ata_dev_cmdset_support_2, g_ata_dev_cmdset_enabled_2); 
  trace2(0, ata, 0, "command/feature set (3) support:%04X enabled:%04X", g_ata_dev_cmdset_support_3, g_ata_dev_cmdset_enabled_3); 
  trace1(0, ata, 2, "Command NOP Supported...................................%c", (g_ata_dev_cmdset_support_1&kbm_ata_iddev_cmd_nop)?'Y':'N' ); 
  trace1(0, ata, 2, "Command READ BUFFER Supported...........................%c", (g_ata_dev_cmdset_support_1&kbm_ata_iddev_cmd_read_buffer)?'Y':'N' ); 
  trace1(0, ata, 2, "Command WRITE BUFFER Supported..........................%c", (g_ata_dev_cmdset_support_1&kbm_ata_iddev_cmd_write_buffer)?'Y':'N' ); 
  trace1(0, ata, 2, "Host Protected Area Supported...........................%c", (g_ata_dev_cmdset_support_1&kbm_ata_iddev_host_protected_area)?'Y':'N' ); 
  trace1(0, ata, 2, "Command DEVICE RESET Supported..........................%c", (g_ata_dev_cmdset_support_1&kbm_ata_iddev_cmd_device_reset)?'Y':'N' ); 
  trace1(0, ata, 2, "Command DEVICE RESET Enabled............................%c", (g_ata_dev_cmdset_enabled_1&kbm_ata_iddev_cmd_device_reset)?'Y':'N' ); 
  trace1(0, ata, 2, "SERVICE interrupt Supported.............................%c", (g_ata_dev_cmdset_support_1&kbm_ata_iddev_service_intrpt)?'Y':'N' ); 
  trace1(0, ata, 2, "Release interrupt Supported.............................%c", (g_ata_dev_cmdset_support_1&kbm_ata_iddev_release_intrpt)?'Y':'N' ); 
  trace1(0, ata, 2, "write cache supported...................................%c", (g_ata_dev_cmdset_support_1&kbm_ata_iddev_cmd_write_cache)?'Y':'N' ); 
  trace1(0, ata, 2, "Feature Set - PACKET Supported..........................%c", (g_ata_dev_cmdset_support_1&kbm_ata_iddev_fs_packet)?'Y':'N' ); 
  trace1(0, ata, 2, "Feature Set - PACKET Supported..........................%c", (g_ata_dev_cmdset_enabled_1&kbm_ata_iddev_fs_packet)?'Y':'N' ); 
  trace1(0, ata, 2, "Feature Set - Power Mgmt Supported......................%c", (g_ata_dev_cmdset_support_1&kbm_ata_iddev_fs_power_mgmt)?'Y':'N' ); 
  trace1(0, ata, 2, "Feature Set - Power Mgmt Enabled........................%c", (g_ata_dev_cmdset_enabled_1&kbm_ata_iddev_fs_power_mgmt)?'Y':'N' ); 
  trace1(0, ata, 2, "Removable Media Feature Set Supported...................%c", (g_ata_dev_cmdset_support_1&kbm_ata_iddev_fs_removable_media)?'Y':'N' ); 
  trace1(0, ata, 2, "Security Mode Feature Set Supported.....................%c", (g_ata_dev_cmdset_support_1&kbm_ata_iddev_fs_security_mode)?'Y':'N' ); 
  trace1(0, ata, 2, "SMART Feature Set Supported.............................%c", (g_ata_dev_cmdset_support_1&kbm_ata_iddev_fs_smart)?'Y':'N' ); 
 
  // support_2 options 
  trace1(0, ata, 2, "Command FLUSH CACHE EXT Supported.......................%c", (g_ata_dev_cmdset_support_2&kbm_ata_iddev_cmd_flush_cache_ext)?'Y':'N' ); 
  trace1(0, ata, 2, "Command FLUSH CACHE Supported...........................%c", (g_ata_dev_cmdset_support_2&kbm_ata_iddev_cmd_flush_cache)?'Y':'N' ); 
  trace1(0, ata, 2, "Feature Set - Device Configuration Overlay Supported....%c", (g_ata_dev_cmdset_support_2&kbm_ata_iddev_fs_device_cfg_overlay)?'Y':'N' ); 
  trace1(0, ata, 2, "Feature Set - 48 Bit Addressing Support.................%c", (g_ata_dev_cmdset_support_2&kbm_ata_iddev_fs_48bit_address)?'Y':'N' ); 
  trace1(0, ata, 2, "Feature Set - Auto Acoustic Mgmt Support................%c", (g_ata_dev_cmdset_support_2&kbm_ata_iddev_fs_auto_acoustic_mgmt)?'Y':'N' ); 
  trace1(0, ata, 2, "Command Set - Max Security Extension Supported..........%c", (g_ata_dev_cmdset_support_2&kbm_ata_iddev_cmd_set_max_security_ext)?'Y':'N' ); 
  trace1(0, ata, 2, "Command Set - Support Set Features to spin-up after por.%c", (g_ata_dev_cmdset_support_2&kbm_ata_iddev_cfg_set_features_spinup_req_on_por)?'Y':'N' ); 
  trace1(0, ata, 2, "Command Set - Enable Set Features to spin-up after por..%c", (g_ata_dev_cmdset_enabled_2&kbm_ata_iddev_cfg_set_features_spinup_req_on_por)?'Y':'N' ); 
  trace1(0, ata, 2, "Feature Set - Support Powerup in Standby................%c", (g_ata_dev_cmdset_support_2&kbm_ata_iddev_fs_powerup_in_standby)?'Y':'N' ); 
  trace1(0, ata, 2, "Feature Set - Enable Powerup in Standby.................%c", (g_ata_dev_cmdset_enabled_2&kbm_ata_iddev_fs_powerup_in_standby)?'Y':'N' ); 
  trace1(0, ata, 2, "Feature Set - Removable Media Status Notification Supported:%c", (g_ata_dev_cmdset_support_2&kbm_ata_iddev_fs_removable_media_status_notify)?'Y':'N' ); 
  trace1(0, ata, 2, "Feature Set - Advanced Power Management Supported.......%c", (g_ata_dev_cmdset_support_2&kbm_ata_iddev_fs_adv_power_mgmt)?'Y':'N' ); 
  trace1(0, ata, 2, "Feature Set - CFA Supported.............................%c", (g_ata_dev_cmdset_support_2& kbm_ata_iddev_fs_cfa)?'Y':'N' ); 
  trace1(0, ata, 2, "Command READ/WRITE DMA QUEUED Supported.................%c", (g_ata_dev_cmdset_support_2&kbm_ata_iddev_cmd_rw_dma_queued)?'Y':'N' ); 
  trace1(0, ata, 2, "Command Download Microcode Supported....................%c", (g_ata_dev_cmdset_support_2&kbm_ata_iddev_cmd_download_microcode)?'Y':'N' ); 
  _mcu_end_critical_section(); 
  if (k_success != ata_wait_status_with_timeout(kbm_ata_status_bsy, 0, 5000, &status)) 
  { 
    // blow all the way out of dfa_ata_initialize_media. 
    // back to the dev thread, which will reinit the unit and restart the mscbot dfa. 
    //thread_end_dfa();  //<--EXIT 
    return k_error; 
  } 
  if (status & kbm_ata_status_err) 
  { 
    status = ata_register_rd(&ata_error); 
    trace1(0, ata, 1, "ata_identify_device() - error: 0x%02X", status); 
    return k_error; 
  } 
  // cf always fails set pio mode cause it can only be set to 4 
  //return ata_set_pio_transfer_mode(); 
  return k_success; 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   dfa_ata_identify_media 
// 
// Declaration: 
//   void dfa_ata_identify_media(void); 
// 
// Purpose: 
//   Go figure out what drive/card type is plugged in. 
// 
// 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 DFA, not a FUNCTION. 
//   It overrides dfa_lun_identify. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
void dfa_ata_identify_media() reentrant 
{ 
  trace0(0, ata, 0, "dfa_ata_identify_media()"); 
  if (k_success != ata_identify_device(k_ata_command_identify_device)) 
  { 
    _thread_return_dfa(k_command_failed); 
  } 
  // looks like media exists 
  _lun_data(media) &= ~kbm_lun_media_unknown; 
  _lun_data(sensep) = &sense_media_change; 
  _thread_return_dfa(k_command_passed); 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   ata_initialize_media2 
// 
// Declaration: 
//   void ata_initialize_media2(void); 
// 
// Purpose: 
//   Wait for the BSY bit to clear, then initialize the ata drive. 
// 
// Arguments: 
//   None. 
// 
// Return: 
//   No return value. 
//   However, the DFA's argument pointer is written with a t_result indicating: 
//     k_success - command completed. 
//     k_usbrst  - usb reset signalling was detected while executing the command. 
//     k_aborted - traffic occurred on the control pipe while wexecuting the command (probably a mass storage reset). 
//     k_timeout - a timeout limit exceeded while executing the command. 
//   (This allows the caller to see the return value, via the argp, after the DFA terminates). 
// 
// Notes: 
//   This is a STATE of the dfa_ata_initialize_media DFA. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
void ata_initialize_media2(void) reentrant; 
void ata_initialize_media2() reentrant 
{ 
  uint8 error; 
  uint8 sector_cnt; 
  uint8 sector_num; 
  uint8 cylinder_lo; 
  uint8 cylinder_hi; 
 
  trace0(0, ata, 1, "ata_initialize_media2()"); 
  trace1(0, ata, 0, "-- imr0:0x%02X", x_imr0); 
  if (thread_got_sync(kbm_sync_ata_rdy)) 
  { 
    _thread_clr_sync(kbm_sync_ata_rdy); 
    trace1(0, ata, 0, "-- after _lun_data:0x%02X", x_imr0); 
    #ifdef k_mcu_97201 
    // setup IDE timing register - compatible PIO, fast time for non-udma dma only 
    _mcu_register_clr_bits(x_ide_tim, kbm_ide_tim_dte);  //clear bit 3 dte so both MWDMA and PIO transfers use fast timing mode 
    _mcu_register_set_bits(x_ide_tim, (kbm_ide_tim_ftb |k_ide_iordy_3clocks |k_ide_recover_1clocks)); 
    #endif 
    // check for diagnostic results 
    error = ata_register_rd(&ata_error); 
    switch (error) 
    { 
      case 0x00: 
        trace0(0, ata, 0, "ata_initialize_media2() - error: no unit found"); 
        break; 
      case 0x01: 
        trace0(0, ata, 0, "ata_initialize_media2() - DEV0 Passed, DEV1 Passed/Not Present"); 
        break; 
      case 0x81: 
        trace0(0, ata, 0, "ata_initialize_media2() - DEV0 Passed, DEV1 Failed"); 
        break; 
      default: 
        trace1(0, ata, 0, "ata_initialize_media2() - error: diagnostic:0x%02X", error); 
        break; 
    } 
    // check for a signature telling us what type of device we have 
    sector_cnt = ata_register_rd(&ata_sector_cnt); 
    sector_num = ata_register_rd(&ata_sector_num); 
    cylinder_lo = ata_register_rd(&ata_cylinder_lo); 
    cylinder_hi = ata_register_rd(&ata_cylinder_hi); 
    trace4(0, ata, 0, "dev signature: sc:0x%02X sn:0x%02X cl:0x%02X sh:0x%02X", sector_cnt, sector_num, cylinder_lo, cylinder_hi); 
    // check for ATA 
    if (sector_cnt == 0x01 && sector_num == 0x01 && cylinder_lo == 0x00 && cylinder_hi == 0x00) 
    { 
      trace0(0, ata, 0, "ata_initialize_media2() - lun is ATA"); 
      _lun_data_wr(k_lun_ata, media, (_lun_data_rd(k_lun_ata, media)&~kbm_lun_media_changed));  
 
      // m'kay - here is the biggest optimization yet... 
      // use pio mode 4 instead of the default mode 0 and viola... 
      // 1.1R:960K 1.1W:923K 2.0R:5.57M 2.0W:3.37M 
      // it is safe to or in the mode bits ONLY because this is after POR and the por value is 0 
      // if it was done elsewhere it would need to be 
 
      // $$$ cds -> 4 not working for some devices... set it to 0 until media is identified 
      // 0x07 is the mask that covers all modes 
      // _mcu_register_clr_bits(x_cfc_ata_mode_ctl, k_cfc_ata_mode_4); 
 
      _thread_return_dfa(k_success); 
    } 
    trace0(0, ata, 0, "ata_initialize_media2() - error: bad signature - cannot identify device"); 
    _thread_return_dfa(k_error); 
  } 
  dev_exception(k_err_unreachable_code+11); 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   dfa_ata_initialize_media 
// 
// Declaration: 
//   void dfa_ata_initialize_media(void); 
// 
// Purpose: 
//   Setup to detect the BSY bit clearing, then yield hoping to initialize the ata drive. 
// 
// Arguments: 
//   None. 
// 
// Return: 
//   No return value. 
//   However, on exit the DFA's argument pointer is written with a t_result indicating: 
//     k_success - command completed. 
//     k_usbrst  - usb reset signalling was detected while executing the command. 
//     k_aborted - traffic occurred on the control pipe while wexecuting the command (probably a mass storage reset). 
//     k_timeout - a timeout limit exceeded while executing the command. 
//   (This allows the caller to see the return value, via the argp, after the DFA terminates). 
// 
// Notes: 
//   This is the start STATE of the dfa_ata_initialize_media DFA. 
//   It overrides dfa_lun_initialize. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
void dfa_ata_initialize_media() reentrant 
{ 
  trace0(0, ata, 1, "dfa_ata_initialize_media()"); 
  //read status to clear a pending irq, and to reset isr&imr 0 
  ata_read_status(); 
  thread_set_poller((t_thd_entry)ata_poll_status_not_busy); 
  thread_yield_ex(kbm_sync_none, kbm_sync_ata_rdy, ata_initialize_media2); 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   ata_read_end_xfer 
// 
// Declaration: 
//   t_result ata_read_end_xfer(void); 
// 
// Purpose: 
//   Wait until the drive is not busy. 
// 
// Arguments: 
//   None. 
// 
// Return: 
//   A t_result indicating: 
//     k_success - done. 
//     k_usbrst  - usb reset signalling was detected while executing the command. 
//     k_aborted - traffic occurred on the control pipe while wexecuting the command (probably a mass storage reset). 
//     k_timeout - a timeout limit exceeded while executing the command. 
// 
// Notes: 
//   This is a FUNCTION, not a DFA. 
//   Do not yeild, or run a DFA, from within this callback. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
t_result ata_read_end_xfer(void) reentrant; 
t_result ata_read_end_xfer() reentrant 
{ 
  t_result rslt; 
  uint8 status; 
 
  trace0(0, ata, 1, "ata_read_end_xfer()"); 
  _stack_check(); 
  // turn off the multiple emulation bit 
  _mcu_register_clr_bits(x_cfc_ata_mode_ctl, kbm_cfc_ata_mode_ctl_mult_emu_en); 
  trace0(0, ata, 0, "multiple emulation off"); 
  if (k_success != _fmc_get_result()) 
    return _fmc_get_result(); 
  status = ata_read_status(); 
  if (status & kbm_ata_status_err) 
    return k_error; 
  if (status & (kbm_ata_status_bsy |kbm_ata_status_drq)) 
    if (k_success != (rslt = ata_wait_status_with_timeout(kbm_ata_status_bsy |kbm_ata_status_drq, 0, 5000, NULL))) 
      return rslt; 
  return k_success; 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   ata_read_begin_split 
// 
// Declaration: 
//   t_result ata_read_begin_split(void); 
// 
// Purpose: 
//   Issue the read command to the ata drive. 
// 
// Arguments: 
//   None. 
// 
// Return: 
//   A t_result indicating: 
//     k_success - command completed. 
// 
// Notes: 
//   This is a FUNCTION, not a DFA. 
//   Do not yeild, or run a DFA, from within this callback. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
t_result ata_read_begin_split(void) reentrant; 
t_result ata_read_begin_split() reentrant 
{ 
  t_udw32 start_lb; 
  t_udw32 lb_count; 
 
  trace0(0, ata, 1, "ata_read_begin_split()"); 
  _stack_check(); 
  _fmc_set_options(0); 
  _fmc_set_timeout(10000); 
  start_lb.u32 = _fmc_get_start_lb_32(); 
  lb_count.u32 = _fmc_get_lb_count_32(); 
 
  // clear any stale ata irq 
  ata_read_status(); 
  _ata_register_wr(&ata_device_head, 
                  (kbm_ata_dev_lba| 
                   (start_lb.u8.hi & kbm_msk_ata_dev_lba_hi))); 
  _ata_register_wr(&ata_cylinder_hi, start_lb.u8.lh); 
  _ata_register_wr(&ata_cylinder_lo, start_lb.u8.hl); 
  _ata_register_wr(&ata_sector_num,  start_lb.u8.lo); 
  _ata_register_wr(&ata_sector_cnt, lb_count.u8.lo); 
  _ata_register_wr(&ata_command, k_ata_command_read_sectors); 
  return k_success; 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   ata_read_begin_burst 
// 
// Declaration: 
//   t_result ata_read_begin_burst(void); 
// 
// Purpose: 
//   Issue the read command to the ata drive. 
// 
// Arguments: 
//   None. 
// 
// Return: 
//   A t_result indicating: 
//     k_success - ready to transfer data. 
//     k_usbrst  - usb reset signalling was detected while executing the command. 
//     k_aborted - traffic occurred on the control pipe while wexecuting the command (probably a mass storage reset). 
//     k_timeout - a timeout limit exceeded while executing the command. 
// 
// Notes: 
//   This is a FUNCTION, not a DFA. 
//   Do not yeild, or run a DFA, from within this callback. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
t_result ata_read_begin_burst(void) reentrant; 
t_result ata_read_begin_burst() reentrant 
{ 
  uint8 status; 
  t_result rslt; 
 
  trace0(0, ata, 1, "ata_read_begin_burst()"); 
  _stack_check(); 
  // wait for the drive to interrupt 
 
  // NOTE: this 'optimization' seems to slow it down 
  //rslt = k_success; 
  //if (!_thread_got_sync(kbm_sync_ata_irq)) 
 
  // it takes about 250 usec minimum to get the interrupt... 
  // on the 24x lexar 256mb card (the fastest yet seen) 
  if (k_success != (rslt = ata_wait_irq_with_timeout(5000))) 
    return rslt; 
 
  // NOTE: this optimization speeds it up 
  status = ata_read_status(); 
  if (status & kbm_ata_status_err) 
    return k_error; 
  if (status & kbm_ata_status_drq != kbm_ata_status_drq) 
    // poll the status register until the drq bit goes active 
    rslt = ata_wait_status_with_timeout(kbm_ata_status_drq, kbm_ata_status_drq, 5000, &status); 
  if (k_error == rslt) 
  { 
    // error... put in the appropriate sense code 
    trace0(0, ata, 0, "sense_read_error"); 
    _lun_data_wr(k_lun_ata, sensep, &sense_read_error); 
    return k_error; 
  } 
  // some other error waiting for the drq 
  if (k_success != rslt) 
    return rslt; 
 
  // mask the isr from the cf device 
  _mcu_register_set_bits(x_cfc_stat_msk, kbm_cfc_stat_intrq); 
 
  // turn on the multiple emulation bit 
  _mcu_register_set_bits(x_cfc_ata_mode_ctl, kbm_cfc_ata_mode_ctl_mult_emu_en); 
 
  trace0(0, ata, 0, "multiple emulation on"); 
 
  // got the drq 
  return k_success; 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   ata_read_end_burst 
// 
// Declaration: 
//   t_result ata_read_end_burst(void); 
// 
// Purpose: 
//   Issue the read command to the ata drive. 
// 
// Arguments: 
//   None. 
// 
// Return: 
//     k_success - ready to transfer data. 
// 
// Notes: 
//   This is a FUNCTION, not a DFA. 
//   Do not yeild, or run a DFA, from within this callback. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
t_result ata_read_end_burst(void) reentrant; 
t_result ata_read_end_burst() reentrant 
{ 
  t_result rslt; 
 
  trace0(0, ata, 0, "ata_read_end_burst()"); 
  // turn off the multiple emulation bit 
  _mcu_register_clr_bits(x_cfc_ata_mode_ctl, kbm_cfc_ata_mode_ctl_mult_emu_en); 
  trace0(0, ata, 0, "multiple emulation off"); 
  _stack_check(); 
  _mcu_register_clr_bits(x_cfc_ata_mode_ctl, kbm_cfc_ata_mode_ctl_mult_emu_en); 
 
  // check to see if the fmc xfer was aborted... if so, reset the drive and return k_aborted 
  rslt = _fmc_get_result(); 
  if( rslt != k_success ) 
  { 
    trace0(0, ata, 0, "fmc xfer was aborted.  reset the device to stop it"); 
    ata_reset(); 
  } 
  return rslt; 
} 
 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   dfa_ata_read 
// 
// Declaration: 
//   void dfa_ata_read(void); 
// 
// Purpose: 
//   Read data from an ata drive. 
// 
// Arguments: 
//   None. 
//   Uses _lun_data and g_bot_cbw to get its parameters. 
// 
// Return: 
//   No return value. 
//   However, on exit the DFA's argument pointer is written with a t_csw_status indicating: 
//     k_command_passed - command completed. 
//     k_command_failed - an error occurred. 
// 
// Notes: 
//   This is the start STATE of the dfa_ata_read DFA. 
//   It overrides dfa_lun_read. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
void dfa_ata_read(void) reentrant 
{ 
  trace0(0, ata, 0, "dfa_ata_read"); 
  _stack_check(); 
  _fmc_set_callback(fmc_dflt_callback, ata_read_end_xfer, 
                    ata_read_begin_split, fmc_dflt_callback, 
                    ata_read_begin_burst, fmc_dflt_callback, ata_read_end_burst); 
  _fmc_set_options(0); 
  dfa_lun_read(); 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   ata_write_end_xfer 
// 
// Declaration: 
//   t_result ata_write_end_xfer(void); 
// 
// Purpose: 
//   Turn off multiple emulation if there was an error. 
// 
// Arguments: 
//   None. 
// 
// Return: 
//   A t_result indicating: 
//     k_success - done. 
//     k_usbrst  - usb reset signalling was detected while executing the command. 
//     k_aborted - traffic occurred on the control pipe while wexecuting the command (probably a mass storage reset). 
//     k_timeout - a timeout limit exceeded while executing the command. 
// 
// Notes: 
//   This is a FUNCTION, not a DFA. 
//   Do not yeild, or run a DFA, from within this callback. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
t_result ata_write_end_xfer(void) reentrant; 
t_result ata_write_end_xfer() reentrant 
{ 
  trace0(0, ata, 1, "ata_write_end_xfer()"); 
  _stack_check(); 
  // turn off the multiple emulation bit 
  _mcu_register_clr_bits(x_cfc_ata_mode_ctl, kbm_cfc_ata_mode_ctl_mult_emu_en); 
  trace0(0, ata, 0, "multiple emulation off"); 
 
  // check to see if the fmc xfer was aborted... if so, reset the drive and return k_aborted 
  if (k_success != _fmc_get_result()) 
  { 
    trace0(0, ata, 0, "fmc xfer was aborted.  reset the device to stop it"); 
    ata_reset(); 
    return _fmc_get_result(); 
  } 
  return k_success; 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   ata_write_begin_split 
// 
// Declaration: 
//   t_result ata_write_begin_split(void); 
// 
// Purpose: 
//   Issue the write command to the ata drive. 
// 
// Arguments: 
//   None. 
// 
// Return: 
//   A t_result indicating: 
//     k_success - command completed. 
// 
// Notes: 
//   This is a FUNCTION, not a DFA. 
//   Do not yeild, or run a DFA, from within this callback. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
t_result ata_write_begin_split(void) reentrant; 
t_result ata_write_begin_split() reentrant 
{ 
  trace0(0, ata, 1, "ata_write_begin_split()"); 
  _stack_check(); 
  _fmc_set_options(0); 
  _fmc_set_timeout(10000); 
  // clear any stale ata irq 
  ata_read_status(); 
  _ata_register_wr(&ata_device_head, 
                  (kbm_ata_dev_lba| 
                   (g_start_lb_this_xfer.u8.hi & kbm_msk_ata_dev_lba_hi))); 
  _ata_register_wr(&ata_cylinder_hi, g_start_lb_this_xfer.u8.lh); 
  _ata_register_wr(&ata_cylinder_lo, g_start_lb_this_xfer.u8.hl); 
  _ata_register_wr(&ata_sector_num,  g_start_lb_this_xfer.u8.lo); 
  _ata_register_wr(&ata_sector_cnt, g_n_lb_this_split.u8.lo); 
  _ata_register_wr(&ata_command, k_ata_command_write_sectors); 
  return k_success; 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   ata_write_begin_burst 
// 
// Declaration: 
//   t_result ata_write_begin_burst(void); 
// 
// Purpose: 
//   Wait until the drive is ready to accept the data. 
// 
// Arguments: 
//   None. 
// 
// Return: 
//   A t_result indicating: 
//     k_success - ready to transfer data. 
//     k_usbrst  - usb reset signalling was detected while executing the command. 
//     k_aborted - traffic occurred on the control pipe while wexecuting the command (probably a mass storage reset). 
//     k_timeout - a timeout limit exceeded while executing the command. 
// 
// Notes: 
//   This is a FUNCTION, not a DFA. 
//   Do not yeild, or run a DFA, from within this callback. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
t_result ata_write_begin_burst(void) reentrant; 
t_result ata_write_begin_burst() reentrant 
{ 
  t_result rslt; 
 
  trace0(0, ata, 1, "ata_write_begin_burst()"); 
  _stack_check(); 
  // poll the status register until the bsy bit goes inactive 
  rslt = ata_wait_status_with_timeout(kbm_ata_status_bsy, 0, 5000, NULL); 
  if (k_error == rslt) 
  { 
    // error... put in the appropriate sense code 
    trace0(0, ata, 0, "sense_write_error"); 
    _lun_data(sensep) = &sense_write_error; 
    return k_error; 
  } 
  // some other error waiting for the drq 
  if (k_success != rslt) 
    return rslt; 
  // poll the status register until the drq bit goes active 
  rslt = ata_wait_status_with_timeout(kbm_ata_status_drq, kbm_ata_status_drq, 5000, NULL); 
  if (k_error == rslt) 
  { 
    // error... put in the appropriate sense code 
    trace0(0, ata, 0, "sense_write_error"); 
    _lun_data(sensep) = &sense_write_error; 
    return k_error; 
  } 
  // some other error waiting for the drq 
  if (k_success != rslt) 
    return rslt; 
 
  // mask the isr from the cf device 
  _mcu_register_set_bits(x_cfc_stat_msk, kbm_cfc_stat_intrq); 
 
  // turn on the multiple emulation bit 
  _mcu_register_set_bits(x_cfc_ata_mode_ctl, kbm_cfc_ata_mode_ctl_mult_emu_en); 
 
  trace0(0, ata, 0, "multiple emulation on"); 
 
 
  // got the drq 
  return k_success; 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   ata_write_end_burst 
// 
// Declaration: 
//   t_result ata_write_end_burst(void); 
// 
// Purpose: 
//   Wait until the drive is done writing the data (flushing its buffers). 
// 
// Arguments: 
//   None. 
// 
// Return: 
//   A t_result indicating: 
//     k_success - done. 
//     k_usbrst  - usb reset signalling was detected while executing the command. 
//     k_aborted - traffic occurred on the control pipe while wexecuting the command (probably a mass storage reset). 
//     k_timeout - a timeout limit exceeded while executing the command. 
// 
// Notes: 
//   This is a FUNCTION, not a DFA. 
//   Do not yeild, or run a DFA, from within this callback. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
t_result ata_write_end_burst(void) reentrant; 
t_result ata_write_end_burst() reentrant 
{ 
  t_result rslt; 
 
  trace0(0, ata, 1, "ata_write_end_burst()"); 
  _stack_check(); 
  // turn off the multiple emulation bit 
  _mcu_register_clr_bits(x_cfc_ata_mode_ctl, kbm_cfc_ata_mode_ctl_mult_emu_en); 
  trace0(0, ata, 0, "multiple emulation off"); 
 
  // check to see if the fmc xfer was aborted... if so, reset the drive and return k_aborted 
  rslt = _fmc_get_result(); 
  if( rslt != k_success ) 
  { 
    trace0(0, ata, 0, "fmc xfer was aborted.  reset the device to stop it"); 
    ata_reset(); 
    return rslt; 
  } 
 
  // wait for the drive to provide the data 
  rslt = ata_read_status(); 
  // poll the status register just to check for errors 
  rslt = ata_wait_status_with_timeout(kbm_ata_status_bsy |kbm_ata_status_drq, 0, 5000, NULL); 
  if (k_error == rslt) 
  { 
    // error... put in the appropriate sense code 
    trace0(0, ata, 0, "sense_write_error"); 
    _lun_data(sensep) = &sense_write_error; 
    return k_error; 
  } 
  // some other error 
  if (k_success != rslt) 
    return rslt; 
  return k_success; 
} 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   dfa_ata_write 
// 
// Declaration: 
//   void dfa_ata_write(void); 
// 
// Purpose: 
//   Write data to an ata drive. 
// 
// Arguments: 
//   None. 
//   Uses _lun_data and g_bot_cbw to get its parameters. 
// 
// Return: 
//   No return value. 
//   However, on exit the DFA's argument pointer is written with a t_csw_status indicating: 
//     k_command_passed - command completed. 
//     k_command_failed - an error occurred. 
// 
// Notes: 
//   This is the start STATE of the dfa_ata_write DFA. 
//   It overrides dfa_lun_write. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
void dfa_ata_write() reentrant 
{ 
  trace0(0, ata, 0, "dfa_ata_write"); 
  _stack_check(); 
  _fmc_set_callback(fmc_dflt_callback, ata_write_end_xfer, 
                    ata_write_begin_split, fmc_dflt_callback, 
                    ata_write_begin_burst, fmc_dflt_callback, ata_write_end_burst); 
  _fmc_set_options(0); 
  dfa_lun_write(); 
} 
 
//------------------------------------------------------------------------------ 
// verify options 
#define kbm_verify_options_dpo      0x04 
#define kbm_verify_options_bytchk   0x02 
#define kbm_verify_options_reladr   0x01 
 
//+----------------------------------------------------------------------------- 
// Name: 
//   dfa_ata_verify 
// 
// Declaration: 
//   void dfa_ata_verify(void); 
// 
// Purpose: 
//   Verify that the data on the drive matches the data sent down. 
// 
// Arguments: 
//   None. 
//   Uses _lun_data and g_bot_cbw to get its parameters. 
// 
// Return: 
//   No return value. 
//   However, on exit the DFA's argument pointer is written with a t_csw_status indicating: 
//     k_command_passed - command completed. 
//     k_command_failed - an error occurred. 
// 
// Notes: 
//   This is the start STATE of the dfa_ata_verify DFA. 
//   It overrides dfa_lun_verify. 
// 
// Since: 
//   fmc-1.0 
//------------------------------------------------------------------------------ 
void dfa_ata_verify() reentrant 
{ 
  trace0(0, ata, 0, "dfa_ata_verify"); 
  _fmc_set_callback(fmc_dflt_callback, ata_write_end_xfer, 
                    ata_write_begin_split, fmc_dflt_callback, 
                    ata_write_begin_burst, fmc_dflt_callback, ata_write_end_burst); 
  dfa_lun_verify(); 
} 
 
//---eof------------------------------------------------------------------------