Source to iokit/Families/IOATABus/IOATADevice.cpp


Enter a symbol's name here to quickly find it.

/*
 * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * The contents of this file constitute Original Code as defined in and
 * are subject to the Apple Public Source License Version 1.1 (the
 * "License").  You may not use this file except in compliance with the
 * License.  Please obtain a copy of the License at
 * http://www.apple.com/publicsource and read it before using this file.
 * 
 * This Original Code and all software distributed under the License are
 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */
/*
 *
 * IOATADevice.cpp
 *
 */

#include <IOKit/IOSyncer.h>
#include <IOKit/ata/IOATA.h>
#include <IOKit/ata/IOATAController.h>
#include "ATAPrivate.h"

#undef  super
#define super IOService

OSDefineMetaClassAndStructors( IOATADevice, IOService );

extern EndianTable 		AppleIdentifyEndianTable[];

extern UInt32			AppleNumPIOModes;
extern ATAModeTable 		ApplePIOModes[];
extern UInt32			AppleNumDMAModes;
extern ATAModeTable		AppleDMAModes[];
extern UInt32			AppleNumUltraModes;
extern ATAModeTable		AppleUltraModes[];

/*
 *
 *
 */
bool IOATADevice::probeDevice()
{
    OSDictionary		*propTable = 0;

    if ( open( this ) != true )
    {
        goto probeDevice_error;
    }

    if ( doIdentify( (void **)&identifyData ) != ataReturnNoError )
    {
        goto probeDevice_error;
    }

    if ( deviceType == ataDeviceATA )
    {
        doSpinUp();
    }

    else if ( deviceType == ataDeviceATAPI )
    {
        atapiPktInt = ((identifyData->generalConfiguration & atapiPktProtocolIntDRQ) != 0);
      
        if ( doInquiry( (void **)&inquiryData ) != ataReturnNoError )
        {
            goto probeDevice_error;
        }

        reqSenseCmd = allocCommand();
    }      

    if ( getATATimings() != true )
    {
        goto probeDevice_error;
    }
    
    propTable = createProperties();
    if ( !propTable )
    {
        goto probeDevice_error;
    }
    
    setPropertyTable( propTable );

    propTable->release();
 
    close( this );

    return true; 

probeDevice_error: ;
    close( this );
    return false;
}

/*
 *
 *
 *
 */
ATADeviceType IOATADevice::probeDeviceType()
{
    ATATaskfile         taskfile;
    ATAResults          results;

    bzero( (void *)&taskfile, sizeof(taskfile) );

    taskfile.protocol     = ataProtocolSetRegs;
    taskfile.regmask      = ATARegtoMask(ataRegDriveHead);  

    taskfile.resultmask   = ATARegtoMask(ataRegSectorCount)
                          | ATARegtoMask(ataRegSectorNumber)
                          | ATARegtoMask(ataRegCylinderLow)
                          | ATARegtoMask(ataRegCylinderHigh)
                          | ATARegtoMask(ataRegStatus);

    taskfile.ataRegs[ataRegDriveHead] = ataModeLBA | (getUnit() << 4);

    utilCmd->setTaskfile( &taskfile );
    utilCmd->execute();

    if ( utilCmd->getResults( &results ) != ataReturnNoError )     
    {
        return (deviceType = ataDeviceNone);
    }
 
    if ( results.ataRegs[ataRegSectorCount] == ataSignatureSectorCount
          && results.ataRegs[ataRegSectorNumber] == ataSignatureSectorNumber
             && results.ataRegs[ataRegCylinderLow] == ataSignatureCylinderLow
                && results.ataRegs[ataRegCylinderHigh] == ataSignatureCylinderHigh )
    { 
        if ( !(results.ataRegs[ataRegStatus] & ataStatusBSY)  
                 && (results.ataRegs[ataRegStatus] & ataStatusDRDY) )
        {
            return (deviceType = ataDeviceATA);
        }
    }
            
    if ( results.ataRegs[ataRegCylinderLow] == atapiSignatureCylinderLow
                && results.ataRegs[ataRegCylinderHigh] == atapiSignatureCylinderHigh )
    {
        return (deviceType = ataDeviceATAPI);
    }       
  
    return (deviceType = ataDeviceNone);
}


/*
 *
 *
 *
 */
ATAReturnCode IOATADevice::doSpinUp()
{
    void		*buffer = NULL;
    ATAReturnCode	rc;

    rc = doSectorCommand( ataCommandReadSector, 0, 1, &buffer );

    if ( rc != ataReturnNoError )
    {
        return rc;
    }

    IOFree( buffer, 512 );

    return rc ; 
}    

/*
 *
 *
 *
 */
ATAReturnCode IOATADevice::doIdentify( void **dataPtr )
{   
    ATACommand		ataCmd;
    ATAReturnCode	rc;
 
    ataCmd = (deviceType == ataDeviceATA) ? ataCommandIdentify : atapiCommandIdentify;

    rc = doSectorCommand( ataCmd, 0, 1, dataPtr );

    if ( rc != ataReturnNoError )
    {
        return rc;
    }
 
    endianConvertData( *dataPtr, AppleIdentifyEndianTable );

    return rc;
}



/*
 *
 *
 *
 */
ATAReturnCode IOATADevice::doSectorCommand( ATACommand ataCmd, UInt32 ataLBA, UInt32 ataCount, void **dataPtr )
{
    ATATaskfile			taskfile;
    ATAResults			result;
    IOMemoryDescriptor  	*desc;
    UInt32			size;
    void			*data;
    UInt32			i;
    ATAReturnCode		rc;

    *dataPtr = NULL;

    size = ataCount * 512;

    if ( !(data = (void *)IOMalloc(size)) )
    {
        return ataReturnNoResource;
    }

    bzero( &taskfile, sizeof(taskfile) );

    desc = IOMemoryDescriptor::withAddress( data, size, kIODirectionIn );
    if ( desc == NULL )
    {
        rc = ataReturnNoResource;
        goto doSectorCommand_error;
    }

    
    taskfile.protocol    		= ataProtocolPIO;
    taskfile.regmask      		= ATARegtoMask(ataRegDriveHead)
                          		| ATARegtoMask(ataRegSectorCount)
                          		| ATARegtoMask(ataRegSectorNumber)
                          		| ATARegtoMask(ataRegCylinderLow)
                          		| ATARegtoMask(ataRegCylinderHigh)
                          		| ATARegtoMask(ataRegFeatures)
                          		| ATARegtoMask(ataRegCommand);


    taskfile.resultmask			= ATARegtoMask(ataRegError) 
                                        | ATARegtoMask(ataRegStatus);

    taskfile.ataRegs[ataRegSectorCount]   = ataCount;
    taskfile.ataRegs[ataRegSectorNumber]  = ataLBA         & 0xff;
    taskfile.ataRegs[ataRegCylinderLow]   = (ataLBA >> 8)  & 0xff;
    taskfile.ataRegs[ataRegCylinderHigh]  = (ataLBA >> 16) & 0xff;
    taskfile.ataRegs[ataRegDriveHead]     = (ataLBA >> 24) & 0x0f;

    taskfile.ataRegs[ataRegDriveHead]   |= ataModeLBA | (getUnit() << 4);
    taskfile.ataRegs[ataRegCommand]      = ataCmd;

    for ( i = 0; i < 2; i++ )
    { 
        utilCmd->setTimeout( 25000 );
        utilCmd->setTaskfile( &taskfile );
        utilCmd->setPointers( desc, size, false ); 
        submitCommand( utilCmd );
    
        rc  = utilCmd->getResults( &result );
        if ( rc == ataReturnNoError )
        {
            break;
        }
    }


doSectorCommand_error: ;

    desc->release();

    if ( rc != ataReturnNoError )
    {
        IOFree( data, size );
        return result.returnCode;
    }

    *dataPtr = data;

    return ataReturnNoError;
}


/*
 *
 *
 */
ATAReturnCode IOATADevice::doInquiry( void **dataPtr )
{
    ATATaskfile			taskfile;
    ATAPICmd			atapiCmd;
    ATAResults			result;
    void                        *data;
    IOMemoryDescriptor  	*desc;
    UInt32			size = sizeof(ATAPIInquiry);

    *dataPtr = 0;

    if ( !(data = (void *)IOMalloc(size)) )
    {
        return ataReturnNoResource;;
    }

    bzero( &taskfile, sizeof(taskfile) );
    bzero( &atapiCmd, sizeof(atapiCmd) );

    desc = IOMemoryDescriptor::withAddress( data, size, kIODirectionIn );
    
    taskfile.protocol   		= atapiProtocolPIO;
    taskfile.regmask      		= ATARegtoMask(atapiRegDeviceSelect) 
                          		| ATARegtoMask(atapiRegCommand)
                                        | ATARegtoMask(atapiRegByteCountLow)
                                        | ATARegtoMask(atapiRegByteCountHigh)
                                        | ATARegtoMask(atapiRegFeatures);
    taskfile.ataRegs[atapiRegDeviceSelect]  = ataModeLBA | (getUnit() << 4);
    taskfile.ataRegs[atapiRegCommand]       = atapiCommandPacket;
    taskfile.ataRegs[atapiRegFeatures]      = 0;
    taskfile.ataRegs[atapiRegByteCountLow]  = 0xfe;
    taskfile.ataRegs[atapiRegByteCountHigh] = 0xff;

    atapiCmd.cdbLength = 12;  // Fix 16 byte cmdpkts??
    atapiCmd.cdb[0]    = 0x12;
    atapiCmd.cdb[4]    = size;

    utilCmd->setATAPICmd( &atapiCmd, 0, 0 );
    utilCmd->setTaskfile( &taskfile );
    utilCmd->setPointers( desc, size, false );
    utilCmd->setTimeout( 5000 ); 
    submitCommand( utilCmd );
 
    if ( utilCmd->getResults(&result) == ataReturnNoError )
    {
        *dataPtr = data;
    }
    else
    {
        IOFree( data, size );
    }

    desc->release();

    return result.returnCode;
}

/*
 *
 *
 */
bool IOATADevice::getDeviceCapacity( UInt32 *blockMax, UInt32 *blockSize )
{
    UInt32		i;
    UInt32		data[2];

    if ( deviceType == ataDeviceATA )
    {
        if ( identifyData != NULL )
        {
            *blockMax = *(UInt32 *)identifyData->userAddressableSectors - 1;
            *blockSize  = 512;
            return true;
        }
    }
    
    if ( deviceType == ataDeviceATAPI )
    {
        for ( i=0; i < 8; i++ )
        {
            if ( doTestUnitReady() == ataReturnNoError )
            {
                break;
            }
        }

        if ( doReadCapacity( data ) == ataReturnNoError )
        {
            *blockMax   = OSSwapBigToHostInt32( data[0] );
            *blockSize  = OSSwapBigToHostInt32( data[1] );
            return true;
        }      
    }

    return false;
}


ATAReturnCode IOATADevice::doTestUnitReady()
{
    ATATaskfile			taskfile;
    ATAPICmd			atapiCmd;
    ATAResults			result;

    bzero( &taskfile, sizeof(taskfile) );
    bzero( &atapiCmd, sizeof(atapiCmd) );

    taskfile.protocol   		= atapiProtocolPIO;

    taskfile.regmask      		= ATARegtoMask(atapiRegDeviceSelect) 
                          		| ATARegtoMask(atapiRegCommand)
                                        | ATARegtoMask(atapiRegByteCountLow)
                                        | ATARegtoMask(atapiRegByteCountHigh)
                                        | ATARegtoMask(atapiRegFeatures);

    taskfile.ataRegs[atapiRegDeviceSelect]  = ataModeLBA | (getUnit() << 4);
    taskfile.ataRegs[atapiRegCommand]       = atapiCommandPacket;
    taskfile.ataRegs[atapiRegFeatures]      = 0;
    taskfile.ataRegs[atapiRegByteCountLow]  = 0xfe;
    taskfile.ataRegs[atapiRegByteCountHigh] = 0xff;

    atapiCmd.cdbLength = 12;  // Fix 16 byte cmdpkts??
    atapiCmd.cdb[0]    = 0x00;

    utilCmd->setATAPICmd( &atapiCmd );
    utilCmd->setTaskfile( &taskfile );
    utilCmd->setPointers( (IOMemoryDescriptor *)NULL, 0, false ); 
    utilCmd->setTimeout( 5000 );
    submitCommand( utilCmd );
    utilCmd->getResults(&result);
 
    return result.returnCode;
}


/*
 *
 *
 */
ATAReturnCode IOATADevice::doReadCapacity( void *data )
{
    ATATaskfile			taskfile;
    ATAPICmd			atapiCmd;
    ATAResults			result;
    IOMemoryDescriptor  	*dataDesc;
    UInt32			size = 8;


    bzero( &taskfile, sizeof(taskfile) );
    bzero( &atapiCmd, sizeof(atapiCmd) );

    dataDesc = IOMemoryDescriptor::withAddress( data, size, kIODirectionIn );
    if ( dataDesc == NULL )
    {
        return ataReturnNoResource;
    }
    
    taskfile.protocol   		= atapiProtocolPIO;
    taskfile.regmask      		= ATARegtoMask(atapiRegDeviceSelect) 
                          		| ATARegtoMask(atapiRegCommand)
                                        | ATARegtoMask(atapiRegByteCountLow)
                                        | ATARegtoMask(atapiRegByteCountHigh)
                                        | ATARegtoMask(atapiRegFeatures);
    taskfile.ataRegs[atapiRegDeviceSelect]  = ataModeLBA | (getUnit() << 4);
    taskfile.ataRegs[atapiRegCommand]       = atapiCommandPacket;
    taskfile.ataRegs[atapiRegFeatures]      = 0;
    taskfile.ataRegs[atapiRegByteCountLow]  = 0xfe;
    taskfile.ataRegs[atapiRegByteCountHigh] = 0xff;

    atapiCmd.cdbLength = 12;  // Fix 16 byte cmdpkts??
    atapiCmd.cdb[0]    = 0x25;

    utilCmd->setATAPICmd( &atapiCmd );
    utilCmd->setTaskfile( &taskfile );
    utilCmd->setPointers( dataDesc, size, false ); 
    utilCmd->setTimeout( 5000 );
    submitCommand( utilCmd );
    
    utilCmd->getResults(&result);

    dataDesc->release();
 
    return result.returnCode;
}


/*
 *
 *
 */
IOATACommand *IOATADevice::makeRequestSense( IOATACommand *origCmd )
{
    ATATaskfile			taskfile;
    ATAPICmd			atapiCmd;

    if ( reqSenseCmd == NULL )
    {
        return NULL;
    }

    bzero( &taskfile, sizeof(taskfile) );
    bzero( &atapiCmd, sizeof(atapiCmd) );

    taskfile.protocol   		= atapiProtocolPIO;
 
    taskfile.regmask      		= ATARegtoMask(atapiRegDeviceSelect) 
                          		| ATARegtoMask(atapiRegCommand)
                                        | ATARegtoMask(atapiRegByteCountLow)
                                        | ATARegtoMask(atapiRegByteCountHigh)
                                        | ATARegtoMask(atapiRegFeatures);

    taskfile.ataRegs[atapiRegDeviceSelect]  = ataModeLBA | (getUnit() << 4);
    taskfile.ataRegs[atapiRegCommand]       = atapiCommandPacket;
    taskfile.ataRegs[atapiRegFeatures]      = 0;
    taskfile.ataRegs[atapiRegByteCountLow]  = origCmd->senseLength & 0xff;
    taskfile.ataRegs[atapiRegByteCountHigh] = 0;

    atapiCmd.cdbLength   = 12;  // Fix 16 byte cmdpkts??
    atapiCmd.cdb[0]      = 0x03;
    atapiCmd.cdb[4]      = origCmd->senseLength;

    reqSenseCmd->setATAPICmd( &atapiCmd, 0, 0 );
    reqSenseCmd->setTaskfile( &taskfile );
    reqSenseCmd->setPointers( origCmd->senseData, origCmd->senseLength, false ); 

    return reqSenseCmd;
}

/*
 *
 *
 */
bool IOATADevice::completeRequestSense( IOATACommand *origCmd, IOATACommand  *reqSense )
{
    ATAResults		result;

    if ( reqSense->getResults(&result) != ataReturnNoError )
    {
        return false;
    }
    
    origCmd->results.senseLength = result.bytesTransferred;
    return true;
}


/*
 *
 *
 */ 
bool IOATADevice::getTimingsSupported( ATATimingProtocol *timingsSupported )
{
    UInt32			i;

    *(UInt32 *)timingsSupported = 0;
 
    for ( i=0; i < numTimings; i++ )
    {
        *(UInt32 *) timingsSupported |= (UInt32)ataTimings[i].timingProtocol; 
    }

    return true;
}

/*
 *
 *
 */ 
bool IOATADevice::getTiming( ATATimingProtocol *timingProtocol, ATATiming *timing )
{
    UInt32			i;

    for ( i=0; i < numTimings; i++ )
    {
        if ( ataTimings[i].timingProtocol == *timingProtocol )
        {
            bcopy( &ataTimings[i], timing, sizeof(ATATiming) );
            return true;
        }
    }

    return false;
}


/*
 *
 *
 */ 
bool IOATADevice::selectTiming( ATATimingProtocol timingProtocol )
{
    ATATaskfile			taskfile;
    ATAResults			result;
    bool			rc = false;
    UInt32			i;

    for ( i=0; i < numTimings; i++ )
    {
        if ( ataTimings[i].timingProtocol == timingProtocol )
        {
            rc = true;
            break;
        }
    }

    if ( rc == false ) return rc;
    
    if ( controller->selectTiming( getUnit(), timingProtocol ) == false )
    {
        return false;
    }
     
    bzero( &taskfile, sizeof(taskfile) );
    bzero( &result, sizeof(result) );

    taskfile.protocol    		= ataProtocolPIO;
    taskfile.regmask      		= ATARegtoMask(ataRegFeatures) 
                          		| ATARegtoMask(ataRegSectorCount) 
                          		| ATARegtoMask(ataRegDriveHead) 
                          		| ATARegtoMask(ataRegCommand);
 
    taskfile.ataRegs[ataRegSectorCount]  = ataTimings[i].featureSetting;
    taskfile.ataRegs[ataRegFeatures]     = ataFeatureTransferMode;
    taskfile.ataRegs[ataRegDriveHead]    = ataModeLBA | (getUnit() << 4);
    taskfile.ataRegs[ataRegCommand]      = ataCommandSetFeatures;

    utilCmd->setTaskfile( &taskfile );
    utilCmd->setPointers( (IOMemoryDescriptor *)NULL, 0, false );
    utilCmd->setTimeout( 5000 );
    utilCmd->setCallback(); 
    submitCommand( utilCmd );
 
    if ( utilCmd->getResults( &result ) != ataReturnNoError )
    {
        rc = false;
    }
    return rc;
}
    

/*
 *
 *
 */ 
bool IOATADevice::getATATimings()
{
    int			i, n;
    UInt32 	        mode		= 0;
    UInt32     		cycleTime	= 0;

    ATATiming		*pTimings;

    pTimings = ataTimings;

    /*
     *  PIO Cycle timing......  
     *
     *  1. Try to match Word 51 (pioCycleTime) with cycle timings
     *     in our pioModes table to get mode/CycleTime. (Valid for Modes 0-2)
     *  2. If Words 64-68 are supported and Mode 3 or 4 supported check, 
     *     update CycleTime with Word 68 (CycleTimeWithIORDY).
     */

    cycleTime = identifyData->pioMode;

    if ( cycleTime > 2 )
    {
        for ( i=AppleNumPIOModes-1; i != -1; i-- )
        {
            if ( cycleTime <= ApplePIOModes[i].minDataCycle )
            {
                mode = i;
                break;
            }
         }

         if ( i == -1 )
         {
             cycleTime = ApplePIOModes[mode].minDataCycle;
         }
    }
    else
    {
        mode      = cycleTime;
        cycleTime = ApplePIOModes[mode].minDataCycle;
    }


    if ( identifyData->validFields & identifyWords_64to70_Valid ) 
    {
	if (identifyData->advancedPIOModes & advPIOModes_Mode4_Supported)
            mode = 4;
	else if (identifyData->advancedPIOModes & advPIOModes_Mode3_Supported)
            mode = 3;

        if ( (mode >= 3) && identifyData->minPIOCyclcTimeIORDY )
        {
            cycleTime = identifyData->minPIOCyclcTimeIORDY;
        }
    }
    
    pTimings->timingProtocol = ataTimingPIO;
    pTimings->mode	      = mode;
    pTimings->featureSetting  = mode | ataTransferModePIOwFC;
    pTimings->minDataCycle    = cycleTime;
    pTimings->minDataAccess   = ApplePIOModes[mode].minDataAccess;

    if ( controller->calculateTiming( getUnit(), pTimings ) == false )
    {
        IOLog("IOATADevice::%s() - Controller driver must support PIO timings\n\r", __FUNCTION__);
        return false;
    }

    pTimings++;
    numTimings++;

    /* 
     *  Multiword DMA timing.....
     *
     *  1. Check Word 63(7:0) (Multiword DMA Modes Supported). Lookup
     *     CycleTime for highest mode we support.
     *  2. If Words 64-68 supported, update CycleTime from Word 66
     *     (RecommendedMultiWordCycleTime) if specified.
     */                                                                

    n = identifyData->dmaModes & dmaModes_Supported;
    if ( n )
    {
        for ( i=0; n; i++, n>>=1 )
          ;

        mode = i - 1;
        if ( mode > AppleNumDMAModes-1 )
        {
            mode = AppleNumDMAModes-1;
        }
        cycleTime = AppleDMAModes[mode].minDataCycle;

        if (identifyData->validFields & identifyWords_64to70_Valid) 
        {
            if ( identifyData->recDMACycleTime )
            {
                cycleTime = identifyData->recDMACycleTime;
            }
        }
        pTimings->timingProtocol = ataTimingDMA;
        pTimings->mode	         = mode;
        pTimings->featureSetting = mode | ataTransferModeDMA;
        pTimings->minDataCycle   = cycleTime;
        pTimings->minDataAccess  = AppleDMAModes[mode].minDataAccess;

        if ( controller->calculateTiming( getUnit(), pTimings ) == true )
        {
            pTimings++;
            numTimings++;
        }
    }

    /* 
     *  Ultra DMA timing.....
     *
     */                                                                
    if ( identifyData->validFields & identifyWords_88to88_Valid ) 
    {
        n = identifyData->ultraDMAModes & ultraDMAModes_Supported;
        if ( n )
        {
            for ( i=0; n; i++, n>>=1 )
              ;

            mode = i - 1;
            if ( mode > AppleNumUltraModes-1 )
            {
                mode = AppleNumUltraModes-1;
            }

            /*
             * Build a separate timing entry for Ultra DMA/33 (mode <= 2) and Ultra DMA/66
             */
            while ( 1 )
            { 
                cycleTime = AppleUltraModes[mode].minDataCycle;

                pTimings->timingProtocol = (mode > 2) ? ataTimingUltraDMA66 : ataTimingUltraDMA33;
                pTimings->mode	         = mode;
                pTimings->featureSetting = mode | ataTransferModeUltraDMA33;
                pTimings->minDataCycle   = cycleTime;
                pTimings->minDataAccess  = AppleUltraModes[mode].minDataAccess;
  
                if ( controller->calculateTiming( getUnit(), pTimings ) == true )
                {
                    pTimings++;
                    numTimings++;
                }
                
                if ( mode < 3 ) break; 
           
                mode = 2;
            }
        }
    }

    return true;            
}

/*
 *
 *
 */
void IOATADevice::submitCommand( IOATACommand *cmd )
{
    cmd->execute();
    IOWriteLock( resetSem );
    IORWUnlock( resetSem );
}

/*
 *
 *
 */
bool IOATADevice::executeCommand( IOATACommand *cmd )
{
    bool	 	isSync;

    isSync = !(cmd->flags & IOATACommand::atacmdCallbackValid);

    if ( isSync )
    {
        cmd->completionInfo.sync.syncer = IOSyncer::create();
    }

    cmd->deviceQueue = deviceQueue;

    controller->executeCommand( cmd );
    
    if ( isSync )
    {
        cmd->completionInfo.sync.syncer->wait();
    }

    return true;
}

/*
 *
 *
 */
void IOATADevice::completeCommand( IOATACommand *cmd )
{
    ATATaskfile		tf;
    ATAResults		res;
    UInt32		i;

    cmd->getTaskfile(&tf);
    cmd->getResults(&res);

#if 0
    IOLog("ATA command = %02x, RegMask = %04x ResultMask = %04x ReturnCode = %04x\n\r", 
           tf.protocol, (int)tf.regmask, (int)tf.resultmask, (int)res.returnCode );

    IOLog("ATA command regs:      ");

    for (i=0; i < MAX_ATA_REGS; i++ )
    {
        IOLog("%04x ", tf.ataRegs[i]);
    }

    IOLog("\n\rATA result regs:       ");

    for (i=0; i < MAX_ATA_REGS; i++ )
    {
        IOLog("%04x ", res.ataRegs[i]);
    }
    IOLog("\n\r");
#endif

    if ( cmd->flags & IOATACommand::atacmdCallbackValid )
    {
        (*cmd->completionInfo.async.ataDoneFn)(              cmd->completionInfo.async.target, 
                                                (IOService *)this, 
                                                             cmd, 
                                                             cmd->completionInfo.async.refcon );
    }
    else
    {
        cmd->completionInfo.sync.syncer->signal();
    }
}

/*
 *
 *
 */
IOReturn IOATADevice::message( UInt32 p0, IOService *p1, void *p2 )
{
    UInt32		  	msgId;
    IOATADevice			*ataDev;    

    msgId  = (UInt32) p0;
    ataDev = (IOATADevice *)p1;

    switch ( msgId )
    {
        case ataMessageResetStarted:
            lock_init( resetSem, true, NULL, NULL );
            IORWLockWrite( resetSem );
            break;
        
        case ataMessageResetComplete:
            IORWLockUnlock( resetSem );
            break;
 
        default:
            ;
    }

    return kIOReturnSuccess;
}

/*
 *
 *
 */
UInt32 IOATADevice::getUnit()
{
    return unit;
}

/*
 *
 *
 */
IOCommandQueue  *IOATADevice::getDeviceQueue()
{
    return deviceQueue;
}


/*
 *
 *
 */
ATADeviceType IOATADevice::getDeviceType()
{
    return deviceType;
}

/*
 *
 *
 */
bool IOATADevice::getATAPIPktInt()
{
    return atapiPktInt;
}

/*
 *
 *
 */
bool IOATADevice::getIdentifyData( ATAIdentify *identifyBuffer )
{
    if ( identifyData == NULL )
    {
        bzero( identifyBuffer, sizeof(ATAIdentify) );
        return false;
    }

    bcopy( identifyData, identifyBuffer, sizeof(ATAIdentify) );
    return true;
}

/*
 *
 *
 */
bool IOATADevice::getInquiryData( UInt32 inquiryBufLength, ATAPIInquiry *inquiryBuffer )
{        
    bzero( inquiryBuffer, inquiryBufLength );

    if ( inquiryData == NULL )
    {
        return false;
    }

    bcopy( inquiryData, inquiryBuffer, inquiryBufLength );

    return true;
}

/*
 *
 *
 */
bool IOATADevice::init( UInt32 unitNum, IOATAController *ctlr )
{
    controller = ctlr;
    unit       = unitNum;

    if ( super::init() != true )
    {
        return false;
    }

    deviceQueue = controller->createDeviceQueue( this ); 
    if ( deviceQueue == NULL )
    {
        return false;
    }

    utilCmd = allocCommand();
    if ( utilCmd == NULL )
    {
        return false;
    }

    resetSem = IORWLockAlloc();
    if ( resetSem == NULL )
    {
        return false;
    }

    return true;
}


/*
 *
 *
 */
OSDictionary *IOATADevice::createProperties()
{
    OSDictionary 	*propTable = 0;
    OSObject		*regObj;
    char		tmpbuf[81];
    char		*s, *d;
   

    propTable = OSDictionary::withCapacity(ATAMaxProperties);
    if ( propTable == NULL )
    {
        return NULL;
    }

    s = (deviceType == ataDeviceATA) ? ATAPropertyProtocolATA : ATAPropertyProtocolATAPI;
    regObj = (OSObject *)OSString::withCString( s );
    if ( addToRegistry( propTable, regObj, ATAPropertyProtocol ) != true )
    {
        goto createprop_error;
    }

    regObj = (OSObject *)OSNumber::withNumber(unit,32);
    if ( addToRegistry( propTable, regObj, ATAPropertyDeviceNumber ) != true )
    {
        goto createprop_error;
    }

    regObj = (OSObject *)OSNumber::withNumber(unit,32);
    if ( addToRegistry( propTable, regObj, ATAPropertyLocation ) != true )
    {
        goto createprop_error;
    }

    d = tmpbuf;
    stripBlanks( d, identifyData->modelNumber, sizeof(identifyData->modelNumber));
    regObj = (OSObject *)OSString::withCString( d );
    if ( addToRegistry( propTable, regObj, ATAPropertyModelNumber ) != true )
    {
        goto createprop_error;
    }

    d = tmpbuf;
    stripBlanks( d, identifyData->firmwareRevision, sizeof(identifyData->firmwareRevision));
    regObj = (OSObject *)OSString::withCString( d );
    if ( addToRegistry( propTable, regObj, ATAPropertyFirmwareRev ) != true )
    {
        goto createprop_error;
    }

    if ( inquiryData )
    {
        stripBlanks( d, inquiryData->vendorName, sizeof(inquiryData->vendorName) );
        regObj = (OSObject *)OSString::withCString( d );
        if ( addToRegistry( propTable, regObj, ATAPropertyVendorName ) != true )
        {
            goto createprop_error;
        }

        stripBlanks( d, inquiryData->productName, sizeof(inquiryData->productName) );
        regObj = (OSObject *)OSString::withCString( d );
        if ( addToRegistry( propTable, regObj, ATAPropertyProductName ) != true )
        {
            goto createprop_error;
        }

        stripBlanks( d, inquiryData->productRevision, sizeof(inquiryData->productRevision) );
        regObj = (OSObject *)OSString::withCString( d );
        if ( addToRegistry( propTable, regObj, ATAPropertyProductRevision ) != true )
        {
            goto createprop_error;
        }
    }
    return propTable;

createprop_error: ;
    propTable->release();
    return NULL;
}

/*
 *
 *
 */

bool IOATADevice::matchPropertyTable( OSDictionary * table )
{
  return( controller->matchNubWithPropertyTable( this, table ));
}

IOService *IOATADevice::matchLocation( IOService * )
{
//    IOLog( "IOATADevice::%s - called\n\r", __FUNCTION__ );

    return this;
}

/*
 *
 *
 */
bool IOATADevice::open( IOService *customer )
{
    bool		rc;

    rc = super::open( customer );
    
    if ( rc == true )
    {
        client = customer;
    }
    
    return rc;
}


/*
 *
 *
 */
void IOATADevice::close( IOService *customer )
{
    super::close( customer );
    client = NULL;
}


/*
 *
 *
 */
void IOATADevice::free()
{
    if ( identifyData ) IOFree( identifyData, sizeof(*identifyData) );
    if ( inquiryData  ) IOFree( inquiryData,  sizeof(*inquiryData)  );
    if ( utilCmd )      utilCmd->release();
    if ( reqSenseCmd )  reqSenseCmd->release();
    if ( deviceQueue )  deviceQueue->release();
    if ( resetSem )  	IORWLockFree( resetSem );

    super::free();
}
    
/*
 *
 *
 */
IOService *IOATADevice::getClient()
{
    return client;
}


/*
 *
 *
 */
IOATACommand *IOATADevice::allocCommand( UInt32 clientDataSize )
{
    IOATACommand	*cmd;

    if ( (cmd = controller->allocCommand( clientDataSize )) )
    {
        cmd->setDevice( this );
    }
    return cmd;
}

/*
 *
 *
 */
bool IOATADevice::addToRegistry( OSDictionary *propTable, OSObject *regObj, char *key )
{
    bool ret;

    if ( regObj == NULL )
    {
        return false;
    }

    ret = propTable->setObject( key, regObj );

    regObj->release();

    return ret;
}
           
/*
 *
 *
 */
void IOATADevice::stripBlanks( char *d, char *s, UInt32 l )
{
    char	*p, c;

    for ( p = d, c = *s; l && c ; l--)
    {
        c = (*d++ = *s++);
        if ( c != ' ' )
        {
            p = d;
        }
    }
    *p = 0;
}   


/*
 *
 *
 */
void IOATADevice::endianConvertData( void *data, void *endianTable )
{
    EndianTable		*t;