📄 advioreg.c
字号:
//********************************************************************
// ATA LOW LEVEL I/O DRIVER -- ADVIOREG.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.
//
// 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 "advio.h"
//*************************************************************
//
// reg_poll_not_busy() - poll for BSY=0
//
//*************************************************************
static void reg_poll_not_busy( int ec );
static void reg_poll_not_busy( int ec )
{
unsigned char status;
// Poll for not BUSY -or- wait for time out.
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 );
ADP->to = 1;
ADP->ec = ec;
trc_llt( 0, ADP->ec, TRC_LLT_ERROR );
break;
}
}
}
//*************************************************************
//
// 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.
//
//*************************************************************
int reg_config( void )
{
int numDev = 0;
unsigned char sc;
unsigned char sn;
unsigned char cl;
unsigned char ch;
unsigned char st;
// 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 is no device
ADP->devType = ADVIO_DEV_TYPE_NONE;
// set up Device Control register
pio_outbyte( CB_DC, 0x00 );
// lets see if there is a device
pio_outbyte( CB_DH, ADP->devBit );
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 ) )
ADP->devType = ADVIO_DEV_TYPE_UNKN;
// if no device, quit now, return no device found
if ( ADP->devType == ADVIO_DEV_TYPE_NONE )
{
// mark end of config and return no devices found
trc_llt( 0, 0, TRC_LLT_E_CFG );
return numDev;
}
// Looks like there is a device,
// try a soft reset (ignoring any errors).
reg_reset( 0 );
// check the device again, is device 0 really there?
// is it ATA or ATAPI?
pio_outbyte( CB_DH, ADP->devBit );
DELAY400NS;
sc = pio_inbyte( CB_SC );
sn = pio_inbyte( CB_SN );
if ( ( sc == 0x01 ) && ( sn == 0x01 ) )
{
// yep, there is a device, what kind is it?
numDev = 1;
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
)
{
ADP->devType = ADVIO_DEV_TYPE_ATAPI;
}
else
if ( ( st != 0 )
&&
( ( ( cl == 0x00 ) && ( ch == 0x00 ) ) // PATA
||
( ( cl == 0x3c ) && ( ch == 0xc3 ) ) ) // SATA
)
{
ADP->devType = ADVIO_DEV_TYPE_ATA;
}
}
// BMCR/BMIDE Error=1?
if ( sub_readBusMstrStatus() & BM_SR_MASK_ERR )
{
ADP->ec = 78; // yes
trc_llt( 0, ADP->ec, TRC_LLT_ERROR );
}
// mark end of config in low level trace
// return the number of devices found
trc_llt( 0, 0, TRC_LLT_E_CFG );
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 )
{
unsigned char sc;
unsigned char sn;
unsigned char status;
// 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();
ADP->flg = TRC_FLAG_SRST;
ADP->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, CB_DC_SRST );
DELAY400NS;
pio_outbyte( CB_DC, 0x00 );
DELAY400NS;
}
// poll for the device to have BSY=0 status.
if ( ADP->devBit )
{
// wait until device 1 allows register access and has BSY=0 status.
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 );
ADP->to = 1;
ADP->ec = 2;
trc_llt( 0, ADP->ec, TRC_LLT_ERROR );
break;
}
}
// Now check if drive 1 set BSY=0.
if ( ADP->ec == 0 )
{
if ( pio_inbyte( CB_STAT ) & CB_STAT_BSY )
{
ADP->ec = 3;
trc_llt( 0, ADP->ec, TRC_LLT_ERROR );
}
}
}
else
{
// wait for device 0 to set BSY=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 );
ADP->to = 1;
ADP->ec = 1;
trc_llt( 0, ADP->ec, TRC_LLT_ERROR );
break;
}
}
}
// RESET_DONE:
// select the device and return its register data.
pio_outbyte( CB_DH, ADP->devBit );
DELAY400NS;
sub_trace_command();
// BMCR/BMIDE Error=1?
if ( sub_readBusMstrStatus() & BM_SR_MASK_ERR )
{
ADP->ec = 78; // yes
trc_llt( 0, ADP->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
// ADVIO.H.
if ( ADP->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( void );
static int exec_non_data_cmd( void )
{
unsigned char secCnt;
unsigned char secNum;
unsigned char status;
// 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 ( ADP->cmd != CMD_DEVICE_RESET )
{
// Select the drive - call the sub_select function.
// Quit now if this fails.
if ( sub_select() )
{
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();
}
// Start the command by setting the Command register. The drive
// should immediately set BUSY status.
pio_outbyte( CB_CMD, ADP->cmd );
// Waste some time by reading the alternate status a few times.
// This gives the drive time to set BUSY in the status register on
// really fast systems. If we don't do this, a slow drive on a fast
// system may not set BUSY fast enough and we would think it had
// completed the command when it really had not even started the
// command yet.
DELAY400NS;
// IF
// This is an Exec Dev Diag command (cmd=0x90)
// and there is no device 0 then
// there will be no interrupt. So we must
// poll device 1 until it allows register
// access and then do normal polling of the Status
// register for BSY=0.
// ELSE
// IF
// This is a Dev Reset command (cmd=0x08) then
// there should be no interrupt. So we must
// poll for BSY=0.
// ELSE
// Do the normal polling for BSY=0.
if ( ( ADP->cmd == CMD_EXECUTE_DEVICE_DIAGNOSTIC )
&&
( ADP->devType == ADVIO_DEV_TYPE_NONE )
)
{
trc_llt( 0, 0, TRC_LLT_PNBSY );
while ( 1 )
{
pio_outbyte( CB_DH, CB_DH_DEV1 );
DELAY400NS;
secCnt = pio_inbyte( CB_SC );
secNum = pio_inbyte( CB_SN );
if ( ( secCnt == 0x01 ) && ( secNum == 0x01 ) )
break;
if ( tmr_chk_timeout() )
{
trc_llt( 0, 0, TRC_LLT_TOUT );
ADP->to = 1;
ADP->ec = 24;
trc_llt( 0, ADP->ec, TRC_LLT_ERROR );
break;
}
}
}
else
{
if ( ADP->cmd == CMD_DEVICE_RESET )
{
// Poll for not BUSY -or- wait for time out.
reg_poll_not_busy( 23 );
}
else
{
// Poll for not BUSY -or- wait for time out.
reg_poll_not_busy( 23 );
}
}
// Read the primary status register. In keeping with the rules
// stated above the primary status register is read only
// ONCE.
status = pio_inbyte( CB_STAT );
// Error if BUSY, DEVICE FAULT, DRQ or ERROR status now.
if ( ADP->ec == 0 )
{
if ( status & ( CB_STAT_BSY | CB_STAT_DF | CB_STAT_DRQ | CB_STAT_ERR ) )
{
ADP->ec = 21;
trc_llt( 0, ADP->ec, TRC_LLT_ERROR );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -