#include "irsir.h"

#pragma alloc_text(PAGE,InitializePacketQueue)



VOID
InitializePacketQueue(
    PPACKET_QUEUE    PacketQueue,
    PVOID            Context,
    PACKET_STARTER   StarterRoutine
    )

{

    NdisZeroMemory(PacketQueue,sizeof(*PacketQueue));

    NdisAllocateSpinLock(&PacketQueue->Lock);

    PacketQueue->Context=Context;

    PacketQueue->Starter=StarterRoutine;

    PacketQueue->Active=TRUE;

    KeInitializeEvent(&PacketQueue->InactiveEvent,NotificationEvent,FALSE);

    return;

}

VOID
QueuePacket(
    PPACKET_QUEUE    PacketQueue,
    PNDIS_PACKET     Packet
    )

{

    NDIS_STATUS      Status;
    PPACKET_RESERVED_BLOCK   Reserved=(PPACKET_RESERVED_BLOCK)&Packet->MiniportReservedEx[0];

    NdisAcquireSpinLock(&PacketQueue->Lock);

    if ((PacketQueue->CurrentPacket == NULL) && PacketQueue->Active && (PacketQueue->HeadOfList == NULL)) {
        //
        //  not currently handling a packet and the queu is active and there are not other packets
        //  queued, so handle it now
        //

        PacketQueue->CurrentPacket=Packet;

        NdisReleaseSpinLock(&PacketQueue->Lock);

        (*PacketQueue->Starter)(
            PacketQueue->Context,
            Packet
            );

        return;

    }

    //
    //  need to queue the packet
    //
    Reserved->Next=NULL;

    if (PacketQueue->HeadOfList == NULL) {
        //
        //  the list is empty
        //
        PacketQueue->HeadOfList=Packet;

    } else {

        Reserved=(PPACKET_RESERVED_BLOCK)&PacketQueue->TailOfList->MiniportReservedEx[0];

        Reserved->Next=Packet;
    }

    PacketQueue->TailOfList=Packet;

    NdisReleaseSpinLock(&PacketQueue->Lock);

    return;

}


VOID
StartNextPacket(
    PPACKET_QUEUE    PacketQueue
    )

{

    NdisAcquireSpinLock(&PacketQueue->Lock);

    ASSERT(PacketQueue->CurrentPacket != NULL);

    //
    //  done with this one
    //
    PacketQueue->CurrentPacket=NULL;

    if (!PacketQueue->InStartNext) {
        //
        //  not already in this function
        //
        PacketQueue->InStartNext=TRUE;

        while ((PacketQueue->CurrentPacket == NULL) && PacketQueue->Active && (PacketQueue->HeadOfList != NULL)) {
            //
            //  there is a packet queued
            //
            PNDIS_PACKET             Packet;
            PPACKET_RESERVED_BLOCK   Reserved;

            //
            //  get the first packet on the list
            //
            Packet=PacketQueue->HeadOfList;

            //
            //  Get a pointer to miniport reserved area
            //
            Reserved=(PPACKET_RESERVED_BLOCK)&Packet->MiniportReservedEx[0];

            //
            //  move to the next one in the list
            //
            PacketQueue->HeadOfList=Reserved->Next;

#if DBG
            Reserved->Next=NULL;

            if (PacketQueue->HeadOfList == NULL) {

                PacketQueue->TailOfList=NULL;
            }
#endif
            //
            //  now the current one
            //
            PacketQueue->CurrentPacket=Packet;

            NdisReleaseSpinLock(&PacketQueue->Lock);

            //
            //  start the processing
            //
            (*PacketQueue->Starter)(
                PacketQueue->Context,
                Packet
                );

            NdisAcquireSpinLock(&PacketQueue->Lock);

        }

        if (!PacketQueue->Active && (PacketQueue->CurrentPacket == NULL)) {
            //
            //  the queue has been paused and we don't have a current packet, signal the event
            //
            KeSetEvent(
                &PacketQueue->InactiveEvent,
                IO_NO_INCREMENT,
                FALSE
                );
        }

        PacketQueue->InStartNext=FALSE;
    }

    NdisReleaseSpinLock(&PacketQueue->Lock);

    return;

}

VOID
PausePacketProcessing(
    PPACKET_QUEUE    PacketQueue,
    BOOLEAN          WaitForInactive
    )

{

    BOOLEAN   CurrentlyActive=FALSE;

    NdisAcquireSpinLock(&PacketQueue->Lock);

    PacketQueue->Active=FALSE;

    if (PacketQueue->CurrentPacket != NULL) {
        //
        //  there is a packet currently being processed
        //
        CurrentlyActive=TRUE;

        KeClearEvent(&PacketQueue->InactiveEvent);

    }

    NdisReleaseSpinLock(&PacketQueue->Lock);

    if (WaitForInactive  && CurrentlyActive) {
        //
        //  the caller wants use to wait for the queue to inactive, and it was active when
        //  theis was called
        //
        KeWaitForSingleObject(
            &PacketQueue->InactiveEvent,
            Executive,
            KernelMode,
            FALSE,
            NULL
            );

    }

    return;

}

VOID
ActivatePacketProcessing(
    PPACKET_QUEUE    PacketQueue
    )

{

    NdisAcquireSpinLock(&PacketQueue->Lock);

    PacketQueue->Active=TRUE;

    if ((PacketQueue->CurrentPacket == NULL) && (PacketQueue->HeadOfList != NULL)) {
        //
        //  there is a packet queued
        //
        PNDIS_PACKET    Packet;
        PPACKET_RESERVED_BLOCK   Reserved;

        Packet=PacketQueue->HeadOfList;

        //
        //  get a pointer to the reserved area
        //
        Reserved=(PPACKET_RESERVED_BLOCK)&Packet->MiniportReservedEx[0];

        PacketQueue->HeadOfList=Reserved->Next;

        //
        //  now the current one
        //
        PacketQueue->CurrentPacket=Packet;

        NdisReleaseSpinLock(&PacketQueue->Lock);

        //
        //  start the processing
        //
        (*PacketQueue->Starter)(
            PacketQueue->Context,
            Packet
            );

        NdisAcquireSpinLock(&PacketQueue->Lock);

    }


    NdisReleaseSpinLock(&PacketQueue->Lock);

    return;

}



VOID
FlushQueuedPackets(
    PPACKET_QUEUE    PacketQueue,
    NDIS_HANDLE      WrapperHandle
    )

{
    //
    //  dispose of all of the queue packets, don't touch the current one though
    //
    NdisAcquireSpinLock(&PacketQueue->Lock);

    while (PacketQueue->HeadOfList != NULL) {
        //
        //  there is a packet queued
        //
        PNDIS_PACKET    Packet;
        PPACKET_RESERVED_BLOCK   Reserved;

        Packet=PacketQueue->HeadOfList;

        Reserved=(PPACKET_RESERVED_BLOCK)&Packet->MiniportReservedEx[0];

        PacketQueue->HeadOfList=Reserved->Next;


        NdisReleaseSpinLock(&PacketQueue->Lock);

        //
        //  start the processing
        //
        NdisMSendComplete(
            WrapperHandle,
            Packet,
            NDIS_STATUS_REQUEST_ABORTED
            );

        NdisAcquireSpinLock(&PacketQueue->Lock);

    }

    NdisReleaseSpinLock(&PacketQueue->Lock);

    return;
}