/*
 * Copyright (c) 2001 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * Copyright (c) 1999-2003 Apple Computer, Inc.  All Rights Reserved.
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The 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, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */

#include "ApplePMUPCCardEject.h"

#ifdef PCMCIA_DEBUG
#define DEBUG(args...)	IOLog(args)
#else
#define DEBUG(args...)
#endif

#define super IOPCCardEjectController
OSDefineMetaClassAndStructors(ApplePMUPCCardEject, IOPCCardEjectController)

#define kPMUpcmcia	0x04   		// pcmcia (buttons and timeout-eject)

bool
ApplePMUPCCardEject::start(IOService * provider)
{
    if (!super::start(provider)) return false;
	
    bridge = OSDynamicCast(IOPCIDevice, provider);
    if (!bridge) return false;

    // mac os 9 uses the interrupt pin reg in config space to determine which socket
    // the card is really in.  since we only run on the newer machinces, we can just
    // look it up in the device tree.

    OSData * socketData = OSDynamicCast(OSData, bridge->getProperty("AAPL,pmu-socket-number"));
    if (!socketData) return false;
    
    pmuSocket = *(UInt32 *)socketData->getBytesNoCopy();
    if (!pmuSocket) return false;

    // Wait for the PMU to show up:
    pmuDriver = waitForService(serviceMatching("ApplePMU"));
    if (!pmuDriver) return false;

    // Register for the eject button interrupts and card ejection timeout
    if (pmuDriver->callPlatformFunction("registerForPMUInterrupts", true, (void*)kPMUpcmcia,
					(void*)handleInterrupt, (void*)this, NULL) != kIOReturnSuccess) {
	return false;
    }

    DEBUG("ApplePMUPCCardEject::start for pmu socket %d was successful\n", pmuSocket);
    
    return true;
}

void
ApplePMUPCCardEject::stop(IOService * provider)
{
    DEBUG("ApplePMUPCCardEject::stop, pmu socket %d\n", pmuSocket);

    if (pmuDriver) {
        pmuDriver->callPlatformFunction("deRegisterClient", true, (void*)this, (void*)kPMUpcmcia, NULL, NULL);
    }
    
    super::stop(provider);
}

//========================================================================================================
//========================================================================================================
//========================================================================================================

// We need this to callPlatformFunction when sending to sendMiscCommand
typedef struct SendMiscCommandParameterBlock {
    int 	command;
    IOByteCount sLength;
    UInt8 *	sBuffer;
    IOByteCount *rLength;
    UInt8 * 	rBuffer;
} SendMiscCommandParameterBlock;
typedef SendMiscCommandParameterBlock *SendMiscCommandParameterBlockPtr;

IOReturn
ApplePMUPCCardEject::localSendMiscCommand(int command, IOByteCount sLength, UInt8 *sBuffer)
{
    if (!pmuDriver) return kIOReturnError;

    SendMiscCommandParameterBlock prmBlock = {command, sLength, sBuffer, 0, 0};

    return pmuDriver->callPlatformFunction("sendMiscCommand", true, (void*)&prmBlock, NULL, NULL, NULL);   
}

#define kPMUDoPCMCIAEject 0x4C        	// eject PCMCIA card(s)

IOReturn
ApplePMUPCCardEject::ejectCard()
{
    DEBUG("ApplePMUPCCardEject::ejectCard sending command\n");

    IOReturn rc = localSendMiscCommand(kPMUDoPCMCIAEject, 1, &pmuSocket);
    if (rc != kIOReturnSuccess) return rc;

    return super::ejectCard();
}

//========================================================================================================
//========================================================================================================
//========================================================================================================

//	
//	Handles interrupts generated by the PMGR micro for users pushing the card eject
//	buttons, and for eject timeouts after an eject operation has been initiated.
//
//	The interrupt data is contained in a buffer passed to this interrupt handler.
//	The buffer contains the following info:
//
//	byte 0:	flags for all PMGR interrupt sources
//		 1:	bit 0=1:	button interrupt
//			    1=1:	timeout interrupt
//		 2:	bit field for sockets, i.e., if bit 0=1, socket 0 is affected, etc.

#define	kEjectRequestPMgrOp		1
#define	kEjectTimeoutPMgrOp		2
#define	kEjectInterruptTypeMask	(kEjectRequestPMgrOp | kEjectTimeoutPMgrOp)

/* static */ void
ApplePMUPCCardEject::handleInterrupt(IOService *client, UInt8 interruptMask, UInt32 length, UInt8 *buffer)
{
    // Check if we are the right client for this interrupt
    if (interruptMask != kPMUpcmcia) return;

    ApplePMUPCCardEject *myThis = OSDynamicCast(ApplePMUPCCardEject, client);
    if (!myThis) return;

    // check to see if this is our socket
    if (!buffer || (length < 2)) return;
    if (buffer[1] != myThis->pmuSocket) return;

    DEBUG("ApplePMUPCCardEject::handleInterrupt mask = 0x%x, pmu socket = %d\n", interruptMask, myThis->pmuSocket);
    DEBUG("ApplePMUPCCardEject::handleInterrupt length = %d, buffer = %x %x\n", (int)length, buffer[0], buffer[1]);
    
    switch (buffer[0] & kEjectInterruptTypeMask) {

    case kEjectRequestPMgrOp:
	DEBUG("ApplePMUPCCardEject::handleInterrupt EJECTION_REQUEST\n");
	(void)myThis->requestCardEjection();

	break;

    case kEjectTimeoutPMgrOp:
	DEBUG("ApplePMUPCCardEject::handleInterrupt EJECTION_FAILED\n");

	break;

    default:
	DEBUG("ApplePMUPCCardEject::handleInterrupt command completed cmd = 0x%x\n", buffer[0]);

	break;
    }
}

