ATA Error Detection.c

/*
    File:       ATA Error Detection.c
    
    Description:Sample code demonstrating how to test for certain kinds of ATA hard
                drives which would cause problems for some PowerBooks in SCSI disk mode.
                Demonstrates using the ATA inquiry command and the fields returned by this
                command to determine information about an ATA drive.  Detects an error documented
                in the technote "PowerBook HD Upgrades and SCSI Disk Mode Compatibility".
 
                Requires: PowerBook with ATA manager (190, 2300, 5300, 1400, 3400, 2400).
                Later PowerBooks are not affected by these bugs, but the test will run.
 
    Author:     by BB based on the ATA Manager Sample by VM,
 
    Copyright:  Copyright: © 1997,1999 by Apple Computer, Inc.
                all rights reserved.
    
    Disclaimer: You may incorporate this sample code into your applications without
                restriction, though the sample code has been provided "AS IS" and the
                responsibility for its operation is 100% yours.  However, what you are
                not permitted to do is to redistribute the source as "DSC Sample Code"
                after having made changes. If you're going to re-distribute the source,
                we require that you make it clear in the source that the code was
                descended from Apple Sample Code, but that you've made changes.
    
    Change History (most recent first):
                6/23/99 Updated for Metrowerks Codewarrior Pro 2.1(KG)
 
*/
//-----------------------------------------------------------------------
#pragma mark Includes
//---------------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <ATA.h>
#ifdef __MWERKS__
    #include <SIOUX.h>
#endif
#include <LowMem.h>
#include <Traps.h>
#include <Devices.h>
 
//----------------------------------------------------------------------------
#pragma mark Defines
//----------------------------------------------------------------------------
 
//
// Identifies the bus protocol type.
//
 
enum 
{
    kDevUnknown     =   0,
    kDevATA         =   1,
    kDevATAPI       =   2,
    kDevPCMCIA      =   3
};
 
//
// Identifies the Socket type.
//
enum 
{
    kSocketUnknown      =   0,
    kSocketInternal     =   1,
    kSocketMediaBay     =   2,
    kkSocketPCMCIA      =   3
};
 
//------------------------------------------------------------------------------------
#pragma mark Macros
//------------------------------------------------------------------------------------
 
#define CLEAR(what) do {                        \
        register Ptr        _ptr = (Ptr) &what; \
        register Size       _len = sizeof what; \
        for (; _len > 0; --_len)                \
            *_ptr++ = 0;                        \
    } while (0)
 
#define RETURN_IF_ERROR(_err_,_str_) if (_err_ != noErr) \
    { printf(_str_, _err_); return (_err_); }
 
//
// This is returned by the device in response to an IDENTIFY command (512 bytes).
//
typedef struct IdentifyBlock 
{                        /* Structure of Identify data */
    UInt16   Signature;  /* Word 00: Constant value      */
    UInt16   NumCyls;    /* Word 01: # of cylinders (default mode) */
    UInt16   RSVD0;      /* Word 02: Constant value of 0 */
    UInt16   NumHds;     /* Word 03: # of heads (default mode) */
    UInt16   TrkBytes;   /* Word 04: # of unformatted bytes/track */
    UInt16   SecBytes;   /* Word 05: # of unformatted bytes/sector */
    UInt16   NumSecs;    /* Word 06: # of sectors/track */
    UInt16   VU0;        /* Word 07: Vendor unique */
    UInt16   VU1;        /* Word 08: Vendor unique */
    UInt16   VU2;        /* Word 09: Vendor unique */
    UInt16   Serial[10]; /* Word 10-19:  Serial Number (right-justified) */
    UInt16   BufType;    /* Word 20: Buffer Type */
    UInt16   BufSize;    /* Word 21: Buffer size,512 byte increments */
    UInt16   NumECC;     /* Word 22: # of ECC bytes */
                         /* these next 2 fields are left justified */
    UInt16   FirmRev[4]; /* Word 23-26:Firmware revision */
    UInt16   ModelNum[20];/* Word 27-46: Model number */
    UInt16   MultCmds;   /* Word 47: R/W multiple commands not impl = 0 */
    UInt16   DblXferFlag;/* Word 48: Double transfer flag */
    UInt16   Capabilities;/* Word 49: LBA, DMA, IORDY support indicator */
    UInt16   Reserved1;  /* Word 50: Reserved */
    UInt16   PIOTiming;  /* Word 51: PIO transfer timing mode */
    UInt16   DMATiming;  /* Word 52: DMA transfer timing mode */
    UInt16   Extension;  /* Word 53: extended info support */
    UInt16   CurCylinders;/* Word 54: # of current cylinders */
    UInt16   CurHeads;   /* Word 55: # of current heads */
    UInt16   CurSPT;     /* Word 56: # of current sectors per track */
    UInt16   CurCapacity[2];/* Word 57-58: current capacity in sectors */
    UInt16   MultSectors;/* Word 59: Multiple sector setting */
    UInt16   LBACapacity[2];/* Word 60-61: total sectors in LBA mode */
    UInt16   SWDMA;      /* Word 62: single word DMA support */
    UInt16   MWDMA;      /* Word 63: multi word DMA support */
    UInt16   APIOModes;  /* Word 64: Advanced PIO Xfr mode supported */
    UInt16   MDMATiming; /* Word 65: Minimum Multiword DMA Xfr Cycle */
    UInt16   RDMATiming; /* Word 66: Recommended Multiword DMA Cycle */
    UInt16   MPIOTiming; /* Word 67: Min PIO XFR Time W/O Flow Control */
    UInt16   PIOwRDYTiming;/* Word 68:   Min PIO XFR Time with IORDY flow ctrl */
    UInt16   Reserved[187];/* Word 69-255: Reserved */
} IdentifyBlock;
 
//------------------------------------------------------------------------------------
#pragma mark Prototypes
//------------------------------------------------------------------------------------
 
Boolean     ATAManagerPresent   (void);
Boolean     ATAHardwarePresent  (void);
Boolean     TrapAvailable       (short theTrap);
void        PrintNumVersion     (char *label, NumVersion version );
OSErr       DisplayATAManagerInquiryInfo (void);
 
OSErr       ScanATABusses       (void);
OSErr       DisplayATADriveIdentity (UInt32 deviceID );
void        DumpRawBuffer       ( UInt8 *bufferPtr, int length );
char*       DrvrRefToName       (short refNum);
Boolean     CheckBug1(IdentifyBlock *ibPtr);
Boolean     CheckBug2(IdentifyBlock *ibPtr);
 
// ---------------------------------------------------------------------------
//
// This is the mixed mode stuff needed to call the ATA manager
// if you are generating CFM code (i.e. if you are compiling for
// Power PC).  The 'ataManager' call is not a publicly exported
// symbol in InterfaceLib.
//
#if GENERATINGCFM
 pascal SInt16 ataManager(ataPB *pb);
 
#define RESULT_OFFSET(type) \
     ((sizeof(type) == 1) ? 3 : ((sizeof(type) == 2) ? 1 : 0))
 #define TBTrapTableAddress(trapNum) (((trapNum & 0x03FF) << 2) + 0xE00)
 
 pascal SInt16 ataManager(ataPB *pb)
 {
     #ifdef applec
         #if sizeof(SInt16) > 4
             #error "Result types larger than 4 bytes are not supported."
         #endif
     #endif
     long    private_result;
 
     private_result = CallUniversalProc(
         *(UniversalProcPtr*)TBTrapTableAddress(0xAAF1),
         kPascalStackBased
          | RESULT_SIZE(SIZE_CODE(sizeof(SInt16)))
          | STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(pb))),
         pb);
     return *(((SInt16*)&private_result) + RESULT_OFFSET(SInt16));
 }
#endif 
// ---------------------------------------------------------------------------
//
//  Display information about the ATA Busses
//
OSErr       ScanATABusses (void)
{
    ataDrvrRegister     pb;
    OSErr               status;
 
// Get first device ID (yes you have to do this once)
    CLEAR(pb);
    pb.ataPBFunctionCode    =   kATAMgrFindDriverRefnum;
    pb.ataPBVers            =   kATAPBVers1;
    pb.ataPBDeviceID        =   (UInt32)0x0000ffff;
    status                  =   ataManager((ataPB*) &pb );
 
// loop through devices
    for (pb.ataPBDeviceID = (UInt32) pb.ataDeviceNextID;
         pb.ataPBDeviceID != 0xff;
         pb.ataPBDeviceID = (UInt32) pb.ataDeviceNextID)
    {           
        status = ataManager((ataPB*) &pb );
        RETURN_IF_ERROR(status, 
            "ATA Find Driver failed with status 0x%04x\n")
        
        printf("\n-----------------------------------------\n\n");    
        printf("Device %d %#s\n", 
            pb.ataPBDeviceID, DrvrRefToName(pb.ataDrvrRefNum) );
        DisplayATADriveIdentity(pb.ataPBDeviceID);
    };
    return (status);
}
 
// ---------------------------------------------------------------------------
void     main   (void)
// ---------------------------------------------------------------------------
{
    OSErr           status;
 
#if __MWERKS__
    SIOUXSettings.asktosaveonclose = false;
#endif
 
    printf("Program to detect potential problems with SCSI disk mode\n\n");
    printf("This program looks at your ATA hard disk inside your PowerBook.\n");
    printf("It does not alter the disk or any data in any way.\n");
    printf("See the technote \"PowerBook HD Upgrades and SCSI Disk Mode\n");
    printf("Compatibility\" for details on the conditions this code tests.\n\n");
    printf("Listing devices on your ATA bus ... \n\n");
 
// Check for ATA Hardware 
// you should do this before calling the ATAManager, since some 
// early ROMS could indicate an ATA manager without the proper 
// HW, thus causing a crash.
    if (ATAHardwarePresent() == false) 
    {
        printf("ATA Hardware is not present on this system\n");
        exit(EXIT_FAILURE);
    }
 
// Check for ATA Manager
    if (ATAManagerPresent() == false) 
    {
        printf("ATA Manager is not present on this system\n");
        exit(EXIT_FAILURE);
    }
        
// Display ATA Device Info
    status = ScanATABusses();
    if (status != noErr) 
    {
        printf("Cannot access ATA Manager: 0x%04x\n", (int) status);
        exit(EXIT_FAILURE);
    }
}
 
// ---------------------------------------------------------------------------
OSErr       DisplayATADriveIdentity (UInt32 deviceID)
// ---------------------------------------------------------------------------
//
//  Display information about the ATA Identify Info
//
{
    ataIdentify         pb;
    ataDevConfiguration pb1;
    
    IdentifyBlock   buffer;
    OSErr           status;
    Boolean         bugsExist;
    UInt32          capacity;
    
 
// Get Driver Configuration
    CLEAR(pb1);
    pb1.ataPBFunctionCode   =   kATAMgrGetDrvConfiguration;
    pb1.ataPBVers           =   kATAPBVers2;
    pb1.ataPBDeviceID       =   deviceID;
    
    status = ataManager((ataPB*) &pb1 );
    RETURN_IF_ERROR(status, 
        "ATA GetDrvConfiguration failed with status 0x%04x\n")
 
 
// Setup Identify block;
    CLEAR(pb);
    pb.ataPBFunctionCode    =   kATAMgrDriveIdentify;
    pb.ataPBVers            =   kATAPBVers1;
    pb.ataPBDeviceID        =   deviceID;
    pb.ataPBFlags           =   mATAFlagIORead + mATAFlagByteSwap ;
    
    if (pb1.ataDeviceType == kDevATAPI) 
        pb.ataPBFlags += mATAFlagProtocol1;
    
    pb.ataPBTimeOut         =   100;
    pb.ataPBBuffer          =   (void*) &buffer;
    
    status = ataManager((ataPB*) &pb );
    if (noErr == status)
    {
        switch(pb1.ataDeviceType){        
            case kDevATA:
            case kDevATAPI: 
                capacity = (buffer.CurCapacity[1] << 16) | 
                            buffer.CurCapacity[0];
                printf("This disk has %lu sectors of 512 bytes (0x%08lX in hexadecimal).\n", 
                        capacity, capacity);
                printf("This is roughly equivalent to %lu000 bytes.", capacity/2);
                bugsExist = CheckBug1(&buffer);
                if (!bugsExist)
                    bugsExist = CheckBug2(&buffer);
                if (!bugsExist)
                {
                        printf("This drive may be safely used in SCSI\n");
                        printf("in SCSI disk mode on a PowerBook 3400,\n");
                        printf("2400, 5300, 1400, 190, or Duo 2300.\n");
                }
                break;
            default:
                break;
        }
    }
    return status;
}
 
// ---------------------------------------------------------------------------
Boolean     ATAManagerPresent   (void)
// ---------------------------------------------------------------------------
//
// returns true if this machine has the ata manager
//
{
        return (TrapAvailable(kATATrap));
}
 
// ---------------------------------------------------------------------------
Boolean     ATAHardwarePresent      (void)
// ---------------------------------------------------------------------------
//
// returns true if this machine has ata hardware
//
{
    UInt16  configFlags;
 
    // Hardware configuration flags
    configFlags = LMGetHWCfgFlags();
    
    return (configFlags & 0x0080);
}
 
//------------------------------------------------------------------------------------
#pragma mark -
 
#define NumToolboxTraps() (                             \
        (NGetTrapAddress(_InitGraf, ToolTrap)           \
                == NGetTrapAddress(0xAA6E, ToolTrap))   \
            ? 0x200 : 0x400                             \
    )
#define GetTrapType(theTrap) (                          \
        (((theTrap) & 0x800) != 0) ? ToolTrap : OSTrap  \
    )
 
// ---------------------------------------------------------------------------
Boolean     TrapAvailable       (short theTrap)
// ---------------------------------------------------------------------------
// (see Inside Mac VI 3-8)
{
        TrapType                trapType;
        
        trapType = GetTrapType(theTrap);
        if (trapType == ToolTrap) 
        {
            theTrap &= 0x07FF;
            if (theTrap >= NumToolboxTraps())
                theTrap = _Unimplemented;
        }
        return (
            NGetTrapAddress(theTrap, trapType)
            != NGetTrapAddress(_Unimplemented, ToolTrap)
            );
}
 
// ---------------------------------------------------------------------------
char*       DrvrRefToName(short refNum)
// ---------------------------------------------------------------------------
//
//  lookup driver name in table
//
 
{
        AuxDCEHandle*       UTable  = 
            (AuxDCEHandle*) LMGetUTableBase();
        DCtlPtr             dctl;
        Ptr                 p;  
        
        if(!refNum)     
            return ((char*) "\p<none>");
 
        dctl = (DCtlPtr) *UTable[~refNum];
        p    =  dctl->dCtlDriver;
        if( dctl->dCtlFlags  & 0x0040) 
            p = (void*) *p;
 
        return  ( p?(char*) (p+18):(char*)"\p-Purged-");
}
 
 
// ---------------------------------------------------------------------------
Boolean CheckBug1(IdentifyBlock *ibPtr)
// ---------------------------------------------------------------------------
//
//  Check for the first of the two bugs.  If a drive reports a capacity that
//  is greater than 4 gigabytes, then you should not use that drive in SCSI
//  disk mode on a 190/2300/1400/5300/3400/2400.  Return false if no bug
//  conditions found.
//
{
    UInt32          capacity;
    Boolean         result = false; // assume no bug    
 
    capacity = (ibPtr->CurCapacity[1] << 16) | 
                ibPtr->CurCapacity[0];
    if (capacity >= (UInt32)0x00800000)
    {
        printf("\n\n******************************************\n");    
            printf("* Warning! This drive is too large to be *\n");
            printf("* used in SCSI disk mode on a PowerBook  *\n");
            printf("* 3400, 2400, 5300, 1400, 190, or 2300!  *\n");
            printf("******************************************\n\n");
            result = true;
    }
    return result;
}
 
// ---------------------------------------------------------------------------
Boolean CheckBug2(IdentifyBlock *ibPtr)
// ---------------------------------------------------------------------------
//
//  Check for the second of the two bugs.  If a drive reports a capacity that
//  has a lower word with the top bit set, the last 16 Mb of that drive won't
//  work correctly on the 5300/190/1400/2300. Return false if no bug
//  conditions found.
//
{
    Boolean         result = false; // assume no bug
 
    if (ibPtr->CurCapacity[0] & 0x8000)
    {
        printf("\n\n******************************************\n");    
            printf("* Warning! This drive should not be used *\n");
            printf("* in SCSI disk mode on a PowerBook 5300, *\n");
            printf("* 1400, 190, or Duo 2300!                *\n");
            printf("* This drive is safe on a 3400 or 2400.  *\n");
            printf("******************************************\n");
            result = true;  
    }
    return result;
}