📄 winhpa48.cpp
字号:
#include <windows.h>
#include <stdio.h>
#include "winio.h"
//********************************************************************
// ATA LOW LEVEL I/O HPA DRIVER -- testhpa.C
//
// by SUN HongZhi (shz@cumtb.edu.cn)
//
// Testhpa has a single header file(test.h) and a single C file.
// This code is based on the ATA/ATAPI-4,-5 and -6 standards and
// on interviews with various ATA controller .
//
// Note that testhpa does not support ATA CHS addressing.
//
// Most of the code is standard C code and should compile
// using any C compiler. It has been tested using Tc C/C++ 3.0.
//
//********************************************************************
#include "testhpa.h"
//**************************************************************
//
struct REG_CMD_INFO reg_cmd_info;
int reg_config_info[2];
unsigned char * pio_bmide_base_addr;
unsigned int 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 + 6 // [8] CB_DC & CB_ASTAT
} ;
unsigned char pio_xfer_width = PIO_DEFAULT_XFER_WIDTH;
//**************************************************************
//
// functions internal and private to testhpa
//
//**************************************************************
static void sub_setup_command( void );
static void sub_trace_command( void );
static int sub_select( unsigned char dev );
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 sub_wait_poll( unsigned char we, unsigned char pe );
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 testhpa.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 )
{ int i=0;
while ( 1 )
{
pio_outbyte( CB_DH, CB_DH_DEV1 );
DELAY400NS;i++;
sc = pio_inbyte( CB_SC );
sn = pio_inbyte( CB_SN );
if ( ( sc == 0x01 ) && ( sn == 0x01 ) )
break;
if ( tmr_chk_timeout() || i>3000 )
{
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
// testhpa.H.
sub_trace_command();
if ( reg_cmd_info.ec )
return 1;
return 0;
}
//*************************************************************
//
// exec_non_data_cmd() - Execute a non-data command.
//
// This includes the strange ATAPI DEVICE RESET 'command'
// (command code 08H).
//
// 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( unsigned char dev );
static int exec_non_data_cmd( unsigned char dev )
{
unsigned char secCnt;
// reset Bus Master Error bit
pio_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.
// Select the drive - call the sub_select function.
// Quit now if this fails.
if ( sub_select( dev ) )
{
return 1;
}
// Set up all the registers except the command register.
sub_setup_command();
for(secCnt=0;secCnt<200;secCnt++){DELAY400NS};
// Start the command by setting the Command register. The drive
// should immediately set BUSY status.
pio_outbyte( CB_CMD, reg_cmd_info.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;
for(secCnt=0;secCnt<200;secCnt++){DELAY400NS};
// NON_DATA_DONE:
// All done. The return values of this function are described in
// testhpa.H.
sub_trace_command();
if ( reg_cmd_info.ec )
return 1;
return 0;
}
//*************************************************************
//
// reg_non_data_lba28() - Easy way to execute a non-data command
// using an LBA sector address.
//
//*************************************************************
int reg_non_data_lba28( unsigned char dev, unsigned char cmd,
unsigned int fr, unsigned int sc,
unsigned long lba )
{
// Setup current command information.
reg_cmd_info.cmd = cmd;
reg_cmd_info.fr = fr;
reg_cmd_info.sc = sc;
reg_cmd_info.dh = (unsigned char) ( CB_DH_LBA | ( dev ? CB_DH_DEV1 : CB_DH_DEV0 ) );
reg_cmd_info.dc = (unsigned char) ( int_use_intr_flag ? 0 : CB_DC_NIEN );
reg_cmd_info.ns = sc;
reg_cmd_info.lbaSize = LBA28;
reg_cmd_info.lbaHigh = 0L;
reg_cmd_info.lbaLow = lba;
// Execute the command.
return exec_non_data_cmd( dev );
}
//*************************************************************
//
// reg_non_data_lba48() - Easy way to execute a non-data command
// using an LBA sector address.
//
//*************************************************************
int reg_non_data_lba48( unsigned char dev, unsigned char cmd,
unsigned int fr, unsigned int sc,
unsigned long lbahi, unsigned long lbalo )
{
// Setup current command infomation.
reg_cmd_info.cmd = cmd;
reg_cmd_info.fr = fr;
reg_cmd_info.sc = sc;
reg_cmd_info.dh = (unsigned char) ( CB_DH_LBA | ( dev ? CB_DH_DEV1 : CB_DH_DEV0 ) );
reg_cmd_info.dc = (unsigned char) ( int_use_intr_flag ? 0 : CB_DC_NIEN );
reg_cmd_info.ns = sc;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -