/*++

Copyright (c) 1999 Microsoft Corporation

Module Name:

    pkt.c

Abstract:

    ARP1394 ARP control packet management.

Revision History:

    Who         When        What
    --------    --------    ----------------------------------------------
    josephj     07-01-99    Created

Notes:

--*/
#include <precomp.h>

//
// File-specific debugging defaults.
//
#define TM_CURRENT   TM_PKT

//=========================================================================
//                  L O C A L   P R O T O T Y P E S
//=========================================================================

NDIS_STATUS
arpAllocateControlPacketPool(
    PARP1394_INTERFACE  pIF,            // LOCKIN LOCKOUT
    UINT                MaxBufferSize,
    PRM_STACK_RECORD    pSR
    )
/*++
Routine Description:

    Allocate & initialize the packet pool used for allocating control packets.
    Control packets are used for ARP and MCAP. This routine MUST be called
    BEFORE the first call to arpAllocateControlPacket.

Arguments:

    pIF             - The interface in which to allocate the pool. Only one such pool
                      is allocated per interface and it occupies a specific field of
                      pIF.
    MaxBufferSize   - Maximum data size of packets to be allocated using this
                      pool. Attempts to allocate a packet
                      (using arpAllocateControlPacket) with a size larger than
                      MaxBufferSize will fail.

Return Value:

    NDIS_STATUS_SUCCESS on success.
    Ndis error code on failure.
    
--*/
{
    NDIS_STATUS Status;
    NDIS_HANDLE PacketPool=NULL;
    NDIS_HANDLE BufferPool=NULL;
    ENTER("arpAllocateControlPacketPool", 0x71579254)

    RM_ASSERT_OBJLOCKED(&pIF->Hdr, pSR);
    ASSERT(pIF->arp.PacketPool == NULL);
    ASSERT(pIF->arp.BufferPool == NULL);
    ASSERT(pIF->arp.NumOutstandingPackets == 0);

    do
    {
        // Allocate the NDIS Packet Pool
        //
        NdisAllocatePacketPool(
                &Status,
                &PacketPool,
                ARP1394_MAX_PROTOCOL_PKTS,
                sizeof(struct PCCommon)
                );
    
        if (FAIL(Status))
        {
            PacketPool = NULL;
            break;
        }
    
        // Allocate the NDIS Buffer Pool
        //
        NdisAllocateBufferPool(
                &Status,
                &BufferPool,
                ARP1394_MAX_PROTOCOL_PKTS
                );
    
        if (FAIL(Status))
        {
            BufferPool = NULL;
            break;
        }
    
        //
        // We could allocate a lookaside list for the Protocol data, but we
        // choose to use NdisAllocateMemoryWithTag on demand instead. Protocol pkts
        // are not high-frequency things; plus we don't have support for lookaside
        // lists on win98 (although we could easily implement our own for 
        // win98, so that's not really an excuse).
        //
        pIF->arp.MaxBufferSize = MaxBufferSize;
    
        //  (DBG only) Add associations for the packet pool and buffer pool.
        //  These associations must be removed before the interface is deallocated.
        //
        DBG_ADDASSOC(
            &pIF->Hdr,                  // pObject
            PacketPool,                 // Instance1
            NULL,                       // Instance2
            ARPASSOC_IF_PROTOPKTPOOL,   // AssociationID
            "    Proto Packet Pool 0x%p\n",// szFormat
            pSR
            );
        DBG_ADDASSOC(
            &pIF->Hdr,                  // pObject
            BufferPool,                 // Instance1
            NULL,                       // Instance2
            ARPASSOC_IF_PROTOBUFPOOL,   // AssociationID
            "    Proto Buffer Pool 0x%p\n",// szFormat
            pSR
            );

        pIF->arp.PacketPool = PacketPool;
        pIF->arp.BufferPool = BufferPool;
        PacketPool = NULL;
        BufferPool = NULL;

    } while (FALSE);

    if (FAIL(Status))
    {
        if (PacketPool != NULL)
        {
            NdisFreePacketPool(PacketPool);
        }
    
        if (BufferPool != NULL)
        {
            NdisFreeBufferPool(BufferPool);
        }
    }
    else
    {
        ASSERT(PacketPool == NULL && BufferPool == NULL);
    }

    return Status;
}


VOID
arpFreeControlPacketPool(
    PARP1394_INTERFACE  pIF,            // LOCKIN LOCKOUT
    PRM_STACK_RECORD    pSR
    )
/*++
Routine Description:

    Free the previously allocated control packet pool. MUST be called AFTER the last
    call to arpFreeControlPacket. See arpAllocateControlPacketPool for more details.

Arguments:

    pIF     - The interface in which to free the pool.

--*/
{
    NDIS_HANDLE PacketPool;
    NDIS_HANDLE BufferPool;
    ENTER("arpFreeControlPacketPool", 0x3c3acf47)

    // Make sure the IF is locked.
    //
    RM_ASSERT_OBJLOCKED(&pIF->Hdr, pSR);

    // Make sure that there are no outstanding allocated packets.
    //
    ASSERT(pIF->arp.NumOutstandingPackets == 0);

    PacketPool = pIF->arp.PacketPool;
    BufferPool = pIF->arp.BufferPool;
    pIF->arp.PacketPool = NULL;
    pIF->arp.BufferPool = NULL;
    
    // (DBG only) Remove associations for the control and packet pools.
    //
    DBG_DELASSOC(
        &pIF->Hdr,                  // pObject
        PacketPool,                 // Instance1
        NULL,                       // Instance2
        ARPASSOC_IF_PROTOPKTPOOL,   // AssociationID
        pSR
        );
    DBG_DELASSOC(
        &pIF->Hdr,                  // pObject
        BufferPool,                 // Instance1
        NULL,                       // Instance2
        ARPASSOC_IF_PROTOBUFPOOL,   // AssociationID
        pSR
        );

    // Free the buffer and packet pools
    // 
    NdisFreeBufferPool(BufferPool);
    NdisFreePacketPool(PacketPool);
}


NDIS_STATUS
arpAllocateControlPacket(
    IN  PARP1394_INTERFACE  pIF,
    IN  UINT                cbBufferSize,
    IN  UINT                PktFlags,
    OUT PNDIS_PACKET        *ppNdisPacket,
    OUT PVOID               *ppvData,
        PRM_STACK_RECORD    pSR
    )
/*++
Routine Description:

    Allocate a control packet from the interfaces control packet pool. Also
    allocate and chain a SINGLE buffer of size cbBufferSize and return a pointer to
    this buffer in *ppvData.

    NOTE1: The packet and associated buffer MUST be freed
    by a subsequent call to arpFreeControlPacket -- do not free the packet & buffer
    by directly calling ndis apis.

    NOTE2: cbBufferSize must be <= the max-buffer-size specified when
    creating the pool (see arpAllocateControlPacketPool for details).

Arguments:

    pIF             - Interface whose control packet pool to use.
    cbBufferSize    - size of the control packet.
    ppNdisPacket    - Location to set to point to the allocated pkt.
    ppvData         - Location to set to point to the packet data (single buffer).

Return Value:

    NDIS_STATUS_SUCCESS     on success.
    NDIS_STATUS_RESOURCES   if buffers or pkts are currently not available.
    Other ndis error         on other kinds of failures.
--*/
{
    NDIS_STATUS             Status;
    PNDIS_PACKET            pNdisPacket = NULL;
    PNDIS_BUFFER            pNdisBuffer = NULL;
    PVOID                   pvData = NULL;
    ENTER("arpAllocateControlPacket", 0x8ccce6ea)

    //
    // NOTE: we don't care if pIF is locked or not.
    //


    pNdisPacket = NULL;
    pvData      = NULL;

    do
    {

        // Allocate space for the packet data.
        // TODO: here is where we could use a lookaside list instead
        // of NdisAllocateMemoryWithTag.
        //
        {
            if (cbBufferSize > pIF->arp.MaxBufferSize)
            {
                ASSERT(FALSE);
                Status = NDIS_STATUS_RESOURCES;
                break;
            }
            NdisAllocateMemoryWithTag(
                &pvData,
                cbBufferSize,
                MTAG_PKT
                );
            if (pvData == NULL)
            {
                Status = NDIS_STATUS_RESOURCES;
                break;
            }
        }

        // Allocate a buffer.
        //
        NdisAllocateBuffer(
                &Status,
                &pNdisBuffer,
                pIF->arp.BufferPool,
                pvData,
                cbBufferSize
            );

        if (FAIL(Status))
        {
            pNdisBuffer = NULL;
            break;
        }
        
        // Allocate a packet
        //
        NdisAllocatePacket(
                &Status,
                &pNdisPacket,
                pIF->arp.PacketPool
            );
    
        if (FAIL(Status))
        {
            pNdisPacket = NULL;
            break;
        }

        // Identify the packet as belonging to us (ARP).
        //
        {
            struct PacketContext    *PC;
            PC = (struct PacketContext *)pNdisPacket->ProtocolReserved;
            PC->pc_common.pc_owner = PACKET_OWNER_LINK;
            PC->pc_common.pc_flags = (UCHAR)PktFlags; // ARP1394_PACKET_FLAGS_CONTROL;
        }

        // Link the packet and buffer.
        //
        NdisChainBufferAtFront(pNdisPacket, pNdisBuffer);

        InterlockedIncrement(&pIF->arp.NumOutstandingPackets);
        *ppNdisPacket   = pNdisPacket;
        *ppvData        = pvData;

        pNdisPacket = NULL;
        pNdisBuffer = NULL;
        pvData      = NULL;

    } while (FALSE);

    if (FAIL(Status))
    {
        if (pNdisPacket != NULL)
        {
            NdisFreePacket(pNdisPacket);
        }
        if (pNdisBuffer != NULL)
        {
            NdisFreeBuffer(pNdisBuffer);
        }
        if (pvData != NULL)
        {
            NdisFreeMemory(pvData, 0, 0);
        }
    }
    else
    {
        ASSERT(pNdisPacket == NULL
                && pNdisBuffer == NULL
                && pvData == NULL);
    }

    return Status;
}

VOID
arpFreeControlPacket(
    PARP1394_INTERFACE  pIF,
    PNDIS_PACKET        pNdisPacket,
    PRM_STACK_RECORD    pSR
    )
/*++
Routine Description:

    Free a packet previously allocated using arpAllocateControlPacket.

Arguments:

    pIF             - Interface whose control packet pool to use.
--*/
{
    PNDIS_BUFFER pNdisBuffer = NULL;
    PVOID        pvData = NULL;

    ENTER("arpFreeControlPacket", 0x01e7fbc7)

    // (DBG only) Verify that this packet belongs to us. 
    //
    #if DBG
    {
        struct PacketContext    *PC;
        PC = (struct PacketContext *)pNdisPacket->ProtocolReserved;
        ASSERT(PC->pc_common.pc_owner == PACKET_OWNER_LINK);
    }
    #endif // DBG

    // Decrement the allocated packet count.
    //
    {
        LONG Count;
        Count = InterlockedDecrement(&pIF->arp.NumOutstandingPackets);
        ASSERT(Count >= 0);
    }

    // Extract the buffer and data
    //
    {
        UINT TotalLength;
        UINT BufferLength;

        NdisQueryPacket(
                    pNdisPacket,
                    NULL,
                    NULL,
                    &pNdisBuffer,
                    &TotalLength
                    );
    
        if (TotalLength > 0)
        {
            NdisQueryBuffer(
                    pNdisBuffer,
                    &pvData,
                    &BufferLength
                    );
        }
        else
        {
            BufferLength = 0;
        }
    
        // There should only be a single buffer!
        //
        ASSERT(TotalLength!=0 && TotalLength == BufferLength);
    }

    // Free the data
    //
    if (pvData != NULL)
    {
        NdisFreeMemory(pvData, 0, 0);
    }
    // Free the buffer
    //
    if (pNdisBuffer != NULL)
    {
        NdisFreeBuffer(pNdisBuffer);
    }
    // Free the packet
    //
    if (pNdisPacket != NULL)
    {
        NdisFreePacket(pNdisPacket);
    }
}


NDIS_STATUS
arpAllocateEthernetPools(
    IN  PARP1394_INTERFACE  pIF,
    IN  PRM_STACK_RECORD    pSR
    )
{
    NDIS_STATUS Status;
    NDIS_HANDLE PacketPool=NULL;
    NDIS_HANDLE BufferPool=NULL;
    ENTER("arpAllocateEthernetPools", 0x9dc1d759)

    RM_ASSERT_OBJLOCKED(&pIF->Hdr, pSR);
    ASSERT(pIF->ethernet.PacketPool == NULL);
    ASSERT(pIF->ethernet.BufferPool == NULL);

    do
    {
        // Allocate the NDIS Packet Pool
        //
        NdisAllocatePacketPool(
                &Status,
                &PacketPool,
                ARP1394_MAX_ETHERNET_PKTS,
                sizeof(struct PCCommon)
                );
    
        if (FAIL(Status))
        {
            PacketPool = NULL;
            break;
        }
    
        // Allocate the NDIS Buffer Pool
        //
        NdisAllocateBufferPool(
                &Status,
                &BufferPool,
                2*ARP1394_MAX_ETHERNET_PKTS // two buffers per packet.
                );
    
        if (FAIL(Status))
        {
            BufferPool = NULL;
            break;
        }
    
        //  (DBG only) Add associations for the ethernet packet pool and buffer pool.
        //  These associations must be removed before the interface is deallocated.
        //
        DBG_ADDASSOC(
            &pIF->Hdr,                  // pObject
            PacketPool,                 // Instance1
            NULL,                       // Instance2
            ARPASSOC_IF_ETHPKTPOOL, // AssociationID
            "    Eth Packet Pool 0x%p\n",// szFormat
            pSR
            );
        DBG_ADDASSOC(
            &pIF->Hdr,                  // pObject
            BufferPool,                 // Instance1
            NULL,                       // Instance2
            ARPASSOC_IF_ETHBUFPOOL, // AssociationID
            "    Eth Buffer Pool 0x%p\n",// szFormat
            pSR
            );

        pIF->ethernet.PacketPool = PacketPool;
        pIF->ethernet.BufferPool = BufferPool;
        PacketPool = NULL;
        BufferPool = NULL;

    } while (FALSE);

    if (FAIL(Status))
    {
        if (PacketPool != NULL)
        {
            NdisFreePacketPool(PacketPool);
        }
    
        if (BufferPool != NULL)
        {
            NdisFreeBufferPool(BufferPool);
        }
    }
    else
    {
        ASSERT(PacketPool == NULL && BufferPool == NULL);
    }

    return Status;
}


VOID
arpFreeEthernetPools(
    IN  PARP1394_INTERFACE  pIF,
    IN  PRM_STACK_RECORD    pSR
    )
{
    NDIS_HANDLE PacketPool;
    NDIS_HANDLE BufferPool;
    ENTER("arpFreeEthernetPools", 0x3e780760)

    // Make sure the IF is locked.
    //
    RM_ASSERT_OBJLOCKED(&pIF->Hdr, pSR);

    PacketPool = pIF->ethernet.PacketPool;
    BufferPool = pIF->ethernet.BufferPool;
    pIF->ethernet.PacketPool = NULL;
    pIF->ethernet.BufferPool = NULL;
    
    // (DBG only) Remove associations for the control and packet pools.
    //
    DBG_DELASSOC(
        &pIF->Hdr,                  // pObject
        PacketPool,                 // Instance1
        NULL,                       // Instance2
        ARPASSOC_IF_ETHPKTPOOL, // AssociationID
        pSR
        );
    DBG_DELASSOC(
        &pIF->Hdr,                  // pObject
        BufferPool,                 // Instance1
        NULL,                       // Instance2
        ARPASSOC_IF_ETHBUFPOOL, // AssociationID
        pSR
        );

    // Free the buffer and packet pools
    // 
    NdisFreeBufferPool(BufferPool);
    NdisFreePacketPool(PacketPool);
}