📄 ataioisa.c
字号:
//********************************************************************
// ATA LOW LEVEL I/O DRIVER -- ATAIOISA.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 ISA bus READ/WRITE DMA command
// processing for ATA and ATAPI.
//********************************************************************
#include <dos.h>
#include "ataio.h"
#define DEBUG_ISA 0x00 // not zero for debug
// 0x01 trace the DMA channel mode
//***********************************************************
//
// Some notes about ISA bus DMA...
//
// ISA bus DMA uses an DMA controller built into an ISA bus
// motherboard. This DMA controller has six DMA channels: 1, 2, 3,
// 5, 6 and 7. Channels 0 and 4 are reserved for other uses.
// Channels 1, 2 and 3 are 8-bit and channels 5, 6 and 7 are 16-bit.
// Since ATA DMA is always 16-bit only channels 5, 6 or 7 can be
// used here.
//
// An ISA bus DMA controller is unable to transfer data across a
// 128K boundary in physical memory. This code is able to cross one
// 128K DMA boundary. It could be enhanced to cross any number of
// boundaries but since a tradional ATA read/write command can not
// transfer more than 256 sectors or 128K bytes, there is little
// need for this code to handle more than one boundary crossing.
//
// Note that the ISA 16-bit DMA channels are restricted to
// transfering data on word boundaries and transfers of an even
// number of bytes. This is because the host memory address and the
// transfer length byte count are both divided by 2. These word
// addresses and word counts are used by the DMA controller.
//
//***********************************************************
//***********************************************************
//
// isa bus dma channel configuration stuff,
// see dma_isa_config().
//
//***********************************************************
static int dmaChan = 0; // dma channel number (5, 6 or 7)
static int dmaPageReg; // page reg addr
static int dmaAddrReg; // addr reg addr
static int dmaCntrReg; // cntr reg addr
static int dmaChanSel; // channel selection bits...
// also see modeByte below
#define DMA_SEL5 0x01 // values used in dmaChanSel
#define DMA_SEL6 0x02
#define DMA_SEL7 0x03
static int dmaTCbit; // terminal count bit status
#define DMA_TC5 0x02 // values used in dmaTCbit
#define DMA_TC6 0x04
#define DMA_TC7 0x08
//***********************************************************
//
// isa bus dma channel configuration and control macros
//
//***********************************************************
#define DMA_MASK_ENABLE 0x00 // bits for enable/disable
#define DMA_MASK_DISABLE 0x04
#define enableChan() outportb( 0xd4, DMA_MASK_ENABLE | dmaChanSel )
#define disableChan() outportb( 0xd4, DMA_MASK_DISABLE | dmaChanSel )
#define clearFF() outportb( 0xd8, 0 ) // macro to reset flip-flop
// so we access the low byte
// of the address and word
// count registers
//***********************************************************
//
// dma channel programming stuff
//
//***********************************************************
static int doTwo; // transfer crosses a physical boundary if != 0
static unsigned int page1; // upper part of physical memory address
// for 1st (or only) transfer
static unsigned int page2; // upper part of physical memory address
// for 2nd transfer
static unsigned long addr1; // physical address for 1st (or only) transfer
static unsigned long addr2; // physical address for 2nd transfer
static unsigned long count1; // byte/word count for 1st (or only) transfer
static unsigned long count2; // byte/word count for 2nd transfer
static int modeByte; // mode byte for the dma channel...
// also see dmaChanSel above
#define DMA_MODE_DEMAND 0x00 // modeByte bits for various dma modes
#define DMA_MODE_BLOCK 0x80
#define DMA_MODE_SINGLE 0x40
#define DMA_MODE_MEMR 0x08 // modeByte memory read or write
#define DMA_MODE_MEMW 0x04
//***********************************************************
//
// set_up_xfer() -- set up for 1 or 2 dma transfers -- either
// 1 or 2 transfers are required per ata
// or atapi command.
//
//***********************************************************
static void set_up_xfer( int dir, long bc, unsigned int seg, unsigned int off );
static void set_up_xfer( int dir, long bc, unsigned int seg, unsigned int off )
{
unsigned long count; // total byte/word count
unsigned long addr; // absolute memory address
// determine number of bytes to be transferred
count = bc;
// convert transfer address from seg:off to a 20-bit absolute memory address
addr = (unsigned long) seg;
addr = addr << 4;
addr = addr + (unsigned long) off;
// determine first transfer address,
// determine first and second transfer counts.
// The absolute address bits a19 - a0 are used as follows:
// bits 7-4 of the page register are set to 0.
// a19 - a17 are placed into the page register bits 3-1.
// bit 0 of the page register is set to 0.
// a16-a1 are placed into the address register bits 15-0.
// a0 is discarded.
// assume that only one transfer is needed and determine the
// page, addr1, count1 and count2 values.
// the transfer count is converted from byte to word counts.
page1 = (int) ( ( addr & 0x000e0000L ) >> 16 );
page2 = page1 + 2;
addr1 = ( addr & 0x0001fffeL ) >> 1;
addr2 = 0;
count1 = count >> 1;
count2 = 0;
// if a dma boundary will be crossed, determine the
// first and second transfer counts. again the
// transfer counts must be converted from byte to word counts.
doTwo = 0;
if ( ( ( addr + count - 1L ) & 0x000e0000L ) != ( addr & 0x000e0000L ) )
{
doTwo = 1;
// determine first and second transfer counts
count1 = ( ( addr & 0x000e0000L ) + 0x00020000L ) - addr;
count2 = count - count1;
// convert counts to word values
count1 = count1 >> 1;
count2 = count2 >> 1;
}
// get dma channel mode
modeByte = DMA_MODE_DEMAND; // change this line for single word dma
modeByte = modeByte | ( dir ? DMA_MODE_MEMR : DMA_MODE_MEMW );
modeByte = modeByte | dmaChanSel;
}
//***********************************************************
//
// prog_dma_chan() -- config the dma channel we will use --
// we call this function to set each
// part of a dma transfer.
//
//***********************************************************
static void prog_dma_chan( unsigned int page, unsigned long addr,
unsigned long count, int mode );
static void prog_dma_chan( unsigned int page, unsigned long addr,
unsigned long count, int mode )
{
// disable interrupts
disable();
// disable the dma channel
trc_llt( 0, 0, TRC_LLT_DMA3 );
disableChan();
// reset channel status (required by some systems)
inportb( 0xd0 );
// set dma channel transfer address
clearFF();
outportb( dmaAddrReg, (int) ( addr & 0x000000ffL ) );
addr = addr >> 8;
outportb( dmaAddrReg, (int) ( addr & 0x000000ffL ) );
// set dma channel page
outportb( dmaPageReg, page );
// set dma channel word count
count -- ;
clearFF();
outportb( dmaCntrReg, (int) ( count & 0x000000ffL ) );
count = count >> 8;
outportb( dmaCntrReg, (int) ( count & 0x000000ffL ) );
// set dma channel mode
outportb( 0xd6, mode );
#if DEBUG_ISA & 0x01
trc_llt( 0, mode, TRC_LLT_DEBUG ); // for debugging
#endif
// enable the dma channel
trc_llt( 0, 0, TRC_LLT_DMA1 );
enableChan();
// enable interrupts
enable();
}
//***********************************************************
//
// chk_cmd_done() -- check for command completion
//
//***********************************************************
static int chk_cmd_done( void );
static int chk_cmd_done( void )
{
int status;
// check for interrupt or poll status
if ( int_use_intr_flag )
{
if ( int_intr_flag ) // interrupt?
{
trc_llt( 0, 0, TRC_LLT_INTRQ );
status = int_ata_status; // get status
trc_llt( CB_STAT, status, TRC_LLT_INB );
return 1; // cmd done
}
}
else
{
status = pio_inbyte( CB_ASTAT ); // poll for not busy & DRQ/errors
if ( ( status & ( CB_STAT_BSY | CB_STAT_DRQ ) ) == 0 )
return 1; // cmd done
if ( ( status & ( CB_STAT_BSY | CB_STAT_DF ) ) == CB_STAT_DF )
return 1; // cmd done
if ( ( status & ( CB_STAT_BSY | CB_STAT_ERR ) ) == CB_STAT_ERR )
return 1; // cmd done
}
return 0; // not done yet
}
//***********************************************************
//
// dma_isa_config() - configure/setup for Read/Write DMA
//
// The caller must call this function before attempting
// to use any ATA or ATAPI commands in ISA DMA mode.
//
//***********************************************************
int dma_isa_config( int chan )
{
// channel must be 0 (disable) or 5, 6 or 7.
switch ( chan )
{
case 0:
dmaChan = 0; // disable ISA DMA
return 0;
case 5: // set up channel 5
dmaChan = 5;
dmaPageReg = 0x8b;
dmaAddrReg = 0xc4;
dmaCntrReg = 0xc6;
dmaChanSel = DMA_SEL5;
dmaTCbit = DMA_TC5;
break;
case 6: // set up channel 6
dmaChan = 6;
dmaPageReg = 0x89;
dmaAddrReg = 0xc8;
dmaCntrReg = 0xca;
dmaChanSel = DMA_SEL6;
dmaTCbit = DMA_TC6;
break;
case 7: // set up channel 7
dmaChan = 7;
dmaPageReg = 0x8a;
dmaAddrReg = 0xcc;
dmaCntrReg = 0xce;
dmaChanSel = DMA_SEL7;
dmaTCbit = DMA_TC7;
break;
default: // not channel 5, 6 or 7
dmaChan = 0; // disable ISA DMA
return 1; // return error
}
return 0;
}
//***********************************************************
//
// exec_isa_ata_cmd() - DMA in ISA Multiword for ATA R/W DMA
//
//***********************************************************
static int exec_isa_ata_cmd( int dev,
unsigned int seg, unsigned int off,
long numSect );
static int exec_isa_ata_cmd( int dev,
unsigned int seg, unsigned int off,
long numSect )
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -