📄 mindrvr.c
字号:
//********************************************************************
// 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 + -