⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ataiopci.c

📁 ATADRVR是DOS下的磁盘驱动程序,采用PIO传输 ,PCI DMA传输,ATA包,等非常全面代码示例... 内部有C与Asm描述. 编译环境:Borland C/C++ 4.52 与 Bo
💻 C
📖 第 1 页 / 共 3 页
字号:
//********************************************************************
// ATA LOW LEVEL I/O DRIVER -- ATAIOPCI.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 functions for executing ATA PCI bus
// mastering READ/WRITE DMA commands for ATA and ATAPI.
//********************************************************************

#include <dos.h>

#include "ataio.h"

#define DEBUG_PCI 0x00  // not zero for debug
                        // 0x01 trace the interrupt counter and flag
                        // 0x02 debug LARGE PRD

#if DEBUG_PCI & 0x02
   #include <stdio.h>
   extern unsigned char far LFB[128];
   extern void prt( void );
   extern void pstr( unsigned char * s );
#endif

//***********************************************************
//
// Some notes about PCI bus mastering DMA...
//
// ATA PCI Bus Master DMA was first described by the SFF-8038
// document.  That document is now very obsolete and generally
// difficult to obtain.  The ANSI INCITS T13 document "ATA Host
// Adapter Standards" (T13 document 1510D) has replaced
// SFF-8038.  This code supports the type of DMA described by
// sections 1 to 5 of the T13 1510D document.
//
// Note that the T13 1510D document also describes (in section 6) a
// complex DMA engine called ADMA.  While ADMA is a good idea it
// will probably never be popular or widely implemented.  This code
// does not support ADMA.
//
// The base address of the Bus Master Control Registers (BMCR) is
// found in the PCI Configuration space for the ATA controller (at
// offset 0x30 in the config space data).  This is normally an I/O
// address.
//
// The BMCR data is 16 bytes of data starting at the BMCR base
// address.  The first 8 bytes is for the primary ATA channel and
// the second 8 bytes is for the secondary ATA channel.  The 8 bytes
// contain a "command" byte and a "status" byte and a 4 byte
// (32-bit) physical memory address pointing to the Physical Region
// Descriptor (PRD) list.  Each PRD entry describes an area of
// memory or data buffer for the DMA transfer.  A region described
// by a PRD may not cross a 64K byte boundary in physical memory.
// Also, the PRD list must not cross a 64K byte boundary.
//
// SIMPLE/COMPLEX PRD lists...
// This code can build a PRD list for data transfers up to 256K
// bytes. These types of PRD lists are built in a data area
// that is statically allocated below. These PRD lists use
// transfer the data to/from the caller's I/O buffer.
//
// LARGE PRD lists...
// This code also supports READ/WRITE DMA EXT command transfers
// up to 65536 sectors. This is done by creating a large
// PRD list in the caller's I/O buffer. This PRD list
// reuses a 64K part of the caller's I/O buffer for the data transfer.
// In this manner transfers up toe 65536 sectors (or about 33Mbytes).
// See the function dma_pci_set_max_xfer().
//
//***********************************************************

//***********************************************************
//
// pci bus master registers and PRD list buffer,
// see dma_pci_config().
//
//***********************************************************

// public data...

int dma_pci_prd_type;                  // type of PRD list to use

long dma_pci_largeMaxB;                // max LARGE dma xfer size in bytes
long dma_pci_largeMaxS;                // max LARGE dma xfer size in sectors
unsigned long far * dma_pci_largePrdBufPtr;  // LARGE PRD buffer ptr
unsigned char far * dma_pci_largeIoBufPtr;   // LARGE PRD I/O address

unsigned long far * dma_pci_prd_ptr;   // current PRD buffer address
int dma_pci_num_prd;                   // current number of PRD entries

// private data...

// data used by SIMPLE/COMPLEX PRD lists

#define MAX_TRANSFER_SIZE  262144L     // max transfer size (in bytes,
                                       // should be multiple of 65536)

#define MAX_SEG ((MAX_TRANSFER_SIZE/65536L)+2L) // number physical segments
#define MAX_PRD (MAX_SEG*4L)                    // number of PRDs required

#define PRD_BUF_SIZE (48+(2*MAX_PRD*8))         // size of PRD list buffer

static unsigned char far prdBuf[PRD_BUF_SIZE];  // PRD buffer address
static unsigned long far * prdBufPtr;           // PRD addr (seg:off) off is 0

// BMIDE data

static unsigned char statReg;          // save BM status reg bits
static unsigned char rwControl;        // read/write control bit setting

//***********************************************************
//
// set_up_xfer() -- set up the PRD entry list
//
// NOTE:
// dma_pci_prd_type == PCI_TYPE_LARGE uses part of the caller's
// I/O buffer to hold a large PRD list and another part of the
// caller's I/O buffer to send/receive the actual data. Each
// PRD entry uses the same memory address.
//
//***********************************************************

static int set_up_xfer( int dir, long bc, unsigned int seg, unsigned int off );

static int set_up_xfer( int dir, long bc, unsigned int seg, unsigned int off )

{
   int numPrd;                      // number of PRD required
   int maxPrd;                      // max number of PRD allowed
   unsigned long temp;
   unsigned long phyAddr;           // physical memory address
   unsigned long savePhyAddr;       // physical memory address
   unsigned long bigCnt;            // complex big count
   unsigned long smallCnt;          // complex small count
   unsigned long far * prdPtr;      // pointer to PRD entry list

   // disable/stop the dma channel, clear interrupt and error bits
   sub_writeBusMstrCmd( BM_CR_MASK_STOP );
   sub_writeBusMstrStatus( statReg | BM_SR_MASK_INT | BM_SR_MASK_ERR );

   // setup to build the PRD list...
   if ( dma_pci_prd_type == PRD_TYPE_LARGE )
   {
      // ...set up for LARGE PRD list
      // ...max PRDs allowed
      maxPrd = 512;
      // ...set big and small counts to max
      bigCnt = smallCnt = 65536L;
      // ...set the LARGE PRD buffer address
      dma_pci_prd_ptr = prdPtr = dma_pci_largePrdBufPtr;
      // ...convert I/O buffer address to physical memory address
      phyAddr = (unsigned long) FP_SEG( dma_pci_largeIoBufPtr );
      phyAddr = phyAddr << 4;
      phyAddr = phyAddr + (unsigned long) FP_OFF( dma_pci_largeIoBufPtr );
      phyAddr = phyAddr & 0xfffffffeL;
   }
   else
   {
      // ...set up for SIMPLE/COMPLEX PRD list
      // ...max PRDs allowed
      maxPrd = (int) MAX_PRD;
      // ...set big and small counts to max
      bigCnt = smallCnt = 65536L;
      // ...adjust PRD buffer address and adjust big and small counts
      prdPtr = prdBufPtr;
      if ( dma_pci_prd_type == PRD_TYPE_COMPLEX )
      {
         temp = tmr_read_bios_timer();
         prdPtr = MK_FP( FP_SEG( prdBufPtr ),
                         (unsigned int) ( temp & 0x0000000cL ) );
         smallCnt = ( temp & 0x000000feL ) + 2L;
         bigCnt = bigCnt - smallCnt - ( temp & 0x0000000eL );
      }
      // ...set the SIMPLE/COMPLEX PRD buffer address
      dma_pci_prd_ptr = prdPtr;
      // ... convert I/O buffer address to an physical memory address
      phyAddr = (unsigned long) seg;
      phyAddr = phyAddr << 4;
      phyAddr = phyAddr + (unsigned long) off;
      phyAddr = phyAddr & 0xfffffffeL;
   }
   savePhyAddr = phyAddr;

   #if DEBUG_PCI & 0x02
      pstr( "=>[prd] set_up_xfer()..." );
      sprintf( LFB, "=>[prd] dir %d bc %lx seg %04x off %04x",
                             dir, bc, seg, off );
      prt();
      sprintf( LFB, "=>[prd] maxPrd %d prdPtr %Fp bigCnt %lx smallCnt %lx",
                             maxPrd, prdPtr, bigCnt, smallCnt );
      prt();
      sprintf( LFB, "=>[prd] phyAddr %08lx", phyAddr );
      prt();
   #endif

   // build the PRD list...
   // ...PRD entry format:
   // +0 to +3 = memory address
   // +4 to +5 = 0x0000 (not EOT) or 0x8000 (EOT)
   // +6 to +7 = byte count
   // ...zero number of PRDs
   numPrd = 0;
   // ...loop to build each PRD
   while ( bc > 0 )
   {
      if ( numPrd >= maxPrd )
         return 1;
      // set this PRD's address
      if ( dma_pci_prd_type == PRD_TYPE_LARGE )
         phyAddr = savePhyAddr;
      prdPtr[0] = phyAddr;
      // set count for this PRD
      if ( ( numPrd & 0x0003 ) == 0x0002 )
         temp = bigCnt;       // use big count (1 of 4 PRDs)
      else                    // else
         temp = smallCnt;     // use small count (3 of 4 PRDs)
      if ( temp > bc )        // count to large?
         temp = bc;           //    yes - use actual count
      // check if count will fit
      phyAddr = phyAddr + temp;
      if ( ( phyAddr & 0xffff0000L ) != ( prdPtr[0] & 0xffff0000L ) )
      {
         phyAddr = phyAddr & 0xffff0000L;
         temp = phyAddr - prdPtr[0];
      }
      // set this PRD's count
      prdPtr[1] = temp & 0x0000ffffL;
      // update byte count
      bc = bc - temp;
      // set the end bit in the prd list
      if ( bc < 1 )
         prdPtr[1] = prdPtr[1] | 0x80000000L;
      prdPtr ++ ;
      prdPtr ++ ;
      numPrd ++ ;
   }

   // return the current PRD list size and
   // set the prd list address in the BMCR:
   // convert PRD buffer seg:off to a physical address
   // and write into BMCR PRD address registers.

   dma_pci_num_prd = numPrd;
   temp = FP_SEG( dma_pci_prd_ptr );
   temp = temp << 4;
   temp = temp + FP_OFF( dma_pci_prd_ptr );
   outport( pio_bmcr_base_addr + BM_PRD_ADDR_LOW,
            (unsigned int) ( temp & 0x0000ffffL ) );
   outport( pio_bmcr_base_addr + BM_PRD_ADDR_HIGH,
            (unsigned int) ( ( temp & 0xffff0000L ) >> 16 ) );

   #if DEBUG_PCI & 0x02
      {
         int ndx;
         unsigned long far * lfp;

         pstr( "=>[prd] ----- Bus Master PRD List -----" );
         sprintf( LFB, "=>[prd] PRD PhyAddr %08lX",
                       ( ( (unsigned long) FP_SEG( dma_pci_prd_ptr ) ) << 4 )
                       + ( (unsigned long) FP_OFF( dma_pci_prd_ptr ) ) );
         prt();
         lfp = dma_pci_prd_ptr;
         ndx = 0;
         while ( ndx < dma_pci_num_prd )
         {
            sprintf( LFB, "=>[prd] PRD %2d - PhyAddr %08lX Cnt %08lX",
                           ndx, * lfp, * ( lfp + 1 ) );
            prt();
            lfp ++ ;
            if (  ( * lfp ) & 0x80000000L )
               break;
            lfp ++ ;
            ndx ++ ;
         }
      }
   #endif

   // set the read/write control:
   // PCI reads for ATA Write DMA commands,
   // PCI writes for ATA Read DMA commands.

   if ( dir )
      rwControl = BM_CR_MASK_READ;     // ATA Write DMA
   else
      rwControl = BM_CR_MASK_WRITE;    // ATA Read DMA
   sub_writeBusMstrCmd( rwControl );
   return 0;
}

//***********************************************************
//
// dma_pci_config() - configure/setup for Read/Write DMA
//
// The caller must call this function before attempting
// to use any ATA or ATAPI commands in PCI DMA mode.
//
//***********************************************************

int dma_pci_config( unsigned int regAddr )

{
   unsigned int off;
   unsigned int seg;
   unsigned long lw;

   // check reg address

   if ( regAddr & 0x0007 )       // error if regs addr
      return 1;                  // are not xxx0h or xxx8h

   // save the base i/o address of the bus master (BMCR) regs

   pio_bmcr_base_addr = regAddr;

   // disable if reg address is zero

   if ( ! regAddr )              // if zero,
      return 0;                  // PCI DMA is disabled.

   // Set up the PRD entry list buffer address - the PRD entry list
   // may not span a 64KB boundary in physical memory. Space is
   // allocated (above) for this buffer such that it will be
   // aligned on a seqment boundary (off of seg:off will be 0)
   // and such that the PRD list will not span a 64KB boundary...
   // ...convert seg:off to physical address.
   seg = FP_SEG( (unsigned char far *) prdBuf );
   off = FP_OFF( (unsigned char far *) prdBuf );
   lw = (unsigned long) seg;
   lw = lw << 4;
   lw = lw + (unsigned long) off;
   // ...move up to a segment boundary.
   lw = lw + 15;
   lw = lw & 0xfffffff0L;
   // ...check for 64KB boundary in the first part of the PRD buffer,
   // ...if so just move the buffer to that boundary.
   if ( ( lw & 0xffff0000L )
        !=
        ( ( lw + ( MAX_PRD * 8L ) - 1L ) & 0xffff0000L )
      )
      lw = ( lw + ( MAX_PRD * 8L ) ) & 0xffff0000L;
   // ...convert back to seg:off, note that off is now 0
   seg = (unsigned int) ( lw >> 4 );
   dma_pci_prd_ptr = prdBufPtr = MK_FP( seg, 0 );
   // ... current size of the SIMPLE/COMPLEX PRD buffer
   dma_pci_num_prd = 0;

   // read the BM status reg and save the upper 3 bits.
   statReg = sub_readBusMstrStatus() & 0x60;

   // initialize the large PRD list info
   dma_pci_largePrdBufPtr = (void *) 0;
   dma_pci_largeIoBufPtr = (void *) 0;
   dma_pci_largeMaxB = 0;
   dma_pci_largeMaxS = 0;

   return 0;
}

//***********************************************************
//
// determine max dma transfer for PRD type LARGE
//
// dma_pci_prd_type == PCI_TYPE_LARGE uses part of the caller's
// I/O buffer to hold a large PRD list and another part of the
// caller's I/O buffer to send/receive the actual data. Each
// PRD entry uses the same memory address.
//
//***********************************************************

void dma_pci_set_max_xfer( unsigned int seg, unsigned int off,
                           long bufSize )

{
   long bufStart;       // buffer starting physical memory address
   long bufEnd;         // buffer ending physical memory address (+1)
   long pmaStart;       // start of 64K area within buffer
   long pmaEnd;         // end of 64K area within buffer

   // save buffer size
   reg_buffer_size = bufSize;

   // large DMA xfers not supported
   dma_pci_largePrdBufPtr = (void *) 0;
   dma_pci_largeIoBufPtr = (void *) 0;
   dma_pci_largeMaxB = 0L;
   dma_pci_largeMaxS = 0L;

   // convert I/O buffer address from seg:off to an absolute memory address
   // note: the physical address must be a word boundary (an even number).
   bufStart = (unsigned long) seg;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -