

                             MINDRVR
     Simple ATA/ATAPI Low Level Driver for Embedded Systems
                          User's Guide

                         by Hale Landis

                     Version 0G and higher


INTRODUCTION
------------

MINDRVR is a simple version of ATADRVR.  ATADRVR is Hale Landis'
C code that shows the low level programming required to configure
and execute commands on ATA and ATAPI devices.  MINDRVR is
suitable for embedded systems that have simple ATA interface
hardware that is memory mapped.  MINDRVR does not have some
features of ATADRVR, such as, the command and low level trace
facility, no support for ATA CHS addressing, no and checking of
ATAPI command protocol errors.

MINDRVR is fully contained in two files:  MINDRVR.H and
MINDRVR.C.

MINDRVR, like ATADRVR, supports all of the ATA and ATA/ATAPI
standards.

The MINDRVR C code has been placed into the public domain by Hale
Landis.  This C code has no copyright or other restrictions on
how it is used.


USING MINDRVR
-------------

Normally a "file system" driver would call MINDRVR to perform actual
phyical I/O operations on ATA or ATAPI devices using PIO or DMA data
transfer modes. Any program or driver that calls MINDRVR must include
the MINDRVR.H header file. MINDRVR.H defines all the MINDRVR public
functions and public data.

The basics of using MINDRVR are:

1) Review the MINDRVR.H and MINDRVR.C files.  In these files look
   for comment lines that have the string '!!!'.  These comments
   explain the information you must provide at compile time, such
   as, the memory addresses for the ATA registers in your
   system's memory.

2) #include "mindrvr.h" in your program or driver that will call
   MINDRVR.

3) Call reg_config() so that MINDRVR can determine what devices
   are attached to the ATA host adapter.

4) Call reg_reset() or any of the other "reg_" functions to issue
   ATA or ATAPI commands in PIO data transfer mode.  If your
   system can support ATA/ATAPI DMA then call the dma_pci_"
   functions to issue ATA or ATAPI commands in DMA data transfer
   mode.

Note that MINDRVR is designed for systems with a single ATA
controller (single ATA inteface).  If you system has multiple ATA
controllers/interfaces then you will need to either:  a) make a
separate MINDRVR for each controller, or b) switch between
controllers by swapping the MINDRVR configuration information
between calls to MINDRVR.


MINDRVR REFERENCE
-----------------

Each of the public functions and symbols of MINDRVR are described
below in alphabetical order.

Note that there is no "interrupt handler" defined by MINDRVR.
You must supply the appropriate interrupt handler as described in
MINDRVR.H and MINDRVR.C.


Public Data
-----------

unsigned char int_ata_status;

   When using interrupts the interrupt handler must return this
   data.  This is the interrupting device's ATA status as read by
   interrupt handler.

unsigned char int_bmide_status;

   When using interrupts the interrupt handler must return this
   data.  This is the interrupting controller's BMIDE status read
   by interrupt handler.

unsigned char int_use_intr_flag;

   MINDRVR can be switched between polling mode and interrupt
   mode.  Note that interrupt mode is required for DMA data
   transfers.

   Make this value not zero to use interrupts.

unsigned char pio_xfer_width;

   This variable controls the width of PIO data transfers.
   This variable can have the following values:

   8 = 8-bits.  PIO transfers will use 8-bit memory write/read
   when accessing the ATA Data register.

   16 = 16-bits.  PIO transfers will use 16-bit memory write/read
   when accessing the ATA Data register.

   32 = 32-bits.  PIO transfers will 32-bit memory write/read
   when accessing the ATA Data register.

   Any other value is treated the same as 16.

struct REG_CMD_INFO
{
   // command code
   unsigned char cmd;         // command code
   // command parameters
   unsigned int  fr;          // feature (8 or 16 bits)
   unsigned int  sc;          // sec cnt (8 or 16 bits)
   unsigned int  sn;          // sec num (8 or 16 bits)
   unsigned int  cl;          // cyl low (8 or 16 bits)
   unsigned int  ch;          // cyl high (8 or 16 bits)
   unsigned char dh;          // device head
   unsigned char dc;          // device control
   long ns;                   // actual sector count
   int mc;                    // current multiple block setting
   unsigned char lbaSize;     // size of LBA used
      #define LBACHS 0           // last command used ATA CHS (not supported by MINDRVR)
                                 //    -or- last command was ATAPI PACKET command
      #define LBA28  28          // last command used ATA 28-bit LBA
      #define LBA48  48          // last command used ATA 48-bit LBA
   unsigned long lbaLow;      // lower 32-bits of ATA LBA
   unsigned long lbaHigh;     // upper 32-bits of ATA LBA
   // status and error regs
   unsigned char st;          // status reg
   unsigned char as;          // alt status reg
   unsigned char er ;         // error reg
   // driver error codes
   unsigned char ec;          // detailed error code
   unsigned char to;          // not zero if time out error
   // additional result info
   long totalBytesXfer;       // total bytes transfered
   long drqPackets;           // number of PIO DRQ packets
} ;
struct REG_CMD_INFO reg_cmd_info;

   This data structure contains information about the last reset or command
   that was executed.

int reg_config( void );

   This function shoul be called so that MINDRVR can correctly
   configure itself. reg_config() sets the values into
   reg_config_info[2].

   Note that the program calling MINDRVR may bypass calling this
   function as long as reg_config_info[] is set appropriately
   before attempting to execute any resets or commands.

int reg_config_info[2];

   This array is set by calling reg_config(). reg_config_info[0]
   describes device 0 and reg_config_info[1] describes device 1.
   The possible values in these words are (see MINDRVR.H):
   REG_CONFIG_TYPE_NONE, REG_CONFIG_TYPE_UNKN,
   REG_CONFIG_TYPE_ATA, REG_CONFIG_TYPE_ATAPI.

   Note that MINDRVR is not able to determine the type of some
   devices.  However, after issuing some commands the calling
   program may be able to determine the exact type of a device.
   The calling program may change the values in this array but
   this must be done very carefully:

   a) DO NOT CHANGE the value REG_CONFIG_TYPE_NONE.
   b) DO NOT CHANGE a value to REG_CONFIG_TYPE_NONE.
   c) The value REG_CONFIG_TYPE_UNKN can be changed to either
      REG_CONFIG_TYPE_ATA or REG_CONFIG_TYPE_ATAPI.

int reg_non_data_lba28( unsigned char dev,     // device (0 or 1)
                        unsigned char cmd,     // command register
                        int fr,                // feature register
                        int sc,                // sector count
                        long lba );            // LBA

   Execute an ATA Non-Data command using LBA sector addressing.

   Returns 0 if no error or 1 if there is an error.  See the
   contents of reg_cmd_info.

int reg_non_data_lba48( unsigned char dev,     // device (0 or 1)
                        unsigned char cmd,     // command register
                        int fr,                // feature register
                        int sc,                // sector count
                        long lbahi,            // LBA upper 16-bits
                        long lbalo );          // LBA lower 32 bits

   Execute an ATA Non-Data command using LBA sector addressing.

   Returns 0 if no error or 1 if there is an error.  See the
   contents of reg_cmd_info.

int reg_packet( unsigned char dev,              // device (0 or 1)
                unsigned int cpbc,              // command packet size
                unsigned char * cdbBufAddr,     // CDB buffer
                int dir,                        // 0 for no data or read, 1 for write
                unsigned int dpbc,              // max data packet size
                unsigned char * dataBufAddr );  // data packet buffer

   Execute an ATAPI Packet (A0H) command in PIO mode.  Note that
   the first byte of the Commmand Packet buffer is the command
   code of the "SCSI CDB" that is to be executed by the device.

   Returns 0 if no error or 1 if there is an error.  See the
   contents of reg_cmd_info.

int reg_pio_data_in_lba28( unsigned char dev,         // device (0 or 1)
                           unsigned char cmd,         // command register
                           int fr,                    // feature register
                           int sc,                    // sector count
                           long lba,                  // LBA
                           unsigned char * bufAddr,   // buffer address
                           int numSect,               // number of sectors to transfer
                           int multiCnt );            // current multiple count

   Execute an ATA PIO Data In command in LBA sector addressing
   mode.

   numSect is the actual number of sectors to be transferred.
   This value may be different than the sc value.

   If cmd is C4H (Read Multiple) then multiCnt MUST be set to the
   current sectors per block.

   Returns 0 if no error or 1 if there is an error.  See the
   contents of reg_cmd_info.

int reg_pio_data_in_lba48( unsigned char dev,         // device (0 or 1)
                           unsigned char cmd,         // command register
                           int fr,                    // feature register
                           int sc,                    // sector count
                           long lbahi,                // LBA upper 16-bits
                           long lbalo,                // LBA lower 32 bits
                           unsigned char * bufAddr,   // buffer address
                           int numSect,               // number of sectors to transfer
                           int multiCnt );            // current multiple count

   Execute an ATA PIO Data In command in LBA sector addressing
   mode.

   numSect is the actual number of sectors to be transferred.
   This value may be different than the sc value.

   If cmd is C4H (Read Multiple) then multiCnt MUST be set to the
   current sectors per block.

   Returns 0 if no error or 1 if there is an error.  See the
   contents of reg_cmd_info.

int reg_pio_data_out_lba28( unsigned char dev,        // device (0 or 1)
                            unsigned char cmd,        // command register
                            int fr,                   // feature register
                            int sc,                   // sector count
                            long lba,                 // LBA
                            unsigned char * bufAddr,  // buffer address
                            int numSect,              // number of sectors to transfer
                            int multiCnt );           // current multiple count

   Execute an ATA PIO Data Out command in LBA sector addressing
   mode.

   numSect is the actual number of sectors to be transferred.
   This value may be different than the sc value.

   If cmd is C5H (Write Multiple) then multiCnt MUST be set to
   the current sectors per block.

   Returns 0 if no error or 1 if there is an error.  See the
   contents of reg_cmd_info.

int reg_pio_data_out_lba48( unsigned char dev,        // device (0 or 1)
                            unsigned char cmd,        // command register
                            int fr,                   // feature register
                            int sc,                   // sector count
                            long lbahi,               // LBA upper 16-bits
                            long lbalo,               // LBA lower 32 bits
                            unsigned char * bufAddr,  // buffer address
                            int numSect,              // number of sectors to transfer
                            int multiCnt );           // current multiple count

   Execute an ATA PIO Data Out command in LBA sector addressing
   mode.

   numSect is the actual number of sectors to be transferred.
   This value may be different than the sc value.

   If cmd is C5H (Write Multiple) then multiCnt MUST be set to
   the current sectors per block.

   Returns 0 if no error or 1 if there is an error.  See the
   contents of reg_cmd_info.

int reg_reset( unsigned char devRtrn );        // device's data returned

   Execute an ATA Soft Reset.  Set devRtrn to 0 or 1 to determine
   which device's register contents are returned in reg_cmd_info.


DMA Data Transfer Functions And Data
------------------------------------

These functions setup PCI bus DMA (ATA Multiword or Ultra DMA)
and perform ATA and ATAPI commands using DMA.  The function
dma_pci_config() MUST be called with no error before any of the
other functions will attempt to execute a command.

int dma_pci_config( void );

   Configure MINDRVR to use PCI bus DMA (ATA Multiword or Ultra
   DMA) on a PCI Bus Mastering ATA controller.

   This function may not be needed in your system - see the MINDRVR.H
   and MINDRVR.C files.

int dma_pci_lba28( unsigned char dev,           // device (0 or 1)
                   unsigned char cmd,           // command register
                   int fr,                      // feature register
                   int sc,                      // sector count
                   long lba,                    // LBA
                   unsigned char * bufAddr );   // buffer address

   Execute an ATA Read DMA (C8H) or ATA Write DMA (CAH) command
   using LBA sector addressing.

   Returns 0 if no error or 1 if there is an error.  See the
   contents of reg_cmd_info.

int dma_pci_lba48( unsigned char dev,           // device (0 or 1)
                   unsigned char cmd,           // command register
                   int fr,                      // feature register
                   int sc,                      // sector count
                   long lbahi,                  // LBA upper 16-bits
                   long lbalo,                  // LBA lower 32 bits
                   unsigned char * bufAddr );   // buffer address

   Execute an ATA Read DMA (C8H) or ATA Write DMA (CAH) command
   using LBA sector addressing.

   Returns 0 if no error or 1 if there is an error.  See the
   contents of reg_cmd_info.

int dma_pci_packet( unsigned char dev,              // device (0 or 1)
                    unsigned int cpbc,              // command packet size
                    unsigned char * cdbBufAddr,     // CDB buffer
                    int dir,                        // 0 for no data or read, 1 for write
                    unsigned int dpbc,              // max data packet size
                    unsigned char * dataBufAddr );  // data packet buffer

   Execute an ATAPI Packet (A0H) command in DMA mode.  Note that
   the first byte of the Commmand Packet buffer is the command
   code of the "SCSI CDB" that is to be executed by the device.

   Returns 0 if no error or 1 if there is an error.  See the
   contents of reg_cmd_info.


QUESTIONS OR PROBLEMS?
----------------------

Send your question(s) or problem description(s) to Hale Landis
via email at this address:

   hlandis@ata-atapi.com

Visit Hale's web site:

   www.ata-atapi.com

/end/
