/*
 * 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@
 */
/*
 * HISTORY
 */
 
#include <IOKit/system.h>
#include <IOKit/IOPlatformExpert.h>
#include <IOKit/IOCPU.h>
#include <IOKit/IODeviceTreeSupport.h>
#include <IOKit/IORangeAllocator.h>
#include <IOKit/IONVRAM.h>
#include <IOKit/IOKitDebug.h>
#include <IOKit/IOWorkLoop.h>
#include <IOKit/pwr_mgt/RootDomain.h>

#include <libkern/c++/OSContainers.h>


extern "C" {
#include <machine/machine_routines.h>
#include <pexpert/pexpert.h>
}

void printDictionaryKeys (OSDictionary * inDictionary, char * inMsg);
static void getCStringForObject (OSObject * inObj, char * outStr);

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#define super IOService

OSDefineMetaClassAndStructors(IOPlatformExpert, IOService)

OSMetaClassDefineReservedUnused(IOPlatformExpert,  0);
OSMetaClassDefineReservedUnused(IOPlatformExpert,  1);
OSMetaClassDefineReservedUnused(IOPlatformExpert,  2);
OSMetaClassDefineReservedUnused(IOPlatformExpert,  3);
OSMetaClassDefineReservedUnused(IOPlatformExpert,  4);
OSMetaClassDefineReservedUnused(IOPlatformExpert,  5);
OSMetaClassDefineReservedUnused(IOPlatformExpert,  6);
OSMetaClassDefineReservedUnused(IOPlatformExpert,  7);
OSMetaClassDefineReservedUnused(IOPlatformExpert,  8);
OSMetaClassDefineReservedUnused(IOPlatformExpert,  9);
OSMetaClassDefineReservedUnused(IOPlatformExpert, 10);
OSMetaClassDefineReservedUnused(IOPlatformExpert, 11);

static IOPlatformExpert * gIOPlatform;

OSSymbol * gPlatformInterruptControllerName;

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

bool IOPlatformExpert::attach( IOService * provider )
{

    if( !super::attach( provider ))
	return( false);

    return( true);
}

bool IOPlatformExpert::start( IOService * provider )
{
    IORangeAllocator *	physicalRanges;
    OSData *		busFrequency;
    
    if (!super::start(provider))
      return false;
    
    // Correct the bus frequency in the device tree.
    busFrequency = OSData::withBytesNoCopy((void *)&gPEClockFrequencyInfo.bus_clock_rate_hz, 4);
    provider->setProperty("clock-frequency", busFrequency);
    busFrequency->release();
    
    gPlatformInterruptControllerName = (OSSymbol *)OSSymbol::withCStringNoCopy("IOPlatformInterruptController");
    
    physicalRanges = IORangeAllocator::withRange(0xffffffff, 1, 16,
						 IORangeAllocator::kLocking);
    assert(physicalRanges);
    setProperty("Platform Memory Ranges", physicalRanges);
    
    setPlatform( this );
    gIOPlatform = this;
    
    PMInstantiatePowerDomains();
    
    return( configure(provider) );
}

bool IOPlatformExpert::configure( IOService * provider )
{
    OSSet *		topLevel;
    OSDictionary *	dict;
    IOService * 	nub;

    topLevel = OSDynamicCast( OSSet, getProperty("top-level"));

    if( topLevel) {
        while( (dict = OSDynamicCast( OSDictionary,
				topLevel->getAnyObject()))) {
            dict->retain();
            topLevel->removeObject( dict );
            nub = createNub( dict );
            if( 0 == nub)
                continue;
            dict->release();
            nub->attach( this );
            nub->registerService();
        }
    }

    return( true );
}

IOService * IOPlatformExpert::createNub( OSDictionary * from )
{
    IOService *		nub;

    nub = new IOPlatformDevice;
    if(nub) {
	if( !nub->init( from )) {
	    nub->release();
	    nub = 0;
	}
    }
    return( nub);
}

bool IOPlatformExpert::compareNubName( const IOService * nub,
				OSString * name, OSString ** matched = 0 ) const
{
    return( nub->IORegistryEntry::compareName( name, matched ));
}

IOReturn IOPlatformExpert::getNubResources( IOService * nub )
{
    return( kIOReturnSuccess );
}

long IOPlatformExpert::getBootROMType(void)
{
  return _peBootROMType;
}

long IOPlatformExpert::getChipSetType(void)
{
  return _peChipSetType;
}

long IOPlatformExpert::getMachineType(void)
{
  return _peMachineType;
}

void IOPlatformExpert::setBootROMType(long peBootROMType)
{
  _peBootROMType = peBootROMType;
}

void IOPlatformExpert::setChipSetType(long peChipSetType)
{
  _peChipSetType = peChipSetType;
}

void IOPlatformExpert::setMachineType(long peMachineType)
{
  _peMachineType = peMachineType;
}

bool IOPlatformExpert::getMachineName( char * /*name*/, int /*maxLength*/)
{
    return( false );
}

bool IOPlatformExpert::getModelName( char * /*name*/, int /*maxLength*/)
{
    return( false );
}

IORangeAllocator * IOPlatformExpert::getPhysicalRangeAllocator(void)
{
    return(OSDynamicCast(IORangeAllocator,
			getProperty("Platform Memory Ranges")));
}

int (*PE_halt_restart)(unsigned int type) = 0;

int IOPlatformExpert::haltRestart(unsigned int type)
{
  if (PE_halt_restart) return (*PE_halt_restart)(type);
  else return -1;
}

void IOPlatformExpert::sleepKernel(void)
{
#if 0
  long cnt;
  boolean_t intState;
  
  intState = ml_set_interrupts_enabled(false);
  
  for (cnt = 0; cnt < 10000; cnt++) {
    IODelay(1000);
  }
  
  ml_set_interrupts_enabled(intState);
#else
//  PE_initialize_console(0, kPEDisableScreen);
  
  IOCPUSleepKernel();
  
//  PE_initialize_console(0, kPEEnableScreen);
#endif
}

long IOPlatformExpert::getGMTTimeOfDay(void)
{
    return(0);
}

void IOPlatformExpert::setGMTTimeOfDay(long secs)
{
}


IOReturn IOPlatformExpert::getConsoleInfo( PE_Video * consoleInfo )
{
    return( PE_current_console( consoleInfo));
}

IOReturn IOPlatformExpert::setConsoleInfo( PE_Video * consoleInfo,
						unsigned int op)
{
    return( PE_initialize_console( consoleInfo, op ));
}

IOReturn IOPlatformExpert::registerInterruptController(OSSymbol *name, IOInterruptController *interruptController)
{
  publishResource(name, interruptController);
  
  return kIOReturnSuccess;
}

IOInterruptController *IOPlatformExpert::lookUpInterruptController(OSSymbol *name)
{
  IOInterruptController *interruptController;
  IOService             *service;
  
  service = waitForService(resourceMatching(name));
  
  interruptController = OSDynamicCast(IOInterruptController, service->getProperty(name));  
  
  return interruptController;
}


void IOPlatformExpert::setCPUInterruptProperties(IOService *service)
{
  IOCPUInterruptController *controller;
  
  controller = OSDynamicCast(IOCPUInterruptController, waitForService(serviceMatching("IOCPUInterruptController")));
  if (controller) controller->setCPUInterruptProperties(service);
}

bool IOPlatformExpert::atInterruptLevel(void)
{
  return ml_at_interrupt_context();
}

bool IOPlatformExpert::platformAdjustService(IOService */*service*/)
{
  return true;
}


//*********************************************************************************
// PMLog
//
//*********************************************************************************

void IOPlatformExpert::PMLog(const char * who,unsigned long event,unsigned long param1, unsigned long param2)
{
    if( gIOKitDebug & kIOLogPower) {
        kprintf("%s %02d %08x %08x\n",who,event,param1,param2);
//        IOLog("%s %02d %08x %08x\n",who,event,param1,param2);
    }
}


//*********************************************************************************
// PMInstantiatePowerDomains
//
// In this vanilla implementation, a Root Power Domain is instantiated.
// All other objects which register will be children of this Root.
// Where this is inappropriate, PMInstantiatePowerDomains is overridden 
// in a platform-specific subclass.
//*********************************************************************************

void IOPlatformExpert::PMInstantiatePowerDomains ( void )
{
    root = new IOPMrootDomain;
    root->init();
    root->attach(this);
    root->start(this);
    root->youAreRoot();
}


//*********************************************************************************
// PMRegisterDevice
//
// In this vanilla implementation, all callers are made children of the root power domain.
// Where this is inappropriate, PMRegisterDevice is overridden in a platform-specific subclass.
//*********************************************************************************

void IOPlatformExpert::PMRegisterDevice(IOService * theNub, IOService * theDevice)
{
    root->addPowerChild ( theDevice );
}

//*********************************************************************************
// hasPMFeature
//
//*********************************************************************************

bool IOPlatformExpert::hasPMFeature (unsigned long featureMask)
{
  return ((_pePMFeatures & featureMask) != 0);
}

//*********************************************************************************
// hasPrivPMFeature
//
//*********************************************************************************

bool IOPlatformExpert::hasPrivPMFeature (unsigned long privFeatureMask)
{
  return ((_pePrivPMFeatures & privFeatureMask) != 0);
}

//*********************************************************************************
// numBatteriesSupported
//
//*********************************************************************************

int IOPlatformExpert::numBatteriesSupported (void)
{
  return (_peNumBatteriesSupported);
}

//*********************************************************************************
// CheckSubTree
//
// This method is called by the instantiated sublass of the platform expert to
// determine how a device should be inserted into the Power Domain. The subclass
// provides an XML power tree description against which a device is matched based
// on class and provider. If a match is found this routine returns true in addition
// to flagging the description tree at the appropriate node that a device has been
// registered for the given service.
//*********************************************************************************

bool IOPlatformExpert::CheckSubTree (OSArray * inSubTree, IOService * theNub, IOService * theDevice, OSDictionary * theParent)
{
  unsigned int    i;
  unsigned int    numPowerTreeNodes;
  OSDictionary *  entry;
  OSDictionary *  matchingDictionary;
  OSDictionary *  providerDictionary;
  OSDictionary *  deviceDictionary;
  OSDictionary *  nubDictionary;
  OSArray *       children;
  bool            nodeFound            = false;
  bool            continueSearch       = false;
  bool            deviceMatch          = false;
  bool            providerMatch        = false;
  bool            multiParentMatch     = false;

  if ( (NULL == theDevice) || (NULL == inSubTree) )
    return false;

  numPowerTreeNodes = inSubTree->getCount ();

  // iterate through the power tree to find a home for this device

  for ( i = 0; i < numPowerTreeNodes; i++ ) {

    entry =  (OSDictionary *) inSubTree->getObject (i);

    matchingDictionary = (OSDictionary *) entry->getObject ("device");
    providerDictionary = (OSDictionary *) entry->getObject ("provider");

    deviceMatch = true; // if no matching dictionary, this is not a criteria and so must match
    if ( matchingDictionary ) {
      deviceMatch = false;
      if ( NULL != (deviceDictionary = theDevice->dictionaryWithProperties ())) {
        deviceMatch = deviceDictionary->isEqualTo ( matchingDictionary, matchingDictionary );
        deviceDictionary->release ();
      }
    }

    providerMatch = true; // we indicate a match if there is no nub or provider
    if ( theNub && providerDictionary ) {
      providerMatch = false;
      if ( NULL != (nubDictionary = theNub->dictionaryWithProperties ()) ) {
        providerMatch = nubDictionary->isEqualTo ( providerDictionary,  providerDictionary );
        nubDictionary->release ();
      }
    }

    multiParentMatch = true; // again we indicate a match if there is no multi-parent node
    if (deviceMatch && providerMatch) {
      if (NULL != multipleParentKeyValue) {
        OSNumber * aNumber = (OSNumber *) entry->getObject ("multiple-parent");
        multiParentMatch   = (NULL != aNumber) ? multipleParentKeyValue->isEqualTo (aNumber) : false;
      }
    }

    nodeFound = (deviceMatch && providerMatch && multiParentMatch);

    // if the power tree specifies a provider dictionary but theNub is
    // NULL then we cannot match with this entry.

    if ( theNub == NULL && providerDictionary != NULL )
      nodeFound = false;
  
    // if this node is THE ONE...then register the device

    if ( nodeFound ) {
      if (RegisterServiceInTree (theDevice, entry, theParent, theNub) ) {

        if ( kIOLogPower & gIOKitDebug)
          IOLog ("PMRegisterDevice/CheckSubTree - service registered!\n");

	numInstancesRegistered++;

	// determine if we need to search for additional nodes for this item
	multipleParentKeyValue = (OSNumber *) entry->getObject ("multiple-parent");
      }
      else
	nodeFound = false;
    }

    continueSearch = ( (false == nodeFound) || (NULL != multipleParentKeyValue) );

    if ( continueSearch && (NULL != (children = (OSArray *) entry->getObject ("children"))) ) {
      nodeFound = CheckSubTree ( children, theNub, theDevice, entry );
      continueSearch = ( (false == nodeFound) || (NULL != multipleParentKeyValue) );
    }

    if ( false == continueSearch )
      break;
  }

  return ( nodeFound );
}

//*********************************************************************************
// RegisterServiceInTree
//
// Register a device at the specified node of our power tree.
//*********************************************************************************

bool IOPlatformExpert::RegisterServiceInTree (IOService * theService, OSDictionary * theTreeNode, OSDictionary * theTreeParentNode, IOService * theProvider)
{
  IOService *    aService;
  bool           registered = false;
  OSArray *      children;
  unsigned int   numChildren;
  OSDictionary * child;

  // make sure someone is not already registered here

  if ( NULL == theTreeNode->getObject ("service") ) {

    if ( theTreeNode->setObject ("service", OSDynamicCast ( OSObject, theService)) ) {

      // 1. CHILDREN ------------------

      // we registered the node in the tree...now if the node has children
      // registered we must tell this service to add them.

      if ( NULL != (children = (OSArray *) theTreeNode->getObject ("children")) ) {
        numChildren = children->getCount ();
        for ( unsigned int i = 0; i < numChildren; i++ ) {
          if ( NULL != (child = (OSDictionary *) children->getObject (i)) ) {
            if ( NULL != (aService = (IOService *) child->getObject ("service")) )
              theService->addPowerChild (aService);
          }
        }
      }

      // 2. PARENT --------------------

      // also we must notify the parent of this node (if a registered service
      // exists there) of a new child.

      if ( theTreeParentNode ) {
        if ( NULL != (aService = (IOService *) theTreeParentNode->getObject ("service")) )
          if (aService != theProvider)
            aService->addPowerChild (theService);
      }

      registered = true;
    }
  }

  return registered;
}

//*********************************************************************************
// printDictionaryKeys
//
// Print the keys for the given dictionary and selected contents.
//*********************************************************************************
void printDictionaryKeys (OSDictionary * inDictionary, char * inMsg)
{
  OSCollectionIterator * mcoll = OSCollectionIterator::withCollection (inDictionary);
  OSSymbol * mkey;
  OSString * ioClass;
  unsigned int i = 0;
 
  mcoll->reset ();

  mkey = OSDynamicCast (OSSymbol, mcoll->getNextObject ());

  while (mkey) {

    // kprintf ("dictionary key #%d: %s\n", i, mkey->getCStringNoCopy () );

    // if this is the IOClass key, print it's contents

    if ( mkey->isEqualTo ("IOClass") ) {
      ioClass = (OSString *) inDictionary->getObject ("IOClass");
      if ( ioClass ) IOLog ("%s IOClass is %s\n", inMsg, ioClass->getCStringNoCopy () );
    }

    // if this is an IOProviderClass key print it

    if ( mkey->isEqualTo ("IOProviderClass") ) {
      ioClass = (OSString *) inDictionary->getObject ("IOProviderClass");
      if ( ioClass ) IOLog ("%s IOProviderClass is %s\n", inMsg, ioClass->getCStringNoCopy () );

    }

    // also print IONameMatch keys
    if ( mkey->isEqualTo ("IONameMatch") ) {
      ioClass = (OSString *) inDictionary->getObject ("IONameMatch");
      if ( ioClass ) IOLog ("%s IONameMatch is %s\n", inMsg, ioClass->getCStringNoCopy () );
    }

    // also print IONameMatched keys

    if ( mkey->isEqualTo ("IONameMatched") ) {
      ioClass = (OSString *) inDictionary->getObject ("IONameMatched");
      if ( ioClass ) IOLog ("%s IONameMatched is %s\n", inMsg, ioClass->getCStringNoCopy () );
    }

#if 0
    // print clock-id

    if ( mkey->isEqualTo ("AAPL,clock-id") ) {
      char * cstr;
      cstr = getCStringForObject (inDictionary->getObject ("AAPL,clock-id"));
      if (cstr)
        kprintf (" ===> AAPL,clock-id is %s\n", cstr );
    }
#endif

    // print name

    if ( mkey->isEqualTo ("name") ) {
      char nameStr[64];
      nameStr[0] = 0;
      getCStringForObject (inDictionary->getObject ("name"), nameStr );
      if (strlen(nameStr) > 0)
        IOLog ("%s name is %s\n", inMsg, nameStr);
    }

    mkey = (OSSymbol *) mcoll->getNextObject ();

    i++;
  }

  mcoll->release ();
}

static void getCStringForObject (OSObject * inObj, char * outStr)
{
   char * buffer;
   unsigned int    len, i;

   if ( (NULL == inObj) || (NULL == outStr))
     return;

   char * objString = (char *) (inObj->getMetaClass())->getClassName();

   if ((0 == strcmp(objString,"OSString")) || (0 == strcmp (objString, "OSSymbol")))
     strcpy (outStr, ((OSString *)inObj)->getCStringNoCopy());

   else if (0 == strcmp(objString,"OSData")) {
     len = ((OSData *)inObj)->getLength();
     buffer = (char *)((OSData *)inObj)->getBytesNoCopy();
     if (buffer && (len > 0)) {
       for (i=0; i < len; i++) {
         outStr[i] = buffer[i];
       }
       outStr[len] = 0;
     }
   }
}

extern "C" {

/*
 * Callouts from BSD for machine name & model
 */ 

boolean_t PEGetMachineName( char * name, int maxLength )
{
    if( gIOPlatform)
	return( gIOPlatform->getMachineName( name, maxLength ));
    else
	return( false );
}

boolean_t PEGetModelName( char * name, int maxLength )
{
    if( gIOPlatform)
	return( gIOPlatform->getModelName( name, maxLength ));
    else
	return( false );
}

int PEGetPlatformEpoch(void)
{
    if( gIOPlatform)
	return( gIOPlatform->getBootROMType());
    else
	return( -1 );
}

int PEHaltRestart(unsigned int type)
{
  if (gIOPlatform) return gIOPlatform->haltRestart(type);
  else return -1;
}

long PEGetGMTTimeOfDay(void)
{
    if( gIOPlatform)
	return( gIOPlatform->getGMTTimeOfDay());
    else
	return( 0 );
}

void PESetGMTTimeOfDay(long secs)
{
    if( gIOPlatform)
	gIOPlatform->setGMTTimeOfDay(secs);
}

} /* extern "C" */

void IOPlatformExpert::registerNVRAMController(IONVRAMController * caller)
{
    publishResource("IONVRAM");
}

IOReturn IOPlatformExpert::callPlatformFunction(const OSSymbol *functionName,
						bool waitForFunction,
						void *param1, void *param2,
						void *param3, void *param4)
{
  IOService *service, *_resources;
  
  if (waitForFunction) {
    _resources = waitForService(resourceMatching(functionName));
  } else {
    _resources = resources();
  }
  if (_resources == 0) return kIOReturnUnsupported;
  
  service = OSDynamicCast(IOService, _resources->getProperty(functionName));
  if (service == 0) return kIOReturnUnsupported;
  
  return service->callPlatformFunction(functionName, waitForFunction,
				       param1, param2, param3, param4);
}


/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#undef super
#define super IOPlatformExpert

OSDefineMetaClassAndAbstractStructors( IODTPlatformExpert, IOPlatformExpert )

OSMetaClassDefineReservedUnused(IODTPlatformExpert,  0);
OSMetaClassDefineReservedUnused(IODTPlatformExpert,  1);
OSMetaClassDefineReservedUnused(IODTPlatformExpert,  2);
OSMetaClassDefineReservedUnused(IODTPlatformExpert,  3);
OSMetaClassDefineReservedUnused(IODTPlatformExpert,  4);
OSMetaClassDefineReservedUnused(IODTPlatformExpert,  5);
OSMetaClassDefineReservedUnused(IODTPlatformExpert,  6);
OSMetaClassDefineReservedUnused(IODTPlatformExpert,  7);

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

IOService * IODTPlatformExpert::probe( IOService * provider,
			       		SInt32 * score )
{
    if( !super::probe( provider, score))
	return( 0 );

    // check machine types
    if( !provider->compareNames( getProperty( gIONameMatchKey ) ))
        return( 0 );

    return( this);
}

bool IODTPlatformExpert::configure( IOService * provider )
{
    if( !super::configure( provider))
	return( false);

    processTopLevel( provider );

    return( true );
}

IOService * IODTPlatformExpert::createNub( IORegistryEntry * from )
{
    IOService *		nub;

    nub = new IOPlatformDevice;
    if( nub) {
	if( !nub->init( from, gIODTPlane )) {
	    nub->free();
	    nub = 0;
	}
    }
    return( nub);
}

bool IODTPlatformExpert::createNubs( IOService * parent, OSIterator * iter )
{
    IORegistryEntry *	next;
    IOService *		nub;
    bool		ok = true;

    if( iter) {
	while( (next = (IORegistryEntry *) iter->getNextObject())) {

            if( 0 == (nub = createNub( next )))
                continue;

            nub->attach( parent );
            nub->registerService();
        }
	iter->release();
    }

    return( ok );
}

void IODTPlatformExpert::processTopLevel( IORegistryEntry * root )
{
    OSIterator * 	kids;
    IORegistryEntry *	next;
    IORegistryEntry *	cpus;
    IORegistryEntry *	options;

    // infanticide
    kids = IODTFindMatchingEntries( root, 0, deleteList() );
    if( kids) {
	while( (next = (IORegistryEntry *)kids->getNextObject())) {
	    next->detachAll( gIODTPlane);
	}
	kids->release();
    }

    // Publish an IODTNVRAM class on /options.
    options = root->childFromPath("options", gIODTPlane);
    if (options) {
      dtNVRAM = new IODTNVRAM;
      if (dtNVRAM) {
        if (!dtNVRAM->init(options, gIODTPlane)) {
	  dtNVRAM->release();
	  dtNVRAM = 0;
        } else {
	  dtNVRAM->attach(this);
	  dtNVRAM->registerService();
	}
      }
    }

    // Publish the cpus.
    cpus = root->childFromPath( "cpus", gIODTPlane);
    if ( cpus)
      createNubs( this, IODTFindMatchingEntries( cpus, kIODTExclusive, 0));

    // publish top level, minus excludeList
    createNubs( this, IODTFindMatchingEntries( root, kIODTExclusive, excludeList()));
}

IOReturn IODTPlatformExpert::getNubResources( IOService * nub )
{
  if( nub->getDeviceMemory())
    return( kIOReturnSuccess );

  IODTResolveAddressing( nub, "reg", 0);

  return( kIOReturnSuccess);
}

bool IODTPlatformExpert::compareNubName( const IOService * nub,
				OSString * name, OSString ** matched ) const
{
    return( IODTCompareNubName( nub, name, matched )
	  || super::compareNubName( nub, name, matched) );
}

bool IODTPlatformExpert::getModelName( char * name, int maxLength )
{
    OSData *		prop;
    const char *	str;
    int			len;
    char		c;
    bool		ok = false;

    maxLength--;

    prop = (OSData *) getProvider()->getProperty( gIODTCompatibleKey );
    if( prop ) {
	str = (const char *) prop->getBytesNoCopy();

	if( 0 == strncmp( str, "AAPL,", strlen( "AAPL," ) ))
	    str += strlen( "AAPL," );

	len = 0;
	while( (c = *str++)) {
	    if( (c == '/') || (c == ' '))
		c = '-';

	    name[ len++ ] = c;
	    if( len >= maxLength)
		break;
	}

	name[ len ] = 0;
	ok = true;
    }
    return( ok );
}

bool IODTPlatformExpert::getMachineName( char * name, int maxLength )
{
    OSData *		prop;
    bool		ok = false;

    maxLength--;
    prop = (OSData *) getProvider()->getProperty( gIODTModelKey );
    ok = (0 != prop);

    if( ok )
	strncpy( name, (const char *) prop->getBytesNoCopy(), maxLength );

    return( ok );
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

void IODTPlatformExpert::registerNVRAMController( IONVRAMController * nvram )
{
  if (dtNVRAM) dtNVRAM->registerNVRAMController(nvram);
  
  super::registerNVRAMController(nvram);
}

int IODTPlatformExpert::haltRestart(unsigned int type)
{
  if (dtNVRAM) dtNVRAM->sync();
  
  return super::haltRestart(type);
}

IOReturn IODTPlatformExpert::readXPRAM(IOByteCount offset, UInt8 * buffer,
				       IOByteCount length)
{
  if (dtNVRAM) return dtNVRAM->readXPRAM(offset, buffer, length);
  else return kIOReturnNotReady;
}

IOReturn IODTPlatformExpert::writeXPRAM(IOByteCount offset, UInt8 * buffer,
					IOByteCount length)
{
  if (dtNVRAM) return dtNVRAM->writeXPRAM(offset, buffer, length);
  else return kIOReturnNotReady;
}

IOReturn IODTPlatformExpert::readNVRAMProperty(
	IORegistryEntry * entry,
	const OSSymbol ** name, OSData ** value )
{
  if (dtNVRAM) return dtNVRAM->readNVRAMProperty(entry, name, value);
  else return kIOReturnNotReady;
}

IOReturn IODTPlatformExpert::writeNVRAMProperty(
	IORegistryEntry * entry,
	const OSSymbol * name, OSData * value )
{
  if (dtNVRAM) return dtNVRAM->writeNVRAMProperty(entry, name, value);
  else return kIOReturnNotReady;
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#undef super
#define super IOService

OSDefineMetaClassAndStructors(IOPlatformExpertDevice, IOService)

OSMetaClassDefineReservedUnused(IOPlatformExpertDevice,  0);
OSMetaClassDefineReservedUnused(IOPlatformExpertDevice,  1);
OSMetaClassDefineReservedUnused(IOPlatformExpertDevice,  2);
OSMetaClassDefineReservedUnused(IOPlatformExpertDevice,  3);

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

bool IOPlatformExpertDevice::compareName( OSString * name,
                                        OSString ** matched = 0 ) const
{
    return( IODTCompareNubName( this, name, matched ));
}

bool
IOPlatformExpertDevice::initWithArgs(
                            void * dtTop, void * p2, void * p3, void * p4 )
{
    IORegistryEntry * 	dt = 0;
    void *		argsData[ 4 ];
    bool		ok;

    // dtTop may be zero on non- device tree systems
    if( dtTop && (dt = IODeviceTreeAlloc( dtTop )))
	ok = super::init( dt, gIODTPlane );
    else
	ok = super::init();

    if( !ok)
	return( false);

    workLoop = IOWorkLoop::workLoop();
    if (!workLoop)
        return false;

    argsData[ 0 ] = dtTop;
    argsData[ 1 ] = p2;
    argsData[ 2 ] = p3;
    argsData[ 3 ] = p4;

    setProperty("IOPlatformArgs", (void *)argsData, sizeof( argsData));

    return( true);
}

IOWorkLoop *IOPlatformExpertDevice::getWorkLoop() const
{
    return workLoop;
}

void IOPlatformExpertDevice::free()
{
    if (workLoop)
        workLoop->release();
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#undef super
#define super IOService

OSDefineMetaClassAndStructors(IOPlatformDevice, IOService)

OSMetaClassDefineReservedUnused(IOPlatformDevice,  0);
OSMetaClassDefineReservedUnused(IOPlatformDevice,  1);
OSMetaClassDefineReservedUnused(IOPlatformDevice,  2);
OSMetaClassDefineReservedUnused(IOPlatformDevice,  3);

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

bool IOPlatformDevice::compareName( OSString * name,
					OSString ** matched = 0 ) const
{
    return( ((IOPlatformExpert *)getProvider())->
		compareNubName( this, name, matched ));
}

IOService * IOPlatformDevice::matchLocation( IOService * /* client */ )
{
    return( this );
}

IOReturn IOPlatformDevice::getResources( void )
{
    return( ((IOPlatformExpert *)getProvider())->getNubResources( this ));
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/*********************************************************************
* IOPanicPlatform class
*
* If no legitimate IOPlatformDevice matches, this one does and panics
* the kernel with a suitable message.
*********************************************************************/

class IOPanicPlatform : IOPlatformExpert {
    OSDeclareDefaultStructors(IOPanicPlatform);

public:
    bool start(IOService * provider);
};


OSDefineMetaClassAndStructors(IOPanicPlatform, IOPlatformExpert);


bool IOPanicPlatform::start(IOService * provider) {
    const char * platform_name = "(unknown platform name)";

    if (provider) platform_name = provider->getName();

    panic("Unable to find driver for this platform: \"%s\".\n",
        platform_name);

    return false;
}
