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

📄 mindrvr.c

📁 ata和atapi的驱动程序
💻 C
📖 第 1 页 / 共 5 页
字号:
   DELAY400NS;

   // Command packet transfer...
   // Poll Alternate Status for BSY=0.

   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 ?
      {
         reg_cmd_info.to = 1;
         reg_cmd_info.ec = 51;
         dir = -1;   // command done
         break;
      }
   }

   // Command packet transfer...
   // Check for protocol failures... no interrupt here please!

   // 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 );

      // 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;
         dir = -1;   // command done
      }
      else
      {
         // Command packet transfer...
         // xfer the command packet (the cdb)

         pio_drq_block_out( CB_DATA, cdbBufAddr, cpbc >> 1 );

         DELAY400NS;    // delay so device can get the status updated
      }
   }

   // Data transfer loop...
   // If there is no error, enter the data transfer loop.

   while ( reg_cmd_info.ec == 0 )
   {
      // Data transfer loop...
      // Wait for interrupt -or- wait for not BUSY -or- wait for time out.

      sub_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 );

      // 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;
         dir = -1;   // command done
         break;
      }

      // Data transfer loop...
      // get the byte count, check for zero...

      byteCnt = ( pio_inbyte( CB_CH ) << 8 ) | pio_inbyte( CB_CL );
      if ( byteCnt < 1 )
      {
         reg_cmd_info.ec = 59;
         dir = -1;   // command done
         break;
      }

      // Data transfer loop...
      // 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 );
      if ( dir )
         pio_drq_block_out( CB_DATA, dataBufAddr, wordCnt );
      else
         pio_drq_block_in( CB_DATA, dataBufAddr, wordCnt );
      dataBufAddr = dataBufAddr + 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_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 );

      // Final status check...
      // check for any error.

      if ( status & ( CB_STAT_BSY | CB_STAT_DRQ | CB_STAT_ERR ) )
      {
         reg_cmd_info.ec = 58;
      }
   }

   // Done...

   // Final status check
   // 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
   // MINDRVR.H.

   if ( reg_cmd_info.ec )
      return 1;
   return 0;
}

#endif   // INCLUDE_ATAPI

#if INCLUDE_ATA_DMA

//***********************************************************
//
// Some notes about PCI bus mastering DMA...
//
// !!! The DMA support in MINDRVR is based on x86 PCI bus mastering
// !!! ATA controller design as described by the T13 document
// !!! '1510 Host Controller Standard' (in sections 1-6).
//
// Note that the T13 1510D document also describes a
// complex DMA engine called ADMA.  While ADMA is a good idea it
// will probably never be popular or widely implemented.  MINDRVR
// does not support ADMA.
//
// The base address of the Bus Master Control Registers (BMIDE) is
// found in the PCI Configuration space for the ATA controller (at
// offset 0x20 in the config space data).  This is normally an I/O
// address.
//
// The BMIDE data is 16 bytes of data starting at the BMIDE 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.
//
//***********************************************************

//***********************************************************
//
// pci bus master registers and PRD list buffer,
// see the dma_pci_config() and set_up_xfer() functions.
//
// !!! Note that the PRD buffer is statically allocated here
// !!! but the actual address of the buffer is adjusted by
// !!! the dma_pci_config() function.
//
//***********************************************************

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

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

#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 prdBuf[PRD_BUF_SIZE];      // PRD buffer
static unsigned long * prdBufPtr;               // first PRD addr

//***********************************************************
//
// 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.
//
// !!! MINDRVR assumes the entire DMA data transfer is contained
// !!! within a single contiguous I/O buffer. You may not need
// !!! the dma_pci_config() function depending on how your system
// !!! allocates the PRD buffer.
//
// !!! This function shows an example of PRD buffer allocation.
// !!! The PRD buffer must be aligned
// !!! on a 8 byte boundary and must not cross a 64K byte
// !!! boundary in memory.
//
//***********************************************************

int dma_pci_config( void )

{
   unsigned long lw;

   // 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
   // and such that the PRD list will not span a 64KB boundary...
   lw = (unsigned long) prdBuf;
   // ...move up to an 8 byte boundary.
   lw = lw + 15;
   lw = lw & 0xfffffff8L;
   // ...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;
   // ... return the address of the first PRD
   dma_pci_prd_ptr = prdBufPtr = (unsigned long *) lw;
   // ... return the current number of PRD entries
   dma_pci_num_prd = 0;

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

   return 0;
}

//***********************************************************
//
// set_up_xfer() -- set up the PRD entry list
//
// !!! MINDRVR assumes the entire DMA data transfer is contained
// !!! within a single contiguous I/O buffer. You may not need
// !!! a much more complex set_up_xfer() function to support
// !!! true scatter/gather lists.
//
// The PRD list must be aligned on an 8 byte boundary and the
// list must not cross a 64K byte boundary in memory.
//
//***********************************************************

static int set_up_xfer( int dir, long bc, unsigned char * bufAddr );

static int set_up_xfer( int dir, long bc, unsigned char * bufAddr )

{
   int numPrd;                      // number of PRD required
   int maxPrd;                      // max number of PRD allowed
   unsigned long temp;
   unsigned long phyAddr;           // physical memory address
   unsigned long * prdPtr;      // pointer to PRD entry list

   // disable/stop the dma channel, clear interrupt and error bits
   pio_writeBusMstrCmd( BM_CR_MASK_STOP );
   pio_writeBusMstrStatus( (unsigned char) ( statReg | BM_SR_MASK_INT | BM_SR_MASK_ERR ) );

   // setup to build the PRD list...
   // ...max PRDs allowed
   maxPrd = (int) MAX_PRD;
   // ...PRD buffer address
   prdPtr = prdBufPtr;
   dma_pci_prd_ptr = prdPtr;
   // ... convert I/O buffer address to an physical memory address
   phyAddr = (unsigned long) bufAddr;

   // 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
      prdPtr[0] = phyAddr;
      // set count for this PRD
      temp = 65536L;          // max PRD length
      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
   // write into BMIDE PRD address registers.

   dma_pci_num_prd = numPrd;
   * (unsigned long *) (pio_bmide_base_addr + BM_PRD_ADDR_LOW )
      = (unsigned long) prdBufPtr;

   // 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
   pio_writeBusMstrCmd( rwControl );
   return 0;
}

//***********************************************************
//
// exec_pci_ata_cmd() - PCI Bus Master for ATA R/W DMA commands
//
//***********************************************************

static int exec_pci_ata_cmd( unsigned char dev,
                             unsigned char * bufAddr,
                             long numSect );

static int exec_pci_ata_cmd( unsigned char dev,
                             unsigned char * bufAddr,
                             long numSect )

{
   unsigned char status;

   // Quit now if the command is incorrect.

   if (    ( reg_cmd_info.cmd != CMD_READ_DMA )
        && ( reg_cmd_info.cmd != CMD_READ_DMA_EXT )
        && ( reg_cmd_info.cmd != CMD_WRITE_DMA )
        && ( reg_cmd_info.cmd != CMD_WRITE_DMA_EXT ) )
   {
      reg_cmd_info.ec = 77;
      return 1;
   }

   // Set up the dma transfer

   if ( set_up_xfer( ( reg_cmd_info.cmd == CMD_WRITE_DMA )
                     ||
                     ( reg_cmd_info.cmd == CMD_WRITE_DMA_EXT ),
                     numSect * 512L, bufAddr ) )
   {
      reg_cmd_info.ec = 61;
      return 1;
   }

   // Set command time out.

   tmr_set_timeout();

⌨️ 快捷键说明

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