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

📄 mindrvr.c

📁 ata和atapi的驱动程序
💻 C
📖 第 1 页 / 共 5 页
字号:
//********************************************************************
// MINIMUM ATA LOW LEVEL I/O DRIVER -- MINDRVR.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.
//
// Minimum ATA Driver (MINDRVR) is a subset of ATADRVR. MINDRVR
// has a single header file and a single C file. MINDRVR can
// be used as the starting point for an ATADRVR for an embedded
// system. NOTE all the places in the MINDRVR.H and MINDRVR.C files
// where there is a comment containing the string "!!!".
//
// Use the header file mindrvr.h in any C files that call MINDRVR
// functions.
//
// This code is based on the ATA/ATAPI-4,-5 and -6 standards and
// on interviews with various ATA controller and drive designers.
//
// Note that MINDRVR does not support ATA CHS addressing.
//
// Most of the MINDRVR code is standard C code and should compile
// using any C compiler. It has been tested using Borland C/C++ 4.5.
//
// This C source file is the header file for the driver
// and is used in the MINDRVR.C files and must also be used
// by any program using the MINDRVR code/functions.
//********************************************************************

#include "mindrvr.h"

//**************************************************************
//
// !!! data that functions outside of MINDRVR must use
//
// Note that there is no actual "interrupt handler" provide in
// MINDRVR. The interrupt handler is usually a small function that
// is very system specific. However, MINDRVR expects that interrupt
// handler function to provide some status data at the time the
// interrupt handler is executed.
//
// In many systems, including PCI bus based systems, when an
// interrupt is received from an ATA controller, the interrupt
// handler must acknowledge the interrupt by reading both the
// ATA/ATAPI device Status register and the controller status
// register. This status must be stored here so that MINDRVR
// can use it.
//
//**************************************************************

unsigned char int_ata_status;    // ATA status read by interrupt handler

unsigned char int_bmide_status;  // BMIDE status read by interrupt handler

unsigned char int_use_intr_flag = INT_DEFAULT_INTERRUPT_MODE;

struct REG_CMD_INFO reg_cmd_info;

int reg_config_info[2];

unsigned char * pio_bmide_base_addr;

unsigned char * pio_reg_addrs[9] =
{
   PIO_BASE_ADDR1 + 0,  // [0] CB_DATA
   PIO_BASE_ADDR1 + 1,  // [1] CB_FR & CB_ER
   PIO_BASE_ADDR1 + 2,  // [2] CB_SC
   PIO_BASE_ADDR1 + 3,  // [3] CB_SN
   PIO_BASE_ADDR1 + 4,  // [4] CB_CL
   PIO_BASE_ADDR1 + 5,  // [5] CB_CH
   PIO_BASE_ADDR1 + 6,  // [6] CB_DH
   PIO_BASE_ADDR1 + 7,  // [7] CB_CMD & CB_STAT
   PIO_BASE_ADDR2 + 0   // [8] CB_DC & CB_ASTAT
} ;

unsigned char pio_xfer_width = PIO_DEFAULT_XFER_WIDTH;

//**************************************************************
//
// functions internal and private to MINDRVR
//
//**************************************************************

static void sub_setup_command( void );
static void sub_trace_command( void );
static int sub_select( unsigned char dev );
static void sub_wait_poll( unsigned char we, unsigned char pe );

static unsigned char pio_inbyte( unsigned char addr );
static void pio_outbyte( int addr, unsigned char data );
static unsigned int pio_inword( unsigned char addr );
static void pio_outword( int addr, unsigned int data );
static unsigned long pio_indword( unsigned char addr );
static void pio_outdword( int addr, unsigned long data );
static void pio_drq_block_in( unsigned char addrDataReg,
                              unsigned char * bufAddr,
                              long wordCnt );
static void pio_drq_block_out( unsigned char addrDataReg,
                               unsigned char * bufAddr,
                               long wordCnt );
static void pio_rep_inbyte( unsigned char addrDataReg,
                            unsigned char * bufAddr,
                            long byteCnt );
static void pio_rep_outbyte( unsigned char addrDataReg,
                             unsigned char * bufAddr,
                             long byteCnt );
static void pio_rep_inword( unsigned char addrDataReg,
                            unsigned char * bufAddr,
                            long wordCnt );
static void pio_rep_outword( unsigned char addrDataReg,
                             unsigned char * bufAddr,
                             long wordCnt );
static void pio_rep_indword( unsigned char addrDataReg,
                             unsigned char * bufAddr,
                             long dwordCnt );
static void pio_rep_outdword( unsigned char addrDataReg,
                              unsigned char * bufAddr,
                              long dwordCnt );

static unsigned char pio_readBusMstrCmd( void );
static unsigned char pio_readBusMstrStatus( void );
static void pio_writeBusMstrCmd( unsigned char x );
static void pio_writeBusMstrStatus( unsigned char x );

static long tmr_cmd_start_time;     // command start time
static void tmr_set_timeout( void );
static int tmr_chk_timeout( void );

// This macro provides a small delay that is used in several
// places in the ATA command protocols:

#define DELAY400NS  { pio_inbyte( CB_ASTAT ); pio_inbyte( CB_ASTAT );  \
                      pio_inbyte( CB_ASTAT ); pio_inbyte( CB_ASTAT ); }

//*************************************************************
//
// 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 MINDRVR.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 dc;

   // setup register values

   dc = (unsigned char) ( int_use_intr_flag ? 0 : CB_DC_NIEN );

   // reset Bus Master Error bit

   pio_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, dc );

   // 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 );

   // 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 ++ ;
   }

   // BMIDE Error=1?

   if ( pio_readBusMstrStatus() & BM_SR_MASK_ERR )
   {
      reg_cmd_info.ec = 78;                  // yes
   }

   // 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( unsigned char devRtrn )

{
   unsigned char sc;
   unsigned char sn;
   unsigned char status;
   unsigned char dc;

   // setup register values

   dc = (unsigned char) ( int_use_intr_flag ? 0 : CB_DC_NIEN );

   // reset Bus Master Error bit

   pio_writeBusMstrStatus( BM_SR_MASK_ERR );

   // 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.

   pio_outbyte( CB_DC, (unsigned char) ( dc | CB_DC_SRST ) );
   DELAY400NS;
   pio_outbyte( CB_DC, dc );
   DELAY400NS;

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

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

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

   if ( reg_config_info[1] != REG_CONFIG_TYPE_NONE )
   {
      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() )
         {
            reg_cmd_info.to = 1;
            reg_cmd_info.ec = 2;
            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;
         }
      }
   }

   // RESET_DONE:

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

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

   // 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;
   }

   // BMIDE Error=1?

   if ( pio_readBusMstrStatus() & BM_SR_MASK_ERR )
   {
      reg_cmd_info.ec = 78;                  // yes
   }

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

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

//*************************************************************
//

⌨️ 快捷键说明

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