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