/*
 * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_OSREFERENCE_HEADER_START@
 * 
 * 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.  The rights granted to you under the 
 * License may not be used to create, or enable the creation or 
 * redistribution of, unlawful or unlicensed copies of an Apple operating 
 * system, or to circumvent, violate, or enable the circumvention or 
 * violation of, any terms of an Apple operating system software license 
 * agreement.
 *
 * 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_OSREFERENCE_HEADER_END@
 */
/*
Copyright (c) 1998 Apple Computer, Inc.  All rights reserved.

HISTORY
    1998-7-13	Godfrey van der Linden(gvdl)
        Created.
*/
#include <IOKit/IOInterruptEventSource.h>
#include <IOKit/IOLib.h>
#include <IOKit/IOService.h>
#include <IOKit/IOInterrupts.h>
#include <IOKit/IOTimeStamp.h>
#include <IOKit/IOWorkLoop.h>

#if KDEBUG

#define IOTimeTypeStampS(t)						\
do {									\
    IOTimeStampStart(IODBG_INTES(t),					\
                     (unsigned int) this, (unsigned int) owner);	\
} while(0)

#define IOTimeTypeStampE(t)						\
do {									\
    IOTimeStampEnd(IODBG_INTES(t),					\
                   (unsigned int) this, (unsigned int) owner);		\
} while(0)

#define IOTimeStampLatency()						\
do {									\
    IOTimeStampEnd(IODBG_INTES(IOINTES_LAT),				\
                   (unsigned int) this, (unsigned int) owner);		\
} while(0)

#else /* !KDEBUG */
#define IOTimeTypeStampS(t)
#define IOTimeTypeStampE(t)
#define IOTimeStampLatency()
#endif /* KDEBUG */

#define super IOEventSource

OSDefineMetaClassAndStructors(IOInterruptEventSource, IOEventSource)
OSMetaClassDefineReservedUnused(IOInterruptEventSource, 0);
OSMetaClassDefineReservedUnused(IOInterruptEventSource, 1);
OSMetaClassDefineReservedUnused(IOInterruptEventSource, 2);
OSMetaClassDefineReservedUnused(IOInterruptEventSource, 3);
OSMetaClassDefineReservedUnused(IOInterruptEventSource, 4);
OSMetaClassDefineReservedUnused(IOInterruptEventSource, 5);
OSMetaClassDefineReservedUnused(IOInterruptEventSource, 6);
OSMetaClassDefineReservedUnused(IOInterruptEventSource, 7);

bool IOInterruptEventSource::init(OSObject *inOwner,
				  Action inAction,
				  IOService *inProvider,
				  int inIntIndex)
{
    bool res = true;

    if ( !super::init(inOwner, (IOEventSourceAction) inAction) )
        return false;

    provider = inProvider;
    producerCount = consumerCount = 0;
    autoDisable = explicitDisable = false;
    intIndex = -1;

    // Assumes inOwner holds a reference(retain) on the provider
    if (inProvider) {
        int intType;

        res = (kIOReturnSuccess
                    == inProvider->getInterruptType(inIntIndex, &intType));
        if (res) {
            IOInterruptAction intHandler;

            autoDisable = (intType == kIOInterruptTypeLevel);
            if (autoDisable) {
                intHandler = OSMemberFunctionCast(IOInterruptAction,
		    this, &IOInterruptEventSource::disableInterruptOccurred);
            }
            else
                intHandler = OSMemberFunctionCast(IOInterruptAction,
		    this, &IOInterruptEventSource::normalInterruptOccurred);

            res = (kIOReturnSuccess == inProvider->registerInterrupt
                                        (inIntIndex, this, intHandler));
            if (res)
                intIndex = inIntIndex;
        }
    }

    return res;
}

IOInterruptEventSource *
IOInterruptEventSource::interruptEventSource(OSObject *inOwner,
					     Action inAction,
					     IOService *inProvider,
					     int inIntIndex)
{
    IOInterruptEventSource *me = new IOInterruptEventSource;

    if (me && !me->init(inOwner, inAction, inProvider, inIntIndex)) {
        me->release();
        return 0;
    }

    return me;
}

void IOInterruptEventSource::free()
{
    if (provider && intIndex != -1)
        provider->unregisterInterrupt(intIndex);

    super::free();
}

void IOInterruptEventSource::enable()
{
    if (provider && intIndex != -1) {
        provider->enableInterrupt(intIndex);
        explicitDisable = false;
    }
}

void IOInterruptEventSource::disable()
{
    if (provider && intIndex != -1) {
        provider->disableInterrupt(intIndex);
        explicitDisable = true;
    }
}

const IOService *IOInterruptEventSource::getProvider() const
{
    return provider;
}

int IOInterruptEventSource::getIntIndex() const
{
    return intIndex;
}

bool IOInterruptEventSource::getAutoDisable() const
{
    return autoDisable;
}

bool IOInterruptEventSource::checkForWork()
{
    unsigned int cacheProdCount = producerCount;
    int numInts = cacheProdCount - consumerCount;
    IOInterruptEventAction intAction = (IOInterruptEventAction) action;

    if (numInts > 0) {

        IOTimeStampLatency();
        IOTimeTypeStampS(IOINTES_CLIENT);
            IOTimeStampConstant(IODBG_INTES(IOINTES_ACTION),
                                (unsigned int) intAction, (unsigned int) owner);
            (*intAction)(owner, this,  numInts);
        IOTimeTypeStampE(IOINTES_CLIENT);

        consumerCount = cacheProdCount;
        if (autoDisable && !explicitDisable)
            enable();
    }
    else if (numInts < 0) {
        IOTimeStampLatency();
        IOTimeTypeStampS(IOINTES_CLIENT);
            IOTimeStampConstant(IODBG_INTES(IOINTES_ACTION),
                                (unsigned int) intAction, (unsigned int) owner);
             (*intAction)(owner, this, -numInts);
        IOTimeTypeStampE(IOINTES_CLIENT);
    
        consumerCount = cacheProdCount;
        if (autoDisable && !explicitDisable)
            enable();
    }

    return false;
}

void IOInterruptEventSource::normalInterruptOccurred
    (void */*refcon*/, IOService */*prov*/, int /*source*/)
{
IOTimeTypeStampS(IOINTES_INTCTXT);
IOTimeStampLatency();

    producerCount++;

IOTimeTypeStampS(IOINTES_SEMA);
    signalWorkAvailable();
IOTimeTypeStampE(IOINTES_SEMA);

IOTimeTypeStampE(IOINTES_INTCTXT);
}

void IOInterruptEventSource::disableInterruptOccurred
    (void */*refcon*/, IOService *prov, int source)
{
IOTimeTypeStampS(IOINTES_INTCTXT);
IOTimeStampLatency();

    prov->disableInterrupt(source);	/* disable the interrupt */

    producerCount++;

IOTimeTypeStampS(IOINTES_SEMA);
    signalWorkAvailable();
IOTimeTypeStampE(IOINTES_SEMA);

IOTimeTypeStampE(IOINTES_INTCTXT);
}

void IOInterruptEventSource::interruptOccurred
    (void *refcon, IOService *prov, int source)
{
    if (autoDisable && prov)
        disableInterruptOccurred(refcon, prov, source);
    else
        normalInterruptOccurred(refcon, prov, source);
}
