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/