AppleKeyswitch.cpp   [plain text]


/*
 * Copyright (c) 1998-2006 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@
 */
/*
 * Copyright (c) 2006 Apple Computer, Inc.  All rights reserved.
 *
 *  DRI: Tom Sherman / Bill Galcher
 *
 */

#include "AppleKeyswitch.h"

#define super IOService
OSDefineMetaClassAndStructors(AppleKeyswitch, IOService);



/* **************************************************
*	    s t a r t                                   *
* **************************************************/

bool AppleKeyswitch::start( IOService * provider )
{
    UInt8 		val = 0;
#if defined( __ppc__ )										// **********************  PowerPC -- Start
    OSData 		*tempOSData;
	IOReturn	status;
#endif														// **********************  PowerPC -- End

	DLOG("AppleKeyswitch::start entered.\n");

    if ( ! super::start(provider) )
	{
		DLOG( "AppleKeyswitch::start - super::start() failed\n" );
        return false;
	}

#if defined( __i386__ ) || defined( __x86_64__ )			// **********************  Intel -- Start

	//myProvider = OSDynamicCast( IOACPIPlatformDevice, provider );
	myProvider = (IOACPIPlatformDevice *) provider->metaCast( "IOACPIPlatformDevice" );
	if ( ! myProvider )
	{
		DLOG( "AppleKeyswitch::start - metaCast( 'IOACPIPlatformDevice' ) failed\n" );
		return false;
	}

	if ( ! myProvider->open(this) )
	{
		IOLog( "AppleKeyswitch::start - open() failed\n" );
		return false;
	}

#endif														// **********************  Intel -- End

    myWorkLoop = (IOWorkLoop *)getWorkLoop();
	if(myWorkLoop == NULL)
    {     
        IOLog("AppleKeyswitch::start failed to get WorkLoop.\n" );
		return false;
    }

#if defined( __ppc__ )										// **********************  PowerPC -- Start

	DLOG( "AppleKeyswitch::start - PPC-specific initialization entered.\n" );

    interruptSource = IOInterruptEventSource::interruptEventSource(this, (IOInterruptEventAction)&AppleKeyswitch::interruptOccurred, provider, 0);
    if (interruptSource == NULL)
    {     
        IOLog("AppleKeyswitch::start failed to create interrupt event source.\n");
		return false;
    }

    status = myWorkLoop->addEventSource(interruptSource);
    if (status != kIOReturnSuccess)
    {
        IOLog("AppleKeyswitch::start failed to add interrupt event source to work loop.\n");
		return false;
    }

    // callPlatformFunction symbols
    keyLargo_safeWriteRegUInt8 = OSSymbol::withCString("keyLargo_safeWriteRegUInt8");
    keyLargo_safeReadRegUInt8  = OSSymbol::withCString("keyLargo_safeReadRegUInt8");

    // Get ExtIntGPIO where interrupt is coming in.
    tempOSData = OSDynamicCast(OSData, provider->getProperty("reg"));
	if (tempOSData == NULL)
    {
        IOLog("AppleKeyswitch::start - failed to get GPIO offset value.\n");     
		return false;
    }
    
    extIntGPIO = (UInt32*)tempOSData->getBytesNoCopy();

    // Configure the ExtIntGPIO for interrupts
    callPlatformFunction(keyLargo_safeWriteRegUInt8, false, (void *)(kKeyLargoGPIOBaseAddr + *extIntGPIO), (void *)0xFF, (void *)0x80, (void *)0);

    IOSleep(100);
    
    // Read initial keyswitch state
	getKeyswitchState( &val );	// returns 0 (locked) or 1 (unlocked) in -val-

    // Enable interrupt
    interruptSource->enable();

															// **********************  PowerPC -- End
#elif defined( __i386__ ) || defined( __x86_64__ )			// **********************  Intel   -- Start

	DLOG( "AppleKeyswitch::start - Intel-specific initialization entered.\n" );

													//       type                handler        target ref
	switchEventNotify = myProvider->registerInterest( gIOGeneralInterest, keyswitchNotification, this,  0 );

	if ( ! switchEventNotify )
	{
		IOLog( "AppleKeyswitch::start - Unable to register interest in getting keyswitch notifications\n" );
		stop( provider );
		return false;
	}

	getKeyswitchState( &val );	// returns 0 (locked) or 1 (unlocked) in -val-

#endif														// **********************  Intel -- End

	DLOG( "AppleKeyswitch::start - Initial keyswitch value: 0x%02X (%s)\n", val, ( val == KEYSWITCH_VALUE_UNLOCKED )?  "UNLOCKED" : "LOCKED" );

	if( val == KEYSWITCH_VALUE_UNLOCKED )
    {
		DLOG( "AppleKeyswitch::start - Initial keyswitch state is UNLOCKED\n" );
        state = kSWITCH_STATE_IS_UNLOCKED;
        setProperty("Keyswitch", false);
    }
    else
    {
		DLOG( "AppleKeyswitch::start - Initial keyswitch state is LOCKED\n" );
        state = kSWITCH_STATE_IS_LOCKED;
        setProperty("Keyswitch", true);
    }


    registerService();

	DLOG("AppleKeyswitch::start - exit.\n");

    return true;
}




/* **************************************************
*	    s t o p                                     *
* **************************************************/

void AppleKeyswitch::stop(IOService *provider)
{
#if defined( __ppc__ )										// **********************  PowerPC -- Start

    interruptSource->disable();
    myWorkLoop->removeEventSource(interruptSource);

    if (interruptSource != NULL)
    {
        interruptSource->release();
        interruptSource = NULL;
    }
															// **********************  PowerPC -- End
#elif defined ( __i386__ ) || defined( __x86_64__ )			// **********************  Intel -- Start

	if ( switchEventNotify )
	{
		DLOG( "AppleKeyswitch::stop - de-registering interest in notifications\n" );
		switchEventNotify->remove();
		switchEventNotify = 0;
	}

#endif														// **********************  Intel -- End

    if (myWorkLoop != NULL)
    {
        myWorkLoop->release();
        myWorkLoop = NULL;
    }
    
    super::stop(provider);
}




#if defined( __ppc__ )										// **********************  PowerPC -- Start

/* **************************************************
*	    i n t e r r u p t O c c u r r e d           *
* **************************************************/

void AppleKeyswitch::interruptOccurred(OSObject* obj, IOInterruptEventSource * source, int count)
{
    AppleKeyswitch *AppleKeyswitchPtr = (AppleKeyswitch *)obj;

    if (AppleKeyswitchPtr != NULL)
        AppleKeyswitchPtr->toggle(false);
    
    return;
}

#endif														// **********************  PowerPC -- End


#if	defined( __i386__ ) || defined( __x86_64__ )			// **********************  Intel -- Start

/* **************************************************
*	    k e y s w i t c h N o t i f i c a t i o n   *
* **************************************************/

IOReturn
AppleKeyswitch::keyswitchNotification(	void *      target,
										void *      refCon,
										UInt32      messageType,
										IOService * provider,
										void *      messageArgument,
										vm_size_t   argSize )
{
AppleKeyswitch	* me  = (AppleKeyswitch *) target;

	if ( me )
	{
		if ( (messageType == kIOACPIMessageDeviceNotification) && messageArgument )
		{
			UInt32  notifyCode = *(UInt32 *) messageArgument;

			DLOG( "AppleKeyswitch::keyswitchNotification - notification received.  notifyCode = %lX\n", notifyCode );

			if ( notifyCode == 0x80 )	// ick -- magic number.  we hate magic numbers
				me->toggle( false );
		}
	}

    return kIOReturnSuccess;

}
#endif														// **********************  Intel -- End



/* **************************************************
*	   t o g g l e                                  *
* **************************************************/

void AppleKeyswitch::toggle( bool disableInts )
{
UInt8 	val;


#if defined( __ppc__ )										// **********************  PowerPC -- Start
UInt8	total = 0;
int		i;

    // Disable KeyLargo ExtInt_GPIO4 interrupts
    if (disableInts)
        interruptSource->disable(); 

    // Get 100 samples and determine state of keyswitch (debounce)
    for(i=0; i<=100; i++)
    {
        // get 100 samples
        //callPlatformFunction(keyLargo_safeReadRegUInt8, false, (void *)(kKeyLargoGPIOBaseAddr + *extIntGPIO), (void *)&val, (void *)0, (void *)0);
		getKeyswitchState( &val );	// returns 0 (locked) or 1 (unlocked) in -val-
        total += val;
        IOSleep(1);
    }

	// define PREVIOUS switch state?
    if( total > 80 )
	{
        state = kSWITCH_STATE_IS_UNLOCKED;
		DLOG( "AppleKeyswitch::toggle - changing state to UNLOCKED (%d)\n", state );
        // set Keyswitch property and registerService
        setProperty("Keyswitch", false);
		registerService();
	}
    else if( total < 20 )
    {
        state = kSWITCH_STATE_IS_LOCKED;
		DLOG( "AppleKeyswitch::toggle - changing state to LOCKED (%d)\n", state );
        
        // set Keyswitch property and registerService
        setProperty("Keyswitch", true);
		registerService();
    }
	else
	{
		DLOG( "AppleKeyswitch::toggle - lock state does not yet debounced; leaving state alone\n" );
	}

    // Enable KeyLargo ExtInt_GPIO4 interrupts
    if (disableInts)
        interruptSource->enable(); 
	
															// **********************  PowerPC -- End
#elif defined ( __i386__ ) || defined( __x86_64__ )			// **********************  Intel -- Start

	// ACPI is going to do the interrupt / switch-debounce for us.
	// All we have to do is read the switch value.

	val = 0;
	getKeyswitchState( &val );	// returns 0 (locked) or 1 (unlocked)

	// since it is debounced for us by ACPI, it will either be 0 (locked) or 1 (unlocked)

	if( val == KEYSWITCH_VALUE_UNLOCKED )
	{
		DLOG( "AppleKeyswitch::toggle - changing state to UNLOCKED (%d)\n", state );
		state = kSWITCH_STATE_IS_UNLOCKED;
		setProperty("Keyswitch", false);
	}
	else	// the beauty of boolean values - if its not unlocked, then its locked
	{
		DLOG( "AppleKeyswitch::toggle - changing state to LOCKED (%d)\n", state );
		state = kSWITCH_STATE_IS_LOCKED;
		setProperty("Keyswitch", true);
	}

	registerService();

#endif														// **********************  Intel -- End
   
    return;
}





/* **************************************************
*	    g e t K e y s w i t c h S t a t e           *
* ***************************************************
*
*	Retrieves and returns the current value of the keyswitch.
*
*	Returns:	0	locked
*				1	unlocked
*/

void AppleKeyswitch::getKeyswitchState( UInt8 * switchState )
{
UInt8		val = 0;	// default value is "unlocked".  should it be "locked"?

#if defined( __ppc__ )										// **********************  PowerPC -- Start

    callPlatformFunction(keyLargo_safeReadRegUInt8, false, (void *)(kKeyLargoGPIOBaseAddr + *extIntGPIO), (void *)&val, (void *)0, (void *)0);
	DLOG( "AppleKeyswitch::getKeyswitchState(ppc) - value read from property: 0x%02X\n", val );
	val = ( val & 0x2 ) >> 1;

															// **********************  PowerPC -- End
#elif defined ( __i386__ ) || defined( __x86_64__ )			// **********************  Intel -- Start

IOReturn	err;
UInt32		num = 0;

	err = myProvider->evaluateInteger( "KLCK", &num );
	if ( err == kIOReturnSuccess )
	{
		DLOG( "AppleKeyswitch::getKeyswitchState(i386) - value read from property: 0x%08lX\n", num );
		// ACPI returns "is this locked?" (1 = true, 0 = false)
		// apparently the MacIO GPIO was active low, meaning 0 = LOCKED, 1 = UNLOCKED
		// So - invert the sense of the returned bit to make the subsequent logic work correctly.
		val = (UInt8)  1 - num;
	}
	else
	{
		DLOG( "AppleKeyswitch::getKeyswitchState(i386) - evaluateInteger did NOT succeed (%d)\n", err );
	}


#endif														// **********************  Intel -- End

	* switchState = val;

	DLOG( "AppleKeyswitch::getKeyswitchState - value returned to caller: 0x%02X\n", val );

    return;
}