#include <IOKit/IOLib.h>
#include <IOKit/IODeviceTreeSupport.h>
#include <IOKit/platform/AppleMacIO.h>
extern "C" {
#include <pexpert/pexpert.h>
}
#include "AppleMediaBay.h"
#include "AppleMediaBayATANub.h"
#define super IOService
OSDefineMetaClassAndStructors(AppleMediaBay, IOService)
bool AppleMediaBay::init(OSDictionary *dict)
{
bool res = super::init(dict);
return res;
}
void AppleMediaBay::free(void)
{
PMstop();
if (commandGate)
commandGate->release();
if(intSource)
intSource->release();
if(workloop)
workloop->release();
if (configAddrMap)
configAddrMap->release();
super::free();
}
bool AppleMediaBay::start(IOService *provider)
{
OSData *compatibleEntry;
if (!super::start(provider))
return false;
mbControllerType = kMBControllerUndefined;
compatibleEntry = OSDynamicCast( OSData, provider->getProperty( "compatible" ) );
if ( compatibleEntry == 0 ) {
#ifdef APPLEMB_VERBOSE
IOLog("No compatible entry found.\n");
#endif //APPLEMB_VERBOSE
return false;
}
if ( compatibleEntry->isEqualTo( "keylargo-media-bay", sizeof("keylargo-media-bay")-1 ) == true ) {
#ifdef APPLEMB_VERBOSE
IOLog("Found KeyLargo compatible property.\n");
#endif // APPLEMB_VERBOSE
mbControllerType = kMBControllerKeyLargo;
myMacIO = waitForService(serviceMatching("KeyLargo"));
}
if ( compatibleEntry->isEqualTo( "heathrow-media-bay", sizeof("heathrow-media-bay")-1 ) == true ) {
#ifdef APPLEMB_VERBOSE
IOLog("Found Heathrow compatible property.\n");
#endif // APPLEMB_VERBOSE
mbControllerType = kMBControllerHeathrow;
myMacIO = OSDynamicCast(AppleMacIO, provider->getProvider());
}
if ( compatibleEntry->isEqualTo( "ohare-media-bay", sizeof("ohare-media-bay")-1 ) == true ) {
#ifdef APPLEMB_VERBOSE
IOLog("Found OHare compatible property.\n");
#endif // APPLEMB_VERBOSE
mbControllerType = kMBControllerOHare;
myMacIO = waitForService(serviceMatching("OHare"));
}
if( (configAddrMap = provider->mapDeviceMemoryWithIndex( 0 )))
{
configAddr = (volatile UInt32 *) configAddrMap->getVirtualAddress();
#ifdef APPLEMB_VERBOSE
IOLog("configAddr = 0x%08lx.\n",(unsigned int)configAddr);
#endif // APPLEMB_VERBOSE
}
else {
#ifdef APPLEMB_VERBOSE
IOLog("configAddrMap failed.\n");
#endif // APPLEMB_VERBOSE
return false;
}
if (myMacIO == NULL) {
#ifdef APPLEMB_VERBOSE
IOLog("myMacIO == NULL.\n");
#endif // APPLEMB_VERBOSE
return false;
}
workloop = IOWorkLoop::workLoop(); if(!workloop) {
#ifdef APPLEMB_VERBOSE
IOLog("Error creating workloop.\n");
#endif // APPLEMB_VERBOSE
return false;
}
intSource = IOInterruptEventSource::interruptEventSource
(this, (IOInterruptEventAction) &handleInterrupt,
provider);
if ((intSource == NULL) || (workloop->addEventSource(intSource) != kIOReturnSuccess)) {
#ifdef APPLEMB_VERBOSE
IOLog("Problem adding interrupt event source...\n");
#endif // APPLEMB_VERBOSE
return false;
}
else
workloop->enableAllInterrupts();
commandGate = IOCommandGate::commandGate(this, commandGateCaller);
if ((commandGate == NULL) ||
(workloop->addEventSource(commandGate) != kIOReturnSuccess))
{
#ifdef VERBOSE_LOGS_ON_PMU_INT
IOLog("Can not add a new IOCommandGate\n");
#endif // VERBOSE_LOGS_ON_PMU_INT
return false;
}
ioClient = NULL;
powerMediaBay = OSSymbol::withCString("powerMediaBay");
mbCurrentDevice = readMBID();
if (!initForPM(provider)) {
#ifdef APPLEMB_VERBOSE
IOLog("Error joining the power managment tree.\n");
#endif // APPLEMB_VERBOSE
return false;
}
registerService();
return true;
}
bool
AppleMediaBay::initForPM(IOService *provider)
{
PMinit(); provider->joinPMtree(this);
if (pm_vars == NULL)
return false;
#define number_of_power_states 2
static IOPMPowerState ourPowerStates[number_of_power_states] = {
{1,0,0,0,0,0,0,0,0,0,0,0},
{1,IOPMDeviceUsable,IOPMPowerOn,IOPMPowerOn,0,0,0,0,0,0,0,0}
};
registerPowerDriver(this, ourPowerStates, number_of_power_states);
return true;
}
IOReturn
AppleMediaBay::setPowerState(unsigned long powerStateOrdinal, IOService* whatDevice)
{
if (powerStateOrdinal == 1)
{
MediaBayDeviceType tmpCurrentDevice = readMBID();
if (mbCurrentDevice != tmpCurrentDevice) {
#ifdef APPLEMB_VERBOSE
IOLog("AppleMediaBay::setPowerState to %d 0x%04x 0x%04x\n", powerStateOrdinal, mbCurrentDevice, tmpCurrentDevice);
IOSleep(1000);
#endif // APPLEMB_VERBOSE
intSource->disable();
if (tmpCurrentDevice == deviceNone) {
mbPreviousDevice = mbCurrentDevice;
mbCurrentDevice = deviceNone;
handleDeviceEjection();
}
dispatchInterrupt();
intSource->enable();
}
else if (mbCurrentDevice != deviceNone) {
setMediaBayDevicePower(true, mbCurrentDevice);
}
}
else {
setMediaBayDevicePower(false, deviceNone);
}
return IOPMAckImplied;
}
IOReturn
AppleMediaBay::registerMediaNub(AppleMacIODevice *registerMe)
{
IOReturn returnValue = kIOReturnError;
if (commandGate != NULL)
returnValue = commandGate->runCommand((void*)registerNub , registerMe);
return returnValue;
}
IOReturn
AppleMediaBay::deRegisterMediaNub(AppleMacIODevice *deRegisterMe)
{
IOReturn returnValue = kIOReturnError;
if (commandGate != NULL)
returnValue = commandGate->runCommand((void*)deRegisterNub , deRegisterMe);
return returnValue;
}
bool
AppleMediaBay::registerMBClient(AppleMediaBayATANub *registerMe)
{
if (ioClient == NULL) {
ioClient = registerMe;
#ifdef APPLEMB_VERBOSE
IOLog("Registering %s.\n", registerMe->getName());
#endif // APPLEMB_VERBOSE
mbCurrentDevice = readMBID();
if (mbCurrentDevice == deviceNone)
handleDeviceEjection();
else
handleDeviceInsertion();
return true;
}
#ifdef APPLEMB_VERBOSE
IOLog("Attempting to register a client twice.\n");
#endif // APPLEMB_VERBOSE
return false;
}
bool
AppleMediaBay::deRegisterMBClient(AppleMediaBayATANub *deRegisterMe)
{
if (ioClient == deRegisterMe) {
ioClient = NULL;
#ifdef APPLEMB_VERBOSE
IOLog("De-registering %s.\n", deRegisterMe->getName());
#endif // APPLEMB_VERBOSE
return true;
}
#ifdef APPLEMB_VERBOSE
IOLog("Attempting to de-register an unregistered client.\n");
#endif // APPLEMB_VERBOSE
return false;
}
bool
AppleMediaBay::isFloppyOn(void)
{
return ((mbCurrentDevice == deviceAutoFloppy) || (mbCurrentDevice == deviceManualFloppy));
}
bool
AppleMediaBay::isATAOn(void)
{
return (mbCurrentDevice == deviceATA);
}
bool
AppleMediaBay::isPCIOn(void)
{
return (mbCurrentDevice == devicePCI);
}
bool
AppleMediaBay::isSoundOn(void)
{
return (mbCurrentDevice == deviceSound);
}
bool
AppleMediaBay::isPowerOn(void)
{
return (mbCurrentDevice == devicePower);
}
IOReturn
AppleMediaBay::dispatchMBCommand(int commandID, void *arg1, void *arg2, void *arg3r)
{
IOReturn returnValue = kIOReturnBadArgument;
switch (commandID)
{
case registerNub:
{
AppleMediaBayATANub *myDevice = OSDynamicCast(AppleMediaBayATANub, (OSObject*)arg1);
if (myDevice != NULL)
returnValue = (registerMBClient(myDevice) ? kIOReturnSuccess : kIOReturnError);
}
break;
case deRegisterNub:
{
AppleMediaBayATANub *myDevice = OSDynamicCast(AppleMediaBayATANub, (OSObject*)arg1);
if (myDevice != NULL)
returnValue = (deRegisterMBClient(myDevice) ? kIOReturnSuccess : kIOReturnError);
}
break;
case powerOn:
break;
case powerOff:
break;
}
return returnValue;
}
void
AppleMediaBay::dispatchInterrupt()
{
MediaBayDeviceType tmpCurrentDevice;
mbPreviousDevice = mbCurrentDevice;
IOSleep(500);
tmpCurrentDevice = readMBID();
#ifdef APPLEMB_VERBOSE
IOLog("AppleMediaBay::dispatchInterrupt mbPreviousDevice = 0x%04x tmpCurrentDevice = 0x%04x\n",
mbPreviousDevice , tmpCurrentDevice);
#endif // APPLEMB_VERBOSE
if ((mbPreviousDevice == deviceNone) && (tmpCurrentDevice != deviceNone)) {
mbCurrentDevice = tmpCurrentDevice;
handleDeviceInsertion();
}
else if ((mbPreviousDevice != deviceNone) && (tmpCurrentDevice == deviceNone)) {
mbCurrentDevice = tmpCurrentDevice;
handleDeviceEjection();
}
else {
}
}
void
AppleMediaBay::handleDeviceInsertion()
{
#ifdef APPLEMB_VERBOSE
IOLog("AppleMediaBay::handleDeviceInsertion of device 0x%04x\n", mbCurrentDevice);
IOSleep(1000);
#endif // APPLEMB_VERBOSE
setMediaBayDevicePower(true, mbCurrentDevice);
if (ioClient != NULL)
ioClient->handleDeviceInsertion();
#ifdef APPLEMB_VERBOSE
else
IOLog("AppleMediaBay::handleDeviceInsertion missing client can't perform\n");
#endif // APPLEMB_VERBOSE
}
void
AppleMediaBay::handleDeviceEjection()
{
#ifdef APPLEMB_VERBOSE
IOLog("AppleMediaBay::handleDeviceEjection of device 0x%04x\n", mbPreviousDevice);
IOSleep(1000);
#endif // APPLEMB_VERBOSE
if (ioClient != NULL)
ioClient->handleDeviceEjection();
#ifdef APPLEMB_VERBOSE
else
IOLog("AppleMediaBay::handleDeviceEjection missing client can't perform\n");
#endif // APPLEMB_VERBOSE
setMediaBayDevicePower(false, mbPreviousDevice);
}
void
AppleMediaBay::setMediaBayDevicePower(bool powerUp, MediaBayDeviceType thisDevice)
{
if (powerUp) {
UInt8 busPower;
switch (thisDevice)
{
case deviceAutoFloppy:
case deviceManualFloppy:
busPower = floppyOn;
break;
case deviceSound:
busPower = mbSoundOn;
break;
case deviceATA:
busPower = ATAOn;
break;
case devicePCI:
busPower = PCIOn;
break;
default:
busPower = mbOff;
break;
}
myMacIO->callPlatformFunction(powerMediaBay, false, (void*)true, (void*)busPower, NULL, NULL);
if (mbControllerType == kMBControllerKeyLargo)
*configAddr &= ~(0x0F000000);
else
*configAddr &= ~(0x000F0000);
eieio();
IOSleep(500);
}
else {
myMacIO->callPlatformFunction(powerMediaBay, false, (void*)false, (void*)mbOff, NULL, NULL);
}
}
AppleMediaBay::MediaBayDeviceType
AppleMediaBay::readMBID()
{
UInt16 mediaBayID;
if (mbControllerType == kMBControllerKeyLargo)
mediaBayID = ((*configAddr) & mediaBayIDMask_KeyLargo ) >> 28;
else
mediaBayID = ((*configAddr) & mediaBayIDMask ) >> 20;
return (MediaBayDeviceType)mediaBayID;
}
void
AppleMediaBay::handleInterrupt(OSObject *owner, IOInterruptEventSource *src, int count)
{
AppleMediaBay *myThis = OSDynamicCast(AppleMediaBay, owner);
#ifdef APPLEMB_VERBOSE
IOLog("AppleMediaBay::handleInterrupt(0x%08lx, 0x%08lx, %d)\n", owner, owner, count);
IOSleep(1000);
#endif // APPLEMB_VERBOSE
if (myThis != NULL)
myThis->dispatchInterrupt();
}
IOReturn
AppleMediaBay::commandGateCaller(OSObject *owner, void *arg0, void *arg1, void *arg2, void *arg3)
{
IOReturn retu = kIOReturnError;
AppleMediaBay *myThis = OSDynamicCast(AppleMediaBay, owner);
if (myThis != NULL)
retu = myThis->dispatchMBCommand((int)arg0, arg1, arg2, arg3);
return retu;
}