⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ataioreg.c

📁 C++实现的atapi源码
💻 C
📖 第 1 页 / 共 4 页
字号:
//********************************************************************
// ATA LOW LEVEL I/O DRIVER -- ATAIOREG.C
//
// by Hale Landis (hlandis@ata-atapi.com)
//
// There is no copyright and there are no restrictions on the use
// of this ATA Low Level I/O Driver code.  It is distributed to
// help other programmers understand how the ATA device interface
// works and it is distributed without any warranty.  Use this
// code at your own risk.
//
// This code is based on the ATA-2, ATA-3 and ATA-4 standards and
// on interviews with various ATA controller and drive designers.
//
// This code has been run on many ATA (IDE) drives and
// MFM/RLL controllers.  This code may be a little
// more picky about the status it sees at various times.  A real
// BIOS probably would not check the status as carefully.
//
// Compile with one of the Borland C or C++ compilers.
//
// This C source contains the main body of the driver code:
// Determine what devices are present, issue ATA Soft Reset,
// execute Non-Data, PIO data in and PIO data out commands.
//********************************************************************

#include <dos.h>

#include "ataio.h"

#define DEBUG_REG 0x00  // not zero for debug
                        // 0x01 trace the interrupt flag

//*************************************************************
//
// ATAPI command packet,
// atapi register data,
// atapi delay flag,
// config information,
// slow transfer flag,
// incompatible flags.
//
//*************************************************************

unsigned char reg_atapi_cp_data[16];
int reg_atapi_cp_size;

int reg_atapi_delay_flag;

unsigned char reg_atapi_reg_fr;  // see reg_packet()
unsigned char reg_atapi_reg_sc;  // see reg_packet()
unsigned char reg_atapi_reg_sn;  // see reg_packet()
unsigned char reg_atapi_reg_dh;  // see reg_packet()

long reg_buffer_size;

struct REG_CMD_INFO reg_cmd_info;

int reg_config_info[2];

void ( * reg_drq_block_call_back ) ( struct REG_CMD_INFO * );

long reg_slow_xfer_flag;

int reg_incompat_flags;

//*************************************************************
//
// reg_wait_poll() - wait for interrupt or poll for BSY=0
//
//*************************************************************

static void reg_wait_poll( int we, int pe );

static void reg_wait_poll( int we, int pe )

{
   unsigned char status;

   // Wait for interrupt -or- wait for not BUSY -or- wait for time out.

   if ( we && int_use_intr_flag )
   {
      trc_llt( 0, 0, TRC_LLT_WINT );
      while ( 1 )
      {
         if ( int_intr_flag )                // interrupt ?
         {
            trc_llt( 0, 0, TRC_LLT_INTRQ );  // yes
            if ( int_bmcr_addr )
               trc_llt( 0, int_bm_status, TRC_LLT_R_BM_SR );
            status = int_ata_status;         // get status
            trc_llt( CB_STAT, status, TRC_LLT_INB );
            if ( int_bmcr_addr )
               trc_llt( 0, 0x04, TRC_LLT_W_BM_SR );
            break;
         }
         if ( tmr_chk_timeout() )            // time out ?
         {
            trc_llt( 0, 0, TRC_LLT_TOUT );   // yes
            reg_cmd_info.to = 1;
            reg_cmd_info.ec = we;
            trc_llt( 0, reg_cmd_info.ec, TRC_LLT_ERROR );
            break;
         }
      }
   }
   else
   {
      trc_llt( 0, 0, TRC_LLT_PNBSY );
      while ( 1 )
      {
         status = pio_inbyte( CB_ASTAT );       // poll for not busy
         if ( ( status & CB_STAT_BSY ) == 0 )
            break;
         if ( tmr_chk_timeout() )               // time out yet ?
         {
            trc_llt( 0, 0, TRC_LLT_TOUT );
            reg_cmd_info.to = 1;
            reg_cmd_info.ec = pe;
            trc_llt( 0, reg_cmd_info.ec, TRC_LLT_ERROR );
            break;
         }
      }
   }

   #if DEBUG_REG & 0x01
      trc_llt( 0, int_intr_cntr, TRC_LLT_DEBUG );  // for debugging
   #endif

   // Reset the interrupt flag.

   if ( int_intr_flag > 1 )      // extra interrupt(s) ?
      reg_cmd_info.failbits |= FAILBIT15;
   int_intr_flag = 0;
}

//*************************************************************
//
// reg_config() - Check the host adapter and determine the
//                number and type of drives attached.
//
// This process is not documented by any of the ATA standards.
//
// Infomation is returned by this function is in
// reg_config_info[] -- see ATAIO.H.
//
//*************************************************************

int reg_config( void )

{
   int numDev = 0;
   unsigned char sc;
   unsigned char sn;
   unsigned char cl;
   unsigned char ch;
   unsigned char st;
   unsigned char devCtrl;

   // setup register values

   devCtrl = CB_DC_HD15 | ( int_use_intr_flag ? 0 : CB_DC_NIEN );

   // mark start of config in low level trace

   trc_llt( 0, 0, TRC_LLT_S_CFG );

   // reset Bus Master Error bit

   sub_writeBusMstrStatus( BM_SR_MASK_ERR );

   // assume there are no devices

   reg_config_info[0] = REG_CONFIG_TYPE_NONE;
   reg_config_info[1] = REG_CONFIG_TYPE_NONE;

   // set up Device Control register

   pio_outbyte( CB_DC, devCtrl );

   // lets see if there is a device 0

   pio_outbyte( CB_DH, CB_DH_DEV0 );
   DELAY400NS;
   pio_outbyte( CB_SC, 0x55 );
   pio_outbyte( CB_SN, 0xaa );
   pio_outbyte( CB_SC, 0xaa );
   pio_outbyte( CB_SN, 0x55 );
   pio_outbyte( CB_SC, 0x55 );
   pio_outbyte( CB_SN, 0xaa );
   sc = pio_inbyte( CB_SC );
   sn = pio_inbyte( CB_SN );
   if ( ( sc == 0x55 ) && ( sn == 0xaa ) )
      reg_config_info[0] = REG_CONFIG_TYPE_UNKN;

   // lets see if there is a device 1

   pio_outbyte( CB_DH, CB_DH_DEV1 );
   DELAY400NS;
   pio_outbyte( CB_SC, 0x55 );
   pio_outbyte( CB_SN, 0xaa );
   pio_outbyte( CB_SC, 0xaa );
   pio_outbyte( CB_SN, 0x55 );
   pio_outbyte( CB_SC, 0x55 );
   pio_outbyte( CB_SN, 0xaa );
   sc = pio_inbyte( CB_SC );
   sn = pio_inbyte( CB_SN );
   if ( ( sc == 0x55 ) && ( sn == 0xaa ) )
      reg_config_info[1] = REG_CONFIG_TYPE_UNKN;

   // now we think we know which devices, if any are there,
   // so lets try a soft reset (ignoring any errors).

   pio_outbyte( CB_DH, CB_DH_DEV0 );
   DELAY400NS;
   reg_reset( 0, 0 );

   // lets check device 0 again, is device 0 really there?
   // is it ATA or ATAPI?

   pio_outbyte( CB_DH, CB_DH_DEV0 );
   DELAY400NS;
   sc = pio_inbyte( CB_SC );
   sn = pio_inbyte( CB_SN );
   if ( ( sc == 0x01 ) && ( sn == 0x01 ) )
   {
      reg_config_info[0] = REG_CONFIG_TYPE_UNKN;
      st = pio_inbyte( CB_STAT );
      cl = pio_inbyte( CB_CL );
      ch = pio_inbyte( CB_CH );
      if ( ( ( cl == 0x14 ) && ( ch == 0xeb ) )       // PATAPI
           ||
           ( ( cl == 0x69 ) && ( ch == 0x96 ) )       // SATAPI
         )
      {
         reg_config_info[0] = REG_CONFIG_TYPE_ATAPI;
      }
      else
      if ( ( st != 0 )
           &&
           ( ( ( cl == 0x00 ) && ( ch == 0x00 ) )     // PATA
             ||
             ( ( cl == 0x3c ) && ( ch == 0xc3 ) ) )   // SATA
         )
      {
         reg_config_info[0] = REG_CONFIG_TYPE_ATA;
      }
   }

   // lets check device 1 again, is device 1 really there?
   // is it ATA or ATAPI?

   pio_outbyte( CB_DH, CB_DH_DEV1 );
   DELAY400NS;
   sc = pio_inbyte( CB_SC );
   sn = pio_inbyte( CB_SN );
   if ( ( sc == 0x01 ) && ( sn == 0x01 ) )
   {
      reg_config_info[1] = REG_CONFIG_TYPE_UNKN;
      st = pio_inbyte( CB_STAT );
      cl = pio_inbyte( CB_CL );
      ch = pio_inbyte( CB_CH );
      if ( ( ( cl == 0x14 ) && ( ch == 0xeb ) )       // PATAPI
           ||
           ( ( cl == 0x69 ) && ( ch == 0x96 ) )       // SATAPI
         )
      {
         reg_config_info[1] = REG_CONFIG_TYPE_ATAPI;
      }
      else
      if ( ( st != 0 )
           &&
           ( ( ( cl == 0x00 ) && ( ch == 0x00 ) )     // PATA
             ||
             ( ( cl == 0x3c ) && ( ch == 0xc3 ) ) )   // SATA
         )
      {
         reg_config_info[1] = REG_CONFIG_TYPE_ATA;
      }
   }

   // If possible, select a device that exists, try device 0 first.

   if ( reg_config_info[1] != REG_CONFIG_TYPE_NONE )
   {
      pio_outbyte( CB_DH, CB_DH_DEV1 );
      DELAY400NS;
      numDev ++ ;
   }
   if ( reg_config_info[0] != REG_CONFIG_TYPE_NONE )
   {
      pio_outbyte( CB_DH, CB_DH_DEV0 );
      DELAY400NS;
      numDev ++ ;
   }

   // BMCR/BMIDE Error=1?

   if ( sub_readBusMstrStatus() & BM_SR_MASK_ERR )
   {
      reg_cmd_info.ec = 78;                  // yes
      trc_llt( 0, reg_cmd_info.ec, TRC_LLT_ERROR );
   }

   // mark end of config in low level trace

   trc_llt( 0, 0, TRC_LLT_E_CFG );

   // return the number of devices found

   return numDev;
}

//*************************************************************
//
// reg_reset() - Execute a Software Reset.
//
// See ATA-2 Section 9.2, ATA-3 Section 9.2, ATA-4 Section 8.3.
//
//*************************************************************

int reg_reset( int skipFlag, int devRtrn )

{
   unsigned char sc;
   unsigned char sn;
   unsigned char status;
   unsigned char devCtrl;

   // setup register values

   devCtrl = CB_DC_HD15 | ( int_use_intr_flag ? 0 : CB_DC_NIEN );

   // mark start of reset in low level trace

   trc_llt( 0, 0, TRC_LLT_S_RST );

   // reset Bus Master Error bit

   sub_writeBusMstrStatus( BM_SR_MASK_ERR );

   // Reset error return data.

   sub_zero_return_data();
   reg_cmd_info.flg = TRC_FLAG_SRST;
   reg_cmd_info.ct  = TRC_TYPE_ASR;

   // initialize the command timeout counter

   tmr_set_timeout();

   // Set and then reset the soft reset bit in the Device Control
   // register.  This causes device 0 be selected.

   if ( ! skipFlag )
   {
      pio_outbyte( CB_DC, devCtrl | CB_DC_SRST );
      DELAY400NS;
      pio_outbyte( CB_DC, devCtrl );
      DELAY400NS;
   }

   // If there is a device 0, wait for device 0 to set BSY=0.

   if ( reg_config_info[0] != REG_CONFIG_TYPE_NONE )
   {
      sub_atapi_delay( 0 );
      trc_llt( 0, 0, TRC_LLT_PNBSY );
      while ( 1 )
      {
         status = pio_inbyte( CB_STAT );
         if ( ( status & CB_STAT_BSY ) == 0 )
            break;
         if ( tmr_chk_timeout() )
         {
            trc_llt( 0, 0, TRC_LLT_TOUT );
            reg_cmd_info.to = 1;
            reg_cmd_info.ec = 1;
            trc_llt( 0, reg_cmd_info.ec, TRC_LLT_ERROR );
            break;
         }
      }
   }

   // If there is a device 1, wait until device 1 allows
   // register access.

   if ( reg_config_info[1] != REG_CONFIG_TYPE_NONE )
   {
      sub_atapi_delay( 1 );
      trc_llt( 0, 0, TRC_LLT_PNBSY );
      while ( 1 )
      {
         pio_outbyte( CB_DH, CB_DH_DEV1 );
         DELAY400NS;
         sc = pio_inbyte( CB_SC );
         sn = pio_inbyte( CB_SN );
         if ( ( sc == 0x01 ) && ( sn == 0x01 ) )
            break;
         if ( tmr_chk_timeout() )
         {
            trc_llt( 0, 0, TRC_LLT_TOUT );
            reg_cmd_info.to = 1;
            reg_cmd_info.ec = 2;
            trc_llt( 0, reg_cmd_info.ec, TRC_LLT_ERROR );
            break;
         }
      }

      // Now check if drive 1 set BSY=0.

      if ( reg_cmd_info.ec == 0 )
      {
         if ( pio_inbyte( CB_STAT ) & CB_STAT_BSY )
         {
            reg_cmd_info.ec = 3;
            trc_llt( 0, reg_cmd_info.ec, TRC_LLT_ERROR );
         }
      }
   }

   // RESET_DONE:

   // We are done but now we must select the device the caller
   // requested before we trace the command.  This will cause
   // the correct data to be returned in reg_cmd_info.

   pio_outbyte( CB_DH, devRtrn ? CB_DH_DEV1 : CB_DH_DEV0 );
   DELAY400NS;
   sub_trace_command();

   // If possible, select a device that exists,
   // try device 0 first.

   if ( reg_config_info[1] != REG_CONFIG_TYPE_NONE )
   {
      pio_outbyte( CB_DH, CB_DH_DEV1 );
      DELAY400NS;
   }
   if ( reg_config_info[0] != REG_CONFIG_TYPE_NONE )
   {
      pio_outbyte( CB_DH, CB_DH_DEV0 );
      DELAY400NS;
   }

   // BMCR/BMIDE Error=1?

   if ( sub_readBusMstrStatus() & BM_SR_MASK_ERR )
   {
      reg_cmd_info.ec = 78;                  // yes
      trc_llt( 0, reg_cmd_info.ec, TRC_LLT_ERROR );
   }

   // mark end of reset in low level trace

   trc_llt( 0, 0, TRC_LLT_E_RST );

   // All done.  The return values of this function are described in
   // ATAIO.H.

   if ( reg_cmd_info.ec )
      return 1;
   return 0;
}

//*************************************************************
//
// exec_non_data_cmd() - Execute a non-data command.
//
// Note special handling for Execute Device Diagnostics
// command when there is no device 0.
//
// See ATA-2 Section 9.5, ATA-3 Section 9.5,
// ATA-4 Section 8.8 Figure 12.  Also see Section 8.5.
//
//*************************************************************

static int exec_non_data_cmd( int dev );

static int exec_non_data_cmd( int dev )

{
   unsigned char secCnt;
   unsigned char secNum;
   unsigned char status;
   int polled = 0;

   // mark start of ND cmd in low level trace

   trc_llt( 0, 0, TRC_LLT_S_ND );

   // reset Bus Master Error bit

   sub_writeBusMstrStatus( BM_SR_MASK_ERR );

   // Set command time out.

   tmr_set_timeout();

   // PAY ATTENTION HERE
   // If the caller is attempting a Device Reset command, then
   // don't do most of the normal stuff.  Device Reset has no
   // parameters, should not generate an interrupt and it is the
   // only command that can be written to the Command register
   // when a device has BSY=1 (a very strange command!).  Not
   // all devices support this command (even some ATAPI devices
   // don't support the command.

   if ( reg_cmd_info.cmd != CMD_DEVICE_RESET )
   {
      // Select the drive - call the sub_select function.
      // Quit now if this fails.

      if ( sub_select( dev ) )
      {
         sub_trace_command();
         trc_llt( 0, 0, TRC_LLT_E_ND );
         return 1;
      }

      // Set up all the registers except the command register.

      sub_setup_command();

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -