/* * Copyright (c) 2002-2004 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) 2002-2004 Apple Computer, Inc. All rights reserved. * * */ #include "IOPlatformPluginSymbols.h" //#include #include "PBG4_PlatformPlugin.h" // Global pointer to our plugin for reference by sensors and control loops PBG4_PlatformPlugin* gPlatformPlugin; //********************************************************************************* // printDictionaryKeys // // Print the keys for the given dictionary for diagnostic purposes //********************************************************************************* static void printDictionaryKeys (OSDictionary * inDictionary, char * inMsg) { #ifdef PLUGIN_DEBUG OSCollectionIterator * mcoll = OSCollectionIterator::withCollection (inDictionary); OSSymbol * mkey; mcoll->reset (); mkey = OSDynamicCast (OSSymbol, mcoll->getNextObject ()); if (!mkey) { DLOG ("No objects found in dictionary '%s'\n", inMsg); return; } DLOG ("Listing objects in dictionary '%s':\n", inMsg); while (mkey) { DLOG (" '%s'\n", mkey->getCStringNoCopy()); mkey = (OSSymbol *) mcoll->getNextObject (); } mcoll->release (); #endif // PLUGIN_DEBUG return; } #define super IOPlatformPlugin OSDefineMetaClassAndStructors(PBG4_PlatformPlugin, IOPlatformPlugin) bool PBG4_PlatformPlugin::start( IOService * provider ) { DLOG("PBG4_PlatformPlugin::start - entered\n"); gPlatformPlugin = this; // Let associated classes find us // See if we need to use PowerPlay for graphics fUsePowerPlay = (provider->getProperty ("UsePowerPlay") != NULL); if (!super::start(provider)) return(false); nub = provider; // set the platform ID if (gIOPPluginPlatformID) gIOPPluginPlatformID->release(); gIOPPluginPlatformID = OSSymbol::withCString("PBG4"); if ( thermalProfile != NULL ) { thermalProfile->adjustThermalProfile(); // Tear down the nub (which also terminates thermalProfile) thermalNub->terminate(); thermalNub = NULL; thermalProfile = NULL; } // These will get set correctly the first time we get an environmental interrupt setEnv(gIOPPluginEnvACPresent, kOSBooleanFalse); setEnv(gIOPPluginEnvBatteryPresent, kOSBooleanFalse); setEnv(gIOPPluginEnvBatteryOvercurrent, kOSBooleanFalse); setEnv(gIOPPluginEnvClamshellClosed, kOSBooleanFalse); // Register for battery current interrupts // Consider adding a capability check here, if we ship any G4 Powerbooks without the Battery Current sensor // Right now, this plugin only supports Q16b and Q41b. Both of these machines have the sensor, so no check // is performed. IOService *PMUdriver = waitForService(serviceMatching("ApplePMU")); // Register for the interrupt: PMUdriver->callPlatformFunction("registerForPMUInterrupts", true, (void*) (kPMUEnvironmentIntBit), (void*)handleEnvironmentalInterruptEvent, (void*)this, NULL); DLOG("PBG4_PlatformPlugin::start - done\n"); return true; } /******************************************************************************* * Method: * initCtrlLoops * ******************************************************************************/ // virtual bool PBG4_PlatformPlugin::initCtrlLoops( const OSArray * ctrlLoopDicts ) { IOPlatformCtrlLoop *loop; OSNumber *flag, *watts; OSDictionary *dict; int count, i; bool result; if (result = super::initCtrlLoops (ctrlLoopDicts)) { // Look for our own properties if (ctrlLoopDicts == NULL) { // this is not a fatal error DLOG("IOPlatformPlugin::initCtrlLoops no ctrlloop array\n"); } else { count = ctrlLoopDicts->getCount(); for (i = 0; i < count; i++) { // grab the array element if ((dict = OSDynamicCast(OSDictionary, ctrlLoopDicts->getObject(i))) == NULL) { DLOG("IOPlatformPlugin::initCtrlLoops error parsing ctrlLoopDicts element %d, skipping\n", i); continue; } // Make the baseline wattage available to the control loop if (watts = OSDynamicCast (OSNumber, dict->getObject(kCtrlLoopPowerAdapterBaseline))) if ((loop = OSDynamicCast(IOPlatformCtrlLoop, ctrlLoops->getObject(i))) != NULL) loop->getInfoDict()->setObject(kCtrlLoopPowerAdapterBaseline, watts); #if 0 char s[64]; sprintf (s, "ctrlLoopDict[%d]", i); printDictionaryKeys (dict, s); #endif if (flag = OSDynamicCast(OSNumber, dict->getObject(kCtrlLoopIsStateDrivenKey))) { //DLOG ("PBG4_PlatformPlugin::initCtrlLoops - got state driven flag '%s' for loop %d\n", //(flag->unsigned32BitValue() != 0) ? "true" : "false", i); fCtrlLoopStateFlagsArray[i] = (flag->unsigned32BitValue() != 0); } } } } return result; } UInt8 PBG4_PlatformPlugin::probeConfig( void ) { char thermalProfilePrefix[ 128 ] = "ThermalProfile_"; OSDictionary* thermalNubDict; OSString* modelString; OSString* name; UInt8 config = 0; IOService *service; DLOG("PBG4_PlatformPlugin::probeConfig - entered\n"); if ( ( thermalNubDict = OSDictionary::withCapacity( 1 ) ) == NULL ) return( 0 ); if ( ( modelString = OSDynamicCast( OSString, getProvider()->getProperty( "model" ) ) ) == NULL ) return( 0 ); strcat( thermalProfilePrefix, modelString->getCStringNoCopy() ); name = OSString::withCString( thermalProfilePrefix ); // By using OSString or OSSymbol and setting the name as IOName, we avoid // overriding compareName. thermalNubDict->setObject( "IOName", name ); if ( ( ( thermalNub = new IOService ) == NULL ) || ( !thermalNub->init( thermalNubDict ) ) ) return( 0 ); thermalNub->attach( this ); thermalNub->start( this ); thermalNub->registerService( kIOServiceSynchronous ); // Get the dictionary from the nub. What do we do if we couldn't find the thermal profile? if (!(service = thermalNub->getClient())) { DLOG("PBG4_PlatformPlugin::probeConfig - no thermal nub client\n"); } else if ( ( thermalProfile = OSDynamicCast( IOPlatformPluginThermalProfile, thermalNub->getClient() ) ) != NULL ) { config = thermalProfile->getThermalConfig(); removeProperty( kIOPPluginThermalProfileKey ); setProperty( kIOPPluginThermalProfileKey, thermalProfile->copyProperty( kIOPPluginThermalProfileKey ) ); } else DLOG("PBG4_PlatformPlugin::probeConfig - thermalProfile not found\n"); thermalNubDict->release(); name->release(); DLOG("PBG4_PlatformPlugin::probeConfig - done, returning config %d\n", config); return( config ); } // ********************************************************************************** // setAggressiveness // // -- We override the superclass on this to filter out unwanted selectors // // ********************************************************************************** IOReturn PBG4_PlatformPlugin::setAggressiveness(unsigned long selector, unsigned long newLevel) { /* * Check here if this is a selector we care about - the only one of interest being kPMSetProcessorSpeed * This is very important because the PBG4_DPSCtrlLoop issues a broadcast setAggressiveness through * the pmRootDomain. That call comes back here. Without this check we attempt to dispatch * a second setAggressiveness message while the first one is pending. dispatchEvent does not * block this because it uses an IORecursiveLock and we are still on the same thread so dispatchEvent * does not block and the CtrlLoop gets re-entered. */ if (selector != kPMSetProcessorSpeed) return IOPMNoErr; // Filter out unwanted messages // Carry on... return super::setAggressiveness(selector, newLevel); } /* * PBG4_PlatformPlugin::environmentChanged - this is a complete override of * IOPlatformPlugin::environmentChanged - see notes below. */ void PBG4_PlatformPlugin::environmentChanged( void ) { IOPlatformCtrlLoop *loop; int i, count; // let the control loops update their meta state and, if necessary, adjust their controls if (ctrlLoops) { count = ctrlLoops->getCount(); for (i=0; igetObject(i))) != NULL) { /* * This differs from IOPlatformPlugin::environmentChanged in that not all * our control loops need both updateMetaState and adjustControls called. * In particular, state driven control loops, like PBG4_DPSCtrlLoop just * have their adjustControls() routine turn around and call updateMetaState(). * So, calling both is redundant. */ DLOG("PBG4_PlatformPlugin::enviromentChanged notifying ctrlLoop %d\n", i); loop->updateMetaState(); // Only call adjustControls if loop is *not* state driven if (!fCtrlLoopStateFlagsArray[i]) { DLOG("PBG4_PlatformPlugin::enviromentChanged also calling adjustControls %d\n", i); loop->adjustControls(); } } } } return; } // ********************************************************************************** // handleEnvironmentalInterruptEvent // // -- Currently just called as an interrupt handler // - this call is unsynchronized // // ********************************************************************************** /* static */ void PBG4_PlatformPlugin::handleEnvironmentalInterruptEvent(IOService *client, UInt8 interruptMask, UInt32 length, UInt8 *buffer) { PBG4_PlatformPlugin *localThis; UInt32 changedIntBits, envIntData, envRawData; // Verify that we are receiving the correct interrupt (probably unnecessary(?)): if (!(interruptMask & kPMUEnvironmentIntBit)) return; // Was this one meant for us? localThis = OSDynamicCast(PBG4_PlatformPlugin, client); if (localThis == NULL) return; // Retrieve the data (little-endian) and mask off bits we don't care about envRawData = OSReadSwapInt16(buffer, 0); envIntData = envRawData & kPMUEnvIntMask; if (!localThis->fEnvDataIsValid) { changedIntBits = kPMUEnvIntMask; // First time through, mark everything as changed localThis->fEnvDataIsValid = true; DLOG("PBG4_PlatformPlugin::handleEnvironmentalInterruptEvent - initial raw data 0x%x, masked data 0x%x\n", envRawData, envIntData); } else changedIntBits = envIntData ^ localThis->fLastPMUEnvIntData; if (changedIntBits != 0) { // Only dispatch the event if something changed IOPPluginEventData event; // dispatch a (serialized) event event.eventType = IOPPluginEventMisc; event.param1 = (void *)localThis; // Pass object reference event.param2 = (void *)envIntData; // Pass current data event.param3 = (void *)changedIntBits; // Pass change mask event.param4 = (void *)PBG4_PlatformPlugin::environmentalIntSyncHandler; DLOG("PBG4_PlatformPlugin::handleEnvironmentalInterruptEvent - dispatching data 0x%x, changed 0x%x\n", envIntData, changedIntBits); localThis->dispatchEvent(&event); localThis->fLastPMUEnvIntData = envIntData; // Save current value } //DLOG("PBG4_PlatformPlugin::handleEnvironmentalInterruptEvent - done\n"); return; } // ********************************************************************************** // environmentalIntSyncHandler // // -- Synchronized handler to set change environment status // -- Should only be called if status changes as it automatically // triggers updates in the control loop(s) // // ********************************************************************************** /* static */ IOReturn PBG4_PlatformPlugin::environmentalIntSyncHandler ( void * p1, void * p2, void * p3 ) { PBG4_PlatformPlugin *localThis; UInt32 intData, changeMask; localThis = OSDynamicCast(PBG4_PlatformPlugin, (OSMetaClassBase *)p1); if (localThis == NULL) return kIOReturnBadArgument; // retrieve the data intData = (UInt32) p2; changeMask = (UInt32) p3; // Examine the change mask to see what we need to update if (changeMask & kACPlugEventMask) { localThis->setEnv(gIOPPluginEnvACPresent, ((intData & kACPlugEventMask) != 0) ? kOSBooleanTrue : kOSBooleanFalse); DLOG("PBG4_PlatformPlugin::environmentalIntSyncHandler - sending A/C present '%s'\n", ((intData & kACPlugEventMask) != 0) ? "true" : "false"); } if (changeMask & kBatteryStatusEventMask) { localThis->setEnv(gIOPPluginEnvBatteryPresent, ((intData & kBatteryStatusEventMask) != 0) ? kOSBooleanTrue : kOSBooleanFalse); DLOG("PBG4_PlatformPlugin::environmentalIntSyncHandler - sending battery present '%s'\n", ((intData & kBatteryStatusEventMask) != 0) ? "true" : "false"); } if (changeMask & kPMUBatteryOvercurrentIntMask) { localThis->setEnv(gIOPPluginEnvBatteryOvercurrent, ((intData & kPMUBatteryOvercurrentIntMask) != 0) ? kOSBooleanTrue : kOSBooleanFalse); DLOG("PBG4_PlatformPlugin::environmentalIntSyncHandler - sending overcurrent '%s'\n", ((intData & kPMUBatteryOvercurrentIntMask) != 0) ? "true" : "false"); } if (changeMask & kClamshellClosedEventMask) { localThis->setEnv(gIOPPluginEnvClamshellClosed, ((intData & kClamshellClosedEventMask) != 0) ? kOSBooleanTrue : kOSBooleanFalse); DLOG("PBG4_PlatformPlugin::environmentalIntSyncHandler - sending clamshell closed '%s'\n", ((intData & kClamshellClosedEventMask) != 0) ? "true" : "false"); } return kIOReturnSuccess; }