📄 ataioreg.c
字号:
if ( ( cmd != CMD_WRITE_MULTIPLE )
&& ( cmd != CMD_CFA_WRITE_MULTIPLE_WO_ERASE )
)
multiCnt = 1;
}
reg_cmd_info.ns = numSect;
reg_cmd_info.mc = multiCnt;
return exec_pio_data_out_cmd( dev, seg, off, numSect, multiCnt );
}
//*************************************************************
//
// reg_pio_data_out_lba48() - Easy way to execute a PIO Data In command
// using an LBA sector address.
//
//*************************************************************
int reg_pio_data_out_lba48( int dev, int cmd,
unsigned int fr, unsigned int sc,
unsigned long lbahi, unsigned long lbalo,
unsigned int seg, unsigned int off,
long numSect, int multiCnt )
{
// Reset error return data.
sub_zero_return_data();
reg_cmd_info.flg = TRC_FLAG_ATA;
reg_cmd_info.ct = TRC_TYPE_APDO;
reg_cmd_info.cmd = cmd;
reg_cmd_info.fr1 = fr;
reg_cmd_info.sc1 = sc;
reg_cmd_info.dh1 = CB_DH_LBA | (dev ? CB_DH_DEV1 : CB_DH_DEV0 );
reg_cmd_info.dc1 = int_use_intr_flag ? 0 : CB_DC_NIEN;
reg_cmd_info.lbaSize = LBA48;
reg_cmd_info.lbaHigh1 = lbahi;
reg_cmd_info.lbaLow1 = lbalo;
// adjust multiple count
if ( multiCnt & 0x0800 )
{
// assume caller knows what they are doing
multiCnt &= 0x00ff;
}
else
{
// only Write Multiple Ext uses multiCnt
if ( cmd != CMD_WRITE_MULTIPLE_EXT )
multiCnt = 1;
}
reg_cmd_info.ns = numSect;
reg_cmd_info.mc = multiCnt;
return exec_pio_data_out_cmd( dev, seg, off, numSect, multiCnt );
}
//*************************************************************
//
// reg_packet() - Execute an ATAPI Packet (A0H) command.
//
// See ATA-4 Section 9.10, Figure 14.
//
//*************************************************************
int reg_packet( int dev,
unsigned int cpbc,
unsigned int cpseg, unsigned int cpoff,
int dir,
long dpbc,
unsigned int dpseg, unsigned int dpoff,
unsigned long lba )
{
unsigned char status;
unsigned char reason;
unsigned char lowCyl;
unsigned char highCyl;
unsigned int byteCnt;
long wordCnt;
int ndx;
unsigned long dpaddr;
unsigned long savedpaddr;
unsigned char far * cfp;
int slowXferCntr = 0;
int prevFailBit7 = 0;
// mark start of PI cmd in low level trace
trc_llt( 0, 0, TRC_LLT_S_PI );
// reset Bus Master Error bit
sub_writeBusMstrStatus( BM_SR_MASK_ERR );
// Make sure the command packet size is either 12 or 16
// and save the command packet size and data.
cpbc = cpbc < 12 ? 12 : cpbc;
cpbc = cpbc > 12 ? 16 : cpbc;
// Setup current command information.
sub_zero_return_data();
reg_cmd_info.flg = TRC_FLAG_ATAPI;
reg_cmd_info.ct = dir ? TRC_TYPE_PPDO : TRC_TYPE_PPDI;
reg_cmd_info.cmd = CMD_PACKET;
reg_cmd_info.fr1 = reg_atapi_reg_fr;
reg_cmd_info.sc1 = reg_atapi_reg_sc;
reg_cmd_info.sn1 = reg_atapi_reg_sn;
reg_cmd_info.cl1 = (unsigned char) ( dpbc & 0x00ff );
reg_cmd_info.ch1 = ( unsigned char) ( ( dpbc & 0xff00 ) >> 8 );
reg_cmd_info.dh1 = ( dev ? CB_DH_DEV1 : CB_DH_DEV0 )
| ( reg_atapi_reg_dh & 0x0f );
reg_cmd_info.dc1 = int_use_intr_flag ? 0 : CB_DC_NIEN;
reg_cmd_info.lbaSize = LBA32;
reg_cmd_info.lbaLow1 = lba;
reg_cmd_info.lbaHigh1 = 0L;
reg_atapi_cp_size = cpbc;
cfp = MK_FP( cpseg, cpoff );
for ( ndx = 0; ndx < cpbc; ndx ++ )
{
reg_atapi_cp_data[ndx] = * cfp;
cfp ++ ;
}
// Zero the alternate ATAPI register data.
reg_atapi_reg_fr = 0;
reg_atapi_reg_sc = 0;
reg_atapi_reg_sn = 0;
reg_atapi_reg_dh = 0;
// Set command time out.
tmr_set_timeout();
// 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_PI );
reg_drq_block_call_back = (void *) 0;
return 1;
}
// Set up all the registers except the command register.
sub_setup_command();
// For interrupt mode, install interrupt handler.
int_save_int_vect();
// Start the command by setting the Command register. The drive
// should immediately set BUSY status.
pio_outbyte( CB_CMD, CMD_PACKET );
// 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;
// Command packet transfer...
// Check for protocol failures,
// the device should have BSY=1 or
// if BSY=0 then either DRQ=1 or CHK=1.
sub_atapi_delay( dev );
status = pio_inbyte( CB_ASTAT );
if ( status & CB_STAT_BSY )
{
// BSY=1 is OK
}
else
{
if ( status & ( CB_STAT_DRQ | CB_STAT_ERR ) )
{
// BSY=0 and DRQ=1 is OK
// BSY=0 and ERR=1 is OK
}
else
{
reg_cmd_info.failbits |= FAILBIT0; // not OK
}
}
// Command packet transfer...
// Poll Alternate Status for BSY=0.
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 ); // yes
reg_cmd_info.to = 1;
reg_cmd_info.ec = 51;
trc_llt( 0, reg_cmd_info.ec, TRC_LLT_ERROR );
dir = -1; // command done
break;
}
}
// Command packet transfer...
// Check for protocol failures... no interrupt here please!
// Clear any interrupt the command packet transfer may have caused.
if ( int_intr_flag ) // extra interrupt(s) ?
reg_cmd_info.failbits |= FAILBIT1;
int_intr_flag = 0;
// Command packet transfer...
// If no error, transfer the command packet.
if ( reg_cmd_info.ec == 0 )
{
// Command packet transfer...
// Read the primary status register and the other ATAPI registers.
status = pio_inbyte( CB_STAT );
reason = pio_inbyte( CB_SC );
lowCyl = pio_inbyte( CB_CL );
highCyl = pio_inbyte( CB_CH );
// Command packet transfer...
// check status: must have BSY=0, DRQ=1 now
if ( ( status & ( CB_STAT_BSY | CB_STAT_DRQ | CB_STAT_ERR ) )
!= CB_STAT_DRQ
)
{
reg_cmd_info.ec = 52;
trc_llt( 0, reg_cmd_info.ec, TRC_LLT_ERROR );
dir = -1; // command done
}
else
{
// Command packet transfer...
// Check for protocol failures...
// check: C/nD=1, IO=0.
if ( ( reason & ( CB_SC_P_TAG | CB_SC_P_REL | CB_SC_P_IO ) )
|| ( ! ( reason & CB_SC_P_CD ) )
)
reg_cmd_info.failbits |= FAILBIT2;
if ( ( lowCyl != reg_cmd_info.cl1 )
|| ( highCyl != reg_cmd_info.ch1 ) )
reg_cmd_info.failbits |= FAILBIT3;
// Command packet transfer...
// trace cdb byte 0,
// xfer the command packet (the cdb)
trc_llt( 0, * (unsigned char far *) MK_FP( cpseg, cpoff ), TRC_LLT_P_CMD );
pio_drq_block_out( CB_DATA, cpseg, cpoff, cpbc >> 1 );
DELAY400NS; // delay so device can get the status updated
}
}
// Data transfer loop...
// If there is no error, enter the data transfer loop.
// First adjust the I/O buffer address so we are able to
// transfer large amounts of data (more than 64K).
dpaddr = dpseg;
dpaddr = dpaddr << 4;
dpaddr = dpaddr + dpoff;
savedpaddr = dpaddr;
while ( reg_cmd_info.ec == 0 )
{
// Data transfer loop...
// Wait for interrupt -or- wait for not BUSY -or- wait for time out.
sub_atapi_delay( dev );
reg_wait_poll( 53, 54 );
// Data transfer loop...
// If there was a time out error, exit the data transfer loop.
if ( reg_cmd_info.ec )
{
dir = -1; // command done
break;
}
// Data transfer loop...
// If using interrupts get the status read by the interrupt
// handler, otherwise read the status register.
if ( int_use_intr_flag )
status = int_ata_status;
else
status = pio_inbyte( CB_STAT );
reason = pio_inbyte( CB_SC );
lowCyl = pio_inbyte( CB_CL );
highCyl = pio_inbyte( CB_CH );
// Data transfer loop...
// Exit the read data loop if the device indicates this
// is the end of the command.
if ( ( status & ( CB_STAT_BSY | CB_STAT_DRQ ) ) == 0 )
{
dir = -1; // command done
break;
}
// Data transfer loop...
// The device must want to transfer data...
// check status: must have BSY=0, DRQ=1 now.
if ( ( status & ( CB_STAT_BSY | CB_STAT_DRQ ) ) != CB_STAT_DRQ )
{
reg_cmd_info.ec = 55;
trc_llt( 0, reg_cmd_info.ec, TRC_LLT_ERROR );
dir = -1; // command done
break;
}
else
{
// Data transfer loop...
// Check for protocol failures...
// check: C/nD=0, IO=1 (read) or IO=0 (write).
if ( ( reason & ( CB_SC_P_TAG | CB_SC_P_REL ) )
|| ( reason & CB_SC_P_CD )
)
reg_cmd_info.failbits |= FAILBIT4;
if ( ( reason & CB_SC_P_IO ) && dir )
reg_cmd_info.failbits |= FAILBIT5;
}
// do the slow data transfer thing
if ( reg_slow_xfer_flag )
{
slowXferCntr ++ ;
if ( slowXferCntr <= reg_slow_xfer_flag )
{
sub_xfer_delay();
reg_slow_xfer_flag = 0;
}
}
// Data transfer loop...
// get the byte count, check for zero...
byteCnt = ( highCyl << 8 ) | lowCyl;
if ( byteCnt < 1 )
{
reg_cmd_info.ec = 59;
trc_llt( 0, reg_cmd_info.ec, TRC_LLT_ERROR );
dir = -1; // command done
break;
}
// Data transfer loop...
// and check protocol failures...
if ( byteCnt > dpbc )
reg_cmd_info.failbits |= FAILBIT6;
reg_cmd_info.failbits |= prevFailBit7;
prevFailBit7 = 0;
if ( byteCnt & 0x0001 )
prevFailBit7 = FAILBIT7;
// Data transfer loop...
// Quit if buffer overrun.
// If DRQ call back in use adjust buffer address.
if ( reg_drq_block_call_back )
{
if ( byteCnt > reg_buffer_size )
{
reg_cmd_info.ec = 61;
trc_llt( 0, reg_cmd_info.ec, TRC_LLT_ERROR );
dir = -1; // command done
break;
}
reg_cmd_info.drqPacketSize = byteCnt;
dpaddr = savedpaddr;
}
else
{
if ( ( reg_cmd_info.totalBytesXfer + byteCnt ) > reg_buffer_size )
{
reg_cmd_info.ec = 61;
trc_llt( 0, reg_cmd_info.ec, TRC_LLT_ERROR );
dir = -1; // command done
break;
}
}
// increment number of DRQ packets
reg_cmd_info.drqPackets ++ ;
// Data transfer loop...
// transfer the data and update the i/o buffer address
// and the number of bytes transfered.
wordCnt = ( byteCnt >> 1 ) + ( byteCnt & 0x0001 );
reg_cmd_info.totalBytesXfer += ( wordCnt << 1 );
dpseg = (unsigned int) ( dpaddr >> 4 );
dpoff = (unsigned int) ( dpaddr & 0x0000000fL );
if ( dir )
{
if ( reg_drq_block_call_back )
(* reg_drq_block_call_back) ( & reg_cmd_info );
pio_drq_block_out( CB_DATA, dpseg, dpoff, wordCnt );
}
else
{
pio_drq_block_in( CB_DATA, dpseg, dpoff, wordCnt );
if ( reg_drq_block_call_back )
(* reg_drq_block_call_back) ( & reg_cmd_info );
}
dpaddr = dpaddr + byteCnt;
DELAY400NS; // delay so device can get the status updated
}
// End of command...
// Wait for interrupt or poll for BSY=0,
// but don't do this if there was any error or if this
// was a commmand that did not transfer data.
if ( ( reg_cmd_info.ec == 0 ) && ( dir >= 0 ) )
{
sub_atapi_delay( dev );
reg_wait_poll( 56, 57 );
}
// Final status check, only if no previous error.
if ( reg_cmd_info.ec == 0 )
{
// Final status check...
// If using interrupts get the status read by the interrupt
// handler, otherwise read the status register.
if ( int_use_intr_flag )
status = int_ata_status;
else
status = pio_inbyte( CB_STAT );
reason = pio_inbyte( CB_SC );
lowCyl = pio_inbyte( CB_CL );
highCyl = pio_inbyte( CB_CH );
// Final status check...
// check for any error.
if ( status & ( CB_STAT_BSY | CB_STAT_DRQ | CB_STAT_ERR ) )
{
reg_cmd_info.ec = 58;
trc_llt( 0, reg_cmd_info.ec, TRC_LLT_ERROR );
}
}
// Final status check...
// Check for protocol failures...
// check: C/nD=1, IO=1.
if ( ( reason & ( CB_SC_P_TAG | CB_SC_P_REL ) )
|| ( ! ( reason & CB_SC_P_IO ) )
|| ( ! ( reason & CB_SC_P_CD ) )
)
reg_cmd_info.failbits |= FAILBIT8;
// Done...
// Read the output registers and trace the command.
if ( ! reg_cmd_info.totalBytesXfer )
reg_cmd_info.ct = TRC_TYPE_PND;
sub_trace_command();
// Final status check
// 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 );
}
// For interrupt mode, remove interrupt handler.
int_restore_int_vect();
// mark end of PI cmd in low level trace
trc_llt( 0, 0, TRC_LLT_E_PI );
// reset reg_drq_block_call_back to NULL (0)
reg_drq_block_call_back = (void *) 0;
// All done. The return values of this function are described in
// ATAIO.H.
if ( reg_cmd_info.ec )
return 1;
return 0;
}
// end ataioreg.c
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -