/*++

Copyright (c) 2000 Microsoft Corporation

Module Name:

    dbgmon.c

Abstract:

    Functions that are specific to the debug monitor stack

Revision History:

    08/04/2000 davidx
        Created it.

--*/

#include "precomp.h"

//
// Debug monitor related global variables
//
PRIVATE IPRECEIVEPROC IpReceivePacketProc;
PRIVATE FREEPKTPROC FreePacketProc;
PRIVATE IfEnet *IfpEnetShared;

VOID
DmExFreePool(
    IN PVOID P
    );

//
// Pass the received frame up to the IP layer
//
VOID IPRECEIVEPACKET(Packet* pkt) {
    // If two stacks are present, we need to figure out
    // who's supposed to get this packet.
    if (IpReceivePacketProc) {
        IpHeader* iphdr;
        TcpHeader* tcphdr;
        UdpHeader* udphdr;
        UINT iphdrlen;

        // We assume the packet data length >= IPHDRLEN
        iphdr = GETPKTDATA(pkt, IpHeader);
        iphdrlen = GETIPHDRLEN(iphdr);
        tcphdr = (TcpHeader*) ((BYTE*) iphdr + iphdrlen);
        udphdr = (UdpHeader*) ((BYTE*) iphdr + iphdrlen);

        if (iphdrlen && 
            (iphdr->protocol == IPPROTOCOL_TCP &&
            iphdrlen+TCPHDRLEN <= pkt->datalen &&
            tcphdr->dstport == HTONS(DEBUGGER_PORT)) ||
            (iphdr->protocol == IPPROTOCOL_UDP &&
            iphdrlen+UDPHDRLEN <= pkt->datalen &&
            udphdr->dstport == HTONS(DEBUGGER_PORT))) {
            IpReceivePacket(pkt);
        } else {
            IpReceivePacketProc(pkt);
        }
    } else {
        IpReceivePacket(pkt);
    }
}


//
// Dispose of a packet after transmission
//
VOID COMPLETEPACKET(Packet* pkt, NTSTATUS status) {
    if (pkt->pktflags & PKTFLAG_DBGMON) {
        XnetCompletePacket(pkt, status);
    } else {
        // This packet came from the regular net stack.
        // We must use the callback function to dispose of it.
        if (pkt->completionCallback) {
            pkt->completionCallback(pkt, status);
        #ifdef DVTSNOOPBUG
        } else if (pkt->pktflags & PKTFLAG_UNCACHED) {
            XnetUncachedFreeProc(pkt);
        #endif
        } else if (!(pkt->pktflags & PKTFLAG_NETPOOL)) {
            // The packet memory came from the system pool
            DmExFreePool(pkt);
        } else if (FreePacketProc) {
            // The packet memory came from the private pool
            FreePacketProc(pkt);
        }
    }
}


//
// Cancel any pending transmissions that
// originated from the regular stack.
//
VOID DmCancelPendingPackets(PacketQueue* pktq) {
    PacketQueue tmpq;
    Packet* pkt;
    INT dropped = 0;
    
    PktQInit(&tmpq);
    while (!PktQIsEmpty(pktq)) {
        pkt = PktQRemoveHead(pktq);
        if (pkt->pktflags & PKTFLAG_DBGMON) {
            PktQInsertTail(&tmpq, pkt);
        } else {
            COMPLETEPACKET(pkt, NETERR_CANCELLED);
            dropped++;
        }
    }

    *pktq = tmpq;
    if (dropped) {
        WARNING_("Pending transmissions cancelled: %d", dropped);
    }
}


//
// Check to see if we should delete the enet interface
//
VOID IFENET_DELETE_CHECK(IfEnet* ifp) {
    ASSERT(ifp->refcount > 0);
    if (--ifp->refcount > 0) {
        //
        // Cancel any pending transmissions that
        // originated from the regular stack.
        //
        INT index;
        DmCancelPendingPackets(&ifp->sendq);
        NicWaitForXmitQEmpty(ifp);
        for (index=0; index < ARP_CACHE_SIZE; index++) {
            DmCancelPendingPackets(&ifp->arpCache[index].waitq);
        }

        IpReceivePacketProc = NULL;
        FreePacketProc = NULL;
    }
}


//
// Notify the debug monitor that regular netstack is starting up
//
PRIVATE IfEnet* EnetStartTitleStack(ENETINITPARAMS* initParams) {
    // NOTE: Wait until we acquired an address 
    // before starting the title stack.
    DhcpWaitForAddress((IfInfo*) IfpEnetShared);

    IpReceivePacketProc = initParams->IpReceiveProc;
    FreePacketProc = initParams->FreePktProc;
    initParams->UncachedAllocProc = XnetUncachedAllocProc;
    initParams->UncachedFreeProc = XnetUncachedFreeProc;
    IfpEnetShared->refcount++;
    return IfpEnetShared;
}



//
// Set the enet interface information in the process control block
//
VOID SET_DBGMON_ENETINFO(IfEnet* ifp) {
    IfpEnetShared = ifp;
    ASSERT(KeGetCurrentPrcb()->DmEnetFunc == 0);

    #pragma warning(disable:4054)
    KeGetCurrentPrcb()->DmEnetFunc = (VOID*) EnetStartTitleStack;
}


//
// Return the IP address of the Ethernet interface to the debugger
//
DWORD DmGetIpAddress(void) {
    return IfpEnetShared ? HTONL(IfpEnetShared->ipaddr) : 0;
}

