/*++

Copyright (c) 1996-1999  Microsoft Corporation

Module Name:

    timestmp.c

Abstract:

    Timestamper module

Author:
    Shreedhar Madhavapeddi (shreem)

Revision History:

--*/

#include <timestmp.h>

//
// The following struct has to be in ssync with
// ndis\trfccntl\tools\qtcp\qtcp.c
//
typedef struct _LOG_RECORD{
    UINT64  TimeSent;
    UINT64  TimeReceived;
    UINT64  TimeSentWire;         // These fields are used by the kernel timestamper
    UINT64  TimeReceivedWire;     // These fields are used by the kernel timestamper
    UINT64  Latency;
    INT     BufferSize;
    INT     SequenceNumber;
} LOG_RECORD, *PLOG_RECORD;

ULONG           GlobalSequenceNumber = 0;        

// 321618 needs checking for PSCHED's existence.
NDIS_STRING     PschedDriverName           = NDIS_STRING_CONST("\\Device\\PSched");
HANDLE          PschedHandle;
NTSTATUS CheckForPsched(VOID);

	
//
// TCP Headers (redefined here, since there are no exported headers
//
#define IP_OFFSET_MASK          ~0x00E0         // Mask for extracting offset field.
#define net_short(x) ((((x)&0xff) << 8) | (((x)&0xff00) >> 8))

/*
 * Protocols (from winsock.h)
 */
#define IPPROTO_IP              0               /* dummy for IP */
#define IPPROTO_ICMP            1               /* control message protocol */
#define IPPROTO_IGMP            2               /* group management protocol */
#define IPPROTO_GGP             3               /* gateway^2 (deprecated) */
#define IPPROTO_TCP             6               /* tcp */
#define IPPROTO_PUP             12              /* pup */
#define IPPROTO_UDP             17              /* user datagram protocol */
#define IPPROTO_IDP             22              /* xns idp */
#define IPPROTO_ND              77              /* UNOFFICIAL net disk proto */
#define IPPROTO_IPSEC                   51              /* ???????? */

#define IPPROTO_RAW             255             /* raw IP packet */
#define IPPROTO_MAX             256

#define IP_MF_FLAG                          0x0020              // 'More fragments flag'
#define IP_VERSION                      0x40
#define IP_VER_FLAG                     0xF0


#define TCP_OFFSET_MASK 0xf0
#define TCP_HDR_SIZE(t) (uint)(((*(uchar *)&(t)->tcp_flags) & TCP_OFFSET_MASK) >> 2)

typedef int             SeqNum;                         // A sequence number.


struct TCPHeader {
        ushort                          tcp_src;                        // Source port.
        ushort                          tcp_dest;                       // Destination port.
        SeqNum                          tcp_seq;                        // Sequence number.
        SeqNum                          tcp_ack;                        // Ack number.
        ushort                          tcp_flags;                      // Flags and data offset.
        ushort                          tcp_window;                     // Window offered.
        ushort                          tcp_xsum;                       // Checksum.
        ushort                          tcp_urgent;                     // Urgent pointer.
};

typedef struct TCPHeader TCPHeader;

struct UDPHeader {
        ushort          uh_src;                         // Source port.
        ushort          uh_dest;                        // Destination port.
        ushort          uh_length;                      // Length
        ushort          uh_xsum;                        // Checksum.
}; /* UDPHeader */

typedef struct UDPHeader UDPHeader;

#ifdef DBG
//
// Define the Trace Level.
//
#define TS_DBG_DEATH               1
#define TS_DBG_TRACE               2

//
// Masks
//
#define TS_DBG_PIPE      0x00000001
#define TS_DBG_FLOW      0x00000002
#define TS_DBG_SEND      0x00000004
#define TS_DBG_RECV      0x00000008
#define TS_DBG_INIT      0x00000010
#define TS_DBG_OID       0x00000020
#define TS_DBG_CLASS_MAP 0x00000040

ULONG DbgTraceLevel = 1;
ULONG DbgTraceMask  = 0x8;

#define TimeStmpTrace(_DebugLevel, _DebugMask, _Out) \
    if ((DbgTraceLevel >= _DebugLevel) &&           \
        ((_DebugMask) & DbgTraceMask)){             \
        DbgPrint("TimeStamp: ");                       \
        DbgPrint _Out;                              \
    }

#else // DBG
#define TimeStmpTrace
#endif

#define         PORT_RANGE  20
USHORT          IPIDList[PORT_RANGE];
NDIS_SPIN_LOCK  IPIDListLock;

#define         PORT_RANGE  20
USHORT          IPIDListRecv[PORT_RANGE];
NDIS_SPIN_LOCK  IPIDListLockRecv;

/*
Let's create a driver unload function, so that timestmp is stoppable via net sto
p timestmp
*/
VOID
TimeStmpUnload(
               IN PDRIVER_OBJECT DriverObject
               )
{

	IoctlCleanup();
    return;

}

NDIS_STATUS
TimeStmpInitializePipe (
    IN HANDLE PsPipeContext,
    IN PPS_PIPE_PARAMETERS PipeParameters,
    IN PPS_PIPE_CONTEXT ComponentPipeContext,
    IN PPS_PROCS PsProcs,
    IN PPS_UPCALLS Upcalls
    )
{
    PPS_PIPE_CONTEXT Pipe = ComponentPipeContext;

    TimeStmpTrace(TS_DBG_TRACE, TS_DBG_PIPE, ("[TimeStmpIndicatePipe]: \n"));
    return (*Pipe->NextComponent->InitializePipe)(
        PsPipeContext,
        PipeParameters,
        Pipe->NextComponentContext,
        PsProcs,
        Upcalls);
}

NDIS_STATUS
TimeStmpModifyPipe (
    IN PPS_PIPE_CONTEXT PipeContext,
    IN PPS_PIPE_PARAMETERS PipeParameters
    )
{
    PPS_PIPE_CONTEXT Pipe = PipeContext;
    TimeStmpTrace(TS_DBG_TRACE, TS_DBG_PIPE, ("[TimeStmpModifyPipe]: \n"));
    return (*Pipe->NextComponent->ModifyPipe)(
        Pipe->NextComponentContext, PipeParameters);
}

VOID
TimeStmpDeletePipe (
    IN PPS_PIPE_CONTEXT PipeContext
    )
{
    PPS_PIPE_CONTEXT Pipe = PipeContext;
    TimeStmpTrace(TS_DBG_TRACE, TS_DBG_PIPE, ("[TimeStmpDeletePipe]: \n"));
    (*Pipe->NextComponent->DeletePipe)(Pipe->NextComponentContext);
}


NDIS_STATUS
TimeStmpCreateFlow (
    IN PPS_PIPE_CONTEXT PipeContext,
    IN HANDLE PsFlowContext,
    IN PCO_CALL_PARAMETERS CallParameters,
    IN PPS_FLOW_CONTEXT ComponentFlowContext
    )
{
    PPS_PIPE_CONTEXT Pipe = PipeContext;

    TimeStmpTrace(TS_DBG_TRACE, TS_DBG_FLOW, ("[TimeStmpCreateFlow]: \n"));
    return (*Pipe->NextComponent->CreateFlow)(
                Pipe->NextComponentContext,
                PsFlowContext,
                CallParameters,
                ComponentFlowContext->NextComponentContext);
}


NDIS_STATUS
TimeStmpModifyFlow (
    IN PPS_PIPE_CONTEXT PipeContext,
    IN PPS_FLOW_CONTEXT FlowContext,
    IN PCO_CALL_PARAMETERS CallParameters
    )
{
    PPS_PIPE_CONTEXT Pipe = PipeContext;
    TimeStmpTrace(TS_DBG_TRACE, TS_DBG_FLOW, ("[TimeStmpModifyFlow]: \n"));
    return (*Pipe->NextComponent->ModifyFlow)(
                Pipe->NextComponentContext,
                FlowContext->NextComponentContext,
                CallParameters);
    
}


VOID
TimeStmpDeleteFlow (
    IN PPS_PIPE_CONTEXT PipeContext,
    IN PPS_FLOW_CONTEXT FlowContext
    )
{
    PPS_PIPE_CONTEXT Pipe = PipeContext;
    TimeStmpTrace(TS_DBG_TRACE, TS_DBG_FLOW, ("[TimeStmpDeleteFlow]: \n"));
    (*Pipe->NextComponent->DeleteFlow)(
        Pipe->NextComponentContext,
        FlowContext->NextComponentContext);
}


BOOLEAN
TimeStmpSubmitPacket (
    IN PPS_PIPE_CONTEXT PipeContext,
    IN PPS_FLOW_CONTEXT FlowContext,
    IN PPS_CLASS_MAP_CONTEXT ClassMapContext,
    IN PPACKET_INFO_BLOCK PacketInfo
    )
{
    PPS_PIPE_CONTEXT    Pipe = PipeContext;
    LARGE_INTEGER       CurrentTime;
    IPHeader UNALIGNED  *IPH    = NULL;
    TCPHeader UNALIGNED *TCPH   = NULL;
    UDPHeader UNALIGNED *UDPH   = NULL;
    PVOID               ArpH    = NULL, GeneralVA = NULL, Data = NULL;
    IPAddr              Src, Dst;
    PNDIS_BUFFER        ArpBuf = NULL, IpBuf = NULL, TcpBuf = NULL, DataBuf = NULL, UdpBuf = NULL;
    ULONG               ArpLen = 0, IpLen = 0, IpHdrLen = 0, TcpLen = 0, DataLen = 0, TotalLen = 0, TcpHeaderOffset = 0;
    ULONG               UdpLen = 0;
    USHORT              SrcPort = 0, DstPort = 0, IPID = 0, FragOffset = 0;
    PLIST_ENTRY         CurrentEntry = NULL, LastEntry = NULL;
    BOOLEAN             bFragment, bFirstFragment, bLastFragment;
    ULONG               i = 0;
    PLOG_RECORD         pRecord = NULL;
    PNDIS_PACKET        Packet = PacketInfo->NdisPacket;

    TimeStmpTrace(TS_DBG_TRACE, TS_DBG_SEND, ("[TimeStmpSubmitPacket]: \n"));
    
    //
    // Steps  
    // Parse the IP Packet. 
    // Look for the appropriate ports.
    // Look for the data portion and put in the Time & length there.
    //

    NdisGetFirstBufferFromPacket(
                                 Packet,
                                 &ArpBuf,
                                 &ArpH,
                                 &ArpLen,
                                 &TotalLen
                                 );

    //
    // We are guaranteed that the ARP buffer if always a different MDL, so
    // jump to the next MDL
    //
    NdisGetNextBuffer(ArpBuf, &IpBuf)

    if (IpBuf) {

        NdisQueryBuffer(IpBuf,
                        &GeneralVA,
                        &IpLen
                        );
        
        IPH = (IPHeader *) GeneralVA;
    
        if (!IPH) {
            goto FAILURE;
        }

        Src = net_short(IPH->iph_src);
        Dst = net_short(IPH->iph_dest);
        IPID = net_short(IPH->iph_id);
        //IpHdrLen = 8 * net_short(IPH->iph_length);
        IpHdrLen = ((IPH->iph_verlen & (uchar)~IP_VER_FLAG) << 2);
        
        FragOffset = IPH->iph_offset & IP_OFFSET_MASK;
        FragOffset = net_short(FragOffset) * 8;

        bFragment = (IPH->iph_offset & IP_MF_FLAG) || (FragOffset > 0);
        bFirstFragment = bFragment && (FragOffset == 0);
        bLastFragment = bFragment && (!(IPH->iph_offset & IP_MF_FLAG));

        if (bFragment && (!bFirstFragment)) {
            
            //
            // Its a fragment alright and NOT the first one.
            //
            NdisAcquireSpinLock(&IPIDListLock);

            for (i = 0; i < PORT_RANGE; i++) {
            
                //
                // Found the match...
                //
                if (IPIDList[i] == IPID) {
                
                    if (bLastFragment) {
                        //
                        // Since it is the last fragment, recall
                        // the IP ID.
                        //
                        IPIDList[i] = 0xffff;
                    }

                    NdisReleaseSpinLock(&IPIDListLock);
                    
                    //
                    // Is the data in the same buffer?
                    //
                    if (IpLen <= IpHdrLen) {
                        
                        NdisGetNextBuffer(IpBuf, &DataBuf);
            
                        if(DataBuf) {
            
                            NdisQueryBuffer(DataBuf,
                                            &Data,
                                            &DataLen
                                            );

                            goto TimeStamp;
        
                        } else {
        
                            goto FAILURE;
                        }


                    } else {

                        //
                        // The Data Offsets need to be primed now.
                        //
                        DataLen = IpLen - FragOffset;
                        Data    = ((PUCHAR) GeneralVA) + IpHdrLen; 
                        goto TimeStamp;
                    }
                }
            }

            NdisReleaseSpinLock(&IPIDListLock);
            //
            // If we are here, we dont care about this IPID for this fragment.
            // Just return TRUE to continue processing.
            //
            
            //
            // Ready to go.
            //
            PacketInfo->FlowContext = FlowContext;
            PacketInfo->ClassMapContext = ClassMapContext;

            return (*Pipe->NextComponent->SubmitPacket)(
                                                 Pipe->NextComponentContext,
                                                 FlowContext->NextComponentContext, 
                                                 ClassMapContext?ClassMapContext->NextComponentContext:0,
                                                 PacketInfo);

        }

        //
        // If it is not a fragment, depending upon the protocol, process differently
        //

        switch (IPH->iph_protocol) {
        
        case IPPROTO_TCP :
            
            TimeStmpTrace(TS_DBG_TRACE, TS_DBG_SEND, ("[TimeStmpSubmitPacket]: Procol TCP\n"));

            if (IPH && ((USHORT)IpLen > IpHdrLen)) {

                //
                // We have more than the IP Header in this MDL.
                //
                TCPH = (TCPHeader *) ((PUCHAR)GeneralVA + IpHdrLen);
                TcpLen = IpLen - IpHdrLen;
                TcpBuf = IpBuf;

            } else {
                
                //
                // TCP Header is in the next MDL
                //
                
                NdisGetNextBuffer(IpBuf, &TcpBuf);
    
                if(TcpBuf) {
    
                    GeneralVA = NULL;
                    NdisQueryBuffer(TcpBuf,
                                    &GeneralVA,
                                    &TcpLen
                                    );
                
                    TCPH = (TCPHeader *) GeneralVA;
                } else {

                    goto FAILURE;

                }
            }

            //
            // Get the port numbers out.
            //
            SrcPort = net_short(TCPH->tcp_src);
            DstPort = net_short(TCPH->tcp_dest);

            //
            // We have the TCP Buffer now. Get to the DATA.
            //
            TcpHeaderOffset = TCP_HDR_SIZE(TCPH);

            if (TcpLen > TcpHeaderOffset) {

                //
                // We have the DATA right here!
                //

                Data = (PUCHAR)TCPH + TcpHeaderOffset;
                DataLen = TcpLen - TcpHeaderOffset;

            } else {
            
                NdisGetNextBuffer(TcpBuf, &DataBuf);
    
                if(DataBuf) {
    
                    GeneralVA = NULL;
                    NdisQueryBuffer(DataBuf,
                                    &Data,
                                    &DataLen
                                    );

                } else {

                    goto FAILURE;
                }
            }

            if (CheckInPortList(DstPort) && bFirstFragment) {

                NdisAcquireSpinLock(&IPIDListLock);
                
                // need new Entry for IPID
                for (i = 0; i < PORT_RANGE; i++) {
                    //
                    // Look for a free slot
                    //
                    if (0xffff == IPIDList[i]) {
                        
                        IPIDList[i] = IPID;
                        break;
                    
                    }


                }

                NdisReleaseSpinLock(&IPIDListLock);
                
                if (i == PORT_RANGE) {

                   TimeStmpTrace(TS_DBG_DEATH, TS_DBG_SEND, ("Couldn't find an empty IPID - Bailing \n"));
                   goto FAILURE;
                }
                //DbgBreakPoint();

            } 
            
            //
            // Let's timestmp this now.
            //
            if (CheckInPortList(DstPort)) {

                goto TimeStamp;

            } else {

                //
                // This is not one of our packet, get out.
                // 
                goto FAILURE;
            }

            break;

        case IPPROTO_UDP:
            
            TimeStmpTrace(TS_DBG_TRACE, TS_DBG_SEND, ("[TimeStmpSubmitPacket]: Protocol UDP\n"));

            if (IPH && (IpLen > IpHdrLen)) {

                //
                // We have more than the IP Header in this MDL.
                //
                UDPH = (UDPHeader *) ((PUCHAR)GeneralVA + IpHdrLen);
                UdpLen = IpLen - IpHdrLen;
                UdpBuf = IpBuf;

            } else {
                
                //
                // UDP Header is in the next MDL
                //
    
                NdisGetNextBuffer(IpBuf, &UdpBuf);

                if(UdpBuf) {

                    GeneralVA = NULL;
                    NdisQueryBuffer(UdpBuf,
                                    &GeneralVA,
                                    &UdpLen
                                    );
    
                    UDPH = (UDPHeader *) GeneralVA;
                } else {
                    
                    goto FAILURE;

                }
            }

            SrcPort = net_short(UDPH->uh_src);      // Source port.
            DstPort = net_short(UDPH->uh_dest);         // Destination port.


            //
            // Get to the data. 
            //
            if (UdpLen > sizeof (UDPHeader)) {

                //
                // We have the DATA right here!
                //
                Data = (PUCHAR) UDPH + sizeof (UDPHeader);
                DataLen = UdpLen - sizeof (UDPHeader);

            } else {

                NdisGetNextBuffer(UdpBuf, &DataBuf);

                if(DataBuf) {

                    GeneralVA = NULL;
                    NdisQueryBuffer(DataBuf,
                                    &Data,
                                    &DataLen
                                    );

                } else {

                    goto FAILURE;

                }
            }


            if (CheckInPortList(DstPort) && bFirstFragment) {

                NdisAcquireSpinLock(&IPIDListLock);
                
                // need new Entry for IPID
                for (i = 0; i < PORT_RANGE; i++) {
                    //
                    // Look for a free slot
                    //
                    if (0xffff == IPIDList[i]) {
                        
                        IPIDList[i] = IPID;
                        break;
                    
                    }

                    ASSERT(FALSE);

                }

                NdisReleaseSpinLock(&IPIDListLock);
                
                //
                // Couldnt find a free IPID place holder, lets bail.
                //
                if (PORT_RANGE == i) {

                    goto FAILURE;

                }

            } 
            
            
            
            //
            // Let's timestmp this now.
            //
            if (CheckInPortList(DstPort)) {

                goto TimeStamp;

            } else {

                //
                // This is not one of our packet, get out.
                // 
                goto FAILURE;
            }

            break;

        case IPPROTO_RAW:
            
            TimeStmpTrace(TS_DBG_TRACE, TS_DBG_SEND, ("[TimeStmpSubmitPacket]: Protocol RAW\n"));
            goto FAILURE;

            break;
        
        case IPPROTO_IGMP:
            
            TimeStmpTrace(TS_DBG_TRACE, TS_DBG_SEND, ("[TimeStmpSubmitPacket]: Protocol IGMP\n"));
            goto FAILURE;

            break;
        
        case IPPROTO_ICMP:

            TimeStmpTrace(TS_DBG_TRACE, TS_DBG_SEND, ("[TimeStmpSubmitPacket]: Protocol TCMP\n"));
            goto FAILURE;

            break;

        default:
            
            //TimeStmpTrace(TS_DBG_DEATH, TS_DBG_SEND, ("[TimeStmpSubmitPacket]: Protocol - UNKNOWN (%d)\n", IPH->iph_protocol));
            goto FAILURE;

            //DbgBreakPoint();

        }

    } else {

        TimeStmpTrace(TS_DBG_TRACE, TS_DBG_SEND, ("[TimeStmpSubmitPacket]: NO Buffer beyond MAC Header\n"));
        goto FAILURE;

    }

TimeStamp:
    //
    // If we get here, the Data and DataLen variables have been primed.
    // Set the Time and Length.
    //
    if (Data) {
        
        pRecord = (PLOG_RECORD) Data;
        
        if (DataLen > sizeof (LOG_RECORD)) {
            
            LARGE_INTEGER   PerfFrequency;
            UINT64          Freq;

            //
            // Set the fields accordingly
            pRecord->BufferSize = DataLen;
            //pRecord->SequenceNumber = InterlockedIncrement(&GlobalSequenceNumber);
            CurrentTime = KeQueryPerformanceCounter(&PerfFrequency);

            //
            // Convert the perffrequency into 100ns interval.
            //
            Freq = 0;
            Freq |= PerfFrequency.HighPart;
            Freq = Freq << 32;
            Freq |= PerfFrequency.LowPart;


            //
            // Convert from LARGE_INTEGER to UINT64
            //
            pRecord->TimeSentWire = 0;
            pRecord->TimeSentWire |= CurrentTime.HighPart;
            pRecord->TimeSentWire = pRecord->TimeSentWire << 32;
            pRecord->TimeSentWire |= CurrentTime.LowPart;

            // Normalize cycles with the frequency.
            pRecord->TimeSentWire *= 10000000;
            pRecord->TimeSentWire /= Freq;

        }
    
    }
    //
    // Ready to go.
    //
    PacketInfo->FlowContext = FlowContext;
    PacketInfo->ClassMapContext = ClassMapContext;

    return (*Pipe->NextComponent->SubmitPacket)(
        Pipe->NextComponentContext,
        FlowContext->NextComponentContext, 
        ClassMapContext?ClassMapContext->NextComponentContext:0,
        PacketInfo);

FAILURE: 

    //
    // Ready to go.
    //
    PacketInfo->FlowContext = FlowContext;
    PacketInfo->ClassMapContext = ClassMapContext;

    return (*Pipe->NextComponent->SubmitPacket)(
        Pipe->NextComponentContext,
        FlowContext->NextComponentContext, 
        ClassMapContext?ClassMapContext->NextComponentContext:0,
        PacketInfo);

}


BOOLEAN
TimeStmpReceivePacket (
    IN PPS_PIPE_CONTEXT         PipeContext,
    IN PPS_FLOW_CONTEXT         FlowContext,
    IN PPS_CLASS_MAP_CONTEXT    ClassMapContext,
    IN PNDIS_PACKET             Packet,
    IN NDIS_MEDIUM              Medium
    )
{
    PPS_PIPE_CONTEXT    Pipe = PipeContext;
    LARGE_INTEGER       CurrentTime;
    IPHeader UNALIGNED  *IPH    = NULL;
    TCPHeader UNALIGNED *TCPH   = NULL;
    UDPHeader UNALIGNED *UDPH   = NULL;
    IPAddr              Src, Dst;
    PUCHAR              headerBuffer = NULL, pData = NULL;
    PNDIS_BUFFER        pFirstBuffer = NULL;
    ULONG               firstbufferLength = 0, bufferLength = 0, HeaderLength = 0;
    ULONG               TotalIpLen = 0, IPDataLength = 0, IpHdrLen = 0;
    ULONG               TotalTcpLen = 0, TcpDataLen = 0, TotalLen = 0, TcpHeaderOffset = 0, Size = 0;
    ULONG               TotalUdpLen = 0, UdpDataLen = 0, UdpHdrLen = 0;
    USHORT              SrcPort = 0, DstPort = 0, IPID = 0, FragOffset = 0;
    BOOLEAN             bFragment, bFirstFragment, bLastFragment;
    ULONG               i = 0;
    PLOG_RECORD         pRecord = NULL;
    UINT  HeaderBufferSize = NDIS_GET_PACKET_HEADER_SIZE(Packet);

    ushort          type;                       // Protocol type
    uint            ProtOffset;                 // Offset in Data to non-media info.

    TimeStmpTrace(TS_DBG_TRACE, TS_DBG_RECV, ("[TimeStmpReceivePacket]: \n"));

    NdisGetFirstBufferFromPacket(Packet,                // packet
                                 &pFirstBuffer,         // first buffer descriptor
                                 &headerBuffer,         // ptr to the start of packet
                                 &firstbufferLength,    // length of the header+lookahead
                                 &bufferLength);        // length of the bytes in the buffers

    IPH = (IPHeader *) ((PUCHAR)headerBuffer + HeaderBufferSize);
    
    // Check the header length and the version. If any of these
    // checks fail silently discard the packet.
    HeaderLength = ((IPH->iph_verlen & (uchar)~IP_VER_FLAG) << 2);


    if (HeaderLength >= sizeof(IPHeader) && HeaderLength <= bufferLength) {

        //
        // Get past the IP Header and get the rest of the stuff out.
        //
        TotalIpLen = (uint)net_short(IPH->iph_length);

        if ((IPH->iph_verlen & IP_VER_FLAG) == IP_VERSION &&
            TotalIpLen >= HeaderLength  && TotalIpLen <= bufferLength) {

            Src = net_short(IPH->iph_src);
            Dst = net_short(IPH->iph_dest);
            IPID = net_short(IPH->iph_id);

            FragOffset = IPH->iph_offset & IP_OFFSET_MASK;
            FragOffset = net_short(FragOffset) * 8;

            bFragment = (IPH->iph_offset & IP_MF_FLAG) || (FragOffset > 0);
            bFirstFragment = bFragment && (FragOffset == 0);
            bLastFragment = bFragment && (!(IPH->iph_offset & IP_MF_FLAG));

            //
            // If this is a fragment and NOT the first one, just put the Timestamp in here.
            // Otherwise, let it get to the protocols for processing.
            //
            if (bFragment && !bFirstFragment) {

                NdisAcquireSpinLock(&IPIDListLockRecv);

                for (i = 0; i < PORT_RANGE; i++) {

                    if (IPID == IPIDListRecv[i]) {
                        
                        if (bLastFragment) {
                            //
                            // If its the last fragment, release the slot.
                            //
                            IPIDListRecv[i] = 0xffff;
                        }

                        break;
                    }

                }

                NdisReleaseSpinLock(&IPIDListLockRecv);

                if (i == PORT_RANGE) {

                    TimeStmpTrace(TS_DBG_TRACE, TS_DBG_RECV, ("Couldnt find an IPID that we care about, get outta here.\n"));
                    goto RECV_FAILURE;

                } 
                //
                // So we found a IPID that matches - set the timestamp and get out after this.
                //
                
                TotalLen = TotalIpLen - FragOffset;
                pData    = ((PUCHAR) IPH) + IpHdrLen; 
                
                if (TotalLen > sizeof (LOG_RECORD)) {

                    LARGE_INTEGER   PerfFrequency;
                    UINT64          RecdTime, Freq;

                    pRecord = (LOG_RECORD *) pData;
                    CurrentTime = KeQueryPerformanceCounter(&PerfFrequency);
                    
                    //
                    // Convert the perffrequency into 100ns interval.
                    //
                    Freq = 0;
                    Freq |= PerfFrequency.HighPart;
                    Freq = Freq << 32;
                    Freq |= PerfFrequency.LowPart;

                    //convert from Largeinteger to uint64
                    pRecord->TimeReceivedWire = 0;
                    pRecord->TimeReceivedWire |= CurrentTime.HighPart;
                    pRecord->TimeReceivedWire = pRecord->TimeReceivedWire << 32;
                    pRecord->TimeReceivedWire |= CurrentTime.LowPart;

                    // Normalize cycles with the frequency.
                    pRecord->TimeReceivedWire *= 10000000;
                    pRecord->TimeReceivedWire /= Freq;

                }
                
                return TRUE;

            }

            //
            // Do the protocol specific stuff.
            //

            switch (IPH->iph_protocol) {
            case IPPROTO_TCP:
            
                TotalTcpLen = TotalIpLen - HeaderLength;
                TCPH = (TCPHeader *) (((PUCHAR)IPH) + HeaderLength);

                SrcPort = net_short(TCPH->tcp_src);
                DstPort = net_short(TCPH->tcp_dest);

                TimeStmpTrace(TS_DBG_TRACE, TS_DBG_RECV, ("[TimeStmpReceivePacket]: *TCP* Address: SRC = %x DST = %x, Port S : %x, Port D: %x\n",
                                                          IPH->iph_src, 
                                                          IPH->iph_dest, 
                                                          SrcPort, 
                                                          DstPort));

                TcpHeaderOffset = TCP_HDR_SIZE(TCPH);
                pData = (PUCHAR) TCPH + TcpHeaderOffset;
                TcpDataLen = TotalTcpLen - TcpHeaderOffset;

                if ((CheckInPortList(DstPort)) && (TcpDataLen > sizeof (LOG_RECORD))) {
                    
                    LARGE_INTEGER   PerfFrequency;
                    UINT64          RecdTime, Freq;

                    pRecord = (LOG_RECORD *) pData;
                    CurrentTime = KeQueryPerformanceCounter(&PerfFrequency);
                    
                    //
                    // Convert the perffrequency into 100ns interval.
                    //
                    Freq = 0;
                    Freq |= PerfFrequency.HighPart;
                    Freq = Freq << 32;
                    Freq |= PerfFrequency.LowPart;

                    //convert from large_integer to uint64

                    pRecord->TimeReceivedWire = 0;
                    pRecord->TimeReceivedWire |= CurrentTime.HighPart;
                    pRecord->TimeReceivedWire = pRecord->TimeReceivedWire << 32;
                    pRecord->TimeReceivedWire |= CurrentTime.LowPart;

                    // Normalize cycles with the frequency.
                    pRecord->TimeReceivedWire *= 10000000;
                    pRecord->TimeReceivedWire /= Freq;


                } else if (CheckInPortList(DstPort)) {

                    if (TcpDataLen < sizeof(LOG_RECORD)) 
                        TimeStmpTrace(TS_DBG_TRACE, TS_DBG_RECV, ("The Datagram was too small!! IpLen:%d, Tcplen:%d HeaderOff(tcp):%d log_record:%d\n", TotalIpLen, TotalTcpLen, TcpHeaderOffset, sizeof (LOG_RECORD)));

                }
    
                //
                // If its the first fragment, keep a place holder so we know which
                // subsequent IP fragments to timestamp.
                //
                if ((CheckInPortList(DstPort)) && bFirstFragment) {

                    NdisAcquireSpinLock(&IPIDListLockRecv);
    
                    // need new Entry for IPID
                    for (i = 0; i < PORT_RANGE; i++) {
                        //
                        // Look for a free slot
                        //
                        if (0xffff == IPIDListRecv[i]) {
            
                            IPIDListRecv[i] = IPID;
                            break;
        
                        }


                    }

                    NdisReleaseSpinLock(&IPIDListLockRecv);
    
                    if (i == PORT_RANGE) {

                        TimeStmpTrace(TS_DBG_TRACE, TS_DBG_RECV, ("Couldn't find an empty IPID - Bailing \n"));
                    }
                }


                break;

            case IPPROTO_UDP:
            
                TotalUdpLen = TotalIpLen - HeaderLength;
                UDPH = (UDPHeader *) (((PUCHAR)IPH) + HeaderLength);
                
                TimeStmpTrace(TS_DBG_TRACE, TS_DBG_RECV, ("PAcket %x, IPH = %x, UDPH = %x, HeaderLength = %x\n", Packet, IPH, UDPH, HeaderLength));

                UdpDataLen = TotalUdpLen - sizeof(UDPHeader);
                pData = ((PUCHAR) UDPH) + sizeof (UDPHeader);

                SrcPort = net_short(UDPH->uh_src);          // Source port.
                DstPort = net_short(UDPH->uh_dest);             // Destination port.

                if (UdpDataLen < sizeof(UDPHeader)) {
                    return TRUE;
                } 
                
                if ((CheckInPortList(DstPort)) && (UdpDataLen > sizeof(LOG_RECORD))) {
                    
                    LARGE_INTEGER   PerfFrequency;
                    UINT64          RecdTime, Freq;

                    pRecord = (LOG_RECORD *) pData;
                    CurrentTime = KeQueryPerformanceCounter(&PerfFrequency);

                    //
                    // Convert the perffrequency into 100ns interval.
                    //
                    Freq = 0;
                    Freq |= PerfFrequency.HighPart;
                    Freq = Freq << 32;
                    Freq |= PerfFrequency.LowPart;

                    // convert to uint64

                    pRecord->TimeReceivedWire = 0;
                    pRecord->TimeReceivedWire |= CurrentTime.HighPart;
                    pRecord->TimeReceivedWire = pRecord->TimeReceivedWire << 32;
                    pRecord->TimeReceivedWire |= CurrentTime.LowPart;
                                        
                    // Normalize cycles with the frequency.
                    pRecord->TimeReceivedWire *= 10000000;
                    pRecord->TimeReceivedWire /= Freq;


                    //
                    // Dont want to get rejected due to bad xsum ...
                    //
                    UDPH->uh_xsum = 0;

                } else if (CheckInPortList(DstPort)) {

                    if ((UdpDataLen) < sizeof(LOG_RECORD))
                        TimeStmpTrace(TS_DBG_DEATH, TS_DBG_RECV, ("The Datagram was too small (UDP)!! IpLen:%d, Size:%d log_record:%d\n", 
                                                                  TotalIpLen, UdpDataLen, sizeof (LOG_RECORD)));

                }

                if ((CheckInPortList(DstPort)) && bFirstFragment) {

                    NdisAcquireSpinLock(&IPIDListLockRecv);

                    // need new Entry for IPID
                    for (i = 0; i < PORT_RANGE; i++) {
                        //
                        // Look for a free slot
                        //
                        if (0xffff == IPIDListRecv[i]) {

                            IPIDListRecv[i] = IPID;
                            break;

                        }


                    }

                    NdisReleaseSpinLock(&IPIDListLockRecv);

                    if (i == PORT_RANGE) {

                        TimeStmpTrace(TS_DBG_DEATH, TS_DBG_RECV, ("Couldn't find an empty IPID - Bailing \n"));
                    }
                }

                TimeStmpTrace(TS_DBG_TRACE, TS_DBG_RECV, ("[TimeStmpReceivePacket]: *UDP* Address: SRC = %x DST = %x, Port S : %x, Port D: %x\n",
                                          IPH->iph_src, 
                                          IPH->iph_dest, 
                                          UDPH->uh_src, 
                                          UDPH->uh_dest));

                break;

            case IPPROTO_RAW:
            
                TimeStmpTrace(TS_DBG_TRACE, TS_DBG_RECV, ("[TimeStmpReceivePacket]: Protocol RAW\n"));

                break;
        
            case IPPROTO_IGMP:
            
                TimeStmpTrace(TS_DBG_TRACE, TS_DBG_RECV, ("[TimeStmpReceivePacket]: Protocol IGMP\n"));

                break;
        
            case IPPROTO_ICMP:

                TimeStmpTrace(TS_DBG_TRACE, TS_DBG_RECV, ("[TimeStmpReceivePacket]: Protocol TCMP\n"));

                break;

            default:
            
                ;
                //TimeStmpTrace(TS_DBG_DEATH, TS_DBG_RECV, ("[TimeStmpReceivePacket]: Protocol - UNKNOWN (%d)\n", IPH->iph_protocol));
                //DbgBreakPoint();

            }
        }
    }

RECV_FAILURE:

    return TRUE;
}

//
// This function receives a buffer from NDIS which is indicated to the transport.
// We use this function and work past the headers (tcp, ip) and get to the data.
// Then, we timestamp and reset the checksum flags.
// We make the assumption that the lookahead is atleast 128. 
// mac header ~ 8+8, ip header ~20, tcp/udp ~ 20+options, LOG_RECORD ~ 44
// they all add up to less than 128. If this is not a good assumption, We will need
// to get into MiniportTransferData and such.
//
BOOLEAN
TimeStmpReceiveIndication(
                          IN PPS_PIPE_CONTEXT PipeContext,
                          IN PPS_FLOW_CONTEXT FlowContext,
                          IN PPS_CLASS_MAP_CONTEXT ClassMapContext,
                          IN PVOID    HeaderBuffer,
                          IN UINT     HeaderBufferSize,
                          IN PVOID    LookAheadBuffer,
                          IN UINT     LookAheadBufferSize,
                          IN UINT     PacketSize,
                          IN UINT     TransportHeaderOffset
                          )
{
    PPS_PIPE_CONTEXT    Pipe = PipeContext;
    LARGE_INTEGER       CurrentTime;
    IPHeader UNALIGNED  *IPH    = NULL;
    TCPHeader UNALIGNED *TCPH   = NULL;
    UDPHeader UNALIGNED *UDPH   = NULL;
    IPAddr              Src, Dst;
    PUCHAR              headerBuffer = NULL, pData = NULL;
    PNDIS_BUFFER        pFirstBuffer = NULL;
    ULONG               firstbufferLength = 0, bufferLength = 0, HeaderLength = 0;
    ULONG               TotalIpLen = 0, IPDataLength = 0, IpHdrLen = 0;
    ULONG               TotalTcpLen = 0, TcpDataLen = 0, TotalLen = 0, TcpHeaderOffset = 0, Size = 0;
    ULONG               TotalUdpLen = 0, UdpDataLen = 0, UdpHdrLen = 0;
    USHORT              SrcPort = 0, DstPort = 0, IPID = 0, FragOffset = 0;
    BOOLEAN             bFragment, bFirstFragment, bLastFragment;
    ULONG               i = 0;
    PLOG_RECORD         pRecord = NULL;
    ushort              type;                       // Protocol type
    uint                ProtOffset;                 // Offset in Data to non-media info.
    UINT                MoreHeaderInLookAhead = 0;
    TimeStmpTrace(TS_DBG_TRACE, TS_DBG_RECV, ("[TimeStmpReceiveIndication]: \n"));

    //
    // Don't know anything about the MAC headers, piggy back from PSCHED...
    // Calculate if the header is more than the standard HeaderBufferSize (i.e. SNAP header, etc.)
    //
    MoreHeaderInLookAhead = TransportHeaderOffset - HeaderBufferSize;

    if (MoreHeaderInLookAhead) {
        
        //
        // Just munge these, so that we can actually get down to business.
        //
        ((PUCHAR) LookAheadBuffer) += MoreHeaderInLookAhead;
        LookAheadBufferSize -= MoreHeaderInLookAhead;

    }

    if (LookAheadBufferSize > sizeof(IPHeader)) {

        IPH = (IPHeader *) (PUCHAR)LookAheadBuffer;
    
        // Check the header length and the version. If any of these
        // checks fail silently discard the packet.
        HeaderLength = ((IPH->iph_verlen & (uchar)~IP_VER_FLAG) << 2);

        if (HeaderLength >= sizeof(IPHeader) && HeaderLength <= LookAheadBufferSize) {

            //
            // Get past the IP Header and get the rest of the stuff out.
            //
            TotalIpLen = (uint)net_short(IPH->iph_length);

            if ((IPH->iph_verlen & IP_VER_FLAG) == IP_VERSION &&
                TotalIpLen >= HeaderLength  && TotalIpLen <= LookAheadBufferSize) {

                Src = net_short(IPH->iph_src);
                Dst = net_short(IPH->iph_dest);
                IPID = net_short(IPH->iph_id);

                FragOffset = IPH->iph_offset & IP_OFFSET_MASK;
                FragOffset = net_short(FragOffset) * 8;

                bFragment = (IPH->iph_offset & IP_MF_FLAG) || (FragOffset > 0);
                bFirstFragment = bFragment && (FragOffset == 0);
                bLastFragment = bFragment && (!(IPH->iph_offset & IP_MF_FLAG));

                //
                // If this is a fragment and NOT the first one, just put the Timestamp in here.
                // Otherwise, let it get to the protocols for processing.
                //
                if (bFragment && !bFirstFragment) {

                    NdisAcquireSpinLock(&IPIDListLockRecv);

                    for (i = 0; i < PORT_RANGE; i++) {

                        if (IPID == IPIDListRecv[i]) {
                        
                            if (bLastFragment) {
                                //
                                // If its the last fragment, release the slot.
                                //
                                IPIDListRecv[i] = 0xffff;
                            }

                            break;
                        }

                    }

                    NdisReleaseSpinLock(&IPIDListLockRecv);

                    if (i == PORT_RANGE) {

                        TimeStmpTrace(TS_DBG_TRACE, TS_DBG_RECV, ("Couldnt find an IPID that we care about, get outta here.\n"));
                        goto RECV_FAILURE;

                    } 
                    //
                    // So we found a IPID that matches - set the timestamp and get out after this.
                    //
                
                    TotalLen = TotalIpLen - FragOffset;
                    pData    = ((PUCHAR) IPH) + IpHdrLen; 
                
                    if (TotalLen >= sizeof (LOG_RECORD)) {

                        LARGE_INTEGER   PerfFrequency;
                        UINT64          RecdTime, Freq;

                        pRecord = (LOG_RECORD *) pData;
                        CurrentTime = KeQueryPerformanceCounter(&PerfFrequency);

                        //
                        // Convert the perffrequency into 100ns interval.
                        //
                        Freq = 0;
                        Freq |= PerfFrequency.HighPart;
                        Freq = Freq << 32;
                        Freq |= PerfFrequency.LowPart;

                        //
                        // Convert from LARGE_INTEGER to UINT64
                        //
                        pRecord->TimeReceivedWire = 0;
                        pRecord->TimeReceivedWire |= CurrentTime.HighPart;
                        pRecord->TimeReceivedWire = pRecord->TimeReceivedWire << 32;
                        pRecord->TimeReceivedWire |= CurrentTime.LowPart;
                        
                        // Normalize cycles with the frequency.
                        pRecord->TimeReceivedWire *= 10000000;
                        pRecord->TimeReceivedWire /= Freq;


                    }
                
                    return TRUE;

                }

                //
                // Do the protocol specific stuff.
                //

                switch (IPH->iph_protocol) {
                case IPPROTO_TCP:
            
                    TotalTcpLen = TotalIpLen - HeaderLength;
                    TCPH = (TCPHeader *) (((PUCHAR)IPH) + HeaderLength);

                    SrcPort = net_short(TCPH->tcp_src);
                    DstPort = net_short(TCPH->tcp_dest);

                    TimeStmpTrace(TS_DBG_TRACE, TS_DBG_RECV, ("[TimeStmpReceiveIndication]: *TCP* Address: SRC = %x DST = %x, Port S : %x, Port D: %x\n",
                                                              IPH->iph_src, 
                                                              IPH->iph_dest, 
                                                              SrcPort, 
                                                              DstPort));

                    TcpHeaderOffset = TCP_HDR_SIZE(TCPH);
                    pData = (PUCHAR) TCPH + TcpHeaderOffset;
                    TcpDataLen = TotalTcpLen - TcpHeaderOffset;

                    if ((CheckInPortList(DstPort)) && (TcpDataLen > sizeof (LOG_RECORD))) {
                    
                        LARGE_INTEGER   PerfFrequency;
                        UINT64          RecdTime, Freq;

                        pRecord = (LOG_RECORD *) pData;
                        CurrentTime = KeQueryPerformanceCounter(&PerfFrequency);

                        //
                        // Convert the perffrequency into 100ns interval.
                        //
                        Freq = 0;
                        Freq |= PerfFrequency.HighPart;
                        Freq = Freq << 32;
                        Freq |= PerfFrequency.LowPart;

                        // convert to uint64
                        pRecord->TimeReceivedWire = 0;
                        pRecord->TimeReceivedWire |= CurrentTime.HighPart;
                        pRecord->TimeReceivedWire = pRecord->TimeReceivedWire << 32;
                        pRecord->TimeReceivedWire |= CurrentTime.LowPart;
                    
                        // Normalize cycles with the frequency.
                        pRecord->TimeReceivedWire *= 10000000;
                        pRecord->TimeReceivedWire /= Freq;


                        //
                        //pRecord->TimeReceivedWire);
                        //

                    } else if (CheckInPortList(DstPort)) {

                        if (TcpDataLen < sizeof(LOG_RECORD)) 
                            TimeStmpTrace(TS_DBG_TRACE, TS_DBG_RECV,
                            ("The Datagram was too small!! IpLen:%d, Tcplen:%d HeaderOff(tcp):%d log_record:%d\n", TotalIpLen, TotalTcpLen, TcpHeaderOffset, sizeof (LOG_RECORD)));

                    }
    
                    //
                    // If its the first fragment, keep a place holder so we know which
                    // subsequent IP fragments to timestamp.
                    //
                    if ((CheckInPortList(DstPort)) && bFirstFragment) {

                        NdisAcquireSpinLock(&IPIDListLockRecv);
    
                        // need new Entry for IPID
                        for (i = 0; i < PORT_RANGE; i++) {
                            //
                            // Look for a free slot
                            //
                            if (0xffff == IPIDListRecv[i]) {
            
                                IPIDListRecv[i] = IPID;
                                break;
        
                            }


                        }

                        NdisReleaseSpinLock(&IPIDListLockRecv);
    
                        if (i == PORT_RANGE) {

                            TimeStmpTrace(TS_DBG_TRACE, TS_DBG_RECV, ("Couldn't find an empty IPID - Bailing \n"));
                        }
                    }


                    break;

                case IPPROTO_UDP:
            
                    TotalUdpLen = TotalIpLen - HeaderLength;
                    UDPH = (UDPHeader *) (((PUCHAR)IPH) + HeaderLength);
                
                    TimeStmpTrace(TS_DBG_TRACE, TS_DBG_RECV, ("PAcket %x, IPH = %x, UDPH = %x, HeaderLength = %x\n", LookAheadBuffer, IPH, UDPH, HeaderLength));

                    UdpDataLen = TotalUdpLen - sizeof(UDPHeader);
                    pData = ((PUCHAR) UDPH) + sizeof (UDPHeader);

                    SrcPort = net_short(UDPH->uh_src);      // Source port.
                    DstPort = net_short(UDPH->uh_dest);         // Destination port.

                    if (UdpDataLen < sizeof(UDPHeader)) {
                        return TRUE;
                    } 
                
                    if ((CheckInPortList(DstPort)) && (UdpDataLen > sizeof(LOG_RECORD))) {

                        LARGE_INTEGER   PerfFrequency;
                        UINT64          RecdTime, Freq;

                        pRecord = (LOG_RECORD *) pData;
                        CurrentTime = KeQueryPerformanceCounter(&PerfFrequency);

                        //
                        // Convert the perffrequency into 100ns interval.
                        //
                        Freq = 0;
                        Freq |= PerfFrequency.HighPart;
                        Freq = Freq << 32;
                        Freq |= PerfFrequency.LowPart;

                        pRecord->TimeReceivedWire = 0;
                        pRecord->TimeReceivedWire |= CurrentTime.HighPart;
                        pRecord->TimeReceivedWire = pRecord->TimeReceivedWire << 32;
                        pRecord->TimeReceivedWire |= CurrentTime.LowPart;

                        // Normalize cycles with the frequency.
                        pRecord->TimeReceivedWire *= 10000000;
                        pRecord->TimeReceivedWire /= Freq;


                        //
                        // Dont want to get rejected due to bad xsum ...
                        //
                        UDPH->uh_xsum = 0;

                    } else if (CheckInPortList(DstPort)) {

                        if ((UdpDataLen) < sizeof(LOG_RECORD))
                            TimeStmpTrace(TS_DBG_DEATH, TS_DBG_RECV, ("The Datagram was too small (UDP)!! IpLen:%d, Size:%d log_record:%d\n", 
                                                                      TotalIpLen, UdpDataLen, sizeof (LOG_RECORD)));

                    }

                    if ((CheckInPortList(DstPort)) && bFirstFragment) {

                        NdisAcquireSpinLock(&IPIDListLockRecv);

                        // need new Entry for IPID
                        for (i = 0; i < PORT_RANGE; i++) {
                            //
                            // Look for a free slot
                            //
                            if (0xffff == IPIDListRecv[i]) {

                                IPIDListRecv[i] = IPID;
                                break;

                            }


                        }

                        NdisReleaseSpinLock(&IPIDListLockRecv);

                        if (i == PORT_RANGE) {

                            TimeStmpTrace(TS_DBG_DEATH, TS_DBG_RECV, ("Couldn't find an empty IPID - Bailing \n"));
                        }
                    }

                    TimeStmpTrace(TS_DBG_TRACE, TS_DBG_RECV, ("[TimeStmpReceiveIndication]: *UDP* Address: SRC = %x DST = %x, Port S : %x, Port D: %x\n",
                                                              IPH->iph_src, 
                                                              IPH->iph_dest, 
                                                              UDPH->uh_src, 
                                                              UDPH->uh_dest));

                    break;

                case IPPROTO_RAW:
            
                    TimeStmpTrace(TS_DBG_TRACE, TS_DBG_RECV, ("[TimeStmpReceiveIndication]: Protocol RAW\n"));

                    break;
        
                case IPPROTO_IGMP:
            
                    TimeStmpTrace(TS_DBG_TRACE, TS_DBG_RECV, ("[TimeStmpReceiveIndication]: Protocol IGMP\n"));

                    break;
        
                case IPPROTO_ICMP:

                    TimeStmpTrace(TS_DBG_TRACE, TS_DBG_RECV, ("[TimeStmpReceiveIndication]: Protocol TCMP\n"));

                    break;

                default:
            
                
                    TimeStmpTrace(TS_DBG_DEATH, TS_DBG_RECV, ("[TimeStmpReceiveIndication]: Protocol - UNKNOWN (%d)\n", IPH->iph_protocol));

                    //DbgBreakPoint();

                }
            }
        }
    }

RECV_FAILURE:

    return TRUE;
}


VOID
TimeStmpSetInformation (
    IN PPS_PIPE_CONTEXT PipeContext,
    IN PPS_FLOW_CONTEXT FlowContext,
    IN NDIS_OID Oid,
    IN ULONG Len,
    IN void *Data)
{
    PPS_PIPE_CONTEXT Pipe = PipeContext;
    PPS_FLOW_CONTEXT Flow = FlowContext;
    TimeStmpTrace(TS_DBG_TRACE, TS_DBG_OID, ("[TimeStmpSetInformation]:\n"));
    (*Pipe->NextComponent->SetInformation)(
        Pipe->NextComponentContext,
        (Flow)?Flow->NextComponentContext:0,
        Oid,
        Len,
        Data);
}


VOID
TimeStmpQueryInformation (
    IN PPS_PIPE_CONTEXT PipeContext,
    IN PPS_FLOW_CONTEXT FlowContext,
    IN NDIS_OID Oid,
    IN ULONG Len,
    IN PVOID Data,
    IN OUT PULONG BytesWritten,
    IN OUT PULONG BytesNeeded,
    IN OUT PNDIS_STATUS Status)
{
    PPS_PIPE_CONTEXT Pipe = PipeContext;
    PPS_FLOW_CONTEXT Flow = FlowContext;
    TimeStmpTrace(TS_DBG_TRACE, TS_DBG_OID, ("[TimeStmpQueryInformation]:\n"));
    (*Pipe->NextComponent->QueryInformation)(
        Pipe->NextComponentContext,
        (Flow)?Flow->NextComponentContext:0,
        Oid,
        Len,
        Data,
        BytesWritten,
        BytesNeeded,
        Status);
}

NDIS_STATUS 
TimeStmpCreateClassMap (
    IN PPS_PIPE_CONTEXT PipeContext,
    IN HANDLE PsClassMapContext,
    IN PTC_CLASS_MAP_FLOW ClassMap,
    IN PPS_CLASS_MAP_CONTEXT ComponentClassMapContext
    )
{
    TimeStmpTrace(TS_DBG_TRACE, TS_DBG_CLASS_MAP, ("[TimeStmpCreateClassMap]: \n"));
    return (*PipeContext->NextComponent->CreateClassMap)(
        PipeContext->NextComponentContext,
        PsClassMapContext,
        ClassMap,
        ComponentClassMapContext->NextComponentContext);
}

NDIS_STATUS 
TimeStmpDeleteClassMap (
    IN PPS_PIPE_CONTEXT PipeContext,
    IN PPS_CLASS_MAP_CONTEXT ComponentClassMapContext
    )
{
    TimeStmpTrace(TS_DBG_TRACE, TS_DBG_CLASS_MAP, ("[TimeStmpDeleteClassMap]: \n"));
    return (*PipeContext->NextComponent->DeleteClassMap)(
        PipeContext->NextComponentContext,
        ComponentClassMapContext->NextComponentContext);
}
    
NTSTATUS
DriverEntry(
    IN PDRIVER_OBJECT DriverObject,
    IN PUNICODE_STRING RegistryPath
    )
{
    PSI_INFO        Component;
    NDIS_HANDLE     ConfigHandle;
    NDIS_STATUS     Status;
    NDIS_STRING     ComponentKey = NDIS_STRING_CONST("DisplayName");
    NDIS_STRING     ComponentName = NDIS_STRING_CONST("TimeStmp");
    PNDIS_CONFIGURATION_PARAMETER pConfigParam;
    NDIS_STRING     PsParamKey;
    PWSTR           p = RegistryPath->Buffer + RegistryPath->Length;
    PS_DEBUG_INFO   Dbg;
    ULONG           i = 0;

    // The last word of Registry Path points to the driver name.
    // NdisOpenProtocol needs that name!
    while(p != RegistryPath->Buffer && *p != L'\\')
        p-- ;
    p++;
    RtlInitUnicodeString(&PsParamKey, p);
    DbgPrint("PsParamKey:%s\n", PsParamKey);

    NdisOpenProtocolConfiguration(&Status, &ConfigHandle, &PsParamKey);

    DbgPrint("Status of NdisOpenProtocol:%x\n", Status);

    if (!NT_SUCCESS(Status)) {
        goto failure;
    }

    //
    // Check if psched is installed by opening it.
    // If it fails, we dont load either.
    //
    Status = CheckForPsched();
    
    if (!NT_SUCCESS(Status)) {
        
        DbgPrint("PSCHED is NOT installed. Timestmp is bailing too\n");
        goto failure;
    }

    IoctlInitialize(DriverObject);

	// this list maintains a list of all ports that need to be timestamped.
    InitializeListHead(&PortList);
    NdisAllocateSpinLock(&PortSpinLock);
    
    DriverObject->DriverUnload = TimeStmpUnload;
    
    
    //
    // We need to keep track of IPIDs for dealing with fragments 
    // that we need to stamp...
    // 
    for (i = 0; i < PORT_RANGE; i++) {
        IPIDList[i] = 0xffff;
    }

    NdisAllocateSpinLock(&IPIDListLock);

    //
    // Do the same for the receive side.
    // 
    for (i = 0; i < PORT_RANGE; i++) {
        IPIDListRecv[i] = 0xffff;
    }

    NdisAllocateSpinLock(&IPIDListLockRecv);


    if ( NT_SUCCESS( Status )) 
    {
        // Read the name of the component from the registry
#if 0
        NdisReadConfiguration( &Status,
                               &pConfigParam,
                               ConfigHandle,
                               &ComponentKey,
                               NdisParameterString);
        if( NT_SUCCESS( Status ))
        {
            RtlInitUnicodeString(&Component.ComponentName,
                                pConfigParam->ParameterData.StringData.Buffer);
#else 
            RtlInitUnicodeString(&Component.ComponentName, ComponentName.Buffer);
#endif

            Component.Version = PS_COMPONENT_CURRENT_VERSION;
            Component.PacketReservedLength = 0;
            Component.PipeContextLength = sizeof(PS_PIPE_CONTEXT);
            Component.FlowContextLength = sizeof(PS_FLOW_CONTEXT);
            Component.ClassMapContextLength = sizeof(PS_CLASS_MAP_CONTEXT);
            Component.SupportedOidsLength  = 0;
            Component.SupportedOidList = 0;
            Component.SupportedGuidsLength = 0;
            Component.SupportedGuidList = 0;
            Component.InitializePipe = TimeStmpInitializePipe;
            Component.ModifyPipe = TimeStmpModifyPipe;
            Component.DeletePipe = TimeStmpDeletePipe;
            Component.CreateFlow = TimeStmpCreateFlow;
            Component.ModifyFlow = TimeStmpModifyFlow;
            Component.DeleteFlow = TimeStmpDeleteFlow;
            Component.CreateClassMap = TimeStmpCreateClassMap;
            Component.DeleteClassMap = TimeStmpDeleteClassMap;
            Component.SubmitPacket = TimeStmpSubmitPacket;
            Component.ReceivePacket = TimeStmpReceivePacket;
            Component.ReceiveIndication = TimeStmpReceiveIndication;
            Component.SetInformation = TimeStmpSetInformation;
            Component.QueryInformation = TimeStmpQueryInformation;

            //
            // Call Psched's RegisterPsComponent
            //
            Status = RegisterPsComponent(&Component, sizeof(Component), 
                                         &Dbg);
            if(Status != NDIS_STATUS_SUCCESS)
            {
                
                DbgPrint("Status of RegisterPsComponent%x\n", Status);

                TimeStmpTrace(TS_DBG_DEATH, TS_DBG_INIT, 
                          ("DriverEntry: RegisterPsComponent Failed \n"));
            } 
            else 
            {
                
                DbgPrint("Status of RegisterPsComponent:%x\n", Status);

            }

#if 0
                
        }
        else 
        {
            DbgPrint("Status of NdisReadProtocol:%x\n", Status);
            
            DbgBreakPoint();
            TimeStmpTrace(TS_DBG_DEATH, TS_DBG_INIT, 
                      ("DriverEntry: ComponentName not specified \n"));
        }
#endif
    }
    else 
    
    {
        DbgPrint("Status of NdisOpenProtocol:%x\n", Status);

        TimeStmpTrace(TS_DBG_DEATH, TS_DBG_INIT,
                  ("DriverEntry: Can't read driver information in registry"
                   "\n"));
    }

failure:
    return Status;
}


//
// The following function checks for the existence of PSCHED on the machine.
// The assumption being that PSCHED gets loaded before TimeStmp on a system.
// If we can open the device, it means that PSCHED is on, otherwise, we bail.
// This fix is for Bug - 321618
//
NTSTATUS
CheckForPsched(
               VOID
               )

{
    NTSTATUS                    status;
    IO_STATUS_BLOCK             ioStatusBlock;
    OBJECT_ATTRIBUTES           objectAttr;

    InitializeObjectAttributes(
        &objectAttr,
        &PschedDriverName,
        OBJ_CASE_INSENSITIVE,
        NULL,
        NULL);

    status = NtCreateFile(
                &PschedHandle,
                GENERIC_READ,
                &objectAttr,
                &ioStatusBlock,
                NULL,
                FILE_ATTRIBUTE_NORMAL,
                FILE_SHARE_READ,
                FILE_OPEN,
                FILE_SYNCHRONOUS_IO_NONALERT,
                NULL,
                0L);

    if (!NT_SUCCESS(status)) {

        return status;

    } else {

        NtClose(PschedHandle);

    }

    return status;
}

PPORT_ENTRY 
CheckInPortList(USHORT Port) {

	PLIST_ENTRY		ListEntry;
	PPORT_ENTRY		pPortEntry;
	
	NdisAcquireSpinLock(&PortSpinLock);
	ListEntry = PortList.Flink;
	
	while (ListEntry != &PortList) {

		pPortEntry = CONTAINING_RECORD(ListEntry, PORT_ENTRY, Linkage);
		if (Port == pPortEntry->Port) {
		
			//DbgPrint("Found Port%d\n", Port);
			NdisReleaseSpinLock(&PortSpinLock);
			return pPortEntry;

		} else {
		
			ListEntry = ListEntry->Flink;
			//DbgPrint("NOT Found Trying NEXT\n");
		}
		
	}

	NdisReleaseSpinLock(&PortSpinLock);
	//DbgPrint("NOT Found returning from function\n");
	return NULL;
}