/*++

Copyright (c) 1996-1999  Microsoft Corporation

Module Name:

    recv.c

Abstract:

    routines to handle receiving data

Author:

    Charlie Wickham (charlwi)  08-May-1996
    Rajesh Sundaram (rajeshsu) 01-Aug-1998.

Environment:

    Kernel Mode

Revision History:

--*/

#include "psched.h"
#pragma hdrstop

/* External */

/* Static */

/* Forward */ /* Generated by Emacs 19.17.0 on Thu May 09 10:34:39 1996 */

INT
ClReceivePacket(
        IN      NDIS_HANDLE                             ProtocolBindingContext,
        IN      PNDIS_PACKET                    Packet
        );

VOID
MpReturnPacket(
        IN      NDIS_HANDLE                             MiniportAdapterContext,
        IN      PNDIS_PACKET                    Packet
        );

VOID
ClReceiveComplete(
        IN      NDIS_HANDLE                             ProtocolBindingContext
        );

NDIS_STATUS
MpTransferData(
        OUT PNDIS_PACKET                        Packet,
        OUT PUINT                                       BytesTransferred,
        IN      NDIS_HANDLE                             MiniportAdapterContext,
        IN      NDIS_HANDLE                             MiniportReceiveContext,
        IN      UINT                                    ByteOffset,
        IN      UINT                                    BytesToTransfer
        );

VOID
ClTransferDataComplete(
    IN  NDIS_HANDLE     ProtocolBindingContext,
    IN  PNDIS_PACKET    pNdisPacket,
    IN  NDIS_STATUS     Status,
    IN  UINT            BytesTransferred
    );

/* End Forward */

VOID
PsAllocateRecvPacket(PNDIS_STATUS  Status,
                     PPNDIS_PACKET Packet,
                     PADAPTER      Adapter)
{

    if(!Adapter->RecvPacketPool)
    {
        PS_LOCK_DPC(&Adapter->Lock);

        if(!Adapter->RecvPacketPool)
        {
            NDIS_HANDLE PoolHandle = (void *) NDIS_PACKET_POOL_TAG_FOR_PSCHED;

            NdisAllocatePacketPoolEx(Status,
                                     &PoolHandle,
                                     MIN_PACKET_POOL_SIZE,
                                     MAX_PACKET_POOL_SIZE,
                                     sizeof(PS_RECV_PACKET_CONTEXT));

            if(*Status != NDIS_STATUS_SUCCESS)
            {
                Adapter->Stats.OutOfPackets ++;
                PS_UNLOCK_DPC(&Adapter->Lock);

                return;
            }

            // 
            // We successfully allocated a packet pool. We can now free the Fixed Size Block pool for the packet-stack API
            //
            Adapter->RecvPacketPool = PoolHandle;

        }

        PS_UNLOCK_DPC(&Adapter->Lock);
    }

    NdisDprAllocatePacket(Status,
                          Packet,
                          Adapter->RecvPacketPool);
    
}


INT
ClReceivePacket(
        IN      NDIS_HANDLE ProtocolBindingContext,
        IN      PNDIS_PACKET MpPacket
        )

/*++

Routine Description:

    Called by the NIC to indicate a data as an NDIS_PACKET. Make a copy of the
    packet struct, switch to miniport mode and continue the packet along its way

Arguments:

    See the DDK...

Return Values:

    None

--*/

{
    PADAPTER                   Adapter = (PADAPTER)ProtocolBindingContext;
    NDIS_STATUS                Status;
    PPS_RECV_PACKET_CONTEXT    ContextArea;
    PNDIS_PACKET               OurPacket;
    BOOLEAN                    Remaining;

    PsStructAssert( Adapter );

    if(!Adapter->PsNdisHandle)
    {
        return 0;
    }

    if(Adapter->MediaType == NdisMediumWan && 
       Adapter->ProtocolType == ARP_ETYPE_IP)
    {
        //
        // Munge s-mac and d-mac so that wanarp is happy.
        //
        PNDIS_BUFFER pNdisBuf;
        UINT         Len;
        PETH_HEADER  pAddr;
        PUSHORT      id;
        PPS_WAN_LINK WanLink;
        
        pNdisBuf = MpPacket->Private.Head;
        NdisQueryBuffer(pNdisBuf, &pAddr, &Len);
        
        if(Len < sizeof(ETH_HEADER))
        {
            return NDIS_STATUS_FAILURE;
        }

        id = (PUSHORT)&pAddr->DestAddr[0];

        PS_LOCK(&Adapter->Lock);

        if((WanLink = (PPS_WAN_LINK)g_WanLinkTable[*id]) == 0)
        {
            PS_UNLOCK(&Adapter->Lock);
            return NDIS_STATUS_FAILURE;
        }

        if(WanLink->State != WanStateOpen)
        {
            PS_UNLOCK(&Adapter->Lock);
            return NDIS_STATUS_FAILURE;
        }

        PS_UNLOCK(&Adapter->Lock);

        NdisMoveMemory(pAddr, 
                       &WanLink->RecvHeader, 
                       FIELD_OFFSET(ETH_HEADER, Type));

    }

    NdisIMGetCurrentPacketStack(MpPacket, &Remaining);

    if(Remaining != 0)
    {
        Status = NDIS_GET_PACKET_STATUS(MpPacket);

        // TimeStamp will always be there; no need to check //
        if (!TimeStmpReceivePacket(   NULL, NULL, NULL, MpPacket, Adapter->MediaType )) 
            PsDbgRecv(DBG_FAILURE, DBG_RECEIVE, CL_RECV_PACKET, OURS, Adapter, MpPacket, 0);

        if (TimeStmpRecvPacket) {
            
            if (!(TimeStmpRecvPacket)(
                                      NULL,
                                      NULL,
                                      NULL,
                                      MpPacket,
                                      Adapter->MediaType
                                      )) {
                
                PsDbgRecv(DBG_FAILURE, DBG_RECEIVE, CL_RECV_PACKET, OURS, Adapter, MpPacket, 0);
            }
            
        }
        
		NdisMIndicateReceivePacket(Adapter->PsNdisHandle, &MpPacket, 1);

		return((Status != NDIS_STATUS_RESOURCES) ? 1 : 0);
    }
    else 
    {

        PsAllocateRecvPacket(&Status, &OurPacket, Adapter);

        if(Status == NDIS_STATUS_SUCCESS)
        {
            PsDbgRecv(DBG_INFO, DBG_RECEIVE, CL_RECV_PACKET, ENTER, Adapter, OurPacket, MpPacket);
            
            //
            // Save Original Packet
            //
            ContextArea = PS_RECV_PACKET_CONTEXT_FROM_PACKET(OurPacket);
            ContextArea->OriginalPacket = MpPacket;
            
    
            OurPacket->Private.Head       = MpPacket->Private.Head;
            OurPacket->Private.Tail       = MpPacket->Private.Tail;
            
            //
            // Get the original packet (it could be the same packet as one received or a different one
            // based on # of layered MPs) and set it on the indicated packet so the OOB stuff is visible
            // correctly at the top.
            //
            NDIS_SET_ORIGINAL_PACKET(OurPacket, NDIS_GET_ORIGINAL_PACKET(MpPacket));
    
            NDIS_SET_PACKET_HEADER_SIZE(OurPacket, NDIS_GET_PACKET_HEADER_SIZE(MpPacket));
            
            //
            // Set Packet Flags
            //
            NdisGetPacketFlags(OurPacket) = NdisGetPacketFlags(MpPacket);
            
            Status = NDIS_GET_PACKET_STATUS(MpPacket);
            
            NDIS_SET_PACKET_STATUS(OurPacket, Status);

            if(!(TimeStmpReceivePacket)(   NULL, NULL, NULL, OurPacket, Adapter->MediaType ))
                PsDbgRecv(DBG_FAILURE, DBG_RECEIVE, CL_RECV_PACKET, OURS, Adapter, OurPacket, OurPacket);
                
            
            if (TimeStmpRecvPacket) {
                
                if (!(TimeStmpRecvPacket)(
                                          NULL,
                                          NULL,
                                          NULL,
                                          OurPacket,
                                          Adapter->MediaType
                                          )) {
                        
                    PsDbgRecv(DBG_FAILURE, DBG_RECEIVE, CL_RECV_PACKET, OURS, Adapter, OurPacket, OurPacket);
                }
                    
            }

            NdisMIndicateReceivePacket(Adapter->PsNdisHandle, &OurPacket, 1);

            if (Status == NDIS_STATUS_RESOURCES)
            {
                NdisDprFreePacket(OurPacket);
            }
            
            return((Status != NDIS_STATUS_RESOURCES) ? 1 : 0);
        }
        else
        {
            
            //
            // out of resources. indicate that we're not hanging onto the packet
            //
            PsDbgRecv(DBG_FAILURE, DBG_RECEIVE, CL_RECV_PACKET, NO_RESOURCES,
                      Adapter, 0, MpPacket);
            
            Adapter->Stats.OutOfPackets ++;
            return 0;
        }
    }

} // ClReceivePacket


VOID
MpReturnPacket(
        IN      NDIS_HANDLE                             MiniportAdapterContext,
        IN      PNDIS_PACKET                    Packet
        )

/*++

Routine Description:

     Potentially return a packet we indicated previously to the
     underlying miniport. It might be one of ours from a ProtocolReceive
     indication, so we disassemble it and return the packet and its
     buffers to their respective S Lists

Arguments:

    See the DDK...

Return Values:

    None

--*/

{
    PADAPTER                Adapter = (PADAPTER)MiniportAdapterContext;
    PPS_RECV_PACKET_CONTEXT PktContext;
    PNDIS_PACKET            MyPacket;
    BOOLEAN                 Remaining;

    PsStructAssert(Adapter);

    NdisIMGetCurrentPacketStack(Packet, &Remaining);

    if(Remaining != 0)
    {
        NdisReturnPackets(&Packet, 1);
    }
    else 
    {

        //
        // see if the OriginalPacket field indicates that this belongs
        // to someone below us and return it now
        //

        PktContext = PS_RECV_PACKET_CONTEXT_FROM_PACKET(Packet);

        MyPacket = PktContext->OriginalPacket;

        PsDbgRecv(DBG_INFO, DBG_RECEIVE, MP_RETURN_PACKET, RETURNING, Adapter, Packet, MyPacket);

        NdisDprFreePacket(Packet);

        NdisReturnPackets(&MyPacket, 1);
    }


} // MpReturnPacket


NDIS_STATUS
ClReceiveIndication(
        IN      NDIS_HANDLE                             ProtocolBindingContext,
        IN      NDIS_HANDLE                             MacReceiveContext,
        IN      PVOID                                   HeaderBuffer,
        IN      UINT                                    HeaderBufferSize,
        IN      PVOID                                   LookAheadBuffer,
        IN      UINT                                    LookAheadBufferSize,
        IN      UINT                                    PacketSize
        )

/*++

 Routine Description:

    Called by NIC to notify protocol of incoming data. Copy the data
    into a cached packet we set up during initialization and indicate
    that packet to the higher layer.

Arguments:

    See the DDK...

Return Values:

    None

--*/

{
    PADAPTER     Adapter = (PADAPTER)ProtocolBindingContext;
    PNDIS_PACKET MyPacket, Packet;
    NDIS_STATUS  Status  = NDIS_STATUS_SUCCESS;

    PsStructAssert(Adapter);

    if(!Adapter->PsNdisHandle)
    {
        Status = NDIS_STATUS_FAILURE;
    }
    else
    {
        do
        {
            //
            // If this was indicated by the miniport below as a packet, then get that packet
            // pointer and indicate it as a packet as well (with appropriate status).
            // This way the OOB stuff is accessible to the transport above us.
            //

            Packet = NdisGetReceivedPacket(Adapter->LowerMpHandle, MacReceiveContext);

            if (Packet != NULL)
            {
                BOOLEAN Remaining;

                //
                // Check if there are any more packet stacks left. If there is need to keep per packet information,
                // then the packet stack (which is returned by the api) can be used to store that
                //
                NdisIMGetCurrentPacketStack(Packet, &Remaining);
                if (Remaining != 0)
                {
                    NDIS_STATUS OldPacketStatus;

                    if(!(TimeStmpReceivePacket)(   NULL, NULL, NULL, Packet, Adapter->MediaType ))
                        PsDbgRecv(DBG_FAILURE, DBG_RECEIVE, CL_RECV_IND, OURS, Adapter, Packet, Packet);
                    
                    if (TimeStmpRecvPacket) {

                        if (!(TimeStmpRecvPacket)(
                                              NULL,
                                              NULL,
                                              NULL,
                                              Packet,
                                              Adapter->MediaType
                                              )) {

                            PsDbgRecv(DBG_FAILURE, DBG_RECEIVE, CL_RECV_IND, OURS, Adapter, Packet, Packet);
                        }

                    }

                    // 
                    // Save the old status, and set packet status to NDIS_STATUS_RESOURCES
                    // because we can't have the protocol above us retain the packet -- it
                    // can go away as soon as we return from this function.
                    //
    
                    OldPacketStatus = NDIS_GET_PACKET_STATUS(Packet);
                    NDIS_SET_PACKET_STATUS(Packet, NDIS_STATUS_RESOURCES);

                    NdisMIndicateReceivePacket(Adapter->PsNdisHandle, &Packet, 1);

                    // 
                    // Restore the old Status
                    //
                    NDIS_SET_PACKET_STATUS(Packet, OldPacketStatus);

                    // Since we had set the packet status to NDIS_STATUS_RESOURCES, our
                    // ReturnPacket handler won't be called for this packet.

                    break;
                }

                //
                // Get a packet off the pool and indicate that up
                //
                PsAllocateRecvPacket(&Status,
                                     &MyPacket,
                                     Adapter);

                if (Status == NDIS_STATUS_SUCCESS)
                {
                    MyPacket->Private.Head = Packet->Private.Head;
                    MyPacket->Private.Tail = Packet->Private.Tail;
				
                    //
                    // Get the original packet (it could be the same packet as one received or
                    // a different one based on # of layered MPs) and set it on the indicated
                    // packet so the OOB stuff is visible  correctly at the top.
                    //
                    NDIS_SET_ORIGINAL_PACKET(MyPacket, NDIS_GET_ORIGINAL_PACKET(Packet));

                    NDIS_SET_PACKET_HEADER_SIZE(MyPacket, HeaderBufferSize);
		
                    //
                    // Set Packet Flags
                    //
                    NdisGetPacketFlags(MyPacket) = NdisGetPacketFlags(Packet);
				
                    //
                    // Make sure the status is set to NDIS_STATUS_RESOURCES.
                    //
                    NDIS_SET_PACKET_STATUS(MyPacket, NDIS_STATUS_RESOURCES);

                    if(!(TimeStmpReceivePacket)(   NULL, NULL, NULL, MyPacket, Adapter->MediaType ))
                        PsDbgRecv(DBG_FAILURE, DBG_RECEIVE, CL_RECV_IND, OURS, Adapter, MyPacket, MyPacket);
		
                    if (TimeStmpRecvPacket) {

                        if (!(TimeStmpRecvPacket)(
                                              NULL,
                                              NULL,
                                              NULL,
                                              MyPacket,
                                              Adapter->MediaType
                                              )) {

                            PsDbgRecv(DBG_FAILURE, DBG_RECEIVE, CL_RECV_IND, OURS, Adapter, MyPacket, MyPacket);
                        }

                    }

                    NdisMIndicateReceivePacket(Adapter->PsNdisHandle, &MyPacket, 1);
		
                    PsAssert (NDIS_GET_PACKET_STATUS(MyPacket) == NDIS_STATUS_RESOURCES);

                    NdisDprFreePacket(MyPacket);

                    break;
                }
            }

            //
            // Fall through if the miniport below us has either not indicated a packet or we
            // could not  allocate one
            //
            Adapter->IndicateRcvComplete = TRUE;

            //
            // If the timestamp driver is present.
            //
            //
            if (TimeStmpRecvIndication) {

                if (!(TimeStmpRecvIndication)(
                                              NULL,
                                              NULL,
                                              NULL,
                                              HeaderBuffer,
                                              HeaderBufferSize,
                                              LookAheadBuffer,
                                              LookAheadBufferSize,
                                              PacketSize,
                                              Adapter->IPHeaderOffset
                                              )) {

                    PsDbgRecv(DBG_FAILURE, DBG_RECEIVE, CL_RECV_IND, OURS, Adapter, (PNDIS_PACKET) LookAheadBuffer, NULL);
                }

            }

            switch (Adapter->MediaType)
            {
              case NdisMedium802_3:
            case NdisMediumWan:


                  NdisMEthIndicateReceive(Adapter->PsNdisHandle,
                                          MacReceiveContext,
                                          HeaderBuffer,
                                          HeaderBufferSize,
                                          LookAheadBuffer,
                                          LookAheadBufferSize,
                                          PacketSize);
                  break;

              case NdisMedium802_5:
                  NdisMTrIndicateReceive(Adapter->PsNdisHandle,
                                         MacReceiveContext,
                                         HeaderBuffer,
                                         HeaderBufferSize,
                                         LookAheadBuffer,
                                         LookAheadBufferSize,
                                         PacketSize);
                  break;

              case NdisMediumFddi:
                  NdisMFddiIndicateReceive(Adapter->PsNdisHandle,
                                           MacReceiveContext,
                                           HeaderBuffer,
                                           HeaderBufferSize,
                                           LookAheadBuffer,
                                           LookAheadBufferSize,
                                           PacketSize);
                  break;

              default:
                  PsAssert (0);
                  Status = NDIS_STATUS_FAILURE;
                  break;
            }

        } while (FALSE);
    }

    return Status;

} // ClReceiveIndication


VOID
ClReceiveComplete(
        IN      NDIS_HANDLE                             ProtocolBindingContext
        )

/*++

Routine Description:

    Called by NIC via NdisIndicateReceiveComplete. Continue this indication
    up to the higher layer

Arguments:

    See the DDK...

Return Values:

    None

--*/

{
    PADAPTER Adapter = (PADAPTER)ProtocolBindingContext;

    PsStructAssert(Adapter);

    PsDbgRecv(DBG_INFO, DBG_RECEIVE, CL_RECV_COMPL, ENTER, Adapter, 0, 0);

    if((Adapter->PsNdisHandle != NULL) && Adapter->IndicateRcvComplete) {

        switch(Adapter->MediaType){

          case NdisMediumWan:
          case NdisMedium802_3:

              NdisMEthIndicateReceiveComplete(Adapter->PsNdisHandle);
              break;

          case NdisMedium802_5:

              NdisMTrIndicateReceiveComplete(Adapter->PsNdisHandle);
              break;

          case NdisMediumFddi:

              NdisMFddiIndicateReceiveComplete(Adapter->PsNdisHandle);
              break;

        default:

            PsAssert(FALSE);
        }
    }
    Adapter->IndicateRcvComplete = FALSE;

}  // ClReceiveComplete


NDIS_STATUS
MpTransferData(
        OUT PNDIS_PACKET Packet,
        OUT PUINT        BytesTransferred,
        IN  NDIS_HANDLE  MiniportAdapterContext,
        IN  NDIS_HANDLE  MiniportReceiveContext,
        IN  UINT         ByteOffset,
        IN  UINT         BytesToTransfer
        )

/*++

Routine Description:



Arguments:

    See the DDK...

Return Values:

    None

--*/

{
    PADAPTER Adapter = (PADAPTER)MiniportAdapterContext;
    NDIS_STATUS Status;

    PsStructAssert(Adapter);

    PsDbgRecv(DBG_INFO, DBG_RECEIVE, MP_XFER_DATA, ENTER, Adapter, 0, 0);

    if(IsDeviceStateOn(Adapter) == FALSE)
    {
        return NDIS_STATUS_FAILURE;
    }

    NdisTransferData(
                &Status,
                Adapter->LowerMpHandle,
                MiniportReceiveContext,
                ByteOffset,
                BytesToTransfer,
                Packet,
                BytesTransferred);

    return Status;

} // MpTransferData


VOID
ClTransferDataComplete(
    IN  NDIS_HANDLE     ProtocolBindingContext,
    IN  PNDIS_PACKET    Packet,
    IN  NDIS_STATUS     Status,
    IN  UINT            BytesTransferred
    )

/*++

Routine Description:

    Completion routine for NdisTransferData

Arguments:

    See the DDK...

Return Values:

    None

--*/

{
    PADAPTER                Adapter = (PADAPTER)ProtocolBindingContext;
    PPS_RECV_PACKET_CONTEXT PktContext;

    PsStructAssert(Adapter);

    PsDbgRecv(DBG_INFO, DBG_RECEIVE, CL_XFER_COMPL, ENTER, Adapter, Packet, 0);

    if(Adapter->PsNdisHandle)
    {
        NdisMTransferDataComplete(
                        Adapter->PsNdisHandle,
                        Packet,
                        Status,
                        BytesTransferred);
    }

} // ClTransferDataComplete

UINT
ClCoReceivePacket(
    IN  NDIS_HANDLE ProtocolBindingContext,
    IN  NDIS_HANDLE ProtocolVcContext,
    IN  PNDIS_PACKET Packet
    )
{
    //
    // We don't do anything special in the coreceive path. Just call ClReceivePacket.
    //

    return ClReceivePacket(ProtocolBindingContext, Packet);

} // ClCoReceivePacket

/* end recv.c */