/*
§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§

    (C) Copyright 1999
        All rights reserved.

§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§

  Portions of this software are:

    (C) Copyright 1995 TriplePoint, Inc. -- http://www.TriplePoint.com
        License to use this software is granted under the same terms 
        outlined in the Microsoft Windows Device Driver Development Kit.

    (C) Copyright 1992 Microsoft Corp. -- http://www.Microsoft.com
        License to use this software is granted under the terms outlined in
        the Microsoft Windows Device Driver Development Kit.

§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§

@doc INTERNAL Interrupt Interrupt_c

@module Interrupt.c |

    This module implements the Miniport interrupt processing routines and
    asynchronous processing routines.  
    
    The sample driver does not support physical hardware, so there is no need
    for the typical interrupt handler routines.  However, the driver does
    have an asynchronous event handler which is contained in this module.

@comm

    This module is very dependent on the hardware/firmware interface and should 
    be looked at whenever changes to these interfaces occur.

@head3 Contents |
@index class,mfunc,func,msg,mdata,struct,enum | Interupt_c

@end
§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§
*/

#define  __FILEID__             INTERRUPT_OBJECT_TYPE
// Unique file ID for error logging

#include "Miniport.h"                   // Defines all the miniport objects

#if defined(NDIS_LCODE)
#   pragma NDIS_LCODE   // Windows 9x wants this code locked down!
#   pragma NDIS_LDATA
#endif


/* @doc EXTERNAL INTERNAL Interupt Interupt_c MiniportCheckForHang
§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§

@func

    <f MiniportCheckForHang> reports the state of the network interface card.

@comm

    The NDIS library calls <f MiniportCheckForHang> once every two seconds to
    check the state of the network interface card.  If this function returns
    TRUE, the NDIS library then attempts to reset the NIC by calling
    <f MiniportReset>.  <f MiniportCheckForHang> should do nothing more than
    check the internal state of the NIC and return TRUE if it detects that
    the NIC is not operating correctly.

    Interrupts can be in any state when MiniportCheckForHang is called.

    <f Note>:
    If your hardware/firmware is flakey you can request that the NDIS
    wrapper call your MiniportReset routine by returning TRUE from this
    routine.  For well behaved hardware/firmware you should always return
    FALSE from this routine.

@rdesc

    <f MiniportCheckForHang> returns FALSE if the NIC is working properly.<nl>
    Otherwise, a TRUE return value indicates that the NIC needs to be reset.

*/

BOOLEAN MiniportCheckForHang(
    IN PMINIPORT_ADAPTER_OBJECT pAdapter                    // @parm
    // A pointer to the <t MINIPORT_ADAPTER_OBJECT> instance.
    )
{
    DBG_FUNC("MiniportCheckForHang")
    // If your hardware can lockup, then you can return TRUE here.
    // If you return TRUE, your MiniportReset routine will be called.
    return (FALSE);
}


#if defined(CARD_REQUEST_ISR)
#if (CARD_REQUEST_ISR == FALSE)

/* @doc EXTERNAL INTERNAL Interupt Interupt_c MiniportDisableInterrupt
§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§

@func

    <f MiniportDisableInterrupt> disables the NIC from generating interrupts.

@comm

    Typically, this function disables interrupts by writing a mask value
    specific to the network interface card.

    If the NIC does not support enabling and disabling interrupts, the
    miniport driver must register a miniport interrupt service routine with
    the NDIS library.  Within the interrupt service routine, the miniport
    driver must acknowledge and save the interrupt information.

    In some cases, the NIC must be in a certain state for
    <f MiniportDisableInterrupt> to execute correctly. In these cases, the
    miniport driver must encapsulate within a function all portions of the
    driver which violate the required state and which can be called when
    interrupts are enabled.  Then the miniport driver must call the
    encapsulated code through the NdisMSynchronizeWithInterrupt function.
    For example, on some network interface cards, the I/O ports are paged
    and must be set to page 0 for the deferred processing routine to run
    correctly.  With this kind of NIC, the DPC must be synchronized with
    interrupts.

    Interrupts can be in any state when <f MiniportDisableInterrupt> is
    called.

*/

void MiniportDisableInterrupt(
    IN PMINIPORT_ADAPTER_OBJECT pAdapter                    // @parm
    // A pointer to the <t MINIPORT_ADAPTER_OBJECT> instance.
    )
{
    DBG_FUNC("MiniportDisableInterrupt")
    DBG_ERROR(pAdapter,("This should not be called!\n"));
}


/* @doc EXTERNAL INTERNAL Interupt Interupt_c MiniportEnableInterrupt
§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§

@func

    <f MiniportEnableInterrupt> enables the NIC to generate interrupts.

@comm

    Typically, this function enables interrupts by writing a mask value
    specific to the network interface card.

    If the NIC does not support enabling and disabling interrupts, the
    miniport driver must register a miniport interrupt service routine with
    the NDIS library. Within the interrupt service routine, the miniport
    driver must acknowledge and save the interrupt information.

    Interrupts can be in any state when <f MiniportEnableInterrupt> is called.

*/

void MiniportEnableInterrupt(
    IN PMINIPORT_ADAPTER_OBJECT pAdapter                    // @parm
    // A pointer to the <t MINIPORT_ADAPTER_OBJECT> instance.
    )
{
    DBG_FUNC("MiniportEnableInterrupt")
    DBG_ERROR(pAdapter,("This should not be called!\n"));
}

#else // !(CARD_REQUEST_ISR == FALSE)

/* @doc EXTERNAL INTERNAL Interupt Interupt_c MiniportISR
§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§

@func

    <f MiniportISR> is the miniport driver's interrupt service routine. This
    function runs at a high priority in response to an interrupt. The driver
    should do as little work as possible in this function. It should set
    <p InterruptRecognized> to TRUE if it recognizes the interrupt as
    belonging to its network interface card, or FALSE otherwise. It should
    return FALSE as soon as possible if the interrupt is not generated by
    its NIC. It should set <f QueueMiniportHandleInterrupt> to TRUE if a
    call to <f MiniportHandleInterrupt> at a lower priority is required to
    complete the handling of the interrupt.

    <f Note>: <f MiniportISR> must not call any support functions in the NDIS
    interface library or the transport driver.

@comm

    <f MiniportISR> is called in the following cases:<nl>

    o   The NIC generates an interrupt when there is an outstanding call to
        <f MiniportInitialize>.

    o   The miniport driver supports sharing its interrupt line with another
        NIC.

    o   The miniport driver specifies that this function must be called for
        every interrupt.

    <f Note>: A deferred processing routine is not queued if the miniport
    driver is currently executing <f MiniportHalt> or <f MiniportInitialize>.

*/

void MiniportISR(
    OUT PBOOLEAN                InterruptRecognized,        // @parm
    // If the miniport driver is sharing an interrupt line and it detects
    // that the interrupt came from its NIC, <f MiniportISR> should set
    // this parameter to TRUE.

    OUT PBOOLEAN                QueueMiniportHandleInterrupt, // @parm
    // If the miniport driver is sharing an interrupt line and if
    // <f MiniportHandleInterrupt> must be called to complete handling of
    // the interrupt, <f MiniportISR> should set this parameter to TRUE.

    IN PMINIPORT_ADAPTER_OBJECT pAdapter                    // @parm
    // A pointer to the <t MINIPORT_ADAPTER_OBJECT> instance.
    )
{
    DBG_FUNC("MiniportISR")

    ULONG                       InterruptStatus;

    // TODO: Get the interrupt status from your card.
    if ((InterruptStatus = pAdapter->TODO) == 0)
    {
        *InterruptRecognized =
        *QueueMiniportHandleInterrupt = FALSE;
    }
    else
    {
        pAdapter->pCard->InterruptStatus = InterruptStatus;
        *InterruptRecognized =
        *QueueMiniportHandleInterrupt = TRUE;
    }
}

#endif // (CARD_REQUEST_ISR == FALSE)
#endif // defined(CARD_REQUEST_ISR)

/* @doc EXTERNAL INTERNAL Interupt Interupt_c MiniportHandleInterrupt
§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§

@func

    <f MiniportHandleInterrupt> is called by the deferred processing routine
    in the NDIS library to process an interrupt.

@comm

    During a call to <f MiniportHandleInterrupt>, the miniport driver should
    handle all outstanding interrupts and start any new operations.

    Interrupts are disabled during a call to <f MiniportHandleInterrupt>.

*/

void MiniportHandleInterrupt(
    IN PMINIPORT_ADAPTER_OBJECT pAdapter                    // @parm
    // A pointer to the <t MINIPORT_ADAPTER_OBJECT> instance.
    )
{
    DBG_FUNC("MiniportHandleInterrupt")

    PBCHANNEL_OBJECT            pBChannel;
    // A Pointer to one of our <t BCHANNEL_OBJECT>'s.

    ULONG                       BChannelIndex;
    // Index into the pBChannelArray.

    /*
    // Process NIC interrupt.
    */
    CardInterruptHandler(pAdapter->pCard);

    /*
    // Walk through all the links to see if there is any post-proccessing
    // that needs to be done.
    */
    for (BChannelIndex = 0; BChannelIndex < pAdapter->NumBChannels; ++BChannelIndex)
    {
        pBChannel = GET_BCHANNEL_FROM_INDEX(pAdapter, BChannelIndex);

        if (pBChannel->IsOpen)
        {
            /*
            // Indicate a receive complete if it's needed.
            */
            if (pBChannel->NeedReceiveCompleteIndication)
            {
                pBChannel->NeedReceiveCompleteIndication = FALSE;

                /*
                // Indicate receive complete to the NDIS wrapper.
                */
                DBG_RXC(pAdapter, pBChannel->ObjectID);
                NdisMCoReceiveComplete(pAdapter->MiniportAdapterHandle);
            }
        }
    }

    /*
    // Indicate a status complete if it's needed.
    */
    if (pAdapter->NeedStatusCompleteIndication)
    {
        pAdapter->NeedStatusCompleteIndication = FALSE;
        NdisMIndicateStatusComplete(pAdapter->MiniportAdapterHandle);
    }
}


/* @doc EXTERNAL INTERNAL Interupt Interupt_c MiniportTimer
§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§

@func

    <f MiniportTimer> is a required function if a Minipor's NIC does not
    generate interrupts.  Otherwise, one or more <f MiniportTimer> functions
    are optional.

    The driver of a NIC that does not generate interrupts must have a <f
    MiniportTimer> function to poll the state of the NIC. After such a
    Miniport's MiniportInitialize function sets up the driver-allocated timer
    object with NdisMInitializeTimer, a call to NdisMSetPeriodicTimer causes
    the <f MiniportTimer> function associated with the timer object to be run
    repeatedly and automatically at the interval specified by
    MillisecondsPeriod. Such a polling <f MiniportTimer> function monitors the
    state of the NIC to determine when to make indications, when to complete
    pending sends, and so forth. In effect, such a polling <f MiniportTimer>
    function has the same functionality as the MiniportHandleInterrupt
    function in the driver of a NIC that does generate interrupts.

    By contrast, calling NdisMSetTimer causes the <f MiniportTimer> function
    associated with the timer object to be run once when the given
    MillisecondsToDelay expires.  Such a <f MiniportTimer> function usually
    performs some driver-determined action if a particular operation times
    out.

    If either type of <f MiniportTimer> function shares resources with other
    driver functions, the driver should synchronize access to those resources
    with a spin lock.

    A Miniport can have more than one <f MiniportTimer> function at the
    discretion of the driver writer. Each such <f MiniportTimer> function must
    be associated with a driver-allocated and initialized timer object.

    A call to NdisMCancelTimer cancels execution of a nonpolling
    <f MiniportTimer> function, provided that the interval passed in the
    immediately preceding call to NdisMSetTimer has not yet expired. After a
    call to NdisMSetPeriodicTimer, a call to NdisMSetTimer or NdisMCancelTimer
    with the same timer object disables a polling <f MiniportTimer> function:
    either the <f MiniportTimer> function runs once, or it is canceled.

    The MiniportHalt function of any driver with a <f MiniportTimer> function
    should call NdisMCancelTimer to ensure that the <f MiniportTimer> function
    does not attempt to access resources that MiniportHalt has already
    released.

    By default, <f MiniportTimer> runs at IRQL DISPATCH_LEVEL.

*/

void MiniportTimer(
    IN PVOID                    SystemSpecific1,            // @parm
    // UNREFERENCED_PARAMETER

    IN PMINIPORT_ADAPTER_OBJECT pAdapter,                   // @parm
    // A pointer to the <t MINIPORT_ADAPTER_OBJECT> instance.

    IN PVOID                    SystemSpecific2,            // @parm
    // UNREFERENCED_PARAMETER

    IN PVOID                    SystemSpecific3             // @parm
    // UNREFERENCED_PARAMETER
    )
{
    DBG_FUNC("MiniportTimer")

    // DBG_ENTER(pAdapter);

    /*
    // If this is a nested callback, just return, and we'll loop back to
    // the DoItAgain before leaving the outermost callback.
    */
    if (++(pAdapter->NestedEventHandler) > 1)
    {
        DBG_WARNING(pAdapter,("NestedEventHandler=%d > 1\n",
                  pAdapter->NestedEventHandler));
        return;
    }

DoItAgain:
#if defined(SAMPLE_DRIVER)
    /*
    // This sample driver uses timer to simulate interrupts.
    */
    MiniportHandleInterrupt(pAdapter);
#else  // SAMPLE_DRIVER
    // TODO - Add code here to handle timer interrupt events.
#endif // SAMPLE_DRIVER

    /*
    // If we got a nested callback, we have to loop back around.
    */
    if (--(pAdapter->NestedEventHandler) > 0)
    {
        goto DoItAgain;
    }
    else if (pAdapter->NestedEventHandler < 0)
    {
        DBG_ERROR(pAdapter,("NestedEventHandler=%d < 0\n",
                  pAdapter->NestedEventHandler));
    }

    // DBG_LEAVE(pAdapter);

    UNREFERENCED_PARAMETER(SystemSpecific1);
    UNREFERENCED_PARAMETER(SystemSpecific2);
    UNREFERENCED_PARAMETER(SystemSpecific3);
}