//
// Copyright (c) 1998-1999, Microsoft Corporation, all rights reserved
//
// send.c
//
// IEEE1394 mini-port/call-manager driver
//
// Mini-port Send routines
//
// 12/28/1998 ADube Created, adapted from the l2tp and 1394diag sources.
//
    
//
// The Send Complete functions begin here. Send.c starts here
// Also present are the all thr buffer management routines that need to be expanded 
//

//
//  A Send follows this simple algorithm:
//  Copy incoming data to local buffers
//  Insert Fragment Headers if necessary
//  Create an Mdl for the local copy
//  Store the IRB and VC in the ndispacket
//  Use the ndispacket as context in the irp's completion routine
//  

#include <precomp.h>

//-----------------------------------------------------------------------------
// Global counts
//-----------------------------------------------------------------------------
extern UINT BusSendCompletes;
extern UINT NicSendCompletes;
extern UINT BusSends;
extern ULONG MdlsAllocated[NoMoreCodePaths];
extern ULONG MdlsFreed[NoMoreCodePaths];

//-----------------------------------------------------------------------------
// prototypes implementations (alphabetically)
//-----------------------------------------------------------------------------

NDIS_STATUS
nicCopyNdisBufferChainToBuffer(
    IN PNDIS_BUFFER pInBuffer,
    IN OUT PVOID pLocalBuffer,
    IN UINT BufferLength )
{


    //
    //  This function copies the data the belongs to the 
    //  pInMdl chain to the local Buffer. 
    //  BufferLength is used for validation purposes only
    //  Fragmentation and insertion of headers will take place here
    //


    NDIS_STATUS NdisStatus = NDIS_STATUS_SUCCESS;
    UINT LocalBufferIndex = 0;      // Used as an index to the LocalBuffer, used for validation
    UINT MdlLength = 0;             
    PVOID MdlAddress = 0;
    PNDIS_BUFFER pCurrBuffer;

    TRACE( TL_T, TM_Send, ( "==>nicCopyNdisBufferChainToBuffer pNdisbuffer %x, Buffer %x, Length %x",
                           pInBuffer, pLocalBuffer,BufferLength ) );

    ASSERT (pLocalBuffer != NULL);

    pCurrBuffer = pInBuffer;
    
    do
    {

        MdlLength = nicNdisBufferLength(pCurrBuffer);
        MdlAddress= nicNdisBufferVirtualAddress(pCurrBuffer);

        if (MdlLength != 0)
        {
            if (MdlAddress == NULL)
            {
                NdisStatus = NDIS_STATUS_FAILURE;
                TRACE (TL_A, TM_Send, ("Ndis Buffer at %x", pCurrBuffer) );
                BREAK (TM_Send, ("   nicCopyNdisBufferChainToBuffer: Mdl Address = NULL") );

            }

            if ( LocalBufferIndex + MdlLength > BufferLength)
            {

                ASSERT(LocalBufferIndex + MdlLength <= BufferLength);

                NdisStatus = NDIS_STATUS_BUFFER_TOO_SHORT;

                BREAK (TM_Send, ("nicCopyNdisBufferChainToBuffer Copy Failed" ) );
            }

            //
            //  Copy the Data to local memory.
            //


            NdisMoveMemory((PVOID)((ULONG_PTR)pLocalBuffer+LocalBufferIndex),
                        MdlAddress,
                        MdlLength);

            LocalBufferIndex += MdlLength;
        }

        pCurrBuffer = pCurrBuffer->Next;

    } while (pCurrBuffer!= NULL);

    TRACE( TL_T, TM_Send, ( "<==nicCopyNdisBufferChainToBuffer %x",NdisStatus ) );

    return NdisStatus;

}


NDIS_STATUS
nicFreeIrb(PIRB pIrb)
    //
    //  Frees the Memory occcupied by the Irb
    //

{

    

    NDIS_STATUS NdisStatus = NDIS_STATUS_SUCCESS;   
    
    ASSERT(pIrb != NULL);

    TRACE( TL_T, TM_Irp, ( "==>nicFreeIrb %x", pIrb ) );

    if (pIrb != NULL)
    {
        FREE_NONPAGED(pIrb); 
    }
    
    TRACE( TL_T, TM_Irp, ( "<==nicFreeIrb, NdisStatus %x",NdisStatus ) );
    
    return NdisStatus;


}

NDIS_STATUS
nicFreePrivateIrb(
    PNDIS1394_IRB pIrb
    )
    //
    //  Frees the Memory occcupied by the Irb
    //

{

    

    NDIS_STATUS NdisStatus = NDIS_STATUS_SUCCESS;   
    
    ASSERT(pIrb != NULL);

    TRACE( TL_T, TM_Irp, ( "==>nicFreeIrb %x", pIrb ) );

    if (pIrb != NULL)
    {
        FREE_NONPAGED(pIrb); 
    }
    
    TRACE( TL_T, TM_Irp, ( "<==nicFreeIrb, NdisStatus %x",NdisStatus ) );
    
    return NdisStatus;


}

NDIS_STATUS
nicFreeIrp(PIRP pIrp)

    //
    //  Frees the memory occupied by the  Irp
    //


{

    NDIS_STATUS NdisStatus  = NDIS_STATUS_SUCCESS;

    ASSERT(pIrp != NULL);   

    TRACE( TL_T, TM_Irp, ( "==>nicFreeIrp at %x",pIrp ) );

    if (pIrp != NULL)
    {
        IoFreeIrp(pIrp);
    }
    
    TRACE( TL_T, TM_Irp, ( "<==nicFreeIrp, NdisStatus %x",NdisStatus  ) );


    return NdisStatus;


}



NDIS_STATUS
nicFreeLocalBuffer (
    IN UINT Length,
    IN PVOID Address )
    //
    //  Free the Memory pointed to by Address.
    //  The Length parameter is superfluous and will be removed
    //  once I am sure we don;t need it
    //
{
    

    NDIS_STATUS NdisStatus = NDIS_STATUS_SUCCESS;

    ASSERT(Address != NULL);

    TRACE( TL_T, TM_Send, ( "==>nicFreeLocalBuffer , Address %x", Address) );

    if (Address != NULL)
    {
        FREE_NONPAGED((PVOID)Address);
    }
    
    TRACE( TL_T, TM_Send, ( "<==niFreeLocalBuffer, NdisStatus %x",NdisStatus ) );
    

    return NdisStatus;

}


NDIS_STATUS
nicFreeMdl(PMDL pMdl)
    //
    //  This frees the memory belonging to the Mdl. Does not free the  
    //  memory that the Mdl Points to
    //

{
    NDIS_STATUS NdisStatus = NDIS_STATUS_SUCCESS;

    ASSERT (pMdl != NULL)
    
    TRACE( TL_T, TM_Send, ( "==> nicFreeMdl pMdl %x", pMdl ) );
    
    if (pMdl != NULL)
    {
        IoFreeMdl(pMdl);
    }
    
    TRACE( TL_T, TM_Send, ( "<== nicFreeMdl, NdisStatus %x",NdisStatus ) );

    return NdisStatus;
}

VOID
nicFreeToNPagedLookasideList (
    IN PNIC_NPAGED_LOOKASIDE_LIST pLookasideList,
    IN PVOID    pBuffer
    )

    // Function Description:
    //   Return the local buffer to the lookaside list
    //
    // Atguments
    // Lookaside list and its buffer
    // Return Value:
    // None 
{

    
    TRACE( TL_T, TM_Send, ( "==> nicFreeToNPagedLookasideList , Lookaside list %x, plocalbuffer %x",pLookasideList, pBuffer ) );

    NdisFreeToNPagedLookasideList (&pLookasideList->List, pBuffer);     
    NdisInterlockedDecrement (&pLookasideList->OutstandingPackets);

    TRACE( TL_T, TM_Send, ( "<== nicFreeToNPagedLookasideList ") );


}













NDIS_STATUS
nicGetIrb(
    OUT     PIRB *ppIrb )


    //
    // This function is to be used in retrieving a free IRB.
    // that will be supplied as an argument for an IRP
    //
    //  Initially, this will simple allocate an IRB 
    //  Intiailization could be added here
    //
    
{

    NDIS_STATUS NdisStatus;

    
    TRACE( TL_T, TM_Irp, ( "==>nicGetIrb" ) );
    
    *ppIrb = (PIRB)ALLOC_NONPAGED ( sizeof(IRB), MTAG_HBUFPOOL );

    if (*ppIrb != NULL)
    {   
        NdisZeroMemory ( *ppIrb, sizeof(IRB) );
        NdisStatus = NDIS_STATUS_SUCCESS;
        TRACE( TL_V, TM_Send, ( "   nicGetIrb: Irb allocated at %x", *ppIrb ) );

    }
    else
    {
        nicIncrementMallocFailure();
        NdisStatus = NDIS_STATUS_FAILURE;
    }
    
    TRACE( TL_T, TM_Irp, ( "<==nicGetIrb NdisStatus %x",NdisStatus ) );
    
    return NdisStatus;
}


NDIS_STATUS
nicGetIrp(
    IN  PDEVICE_OBJECT pPdo,
    OUT PIRP *ppIrp 
    )


    //
    // This function returns am irp to the calling routine
    // The irp is free and is owned by the nic1394. 
    // NEED TO CHANGE THE STACK SIZE
    //
{

    NDIS_STATUS NdisStatus;
    PIRP  pIrp;
    CCHAR StackSize =0; 

    ASSERT (pPdo != NULL);
    
    TRACE( TL_T, TM_Irp, ( "==>nicGetIrp Pdo %x", pPdo ) );

    if (pPdo == NULL)
    {
        ASSERT (pPdo != NULL);      
        NdisStatus = NDIS_STATUS_FAILURE;
        *ppIrp = NULL;
        return NdisStatus;
    }


    //
    //  Allocate the Irp with the correct stacksize
    //
    StackSize = pPdo->StackSize+1;

    ASSERT (StackSize <=3);
    
    pIrp = IoAllocateIrp (StackSize, FALSE);

    do
    {
    
        if (pIrp == NULL)
        {
            NdisStatus = NDIS_STATUS_FAILURE;
            break;
        }

        TRACE( TL_V, TM_Send, ( "  Irp allocated at %x, Stacksize %x",pIrp , StackSize ) );

        *ppIrp = pIrp;
    
        //
        // Initialize the Irp
        //

        IoInitializeIrp ( *ppIrp, sizeof(IRP), StackSize );

        if (*ppIrp != NULL)
        {
            NdisStatus = NDIS_STATUS_SUCCESS;
        }
        else
        {
            nicIncrementMallocFailure();
            NdisStatus = NDIS_STATUS_FAILURE;
        }
    
    } while (FALSE);

    TRACE( TL_T, TM_Irp, ( "<==nicGetIrp ,irp %x",*ppIrp  ) );
    
    return NdisStatus;
}



NDIS_STATUS
nicGetPrivateIrb(
    IN PADAPTERCB pAdapter OPTIONAL,
    IN PREMOTE_NODE pRemoteNode OPTIONAL,
    IN PVCCB pVc,
    IN PVOID pContext,
    OUT PNDIS1394_IRB *ppIrb 
    )


    //
    // This function is to be used in retrieving a free IRB.
    // that will be supplied as an argument for an IRP
    //
    //  Initially, this will simple allocate an IRB 
    //  Intiailization could be added here
    //
    
{

    NDIS_STATUS NdisStatus;
    PNDIS1394_IRB pIrb = NULL;
    
    TRACE( TL_T, TM_Irp, ( "==> nicGetPrivateIrb pRemoteNode %x, pVc %x", pRemoteNode, pVc  ) );
    
    *ppIrb = (PNDIS1394_IRB)ALLOC_NONPAGED ( sizeof(NDIS1394_IRB), MTAG_HBUFPOOL );

    if (*ppIrb != NULL)
    {   
        NdisZeroMemory ( *ppIrb, sizeof(IRB) );
        NdisStatus = NDIS_STATUS_SUCCESS;

        pIrb = *ppIrb;

        pIrb->pAdapter = pAdapter;
        pIrb->pRemoteNode = pRemoteNode;
        pIrb->pVc = pVc;
        pIrb->Context = pContext;
        
        TRACE( TL_V, TM_Send, ( "   nicGetPrivateIrb: Irb allocated at %x", *ppIrb ) );

    }
    else
    {
        nicIncrementMallocFailure();
        NdisStatus = NDIS_STATUS_FAILURE;
    }

    TRACE( TL_T, TM_Irp, ( "<==nicGetPrivateIrb NdisStatus %x",NdisStatus ) );
    
    return NdisStatus;
}


NDIS_STATUS
nicGetLocalBuffer(
    OPTIONAL IN  ULONG Length,
    OUT PVOID *ppLocalBuffer 
    )

    //
    //  If the lookaside list is not NULL then it is used to allocate the local buffer
    //
    //  This function allocates memory of size 'Length' and returns 
    //  a pointer to this memory 
    //  Subsequently allocation will be done away with and pools will
    //  be used
    //
{

    NDIS_STATUS NdisStatus = NDIS_STATUS_FAILURE;

    TRACE( TL_T, TM_Send, ( "==>nicGetLocalBuffer  Length %x",  Length ) );
    
    //
    // There is a bug in the Nic if this is zero
    // 
    ASSERT (Length != 0 );


    //
    // There is no lookaside list, We need to allocate memory
    //
    *ppLocalBuffer = ALLOC_NONPAGED (Length, MTAG_FBUFPOOL);
        

    if (*ppLocalBuffer != NULL)
    {
        NdisStatus = NDIS_STATUS_SUCCESS;   
    }
    else
    {
        nicIncrementMallocFailure();
        NdisStatus = NDIS_STATUS_FAILURE;
    }

    
    TRACE( TL_T, TM_Send, ( "<==nicGetLocalBuffer, NdisStatus %x at %x",NdisStatus,*ppLocalBuffer ) );
    
    return NdisStatus;

}





PVOID
nicGetLookasideBuffer(
    IN  PNIC_NPAGED_LOOKASIDE_LIST pLookasideList
    )
    // Function Description:
    //    Allocate an buffer from the lookaside list.
    //    will be changed to a macro
    //
    //
    //
    // Arguments
    //  Lookaside list - from which the buffer is allocated
    //
    //
    // Return Value:
    //  Return buffer can be NULL
    //
{

    PVOID pLocalBuffer = NULL;
    
    TRACE( TL_T, TM_Send, ( "==>nicGetLookasideBuffer pLookasideList %x", pLookasideList) );
    
    ASSERT (pLookasideList != NULL);

    //
    // Optimize the lookaside list code path
    //
    pLocalBuffer = NdisAllocateFromNPagedLookasideList (&pLookasideList->List);

    if (pLocalBuffer != NULL)
    {   
        NdisZeroMemory (pLocalBuffer, pLookasideList->Size); 
        NdisInterlockedIncrement (&pLookasideList->OutstandingPackets);
    }
    else
    {
        nicIncrementMallocFailure();
    }

        
    
    TRACE( TL_T, TM_Send, ( "<==nicGetLookasideBuffer, %x", pLocalBuffer ) );
    
    return pLocalBuffer ;

}



NDIS_STATUS
nicGetMdl(
    IN UINT Length,
    IN PVOID pLocalBuffer,
    OUT PMDL *ppMyMdl)


    //
    //  Return a locally owned Mdl to the caller.
    //  This will also initialize the MDl with the localbuffer
    //  Initial implementation will allocate mdls
    //  
{

    NDIS_STATUS NdisStatus;
    
    TRACE( TL_T, TM_Send, ( "==>nicGetMdl" ) );
    
    ASSERT(pLocalBuffer != NULL);

    //
    // Allocate an MDl to point to the structure
    //
    (*ppMyMdl) = NULL;
    (*ppMyMdl) = IoAllocateMdl( pLocalBuffer,
                             Length,
                             FALSE,
                             FALSE,
                             NULL );
    
    //
    //  Initialize the data structures with correct values
    //

    if (*ppMyMdl != NULL)
    {
        MmBuildMdlForNonPagedPool(*ppMyMdl);

        (*ppMyMdl)->Next = NULL;
        
        NdisStatus = NDIS_STATUS_SUCCESS;
    }
    else
    {
        nicIncrementMallocFailure();
        NdisStatus =  NDIS_STATUS_FAILURE;
        *ppMyMdl = NULL;
    }
    
    
    
    TRACE( TL_T, TM_Send, ( "<==nicGetMdl, Mdl %x, LocalBuffer %x",
                                        *ppMyMdl, pLocalBuffer) );
    
    return NdisStatus;
}






VOID
nicInitAsyncStreamIrb(
    IN     PCHANNEL_VCCB pChannelVc, 
    IN     PMDL pMdl, 
    IN OUT PIRB pIrb
    )
    
    // This function initializes the Irb that will be used in the Irb
    // It specifically handles the AsyncStream IRB
    // It arguments are the Vc block (for destination address), 
    // Mdl (Memory desctiptor for the data and a pointer to the 
    // Irb structure that will be initialized 




{
    ASSERT (pMdl != NULL);
    ASSERT (pIrb != NULL);

    NdisZeroMemory (pIrb, sizeof (IRB) );
    pIrb->FunctionNumber = REQUEST_ASYNC_STREAM;
    pIrb->Flags = 0;
    pIrb->u.AsyncStream.nNumberOfBytesToStream = MmGetMdlByteCount(pMdl);
    pIrb->u.AsyncStream.fulFlags = 0;

    //
    // See comments for ISOCH_TAG 
    //
    pIrb->u.AsyncStream.ulTag = g_IsochTag; 
    pIrb->u.AsyncStream.nChannel = pChannelVc->Channel;
    pIrb->u.AsyncStream.ulSynch = pChannelVc->ulSynch;
    pIrb->u.AsyncStream.nSpeed = (INT)pChannelVc->Speed;
    pIrb->u.AsyncStream.Mdl = pMdl;
    
        
        
    TRACE( TL_V, TM_Send, ( "Number of Bytes to Stream %x ", pIrb->u.AsyncStream.nNumberOfBytesToStream  ) );
    TRACE( TL_V, TM_Send, ( "fulFlags %x ", pIrb->u.AsyncStream.fulFlags  ) );
    TRACE( TL_V, TM_Send, ( "ulTag %x ", pIrb->u.AsyncStream.ulTag ) );
    TRACE( TL_V, TM_Send, ( "Channel %x", pIrb->u.AsyncStream.nChannel  ) );
    TRACE( TL_V, TM_Send, ( "Synch %x", pIrb->u.AsyncStream.ulSynch  ) );
    TRACE( TL_V, TM_Send, ( "Speed %x", pIrb->u.AsyncStream.nSpeed  ) );
    TRACE( TL_V, TM_Send, ( "Mdl %x", pIrb->u.AsyncStream.Mdl ) );

}


VOID
nicInitAsyncWriteIrb(
    IN     PSENDFIFO_VCCB pSendFIFOVc, 
    IN     PMDL pMyMdl, 
    IN OUT PIRB pMyIrb
    )
    
    // This function initializes the Irb that will be used in the Irb
    // It specifically handles the AsyncWrite IRB
    // It arguments are the Vc block (for destination address), 
    // Mdl (Memory desctiptor for the data and a pointer to the 
    // Irb structure that will be initialized 




{

        //
        // Sanity check
        //
        ASSERT ((*(PULONG)pMyIrb) == 0)

        pMyIrb->u.AsyncWrite.nNumberOfBytesToWrite = MmGetMdlByteCount(pMyMdl);
        pMyIrb->u.AsyncWrite.nBlockSize = 0;
        pMyIrb->u.AsyncWrite.fulFlags = 0;
        pMyIrb->u.AsyncWrite.Mdl = pMyMdl;
    
        pMyIrb->FunctionNumber = REQUEST_ASYNC_WRITE;
        pMyIrb->Flags = 0;
        pMyIrb->u.AsyncWrite.nSpeed = (UCHAR)pSendFIFOVc->MaxSendSpeed ;

        pMyIrb->u.AsyncWrite.DestinationAddress.IA_Destination_Offset.Off_High = pSendFIFOVc->FifoAddress.Off_High;
        pMyIrb->u.AsyncWrite.DestinationAddress.IA_Destination_Offset.Off_Low = pSendFIFOVc->FifoAddress.Off_Low;
        pMyIrb->u.AsyncWrite.ulGeneration = *pSendFIFOVc->Hdr.pGeneration;

    
        pMyIrb->u.AsyncWrite.nBlockSize = 0;
        pMyIrb->u.AsyncWrite.fulFlags = ASYNC_FLAGS_NONINCREMENTING;

        //temporary additions from the 1394diag
        pMyIrb->u.AsyncWrite.DestinationAddress.IA_Destination_ID.NA_Bus_Number = 0x3ff;
        
        
        
        TRACE(TL_V, TM_Send, ("DestinationAddress.IA_Destination_ID.NA_Bus_Number = 0x%x\n", pMyIrb->u.AsyncWrite.DestinationAddress.IA_Destination_ID.NA_Bus_Number) );
        TRACE(TL_V, TM_Send, ("DestinationAddress.IA_Destination_ID.NA_Node_Number = 0x%x\n", pMyIrb->u.AsyncWrite.DestinationAddress.IA_Destination_ID.NA_Node_Number) );
        TRACE(TL_V, TM_Send, ("DestinationAddress.IA_Destination_Offset.Off_High = 0x%x at 0x%x\n", pMyIrb->u.AsyncWrite.DestinationAddress.IA_Destination_Offset.Off_High, &pMyIrb->u.AsyncWrite.DestinationAddress.IA_Destination_Offset.Off_High) );
        TRACE(TL_V, TM_Send, ("DestinationAddress.IA_Destination_Offset.Off_Low = 0x%x at 0x%x\n", pMyIrb->u.AsyncWrite.DestinationAddress.IA_Destination_Offset.Off_Low,&pMyIrb->u.AsyncWrite.DestinationAddress.IA_Destination_Offset.Off_Low) );
        TRACE(TL_V, TM_Send, ("nNumberOfBytesToWrite = 0x%x\n", pMyIrb->u.AsyncWrite.nNumberOfBytesToWrite));
        TRACE(TL_V, TM_Send, ("nBlockSize = 0x%x\n", pMyIrb->u.AsyncWrite.nBlockSize));
        TRACE(TL_V, TM_Send, ("fulFlags = 0x%x\n", pMyIrb->u.AsyncWrite.fulFlags));
        TRACE(TL_V, TM_Send, ("Mdl = 0x%x\n", pMyIrb->u.AsyncWrite.Mdl ));
        TRACE(TL_V, TM_Send, ("ulGeneration = 0x%x at 0x%x \n", pMyIrb->u.AsyncWrite.ulGeneration, &pMyIrb->u.AsyncWrite.ulGeneration));
        TRACE(TL_V, TM_Send, ("chPriority = 0x%x\n", pMyIrb->u.AsyncWrite.chPriority));
        TRACE(TL_V, TM_Send, ("nSpeed = 0x%x\n", pMyIrb->u.AsyncWrite.nSpeed));

}





NDIS_STATUS
DummySendPacketsHandler(
    IN PVCCB        pVc,
    IN PNDIS_PACKET  pPacket 
    )
    //
    // To be used on a non-send VC
    //
{

    return NDIS_STATUS_FAILURE;

}
















VOID
nicSendFailureInvalidGeneration(
    PVCCB pVc
    )

    
    // Function Description:
    //
    //  An AsyncStream or AnsyncWrite Irp  may be completed
    //  with a status of InvalidGeneration. This function will try and
    //  get a new generation, so that future sends will not be blocked
    //
    // Arguments
    //
    //
    //
    // Return Value:
    //
    //
    //
    //

{
    NDIS_STATUS NdisStatus  = NDIS_STATUS_FAILURE;
    PADAPTERCB pAdapter = pVc->Hdr.pAF->pAdapter;
    
    TRACE( TL_T, TM_Send, ( "==>nicSendFailureInvalidGeneration ") );

    ASSERT (pVc != NULL);
    
    do
    {
        PNDIS_WORK_ITEM pGetGenerationWorkItem  = NULL;
        BOOLEAN fWorkItemAlreadyLaunched  = FALSE;
        BOOLEAN fQueueWorkItem = FALSE;

        
        TRACE( TL_A, TM_Send, ( "Cause: Invalid generation on the asyncwrite packet"  ) );

        VC_ACQUIRE_LOCK (pVc);

        if (VC_ACTIVE(pVc) == TRUE)
        {
            fQueueWorkItem = TRUE;
        }
        
        fWorkItemAlreadyLaunched = (VC_TEST_FLAGS (pVc, VCBF_GenerationWorkItem));
        
        if (fWorkItemAlreadyLaunched)
        {
            //
            // If the Work Item has already been launched, then do not launch another instance
            //
            fQueueWorkItem = FALSE;
        }
        
        if ( fQueueWorkItem )
        {
            nicReferenceCall (pVc, "nicSendFailureInvalidGeneration");
        }
        
        VC_RELEASE_LOCK (pVc);

        

        if (fQueueWorkItem == FALSE)
        {
            // this thread simply exits 
            break;
        }
        //
        // We need to update the generation count
        //
        pGetGenerationWorkItem = ALLOC_NONPAGED (sizeof(NDIS_WORK_ITEM), MTAG_WORKITEM); 

        if (pGetGenerationWorkItem == NULL)
        {
            TRACE( TL_A, TM_Cm, ( "Local Alloc failed for WorkItem - GetGeneration FAILED" ) );

            break;
        }

        VC_ACQUIRE_LOCK (pVc);

        VC_SET_FLAG(pVc, VCBF_GenerationWorkItem    );
        
        VC_RELEASE_LOCK (pVc);

        NdisInitializeWorkItem ( pGetGenerationWorkItem , 
                              (NDIS_PROC)nicGetGenerationWorkItem,
                              (PVOID)pVc );

        NdisInterlockedIncrement(&pAdapter->OutstandingWorkItems);

        NdisScheduleWorkItem (pGetGenerationWorkItem );

        
        NdisStatus = NDIS_STATUS_SUCCESS;

    } while (FALSE);





    TRACE( TL_T, TM_Send, ( "<==nicSendFailureInvalidGeneration %x", NdisStatus) );



}





NDIS_STATUS
nicInsertGaspHeader (
    IN PADAPTERCB pAdapter,
    IN PNDIS_PACKET pNdisPacket
    )
    // Function Description:
    //   For an async stream a GASP header needs to inserted at the head of the packet
    //   This will then be used in the send.
    //   The GASP Header will be removed before returning the Packet to the protocol
    // Arguments
    //  pAdapter- Local Host
    //  pNdisPacket - Packet To be transmitted
    //
    // Return Value:
    //  Status - success, if all allocations and insertions succeed
    //
{
    NDIS_STATUS         NdisStatus = NDIS_STATUS_FAILURE;
    PNDIS_BUFFER        pGaspNdisBuffer = NULL;
    PGASP_HEADER        pGaspHeader=NULL;
    USHORT              SourceID;
    TRACE( TL_T, TM_Send, ( "==>nicInsertGaspHeader pAdapter %x, pNdisPacket%x", pAdapter, pNdisPacket) );

    do
    {
        //
        //  Get Mdl and Memory for the GASP Header. Eventually we will have lookaside list of MDLS and Buffers
        //

        NdisStatus = nicGetGaspHeader ( &pGaspNdisBuffer);

        if (NdisStatus != NDIS_STATUS_SUCCESS || pGaspNdisBuffer== NULL)
        {
            BREAK( TL_A, ( "nicInsertGaspHeader : nicGetGaspHeader FAILED") );
        }

        pGaspHeader = nicNdisBufferVirtualAddress(pGaspNdisBuffer);

        ASSERT (pGaspHeader != NULL);
        
        TRACE( TL_V, TM_Send, ( "pGaspNdisBuffer %x, GaspHeader %x", pGaspNdisBuffer, pGaspHeader) );

        TRACE( TL_V, TM_Send, ( "pAdapter->NodeAddress %x", pAdapter->NodeAddress) );
            

        SourceID = *((PUSHORT)&pAdapter->NodeAddress);

        TRACE( TL_V, TM_Send, ( "SourceId %x at %x", SourceID, &SourceID) );
        
        pGaspHeader->FirstQuadlet.Bitmap.GH_Source_ID = SourceID ; 

        pGaspHeader->FirstQuadlet.Bitmap.GH_Specifier_ID_Hi = GASP_SPECIFIER_ID_HI;
        
        pGaspHeader->SecondQuadlet.Bitmap.GH_Specifier_ID_Lo = GASP_SPECIFIER_ID_LO;

        pGaspHeader->SecondQuadlet.Bitmap.GH_Version = 0;       

        NdisChainBufferAtFront (pNdisPacket, pGaspNdisBuffer);

        pGaspHeader->FirstQuadlet.GaspHeaderHigh = SWAPBYTES_ULONG (pGaspHeader->FirstQuadlet.GaspHeaderHigh );
        pGaspHeader->SecondQuadlet.GaspHeaderLow  = SWAPBYTES_ULONG (pGaspHeader->SecondQuadlet.GaspHeaderLow   );

        TRACE( TL_V, TM_Send, ( "Gasp Header High %x", pGaspHeader->FirstQuadlet.GaspHeaderHigh) );
        TRACE( TL_V, TM_Send, ( "Gasp Header Low  %x", pGaspHeader->SecondQuadlet.GaspHeaderLow  ) );

        NdisStatus = NDIS_STATUS_SUCCESS;

    } while (FALSE);
    
    TRACE( TL_T, TM_Send, ( "<==nicInsertGaspHeader pNdisBuffer %x, NdisStatus %x ", pGaspNdisBuffer, NdisStatus) );

    return NdisStatus;
}


NDIS_STATUS
nicGetGaspHeader (
    IN OUT PNDIS_BUFFER *ppNdisBuffer
    )
    // Function Description:
    //
    //   Gets memory and an initialized NdisBuffer that can be used for 
    //   the gasp header
    // Arguments
    //  ppNdisBuffer  - returned NdisBuffer 
    // Return Value:
    //  Status - success, if all allocations succeed
    //
{

    PVOID pBuffer= NULL;
    NDIS_STATUS NdisStatus = NDIS_STATUS_FAILURE;
    
    TRACE( TL_T, TM_Send, ( "==>nicGetGaspHeader  ppNdisBuffer %x", ppNdisBuffer) );

    *ppNdisBuffer = NULL;
    
    do
    {
        pBuffer = ALLOC_NONPAGED(sizeof(GASP_HEADER), MTAG_DEFAULT);
        
        if (pBuffer == NULL)
        {
            NdisStatus = NDIS_STATUS_FAILURE;
            nicIncrementMallocFailure();
            BREAK ( TM_Send, ( "nicGetGaspHeader : MEM Alloc FAILED ") );
        }


        NdisStatus = nicGetNdisBuffer(sizeof(GASP_HEADER), pBuffer, ppNdisBuffer);

        if (NdisStatus != NDIS_STATUS_SUCCESS)
        {
            BREAK(  TM_Send, ( "nicGetGaspHeader : nicGetNdisBuffer FAILED") );
        }

        NdisStatus = NDIS_STATUS_SUCCESS;
            
    } while (FALSE);


    TRACE( TL_T, TM_Send, ( "==>nicGetGaspHeader pNdisBuffer %x , Status %x", *ppNdisBuffer, NdisStatus) );
    return NdisStatus;  

}


VOID
nicFreeGaspHeader (
    IN PNDIS_BUFFER pGaspNdisBuffer
    )
    // Function Description:
    //
    //   Frees memory occupied by the Gasp Header. frees the Ndis Buffer pointing to the Gasp Header
    //
    // Arguments
    // pGapsNdisBuffer  - returned NdisBuffer 
    //
    // Return Value:
    //  None
{

   TRACE( TL_T, TM_Send, ( "==>nicFreeGaspHeader  pGaspNdisBuffer %x ", pGaspNdisBuffer) );
    ASSERT (pGaspNdisBuffer != NULL); 
    //
    // Free the locally allocated memory that the ndis buffer points to 
    //

    FREE_NONPAGED (nicNdisBufferVirtualAddress (pGaspNdisBuffer) );

    //
    // Free the ndis buffer itself
    //
    NdisFreeBuffer (pGaspNdisBuffer);


   TRACE( TL_T, TM_Send, ( "<==nicFreeGaspHeader  pGaspNdisBuffer %x ", pGaspNdisBuffer) );


}
    

VOID
nicMakeGaspHeader (
    IN PADAPTERCB pAdapter,
    IN PGASP_HEADER pGaspHeader
    )
    // Function Description:
    //   This function will take the adapter structure and construct a Gasp Header out of it. 
    //   This will be used to make the AsyncStream packets.
    //  
    //
    //
    // Arguments
    //  pAdapter - Local Host in question
    //  pGaspHeader - Location where the Gasp Header is to be stored
    //
    // Return Value:
    //  None
    //

{
    USHORT              SourceID;
    NODE_ADDRESS        LocalNodeAddress;
    NDIS_STATUS         NdisStatus;

    TRACE( TL_T, TM_Send, ( "==>nicMakeGaspHeader  padapter %x, pGaspNdisBuffer %x ", pAdapter, pGaspHeader) );
    
    ASSERT (pGaspHeader != NULL);
    

    TRACE( TL_V, TM_Send, ( "pAdapter->NodeAddress %x", pAdapter->NodeAddress) );
        

    SourceID = *((PUSHORT)&pAdapter->NodeAddress);

    if(SourceID ==0)
    {
        NdisStatus  = nicGet1394AddressFromDeviceObject (pAdapter->pNdisDeviceObject, 
                                                          &LocalNodeAddress, 
                                                          USE_LOCAL_NODE);

        if ( NdisStatus == NDIS_STATUS_SUCCESS)
        {       
            SourceID = *((PUSHORT)&LocalNodeAddress);

            ADAPTER_ACQUIRE_LOCK (pAdapter);

            pAdapter->NodeAddress = LocalNodeAddress;
                
            ADAPTER_RELEASE_LOCK (pAdapter);
        }
        //
        // Do not handle failure. As the BCM or a Reset will fix this problem
        //
    }


    TRACE( TL_V, TM_Send, ( "SourceId %x at %x", SourceID, &SourceID) );
    
    pGaspHeader->FirstQuadlet.Bitmap.GH_Source_ID = SourceID ; 

    pGaspHeader->FirstQuadlet.Bitmap.GH_Specifier_ID_Hi = GASP_SPECIFIER_ID_HI;
    
    pGaspHeader->SecondQuadlet.Bitmap.GH_Specifier_ID_Lo = GASP_SPECIFIER_ID_LO;

    pGaspHeader->SecondQuadlet.Bitmap.GH_Version = 1;       

    pGaspHeader->FirstQuadlet.GaspHeaderHigh = SWAPBYTES_ULONG (pGaspHeader->FirstQuadlet.GaspHeaderHigh );
    pGaspHeader->SecondQuadlet.GaspHeaderLow  = SWAPBYTES_ULONG (pGaspHeader->SecondQuadlet.GaspHeaderLow   );

    TRACE( TL_V, TM_Send, ( "Gasp Header High %x", pGaspHeader->FirstQuadlet.GaspHeaderHigh) );
    TRACE( TL_V, TM_Send, ( "Gasp Header Low  %x", pGaspHeader->SecondQuadlet.GaspHeaderLow  ) );

    TRACE( TL_T, TM_Send, ( "<==nicFreeGaspHeader %x, %x ", pGaspHeader->FirstQuadlet.GaspHeaderHigh, pGaspHeader->SecondQuadlet.GaspHeaderLow  ) );


}








NTSTATUS
AsyncWriteStreamSendComplete(
    IN PDEVICE_OBJECT   DeviceObject,
    IN PIRP             pMyIrp,
    IN PVOID            Context   
    )


    //
    //  N.B.  this completes both Fifo and channels
    //
    //
    //  This function is Completion handler for the Irp used to send data.
    //  This function will invoke NDisCoSendComplete Handler
    //  Needs to use the VC Handle stored in the MiniportReserved[0] 
    //  of the packet. 
    //  We free all the data structures allocated on the way down,  
    //  by SendPacketsHandler (the Irb. Irp and Local memory used and Mdl) 
    //
    //  The LookasideHeader->OutstandingFragments should normally be one for 
    //  the defualt ( non-fragmented) case. However, if a failure in SendPackets
    //  occurs, Outstanding fragments  will be zero or the context will be null, 
    //  in that case we will only free the lookaside buffer (if it exists) and exit,  
    //  it will be the responsibility of the SendPacketsHandler
    //  to fail the packet.
    //

{

    
    NDIS_STATUS                 NdisStatus  = NDIS_STATUS_SUCCESS;  
    NTSTATUS                    IrpStatus   = STATUS_UNSUCCESSFUL;
    PVOID                       pLookasideListBuffer  = Context;  
    PLOOKASIDE_BUFFER_HEADER    pLookasideHeader = (PLOOKASIDE_BUFFER_HEADER)pLookasideListBuffer ; 
    PNDIS_PACKET                pPacket = NULL;
    PVCCB                       pVc = NULL; 
    PREMOTE_NODE                pRemoteNode = NULL;
    NDIS_HANDLE                 NdisVcHandle = NULL;
    ULONG                       OutstandingFragments  = 0;
    BUS_OPERATION               AsyncOp;
    PNIC_NPAGED_LOOKASIDE_LIST  pLookasideList = NULL;
    STORE_CURRENT_IRQL;


    TRACE( TL_T, TM_Send, ( "==>AsyncWriteStreamSendComplete, pMyIrp %x, Context %x", 
                                 pMyIrp, Context   ) );

    do 
    {
        if (pLookasideHeader == NULL)
        {
            TRACE( TL_V, TM_Send, ( "   AsyncSendComplete -  pLookasideHeader == NULL") );
            break;

        }
    

        //
        // This means that a lookaside buffer was allocated and
        // perhaps MDLS were allocated 
        //  if this is the last fragment, Free all the MDLs first
        //

        //
        // Get all the valuable information out of the header. 
        //

        pPacket         = pLookasideHeader->pNdisPacket;
        pVc             = pLookasideHeader->pVc; 
        pRemoteNode     = pVc->Hdr.pRemoteNode;
        pLookasideList  = pLookasideHeader->pLookasideList;
        AsyncOp         = pLookasideHeader->AsyncOp;

        ASSERT (AsyncOp != InvalidOperation);
        
        TRACE( TL_V, TM_Send, ( " Vc %x,, pLookaside Buffer %x, pPacket, %x", 
                                pVc, pLookasideHeader ,pPacket  ) );

        ASSERT (pLookasideList != NULL);

        //
        // This will tell us if this thread has received the last fragment
        // OustandingPackets == 0 gets to free the MDLS, and complete the packet
        //
        OutstandingFragments = NdisInterlockedDecrement (&pLookasideHeader->OutstandingFragments );


        if (OutstandingFragments == 0)
        {
            //
            // If there are no more fragments, then we need to 
            // free all the allocated structures ( the MDLS) on this buffer
            //
            
            ULONG  MdlsToFree = pLookasideHeader->FragmentsGenerated;

            PIRB pIrb = &((PUNFRAGMENTED_BUFFER)pLookasideHeader)->Irb;
            

            //
            // The maximum number of MDLS we can have is equal to
            // the maximum number of Fragments that were generated
            //
            while (MdlsToFree != 0)
            {
                PMDL pMdl = NULL;

                GET_MDL_FROM_IRB (pMdl, pIrb, AsyncOp);
                

                TRACE( TL_V, TM_Send, ( " Freeing Mdl %x of Irb %x ", 
                                         pMdl, pIrb) );

        
                if (pMdl != NULL)
                {
                    nicFreeMdl (pMdl);
                    if (pVc->Hdr.VcType == NIC1394_SendFIFO)  
                    {                                           
                        nicDecFifoSendMdl();                    
                    }                                           
                    else                                           
                    {                                           
                        nicDecChannelSendMdl();                 
                    }                                           
                
                }

                //
                // Set up for the next iteration
                //
                MdlsToFree --; 

                pIrb = (PVOID)((ULONG_PTR)pIrb + sizeof (IRB));


            } //while (MdlsToFree  != 0)



        } //if (OutstandingFragments == 0)


        //
        //  Map the NT_STATUS belonging to the Irp to an NdisStatus and call NdisMCoSendComplete
        //  Print Debug Output to help in testing. Need to Add more status cases  
        //
        if (pMyIrp == NULL)
        {   
            TRACE( TL_V, TM_Send, ( "   AsyncSendComplete - pIrp is NULL") );
            IrpStatus = STATUS_UNSUCCESSFUL;
        }
        else
        {
            

            //
            // We have a valid IRP, lets see if we failed the IRP and why
            // 
            IrpStatus   = pMyIrp->IoStatus.Status;

            nicIncrementBusSendCompletes(pVc);
        }
        
        if (IrpStatus != STATUS_SUCCESS)
        {
            TRACE( TL_A, TM_Send, ( "==>IRP FAILED StatusCode = %x",IrpStatus  ) );

            nicIncrementBusFailure();
            nicIncrementVcBusSendFailures(pVc, pPacket);

            //
            // The generation of the bus has changed. Lets get a new one.
            //
            
            if (IrpStatus == STATUS_INVALID_GENERATION)
            {
                nicSendFailureInvalidGeneration((PVCCB)pVc);
            }
            
            NdisStatus = NtStatusToNdisStatus(IrpStatus);
            
            NdisInterlockedIncrement (&pVc->Hdr.pAF->pAdapter->AdaptStats.ulXmitError);
        }
        else
        {
            NdisInterlockedIncrement(&pVc->Hdr.pAF->pAdapter->AdaptStats.ulXmitOk);
            nicIncrementVcBusSendSucess(pVc, pPacket);
        }

        //
        // Free the Irp and don't touch it after this
        //
        if (pMyIrp != NULL)
        {
            nicFreeIrp (pMyIrp);
            pMyIrp = NULL;
        }
        
        //
        // At this point, we know that the IRP went down to the bus driver
        // We know if this is the last fragment. So lets figure out if we need
        // to Complete the packet
        //

        if (OutstandingFragments != 0)
        {
            //
            // We need to wait for other fragments to complete
            //
            TRACE( TL_V, TM_Send, ( "   AsyncSendComplete = NOT  the last fragment") );

            break;

        }
        
    
        //
        // This means that this thread has marked the lookaside header as 'to be freed'
        // and it is this thread's responsibility to free it.
        //
        NdisVcHandle = pVc->Hdr.NdisVcHandle;
        
        TRACE( TL_V, TM_Send, ( "Calling NdisCoSendComplete, status %x, VcHandle %x, pPacket %x",
                                NdisStatus,NdisVcHandle, pPacket ) );

        
        nicMpCoSendComplete (NdisStatus,
                            pVc,
                            pPacket);


        nicFreeToNPagedLookasideList (pLookasideList, pLookasideListBuffer); 

        nicDereferenceCall (pVc, "AsyncWriteStreamSendComplete");

        //
        // Remove the reference on the PDO that the IRP was sent to
        //
        if (AsyncOp == AsyncWrite)
        {
            //
            // Async Write references the remote node 
            //
            ASSERT (pRemoteNode != NULL);
            nicDereferenceRemoteNode (pRemoteNode, "AsyncWriteStreamSendComplete");
        }
        

    } while (FALSE);


    TRACE( TL_T, TM_Send, ( "<== AsyncWriteStreamSendComplete, NdisStatus %x,IrpStatus %x ",
                             NdisStatus, IrpStatus ) );
  
    //
    // ALWAYS RETURN STATUS_MORE_PROCESSING_REQUIRED
    //

    MATCH_IRQL;

    return STATUS_MORE_PROCESSING_REQUIRED;
}




NDIS_STATUS
AsyncWriteSendPacketsHandler(
    IN PVCCB        pVc,
    IN PNDIS_PACKET  pPacket 
    )
    
    //
    //  This is the VC handler when packet is sent using the 
    //  AsyncWrite 1394 Bus Api
    //  This function, copies the contents of the packet to locally 
    //  owned memory, sets up the Irb and the Irp and calls 
    //  nicSubmitIrp which is the generic cal to do a IoCallDriver
    // 
    // The return value is success, if the I/o was successfully pended
{
    
    PSENDFIFO_VCCB                  pSendFIFOVc = (SENDFIFO_VCCB*)pVc;
    PMDL                            pMyMdl = NULL;
    PIRB                            pMyIrb = NULL;
    PIRP                            pMyIrp = NULL;
    BOOLEAN                         fVcActive = TRUE;
    PREMOTE_NODE                    pRemoteNode = NULL;
    NDIS_STATUS                     NdisStatus = NDIS_STATUS_FAILURE;
    NTSTATUS                        NtStatus = STATUS_UNSUCCESSFUL;
    ULONG                           PacketLength = 0;
    PVOID                           pLookasideListBuffer  = NULL;
    PADAPTERCB                      pAdapter = NULL;
    USHORT                          FragmentLength = 0;
    PNDIS_BUFFER                    pStartNdisBuffer = NULL;
    PVOID                           pStartPacketData  = NULL;
    PLOOKASIDE_BUFFER_HEADER        pLookasideHeader = NULL;
    PNIC_NPAGED_LOOKASIDE_LIST      pLookasideList = NULL;
    ENUM_LOOKASIDE_LIST             WhichLookasideList = NoLookasideList;
    FRAGMENTATION_STRUCTURE         Fragment;
    ULONG                           NumFragmentsNeeded ;
    STORE_CURRENT_IRQL;


    NdisZeroMemory (&Fragment, sizeof (FRAGMENTATION_STRUCTURE));
    
    TRACE( TL_T, TM_Send, ( "==>AsyncWriteSendPacketHandler, Vc %x,Packet %x, FragmentationStruct %x", 
                           pSendFIFOVc, pPacket , &Fragment ) );

    pRemoteNode = pSendFIFOVc->Hdr.pRemoteNode;
    ASSERT (pRemoteNode != NULL);



    do 
    {
        
        VC_ACQUIRE_LOCK (pSendFIFOVc);

        //
        // Make sure that the Vc is Activated and that no close calls 
        // are pending or that we have already completed a close call
        //

        
        if ( VC_ACTIVE (pSendFIFOVc) == FALSE || REMOTE_NODE_ACTIVE(pRemoteNode) == FALSE)
        {
            fVcActive = FALSE;  
        }

        if (VC_TEST_FLAG( pSendFIFOVc, VCBF_GenerationWorkItem) == TRUE)
        {
            TRACE( TL_N, TM_Send, ( "AsyncWriteSendPacketHandler, Getting a new Gen, Fail send ") );

            fVcActive = FALSE;  
        }

        //
        // This reference will either be dereferenced below in a call to FreeSendPacketDataStructure
        // below or a call to FreeSendPacketDataStructure made from the Irp's completion routine
        //

        if (fVcActive == TRUE)
        {
            nicReferenceCall (pVc, "AsyncWriteSendPacketsHandler");

            nicReferenceRemoteNode (pRemoteNode, "AsyncWriteSendPacketsHandler");

        }
        
        VC_RELEASE_LOCK (pSendFIFOVc);

        if (fVcActive  == FALSE)
        {
            TRACE( TL_N, TM_Send, ( "AsyncWriteSendPacketHandler, VC Not Active, Vc %x Flag %x", pSendFIFOVc,pSendFIFOVc->Hdr.ulFlags ) );

            NdisStatus = NDIS_STATUS_FAILURE;
            break;
        }

        pAdapter = pSendFIFOVc->Hdr.pAF->pAdapter;
        
        //
        //  Copy NdisBuffer in Packet to Local Memory and get an Mdl that points 
        //  to this memory 
        //
        NdisQueryPacket( pPacket,
                         NULL,
                         NULL,
                         NULL,
                         &PacketLength);

        ASSERT (pPacket->Private.Head != NULL);

        //
        // Temporary debug spew
        //
        PrintNdisPacket (TM_Send, pPacket);


        //
        // Initialize the start variables
        //

        pStartNdisBuffer = pPacket->Private.Head;
        pStartPacketData = nicNdisBufferVirtualAddress (pStartNdisBuffer);

        if (pStartPacketData == NULL)
        {
            NdisStatus = NDIS_STATUS_RESOURCES;
            TRACE( TL_N, TM_Send, ( "AsyncWriteSendPacketHandler, pStartPacketData ") );

            break;
        }

        TRACE( TL_V, TM_Send, ( "PacketLength %x", PacketLength) );

        //
        // Make a decision on which lookaside list to use. If the tx is unfragmented 
        // then copy over the ndis packet as well
        //

        //
        // first choose the lookaside list. the actual lookaside list is chosen so that the 
        // each can accomodate the maximum number of fragments at its payload
        //
        //
        if (PacketLength < PAYLOAD_100)
        {
            pLookasideList = &pAdapter->SendLookasideList100;
            WhichLookasideList = SendLookasideList100;
            TRACE( TL_V, TM_Send, ( " PAYLOAD_100 Lookaside List %x", 
                                    &pAdapter->SendLookasideList100) );

        }
        else 
        if (PacketLength < PAYLOAD_2K)
        {
            pLookasideList = &pAdapter->SendLookasideList2K;
            WhichLookasideList = SendLookasideList2K;
            TRACE( TL_V, TM_Send, ( " PAYLOAD_2K Lookaside List %x", 
                                    &pAdapter->SendLookasideList2K) );


        } 
        else
        if (PacketLength < PAYLOAD_8K)
        {
            pLookasideList = &pAdapter->SendLookasideList8K;
            WhichLookasideList  = SendLookasideList8K;
            TRACE( TL_V, TM_Send, ( " PAYLOAD_8K Lookaside List %x", 
                                    &pAdapter->SendLookasideList8K) );

    
        }
        else
        {
            //
            // Large Sends not supported
            // TODO : Add code for local allocation
            //
            ASSERT (!"SendPacket Too Large - Not supported Yet" );
            break;  
        }

        //
        // are we going to fragment
        // 
        ASSERT (pLookasideList != NULL)

        //
        // We are not going to fragment. Optimize this path
        //
        pLookasideListBuffer = nicGetLookasideBuffer (pLookasideList);
        
        if (pLookasideListBuffer == NULL )
        {
            NdisStatus = NDIS_STATUS_FAILURE;
            BREAK (TM_Send, ("nicGetLookasideBuffer  FAILED") );
        }

        //
        // Initialize the header with relevant information that the send complete
        // will need
        //
    
        pLookasideHeader = (PLOOKASIDE_BUFFER_HEADER)pLookasideListBuffer;
        pLookasideHeader->IsFragmented          = FALSE;  // Default
        pLookasideHeader->FragmentsGenerated    = 0;
        pLookasideHeader->pLookasideList        = pLookasideList;
        pLookasideHeader->pNdisPacket           = pPacket;
        pLookasideHeader->pVc                   =(PVCCB)pVc;
        pLookasideHeader->AsyncOp               = AsyncWrite;

        //
        // Initialize the Fragment structure
        //
        //
        //  Do we fragment or not. Base it on the MaxPayload possible
        //
        
        TRACE( TL_V, TM_Send, ( "    PacketLength %x, pSendFIFOVc->MaxPayload%x ", 
                                 PacketLength ,pSendFIFOVc->Hdr.MaxPayload) );


        if (PacketLength <= pSendFIFOVc->Hdr.MaxPayload)
        {
            //
            // No need to fragment here. We will use the UNFRAGMENTED Layout
            //
            // First Get a local buffer from our lookaside list
            //
            PUNFRAGMENTED_BUFFER pUnfragmentedBuffer = (PUNFRAGMENTED_BUFFER )pLookasideHeader;

            NumFragmentsNeeded = 1;

            NdisStatus = nicCopyNdisBufferChainToBuffer (pStartNdisBuffer, 
                                                        (PVOID)&pUnfragmentedBuffer ->Data[0],
                                                         pLookasideList->MaxSendSize );
                                             
    
            if (NdisStatus != NDIS_STATUS_SUCCESS)
            {
                pLookasideHeader->OutstandingFragments  = 1;  // this is our refcount
                BREAK ( TM_Send, ( "   AsyncStreamSendPacketHandler, nicCopyNdisPacketToUnfragmentedBuffer Failed ") );
            }
    
            ASSERT (pLookasideListBuffer != NULL);

            // 
            // Initialize all the variable needed by the Next section of the code.
            // This deals with setting up the Mdl and the IRB
            //
            
            pStartNdisBuffer = NULL;

            Fragment.pStartFragment = (PVOID) &pUnfragmentedBuffer ->Data[0];
            Fragment.FragmentLength  = PacketLength;
            Fragment.pCurrNdisBuffer = NULL;

            pLookasideHeader->FragmentsGenerated = 1; 
            pLookasideHeader->IsFragmented = FALSE;                                    
            pLookasideHeader->OutstandingFragments  = 1;  // this is our refcount
        
        }
        else
        {
            //
            // We need to fragment
            //
            ULONG Dgl = NdisInterlockedIncrement(&pAdapter->dgl);

            //
            // Fragments will be needed . Make sure the calculation for numFragments catches the boundary conditions
            //

            
            NumFragmentsNeeded = nicNumFragmentsNeeded (PacketLength,
                                                        pSendFIFOVc->Hdr.MaxPayload,
                                                        sizeof (NDIS1394_FRAGMENT_HEADER) );


            //
            // Initialize the fragment structure. The unfragmented code path
            // does not care about these fields
            //

            //
            // This structure is local to this function and this thread. 
            //
            Fragment.TxHeaderSize = sizeof (NDIS1394_FRAGMENT_HEADER);
            Fragment.pLookasideListBuffer = pLookasideListBuffer;
            Fragment.AsyncOp = AsyncWrite;
            Fragment.pAdapter = pRemoteNode->pAdapter;
            Fragment.pLookasideList = pLookasideList;
            Fragment.IPDatagramLength = (USHORT)PacketLength - sizeof (NDIS1394_UNFRAGMENTED_HEADER); 
            
            //
            // Get Start of first Dest fragment
            //
            Fragment.MaxFragmentLength = pSendFIFOVc->Hdr.MaxPayload;                                                                   
            Fragment.NumFragmentsNeeded = NumFragmentsNeeded;
        
            

            
            // 
            // Set up the Fragment Headers that will be used in fragmentation
            //

            NdisStatus = nicFirstFragmentInitialization (pPacket->Private.Head,
                                                     Dgl,
                                                     &Fragment);


            if (pLookasideListBuffer  == NULL || NdisStatus != NDIS_STATUS_SUCCESS)
            {
                BREAK (TM_Send, (" AsyncWriteSendPacketsHandler: nicFirstFragmentInitialization : FAILED" )) ;
            }       

            ASSERT (pLookasideListBuffer != NULL);
            
            pLookasideHeader->IsFragmented = TRUE;                                     
            pLookasideHeader->OutstandingFragments  = NumFragmentsNeeded ;  // this is our refcount
            
        }


        TRACE( TL_V, TM_Send, ( "NumFragments  %x, pSendFIFOVc->MaxSendSize %x, Packet Size %x", 
                                 NumFragmentsNeeded,pSendFIFOVc->Hdr.MaxPayload, PacketLength) );


        //
        // Now begin the loop which will send n fragments
        //
        do 
        {   

            //
            // Do we need to fragment. If so , extract one fragment out of the NdisPacket
            //
            if (pLookasideHeader->IsFragmented == TRUE )
            {   
            
                //
                // We copy one fragment over and this will allocate the lookaside list
                //

                NdisStatus = nicCopyOneFragment (&Fragment);
                
                if (NDIS_STATUS_SUCCESS != NdisStatus)
                {
                    BREAK ( TM_Send, ( "   AsyncWriteSendPacketHandler, nicCopyOneFragment  Failed ") );
                }
                                              
                //
                // Get the pointer to the Irb here . Amd set it up for the next time
                //
                pMyIrb = Fragment.pCurrentIrb;
                Fragment.pCurrentIrb = (PIRB)((ULONG_PTR)Fragment.pCurrentIrb + sizeof (IRB) );
                TRACE( TL_V, TM_Send, ( " pMyIrb  %x, Next Irb %x  ", pMyIrb  , Fragment.pCurrentIrb ) );

            }
            else
            {
                //
                // No Curr NdisBuffer as this packet was never fragmented. 
                //
                
                ASSERT (pLookasideHeader->IsFragmented == FALSE);                                      

                pMyIrb =  &((PUNFRAGMENTED_BUFFER )pLookasideHeader)->Irb;
            }
            
            //
            // At this point we have one fragment that needs to be transmitted.
            // Data structures have been updated to set up the MDL and the IRB
            //
            ASSERT (Fragment.pStartFragment != NULL);

            NdisStatus = nicGetMdl (Fragment.FragmentLength  , 
                                    Fragment.pStartFragment, 
                                    &pMyMdl);

            if (NdisStatus != NDIS_STATUS_SUCCESS)
            {
                pMyMdl = NULL;
                BREAK ( TM_Send, ( "   AsyncWriteSendPacketHandler, nicCopyNdisBufferChainToBuffer Failed ") );
            }       

            nicIncFifoSendMdl();
            //
            //  Fill in the Irb with the correct values from the VC
            //  Stuff we need to add to the send VC - BlockSize,Generation
            //  

            nicInitAsyncWriteIrb(pSendFIFOVc, pMyMdl, pMyIrb);

            //
            // Get a free Irp 
            //

            NdisStatus  = nicGetIrp (pRemoteNode->pPdo, &pMyIrp); 
        
            if (NdisStatus != NDIS_STATUS_SUCCESS)
            {
                pMyIrp = NULL;
                break;
            }
            //
            // At this point, we have a guarantee that the Completion routine will be called
            //
            ASSERT (NdisStatus == NDIS_STATUS_SUCCESS);

            //
            // Dump the Fragment 
            //
            nicDumpMdl (pMyMdl , 0, "AsyncWrite Fragment");

            NIC1394_LOG_PKT(
                pAdapter,
                NIC1394_LOGFLAGS_SEND_FIFO,
                pAdapter->BCRData.LocalNodeNumber,          // SourceID
                pRemoteNode->RemoteAddress.NA_Node_Number,  // DestID
                Fragment.pStartFragment, 
                Fragment.FragmentLength
                );


            //
            // This function implements the common functionality to be implemented by
            // all other send/recv cals to IoCallDriver
            //
            //
            // We IGNORE the NtStatus as the completion handler will be called
            //
            nicIncrementBusSends(pVc);
                    
            NtStatus = nicSubmitIrp(pRemoteNode->pPdo,
                                    pMyIrp,
                                    pMyIrb,
                                    AsyncWriteStreamSendComplete,
                                   (PVOID)pLookasideListBuffer);

            TRACE( TL_V, TM_Send, ( " pCurrNdisBuffer  %x, NdisStatus %x ", Fragment.pCurrNdisBuffer , NdisStatus ) );

            
        } while (Fragment.pCurrNdisBuffer != NULL && NdisStatus == NDIS_STATUS_SUCCESS);

    
    } while (FALSE);

    //
    // DO NOT touch the packet if status == NDIS_STATUS_SUCCESS. 
    //

    
    //
    //  CleanUp if any of the allocations failed. We do not have a pointer
    //  to the LocalBuffer (it is embedded in the Mdl)  so it remains NULL
    //
    //  NdisStatus != Success means that we never got to nicSubmitIrp
    //
    
    if (NdisStatus != NDIS_STATUS_SUCCESS)
    {   

        ASSERT (pMyIrp == NULL);

        //
        // fVc Active makes sure that we actually got around to allocating 
        // and referencing structures
        //
        
        if (fVcActive == TRUE)
        {

            if (pLookasideListBuffer != NULL)
            {

                //
                // Complete this fragment, as we never submit'd the IRP to
                // the 1394 bus driver
                //
                AsyncWriteStreamSendComplete(NULL, // PDO
                                             NULL, 
                                             pLookasideListBuffer);

                NdisStatus =NDIS_STATUS_SUCCESS;
            }                                  
            else
            {

                //
                // This thread needs to decrement the refcounts as 
                // AsyncWriteStreamSendComplete was not called
                //
                nicDereferenceCall ((PVCCB) pSendFIFOVc, "AsyncWriteSendPacketsHandler");

                nicDereferenceRemoteNode (pRemoteNode, "AsyncWriteSendPacketsHandler");

            }

            
        }

    }


    

    TRACE( TL_T, TM_Send, ( "<==AsyncWriteSendPacketHandler, NdisStatus  %x", NdisStatus ) );
    MATCH_IRQL;

    return NdisStatus;
}





//
// Split the lookaside header to a local variable + context
//

NDIS_STATUS
nicCopyOneFragment (
    PFRAGMENTATION_STRUCTURE pFragment
    )
    
    // Function Description:
    //   This creates one fragment filled with valid data and returns it
    //
    //
    // Arguments
    // ppCurrNdisBuffer - CurrNdisBuffer from which the data is to be copied
    // ppLookasideListBuffer - if this is NULL, it implies that this is the first fragment 
    //                      and a lookaside buffer will be allocated
    //
    // ppSourceAddress  -  is the current pointer to the start of the data that needs to be copied
    //                     Should always lie within the NdisBuffer or be NULL 
    // Return Value:
    //  ppCurrNdisBuffer - If the CurrNdisBuffer does not contain enough data for the 
    //                   fragment, then CurrNdisBuffer will be incremented and the new
    //                  CurrNdisBuffer will be returned here
    //
    //
{
    NDIS_STATUS                         NdisStatus = NDIS_STATUS_FAILURE;
    PNDIS_BUFFER                        pCurrNdisBuffer = pFragment->pCurrNdisBuffer;
    PVOID                               pSourceAddressInNdisBuffer = pFragment->pSourceAddressInNdisBuffer;
    ULONG                               FragmentLengthRemaining = pFragment->MaxFragmentLength;
    USHORT                              FragmentLength=0;
    PVOID                               pSource = NULL;
    PVOID                               pDestination = NULL;
    PVOID                               pStartFragmentData = NULL;
    ULONG                               NdisBufferLengthRemaining = pFragment->NdisBufferLengthRemaining;
    ULONG                               LengthToCopy = 0;
    ULONG                               FragmentCopyStatus=0;
    PLOOKASIDE_BUFFER_HEADER            pLookasideHeader = NULL;
    enum 
    {
        FRAGMENT_COPY_Invalid,
        FRAGMENT_COPY_NdisBufferCompleted,
        FRAGMENT_COPY_NdisBufferAndFragmentCompleted,
        FRAGMENT_COPY_FragmentCompleted
    };
    
    TRACE( TL_T, TM_Send, ( "==>nicCopyOneFragment  pFragment %x", pFragment )  );
    ASSERT (pCurrNdisBuffer != NULL);
    do 
    {

        //
        //  lets get the destination. We need to account for 
        //  ther fragment size and add it to the previous start address
        //

        {
            ULONG   CurrFragOffset;

            CurrFragOffset  = pFragment->MaxFragmentLength * (pFragment->CurrFragmentNum++);
            
            pStartFragmentData  = (PVOID) ((ULONG_PTR) pFragment->pStartOfFirstFragment + CurrFragOffset );
        
        }



        pFragment->pStartFragment  = pStartFragmentData;

        TRACE( TL_V, TM_Send, ( " pStartFragmentData  %x", pStartFragmentData) );

        pLookasideHeader = (PLOOKASIDE_BUFFER_HEADER)pFragment->pLookasideListBuffer;



        //
        // Do the bookkeeping , Increase refcount and num of fragments used. Refcount decremented in FreeSendDataStructures
        // 

        NdisInterlockedIncrement (&pLookasideHeader->FragmentsGenerated);

        //
        // The Start of the data beginning with the fragment header goes here or in the 
        // case of async stream fragment header and gasp header go here
        //
        ASSERT (pFragment->TxHeaderSize  == 8 || pFragment->TxHeaderSize  == 16);

        
        pDestination = (PVOID) ((ULONG_PTR)pStartFragmentData + pFragment->TxHeaderSize );

        FragmentLengthRemaining -= pFragment->TxHeaderSize;

        //
        // Now we start the copy. Keep on copying into the current fragment until the MaxLength is reached 
        // or the NdisBufferChain is exhausted
        //

        pSource = pSourceAddressInNdisBuffer; 


        do
        {


            TRACE( TL_T, TM_Send, ( " LengthNdisBuffer  %x, FragmentLengthRemaining %x, pCurrNdisBuffer %x", 
                    NdisBufferLengthRemaining , FragmentLengthRemaining ,pCurrNdisBuffer ) ); 

            if (FragmentLengthRemaining > NdisBufferLengthRemaining )
            {
                //
                // Copy the complete NdisBuffer over
                //

                LengthToCopy = NdisBufferLengthRemaining; 
                FragmentCopyStatus = FRAGMENT_COPY_NdisBufferCompleted;

            }

            
            if (FragmentLengthRemaining < NdisBufferLengthRemaining )
            {
                //
                // Copy only as much as required
                //

                LengthToCopy = FragmentLengthRemaining;
                FragmentCopyStatus = FRAGMENT_COPY_FragmentCompleted;
                    
            }
            
            if (FragmentLengthRemaining == NdisBufferLengthRemaining  )
            {
                //
                // Copy the complete  Ndis Buffer , move  to the next ndis buffer
                // and update the NdisBufferLengthRemaining field  
                //
                LengthToCopy = NdisBufferLengthRemaining; 
                FragmentCopyStatus = FRAGMENT_COPY_NdisBufferAndFragmentCompleted;


            }

            //
            // Sanity check to make sure we are not overwriting into free memory.
            // As this should never happen, there is no recovery mechanism in place.
            //
            ASSERT (((PUCHAR)pDestination +  LengthToCopy) <=  (((PUCHAR) pLookasideHeader) + (pLookasideHeader->pLookasideList->Size) ));
            
            //
            // Do the copy
            //
                    
            TRACE ( TL_V, TM_Send, (" nicCopyOneFragment  pSource  %x , pDestination %x, Length %x", pSource, pDestination, LengthToCopy ) );
            
            NdisMoveMemory (pDestination, pSource, LengthToCopy);
            

            //
            // Update the fragment length remaininig and Total Buffer Size
            //
            FragmentLengthRemaining  -= LengthToCopy;

            FragmentLength += (USHORT)LengthToCopy;

            pDestination = (PVOID) ((ULONG_PTR) pDestination + LengthToCopy);
            //
            // Update the NdisBuffer variables 
            //
            ASSERT (pCurrNdisBuffer != NULL);
    
            TRACE( TL_V, TM_Send, ( " FragmentCopyStatus %x", FragmentCopyStatus) ); 

            switch (FragmentCopyStatus)
            {
                case FRAGMENT_COPY_NdisBufferCompleted:
                case FRAGMENT_COPY_NdisBufferAndFragmentCompleted:
                {
                    
                    //
                    // Move to the next Ndisbuffer
                    //
                    pCurrNdisBuffer = pCurrNdisBuffer->Next;
    
                    if (pCurrNdisBuffer  != NULL)
                    {
                        NdisBufferLengthRemaining = nicNdisBufferLength (pCurrNdisBuffer);

                        pSourceAddressInNdisBuffer = nicNdisBufferVirtualAddress(pCurrNdisBuffer);

                        if (pSourceAddressInNdisBuffer == NULL)
                        {
                            NdisStatus = NDIS_STATUS_RESOURCES;
                            BREAK (TM_Send, ("nicNdisBufferVirtualAddress FAILED " ) );                         
                        }

                        //
                        // Set up the values for the next iteration
                        //
                        pSource = pSourceAddressInNdisBuffer;
                        NdisBufferLengthRemaining   = nicNdisBufferLength (pCurrNdisBuffer);

                    }
                    else
                    {
                        //
                        // we have reached the end of the NdisPAcket. Mark the fragment header as such
                        //
                        pFragment->lf = lf_LastFragment;
                    }
                        
                    break;
                }
                
                case FRAGMENT_COPY_FragmentCompleted:
                {   
                    //
                    // Fragment has completed. Do not move to the next NdisBuffer
                    // However update StartCopy Address  in the NdisBuffer
                    // 
                    pSourceAddressInNdisBuffer  = (PVOID) ((ULONG_PTR) pSource + LengthToCopy );    

                    NdisBufferLengthRemaining -= LengthToCopy ;
                    
                    break;
                }


                default :
                {
                    ASSERT (0);
                }

            }

            TRACE( TL_T, TM_Send, ( "      LengthToCopy %x, FragmentLength %x, ", LengthToCopy, FragmentLength) ); 
            TRACE( TL_T, TM_Send, ( "      FragmentLengthRemaining %x, pCurrNdisBuffer %x",FragmentLengthRemaining , pCurrNdisBuffer ) ); 
            

        }while (FragmentLengthRemaining  > 0 && pCurrNdisBuffer != NULL);       


            
        
        //
        // Now that we have the buffer size. Add the fragment header
        //
        
        nicAddFragmentHeader (pStartFragmentData, 
                                pFragment,
                                FragmentLength);
                                     
                            
        TRACE( TL_T, TM_Send, ( " Fragment Header added %x", *(PULONG)pStartFragmentData) ); 

        NdisStatus = NDIS_STATUS_SUCCESS;

    }while (FALSE);

    //
    // Now update the output parameters.
    //

    if (NdisStatus == NDIS_STATUS_SUCCESS)
    {

        //
        // Update the Lookaside Header structure, to reflect the new position of all the pointers
        //
        pFragment->pCurrNdisBuffer  = pCurrNdisBuffer; 
        pFragment->pSourceAddressInNdisBuffer = pSourceAddressInNdisBuffer;

        //
        // Update the fragment structure with the length remaining in the NdisBuffer
        //
    
        pFragment->NdisBufferLengthRemaining = NdisBufferLengthRemaining ;
        pFragment->FragmentLength =  FragmentLength + pFragment->TxHeaderSize;  

        

    }
    

    TRACE( TL_T, TM_Send, ( "<==nicCopyOneFragment   pStartFragmentData %x, pLookasideListBuffer %x, pSourceAddressInNdisBuffer %x, NdisStatus %x", 
                            pStartFragmentData, pSourceAddressInNdisBuffer, NdisStatus) );

    return NdisStatus;
}










VOID
nicCopyUnfragmentedHeader ( 
    IN PNIC1394_UNFRAGMENTED_HEADER pDestUnfragmentedHeader,
    IN PVOID pSrcUnfragmentedHeader
    )
    // Function Description:
    //  Expect the  Src to be a big Endian  unfragmented packet header
    //  It will reverse the byte order in a temp variable and copy it into the 
    //  Destination provided.
    //
    // Arguments
    //   pDestUnfragmentedHeader - Destination (Little Endian
    //   pSrcUnfragmentedHeader - Source (Big Endian)
    //
    // Return Value:
    //
    //   Success if all the pointers and copy is valid
    //
{

    ULONG UnfragmentedHeader;
    
    TRACE( TL_T, TM_Send, ( "==> nicCopyUnfragmentedHeader  pDestUnfragmentedHeader %x, pSrcUnfragmentedHeader %x", 
                            pDestUnfragmentedHeader, pSrcUnfragmentedHeader ) );

    ASSERT (pSrcUnfragmentedHeader != NULL && pDestUnfragmentedHeader != NULL) ;
    
    *((PULONG)pDestUnfragmentedHeader) = SWAPBYTES_ULONG ( *(PULONG) pSrcUnfragmentedHeader);

    TRACE( TL_T, TM_Send, ( "pDestUnfragmentedHeader %x, ", *(PULONG)pDestUnfragmentedHeader) );

    
    TRACE( TL_T, TM_Send, ( " <== nicCopyUnfragmentedHeader   " ) );

}




NDIS_STATUS
nicFirstFragmentInitialization (
    IN PNDIS_BUFFER pStartNdisBuffer,
    IN ULONG DatagramLabelLong,
    IN OUT PFRAGMENTATION_STRUCTURE  pFragment           
    )
    // Function Description:
    //   This will set up the fragement headers that are required for 
    //   transmitting multiple fragments.
    //   Sets up the first source and destination for the first fragment
    //
    // Arguments
    //  pAdapter - to be used to get the dgl label and the lookaside list
    //  pStartOfData - start of the packet data . To be used in extracting the Unfragmented Header 
    //  ppLookasideListBuffer Points to the allocated lookaside buffer
    //  pplookasideheader - points to the lookaside header
    //
    // Return Value:
    //  Success if the allocation succeeds
    //
    //
{

    NDIS_STATUS                   NdisStatus = NDIS_STATUS_FAILURE;
    PNDIS1394_FRAGMENT_HEADER   pHeader = NULL;
    PVOID                       pPacketStartData = NULL;
    USHORT                      dgl = (USHORT) DatagramLabelLong;

    
    TRACE( TL_T, TM_Send, ( "==> nicFirstFragmentInitialization  pStartNdisBuffer%x,  pFragment%x dgl %x ", 
                             pStartNdisBuffer, pFragment,    dgl ) );

    do
    {

        //
        // Get the start address for the 1st NdisBuffer.    This contains
        // the unfragmented header
        //
        pPacketStartData = nicNdisBufferVirtualAddress(pStartNdisBuffer);

        if (pPacketStartData == NULL) 
        {
            NdisStatus = NDIS_STATUS_RESOURCES;
            BREAK (TM_Send, ("NdisBufferVirtual Address is NULL " ) );
        }


        pFragment->UnfragmentedHeader.HeaderUlong = 
                SWAPBYTES_ULONG (((PNDIS1394_UNFRAGMENTED_HEADER)pPacketStartData)->HeaderUlong);

        TRACE ( TL_V, TM_Send, (" Unfragmented Header %x, pPacketStartData %x", 
                                   pFragment->UnfragmentedHeader.HeaderUlong , pPacketStartData) );

        TRACE ( TL_V, TM_Send, (" original Header lf %x, etherType %x", 
                                   pFragment->UnfragmentedHeader.u.FH_lf,
                                   pFragment->UnfragmentedHeader.u.FH_EtherType) );

                                   
    
        //
        // Now construct a fragmentation header to be used by all the fragments.
        //
        pHeader  = &pFragment->FragmentationHeader;

            
        pHeader ->u.FirstQuadlet.FH_lf = lf_FirstFragment;
        pHeader ->u.FirstQuadlet.FH_buffersize = pFragment->IPDatagramLength-1;
        
        pHeader ->u.FirstQuadlet_FirstFragment.FH_EtherType 
                        = pFragment->UnfragmentedHeader.u.FH_EtherType;
        
        pHeader ->u1.SecondQuadlet.FH_dgl = dgl;

        TRACE ( TL_V, TM_Send, (" fragmented Header Hi %x   Lo %x", 
                                   pHeader->u.FH_High, 
                                   pHeader->u1.FH_Low) );

        TRACE ( TL_V, TM_Send, (" fragmented Header lf %x  EtherType  %x", 
                                   pHeader ->u.FirstQuadlet_FirstFragment.FH_lf ,
                                   pHeader ->u.FirstQuadlet_FirstFragment.FH_EtherType ) );

        //
        // temporaty debug spew
        //
        TRACE (TL_V, TM_Send, (" copy Header at %x, Orig Header at %x", 
                               &pHeader ->u.FirstQuadlet_FirstFragment, 
                               pFragment->UnfragmentedHeader) );
        
        //
        // Initialize the fragmentation structure with packet's first ndis buffer
        //
        pFragment->pSourceAddressInNdisBuffer = NdisBufferVirtualAddress (pStartNdisBuffer);

        if (pFragment->pSourceAddressInNdisBuffer  == NULL)
        {
            NdisStatus = NDIS_STATUS_FAILURE;   

        }

        //
        // Set up the copy source . The first four bytes of data contain the unfragmented header.
        // We need to skip past these bytes and start the copy from the next byte
        // 
        pFragment->pSourceAddressInNdisBuffer  = (PVOID) ((ULONG_PTR)pFragment->pSourceAddressInNdisBuffer  +
                                                         sizeof (NDIS1394_UNFRAGMENTED_HEADER) );
        
        
        pFragment->NdisBufferLengthRemaining = NdisBufferLength (pStartNdisBuffer) - sizeof (NDIS1394_UNFRAGMENTED_HEADER);
        pFragment->pCurrNdisBuffer = pStartNdisBuffer;
        //
        // Set up the destination
        //
        pFragment->pStartFragment = (PVOID)((ULONG_PTR)pFragment->pLookasideListBuffer 
                                                  + (pFragment->NumFragmentsNeeded*sizeof(IRB)));


        ((PLOOKASIDE_BUFFER_HEADER)pFragment->pLookasideListBuffer)->pStartOfData = pFragment->pStartFragment;
        
        pFragment->pStartOfFirstFragment = pFragment->pStartFragment ;
        pFragment->CurrFragmentNum = 0;


        pFragment->lf = lf_FirstFragment;

        //
        // The First IRB will reside at the end of the lookaside-header 
        //
        pFragment->pCurrentIrb = &((PUNFRAGMENTED_BUFFER)pFragment->pLookasideListBuffer)->Irb;
        
        TRACE( TL_T, TM_Send, ( " pStartFragment %x, pFragment %x,NumFragmentsNeeded %x,MaxFragmentLength %x  ", 
                                  pFragment->pStartFragment, 
                                  pFragment->NumFragmentsNeeded,
                                  pFragment->MaxFragmentLength) );

        
        NdisStatus = NDIS_STATUS_SUCCESS;

    } while (FALSE);

    
    
    TRACE( TL_T, TM_Send, ( " <== nicFirstFragmentInitialization  NdisStautus %x, pFragment %x, ", 
                             NdisStatus, pFragment) );

    return NdisStatus;
}





VOID
nicAddFragmentHeader (
    IN PVOID pStartFragmentData, 
    IN PFRAGMENTATION_STRUCTURE pFragmentStructure,
    IN ULONG FragmentLength
    )
    // Function Description:
    //   Copies the Fragment header over after byteswapping it.
    //   For the the first time, the ether type and so forth is already initialized and waiting to be copied.
    //   This funciotn also sets up the values for the next invocation of this function
    // Arguments
    //   pStartFragmentData - Start of the fragment. Header goes after the gasp header if necessary .
    //   pFragmentationHeader - Header to copy over
    //   fIsFirstFragment - TRUE if this is the first fragment and needs a special header
    // Return Value:
    //   None
    //
{
    
    PNDIS1394_FRAGMENT_HEADER  pDestFragmentHeader = (PNDIS1394_FRAGMENT_HEADER)pStartFragmentData;
    PNDIS1394_FRAGMENT_HEADER  pSrcFragmentHeader = &pFragmentStructure->FragmentationHeader;   
    
    
    TRACE( TL_T, TM_Send, ( "==> nicAddFragmentHeader pStartFragmentData %x, pFragmentationHeader %x, , FragmentLength %x,  lf %x", 
                            pStartFragmentData , pSrcFragmentHeader , FragmentLength, pFragmentStructure->lf) );

    if (pFragmentStructure->AsyncOp == AsyncStream)
    {
        //
        // First Copy the GaspHeader
        //
        NdisMoveMemory (pStartFragmentData , 
                          &pFragmentStructure->pAdapter->GaspHeader, 
                          sizeof (GASP_HEADER) );

        //
        // Increment the pointers so that the fragment header will be copied after the gasp header
        //
        pStartFragmentData = (PVOID) ((ULONG_PTR) pStartFragmentData + sizeof (GASP_HEADER) );
        pDestFragmentHeader  = (PVOID) pStartFragmentData;
        
        TRACE( TL_T, TM_Send, ( " nicAddFragmentHeader Added Gasp Header from  %x ", 
                               pFragmentStructure->pAdapter->GaspHeader) );
        
    }


    //
    // Sanity check , are we overwriting anybody ?
    //
    ASSERT (*(PULONG)pDestFragmentHeader == 0);
    ASSERT (*(PULONG)pFragmentStructure->pCurrentIrb == 0);

    TRACE( TL_V, TM_Send, ( " pSrcFragmentHeader Hi %x,Lo %x", 
                             pSrcFragmentHeader->u.FH_High, pSrcFragmentHeader->u1.FH_Low) ); 

    //
    //  Copy over the lf;
    //
    pSrcFragmentHeader->u.FirstQuadlet.FH_lf = pFragmentStructure->lf;

    //
    // Now copy over the 8 bytes of the fragment header and byteswap them into big endian
    //

    
    pDestFragmentHeader->u.FH_High =  SWAPBYTES_ULONG ( pSrcFragmentHeader->u.FH_High);

    pDestFragmentHeader->u1.FH_Low = SWAPBYTES_ULONG ( pSrcFragmentHeader->u1.FH_Low);

    TRACE( TL_V, TM_Send, ( "  Fragment Offset %x", pSrcFragmentHeader->u.FirstQuadlet.FH_fragment_offset   ) );

    //
    // PREPARE the FRAGMENT STRUCTURE FOR THE NEXT ITERATION
    //

    //
    // Set the first fragment completed flag to true and set up the header for the next fragment
    //
    if (pFragmentStructure->lf == lf_FirstFragment)
    {
        pFragmentStructure->lf = lf_InteriorFragment;
        pSrcFragmentHeader->u.FirstQuadlet.FH_fragment_offset = 0;

    }

    //
    // Increase the fragment offset for use in the next fragment
    //
    pSrcFragmentHeader->u.FirstQuadlet.FH_fragment_offset += FragmentLength;





    TRACE( TL_T, TM_Send, ( "<== nicAddFragmentHeader lf %x", pFragmentStructure->lf) );

}



NDIS_STATUS
AsyncStreamSendPacketsHandler (
    IN PVCCB pVc,
    IN PNDIS_PACKET pPacket 
    )
    // Function Description:
    //  This function is used to send packets to the bus 
    //  via the async stream irp. the Ndis Packet is copied 
    //  to locally owned buffers and mdls and then sent
    //  down to the bus driver
    //
    //  This code is borrowed heavily from the AsyncStreamIrp code below
    //
    // Arguments
    // pChannelVc - The Vc which needs to send the packets
    // pPacket - the packet being transmitted
    //
    // Return Value:
    // NdisStatus - if all allocations and irp operations complete 
    // successfully, and the i/o will be completed asynchronously
    //
{
    NDIS_STATUS                     NdisStatus = NDIS_STATUS_FAILURE;
    NTSTATUS                        NtStatus = STATUS_UNSUCCESSFUL;
    PCHANNEL_VCCB                   pChannelVc = (PCHANNEL_VCCB) pVc;
    BOOLEAN                         fVcActive = TRUE;
    PMDL                            pMyMdl = NULL;
    PIRB                            pMyIrb = NULL;
    PIRP                            pMyIrp = NULL;
    ULONG                           PacketLength = 0;
    PVOID                           pLookasideListBuffer = NULL;
    PADAPTERCB                      pAdapter = NULL;
    PNDIS_BUFFER                    pStartNdisBuffer = NULL ;
    PVOID                           pStartPacketData= NULL ;
    PNIC_NPAGED_LOOKASIDE_LIST      pLookasideList = NULL;
    PLOOKASIDE_BUFFER_HEADER        pLookasideHeader = NULL;
    ULONG                           NumFragmentsNeeded = 0;
    FRAGMENTATION_STRUCTURE         Fragment;
    STORE_CURRENT_IRQL;


    NdisZeroMemory (&Fragment, sizeof (FRAGMENTATION_STRUCTURE));
    
    TRACE( TL_T, TM_Send, ( "==>AsyncStreamSendPacketsHandler , pVc  %x, pPacket %x", 
                                 pChannelVc , pPacket ) );
    
    pAdapter = pChannelVc->Hdr.pAF->pAdapter;
    //
    // This reference will either be dereferenced below in a call to FreeSendPacketDataStructure
    // below or a call to FreeSendPacketDataStructure made from the Irp's completion routine
    //
    


    do 
    {
        VC_ACQUIRE_LOCK (pVc);



        //
        // Make sure that the Vc is Activated and that no close calls 
        // are pending or that we have already completed a close call
        //

        
        if ( VC_ACTIVE (pChannelVc) == FALSE || ADAPTER_ACTIVE(pAdapter) == FALSE)
        {
            fVcActive = FALSE;  
        }

        if (VC_TEST_FLAG( pChannelVc, VCBF_GenerationWorkItem) == TRUE)
        {
            TRACE( TL_N, TM_Send, ( "AsyncStreamSendPacketHandler, Getting a new Gen, Fail send ") );

            fVcActive = FALSE;  
        }

        if (fVcActive == TRUE)
        {
            nicReferenceCall (pVc, "AsyncStreamSendPacketsHandler");
        }
        
        VC_RELEASE_LOCK (pVc);

        if (fVcActive  == FALSE)
        {
            TRACE( TL_N, TM_Send, ( "AsyncStreamSendPacketHandler, VC Not Active VC %x , Flag %x", pVc, pVc->Hdr.ulFlags ) );

            NdisStatus = NDIS_STATUS_FAILURE;
            break;
        }

        
        //
        //  Copy NdisBuffer in Packet to Local Memory and get an Mdl that points 
        //  to this memory (we get 1 Mdl only)
        NdisQueryPacket( pPacket,
                       NULL,
                       NULL,
                       NULL,
                       &PacketLength);

        ASSERT (pPacket->Private.Head != NULL);


        pStartNdisBuffer = pPacket->Private.Head;
        pStartPacketData = nicNdisBufferVirtualAddress (pStartNdisBuffer);

        if (pStartPacketData == NULL)
        {
            NdisStatus = NDIS_STATUS_RESOURCES;
            TRACE( TL_N, TM_Send, ( "AsyncStreamSendPacketHandler, pStartPacketData ") );

            break;
        }


        TRACE( TL_V, TM_Send, ( "PacketLength %x", PacketLength) );



        NumFragmentsNeeded = nicNumFragmentsNeeded (PacketLength,
                                                    pChannelVc->Hdr.MaxPayload,
                                                    sizeof (NDIS1394_FRAGMENT_HEADER) + ISOCH_PREFIX_LENGTH );

        TRACE( TL_V, TM_Send, ( "NumFragments  %x, pVc->MaxSendSize", 
                                 NumFragmentsNeeded,pVc->Hdr.MaxPayload) );

        //
        // first choose the lookaside list
        //
        //

        
        if (PacketLength < PAYLOAD_100)
        {
            pLookasideList = &pAdapter->SendLookasideList100;
            TRACE( TL_V, TM_Send, ( " PAYLOAD_100 Lookaside List %x", 
                                    &pAdapter->SendLookasideList100) );

        }
        else 
        if (PacketLength < PAYLOAD_2K)
        {
            pLookasideList = &pAdapter->SendLookasideList2K;
            TRACE( TL_V, TM_Send, ( " PAYLOAD_2K Lookaside List %x", 
                                    &pAdapter->SendLookasideList2K) );


        } else
        if (PacketLength < PAYLOAD_8K)
        {
            pLookasideList = &pAdapter->SendLookasideList8K;
            TRACE( TL_V, TM_Send, ( " PAYLOAD_8K Lookaside List %x", 
                                    &pAdapter->SendLookasideList8K) );

    
        }else
        {
            //
            // Add code for local allocation
            //
            ASSERT (0);
        }

        //
        // are we going to fragment
        // 
        ASSERT (pLookasideList != NULL)

        //
        // We are not going to fragment. Optimize this path
        //
        pLookasideListBuffer = nicGetLookasideBuffer (pLookasideList);
        
        if (pLookasideListBuffer == NULL )
        {
            NdisStatus = NDIS_STATUS_FAILURE;
            BREAK (TM_Send, ("nicGetLookasideBuffer  FAILED") );
        }

        //
        // Initialize the header with relevant information that the send complete
        // will need
        //
    
        pLookasideHeader = (PLOOKASIDE_BUFFER_HEADER)pLookasideListBuffer;
        pLookasideHeader->IsFragmented          = FALSE;  // Default
        pLookasideHeader->FragmentsGenerated    = 0;
        pLookasideHeader->pLookasideList        = pLookasideList;
        pLookasideHeader->pNdisPacket           = pPacket;
        pLookasideHeader->pVc                   =(PVCCB)pVc;
        pLookasideHeader->AsyncOp               = AsyncStream;
        pLookasideHeader->OutstandingFragments = NumFragmentsNeeded ;

        //
        // Initialize the Fragment structure
        //
        //
        //  Do we fragment or not. Base it on the MaxPayload field
        //
        
        TRACE( TL_V, TM_Send, ( "   Fragment  PacketLength %x, pVc->MaxPayload %x ", 
                                 PacketLength ,pVc->Hdr.MaxPayload) );

                                 
        //
        // Do we need to fragment. Use the number of fragments generated to figure it out
        //
        
        if (NumFragmentsNeeded == 1)
        {
            //
            // No need to fragment here. We will use the UNFRAGMENTED Layout
            //
            // First Get a local buffer from our lookaside list
            //
            PUNFRAGMENTED_BUFFER pUnfragmentedBuffer = (PUNFRAGMENTED_BUFFER )pLookasideHeader;
            PPACKET_FORMAT pDestination = (PPACKET_FORMAT)&pUnfragmentedBuffer->Data[0];
            //
            // Add the gasp header
            //
            NdisMoveMemory ((PVOID)&pDestination->AsyncStreamNonFragmented.GaspHeader, 
                            &pAdapter->GaspHeader,
                            sizeof (GASP_HEADER) );


            //
            // copy the data over,  to the location just after the Gasp Header
            // In the unfragmented case, the packet already has the correct header
            //
            NdisStatus = nicCopyNdisBufferChainToBuffer (pStartNdisBuffer, 
                                                         (PVOID)&pDestination->AsyncStreamNonFragmented.NonFragmentedHeader,
                                                         pLookasideList->MaxSendSize);
                                             
    
            if (NdisStatus != NDIS_STATUS_SUCCESS)
            {
                BREAK ( TM_Send, ( "   AsyncStreamSendPacketHandler, nicCopyNdisPacketToUnfragmentedBuffer Failed ") );
            }
    
            
            ASSERT (pLookasideListBuffer != NULL);

            // 
            // Initialize all the variable needed by the Next section of the code.
            // This deals with setting up the Mdl and the IRB
            //
            
            pStartNdisBuffer = NULL;

            Fragment.pStartFragment = (PVOID)pDestination;
            Fragment.FragmentLength  = PacketLength + sizeof (GASP_HEADER);
            Fragment.pCurrNdisBuffer = NULL;

            pLookasideHeader->FragmentsGenerated = 1; 
            pLookasideHeader->IsFragmented = FALSE;                                    
            
        }
        else
        {
            //
            // We need to fragment
            //
            ULONG Dgl = NdisInterlockedIncrement(&pAdapter->dgl);

            //
            // Initialize the fragment header. The unfragmented code path
            // does not care about these fields
            //
            Fragment.TxHeaderSize = sizeof (NDIS1394_FRAGMENT_HEADER) + sizeof (GASP_HEADER);
            Fragment.AsyncOp = AsyncStream;
            Fragment.pLookasideList = pLookasideList;
            Fragment.pAdapter = pAdapter;
            Fragment.pLookasideListBuffer = pLookasideListBuffer;
            Fragment.IPDatagramLength = (USHORT)PacketLength - sizeof (NDIS1394_UNFRAGMENTED_HEADER);   

            Fragment.MaxFragmentLength = pChannelVc->Hdr.MaxPayload;                                                                    
            Fragment.NumFragmentsNeeded = NumFragmentsNeeded;

            //
            // Allocate from the fragmented pool and initialize the fragment header structure
            //
            

            NdisStatus = nicFirstFragmentInitialization (pPacket->Private.Head,
                                                         Dgl,
                                                         &Fragment);


            if (pLookasideListBuffer  == NULL || NdisStatus != NDIS_STATUS_SUCCESS)
            {
                BREAK (TM_Send, (" AsyncStreamSendPacketsHandler: nicFirstFragmentInitialization : FAILED" )) ;
            }       

            ASSERT (pLookasideListBuffer != NULL);
            
            pLookasideHeader->IsFragmented = TRUE;                                     
                                       
            
        }


        //
        // Now begin the loop which will send n fragments
        //
        do 
        {   

            //
            // Do we need to fragment. If so , extract one fragment out of the NdisPacket
            //
            if (pLookasideHeader->IsFragmented == TRUE )
            {   
            
                //
                // We copy one fragment over and this will allocate the lookaside list
                //

                NdisStatus = nicCopyOneFragment (&Fragment);
                if (NDIS_STATUS_SUCCESS != NdisStatus)
                {
                    BREAK ( TM_Send, ( "   AsyncStreamSendPacketHandler, nicCopyOneFragment  Failed ") );
                }
                                              
                //
                // Get the pointer to the Irb here. and set it up for the next time
                //
                //
                pMyIrb = Fragment.pCurrentIrb;
                Fragment.pCurrentIrb = (PIRB)((ULONG_PTR)Fragment.pCurrentIrb + sizeof (IRB) );
                
            }
            else
            {
                //
                // No Curr NdisBuffer as this packet was never fragmented. 
                //
                
                ASSERT (pLookasideHeader->IsFragmented == FALSE);                                      

                pMyIrb =  &((PUNFRAGMENTED_BUFFER )pLookasideHeader)->Irb;
            }
            
            //
            // At this point we have one fragment that needs to be transmitted.
            // Data structures have been updated to set up the MDL and the IRB
            //

            NdisStatus = nicGetMdl (Fragment.FragmentLength  , 
                                    Fragment.pStartFragment , 
                                    &pMyMdl);

            if (NdisStatus != NDIS_STATUS_SUCCESS)
            {
                BREAK ( TM_Send, ( "   AsyncStreamSendPacketHandler, nicCopyNdisBufferChainToBuffer Failed ") );
            }       

            nicIncChannelSendMdl()
            //
            //  Fill in the Irb with the correct values from the VC
            //  Stuff we need to add to the send VC - BlockSize,Generation
            //  

            nicInitAsyncStreamIrb((PCHANNEL_VCCB)pVc, pMyMdl, pMyIrb);

            //
            // Get a free Irp 
            //

            NdisStatus  = nicGetIrp (pAdapter->pNdisDeviceObject, &pMyIrp); 
        
            if (NdisStatus != NDIS_STATUS_SUCCESS)
            {
                break;
            }
            //
            // At this point, we have a guarantee that the Completion routine will be called
            //
            ASSERT (NdisStatus == NDIS_STATUS_SUCCESS);

            //
            // Dump the Fragment 
            //
            nicDumpMdl (pMyMdl , 0, "AsyncStream Fragment");




            NIC1394_LOG_PKT(
                pAdapter,
                NIC1394_LOGFLAGS_SEND_CHANNEL,
                pAdapter->BCRData.LocalNodeNumber,          // SourceID
                pChannelVc->Channel,
                Fragment.pStartFragment, 
                Fragment.FragmentLength
                );

            //
            // This function implements the common functionality to be implemented by
            // all other send/recv cals to IoCallDriver
            //

            //
            // We IGNORE the NtStatus as the completion handler will be called
            //
            nicIncrementBusSends(pVc);
            
            NtStatus = nicSubmitIrp(pAdapter->pNdisDeviceObject,
                                    pMyIrp,
                                    pMyIrb,
                                    AsyncWriteStreamSendComplete,
                                   (PVOID)pLookasideListBuffer);

            TRACE( TL_V, TM_Send, ( " pCurrNdisBuffer  %x, NdisStatus %x ", Fragment.pCurrNdisBuffer , NdisStatus ) );

            
            
        } while (Fragment.pCurrNdisBuffer != NULL && NdisStatus == NDIS_STATUS_SUCCESS);

    
    } while (FALSE);

    //
    // DO NOT touch the packet if status == NDIS_STATUS_SUCCESS. 
    //

    
    //
    //  CleanUp if any of the allocations failed. We do not have a pointer
    //  to the LocalBuffer (it is embedded in the Mdl)  so it remains NULL
    //
    //  NdisStatus != Success means that we never got to nicSubmitIrp
    //
    
    if (NdisStatus != NDIS_STATUS_SUCCESS)
    {   

        ASSERT (pMyIrp == NULL);

        //
        // fVc Active makes sure that we actually got around to allocating 
        // and referencing structures
        //
        
        if (fVcActive == TRUE)
        {

            if (pLookasideListBuffer != NULL)
            {

                //
                // Complete this fragment, as we never submit'd the IRP to
                // the 1394 bus driver
                //
                AsyncWriteStreamSendComplete(NULL, // PDO
                                             NULL, 
                                             pLookasideListBuffer);

                NdisStatus =NDIS_STATUS_SUCCESS;
            }                                  
            else
            {

                //
                // This thread needs to decrement the refcounts as 
                // AsyncWriteStreamSendComplete was not called
                //
                nicDereferenceCall ((PVCCB) pVc, "AsyncStreamSendPacketsHandler");

            }

        }

    }


    

    TRACE( TL_T, TM_Send, ( "<==AsyncStreamSendPacketHandler, NdisStatus  %x", NdisStatus ) );
    MATCH_IRQL;

    //
    // Make sure this is NDIS_STATUS_PENDING if the Irp was sent down or 
    // AsyncWriteStreamSendCOmplete was called.
    //
    return NdisStatus;
}
        

NDIS_STATUS
nicEthernetVcSend(
    IN PVCCB        pVc,
    IN PNDIS_PACKET  pPacket 
    )
/*++

Routine Description:
  reroutes all sends as an CL receive

Arguments:


Return Value:


--*/
{

    PETHERNET_VCCB      pEthernetVc = (PETHERNET_VCCB)pVc;
    PADAPTERCB          pAdapter = pVc->Hdr.pAF->pAdapter;
    BOOLEAN             fVcActive = FALSE;
    NDIS_STATUS         NdisStatus = NDIS_STATUS_FAILURE;
    PNDIS_PACKET        pMyPacket = NULL;
    NDIS_STATUS         IndicatedStatus= NDIS_STATUS_FAILURE;
    PPKT_CONTEXT        pPktContext = NULL;
    
    TRACE( TL_T, TM_Send, ( "==>nicEthernetVcSend, pVc   %x, pPacket %x", 
                                 pVc , pPacket ) );

    do
    {




    
        ADAPTER_ACQUIRE_LOCK (pAdapter);

        if (VC_ACTIVE (pEthernetVc)==TRUE)
        {
            fVcActive = TRUE;
            nicReferenceCall (pVc, "nicEthernetVcSend" ) ;
            
        }

        ADAPTER_RELEASE_LOCK (pAdapter);
        
        if (fVcActive == FALSE)
        {
            NdisStatus = NDIS_STATUS_FAILURE;
            break;
        }

        nicAllocatePacket (&NdisStatus,
                       &pMyPacket ,
                       &pEthernetVc->PacketPool ); 

        if (NdisStatus != NDIS_STATUS_SUCCESS || pMyPacket == NULL)
        {
            pMyPacket = NULL;
            BREAK (TM_Send, "Ethernet VC - AllocatePacket failed" ) ;
        }


        pMyPacket->Private.Head = pPacket->Private.Head;
        pMyPacket->Private.Tail = pPacket->Private.Tail;
        

        IndicatedStatus = NDIS_STATUS_RESOURCES;
        NDIS_SET_PACKET_STATUS(pMyPacket, IndicatedStatus);

        //
        // Set up the context
        // 
        pPktContext = (PPKT_CONTEXT)&pMyPacket->MiniportReservedEx; 
        pPktContext->EthernetSend.pOrigPacket = pPacket;    

        //
        // Dump the packet
        //

        {
            nicDumpPkt (pMyPacket, "Conn Less Rcv ");
            nicCheckForEthArps (pMyPacket);
        }
        //
        // Now indicate the packet
        //

        //
        // Bluff the OOB Size. To get past an assert on debug Ndis
        //
        NDIS_SET_PACKET_HEADER_SIZE (pMyPacket, 14); 
        NdisMIndicateReceivePacket (pAdapter->MiniportAdapterHandle,
                                &pMyPacket,
                                1);
        

        
        pPktContext = (PPKT_CONTEXT)&pMyPacket->MiniportReservedEx; 
        ASSERT ( pPacket == pPktContext->EthernetSend.pOrigPacket );

        nicMpCoSendComplete (NDIS_STATUS_SUCCESS,
                             pVc,
                             pPacket);


        //
        // We have successfully pended the Io/ 
        // Now the completion routine will be called
        //
        NdisStatus = NDIS_STATUS_SUCCESS;
        

    } while (FALSE);

    if (pMyPacket != NULL)
    {
        //
        // Free the locally allcoate packet
        //
        nicFreePacket(pMyPacket, &pEthernetVc->PacketPool);
    }

    if (fVcActive == TRUE)
    {
        nicDereferenceCall (pVc, "nicEthernetVcSend" ) ;

    }



    TRACE( TL_T, TM_Send, ( "<==nicEthernetVcSend, ") );

    return NdisStatus;
}






VOID
nicGetGenerationWorkItem(
    NDIS_WORK_ITEM* pGetGenerationWorkItem,
    IN PVOID Context 
    )
    // Function Description:
    // Work Item used to submit a Get Generation IRP at Passive Level
    //
    // Arguments
    //
    // Return Value:
    //    Generation - 



{
    PVCCB               pVc = (PVCCB) Context;
    PADAPTERCB          pAdapter = pVc->Hdr.pAF->pAdapter;
    NDIS_STATUS         NdisStatus = NDIS_STATUS_FAILURE;
    UINT                Generation = 0;

    TRACE( TL_T, TM_Mp, ( "==>nicGetGenerationWorkItem, pVc", Context ) );


    NdisStatus = nicGetGenerationCount (pAdapter , &Generation);


    //
    // Update the generation
    //
    VC_ACQUIRE_LOCK (pVc);
    
    if (NdisStatus == NDIS_STATUS_SUCCESS && Generation > *pVc->Hdr.pGeneration )
    {
        pAdapter->Generation = Generation;
    
    }
    
    VC_CLEAR_FLAGS(pVc, VCBF_GenerationWorkItem);
    
    VC_RELEASE_LOCK (pVc);

    // Dereference the call, this will allow the close call to complete. Do not touch VC after this.
    //
    nicDereferenceCall(pVc, "nicSendFailureInvalidGeneration");

    

    TRACE( TL_T, TM_Mp, ( "<==nicGetGenerationWorkItem, Gen %x", Generation) );

    FREE_NONPAGED (pGetGenerationWorkItem);
    NdisInterlockedDecrement(&pAdapter->OutstandingWorkItems);

}

VOID
nicUpdatePacketState (
    IN PNDIS_PACKET pPacket,
    IN ULONG Tag
    )
/*++

Routine Description:
    Validates and then updates that packet tag. So we can heep track of the packet

Arguments:


Return Value:


--*/
{

    switch (Tag)
    {
        case NIC1394_TAG_COMPLETED:
        {

            *(PULONG)(&pPacket->MiniportReserved[0]) = NIC1394_TAG_COMPLETED;

            break;
        }

        case NIC1394_TAG_IN_SEND:
        {
            *(PULONG)(&pPacket->MiniportReserved[0]) = NIC1394_TAG_IN_SEND;
            break;      
        }

        default:
        {
            ASSERT (!"Invalid Tag on NdisPacket");
        }

    }


}










NDIS_STATUS
nicQueueSendPacket(
    PNDIS_PACKET pPacket, 
    PVCCB pVc 
    )
/*++

Routine Description:

    This function inserts a packet into the send queue. If there is no timer servicing the queue
    then it queues a timer to dequeue the packet in Global Event's context


Arguments:

    Self explanatory 
    
Return Value:
    Success - if inserted into the the queue

--*/
    
{
    NDIS_STATUS Status = NDIS_STATUS_FAILURE;

    BOOLEAN fSetTimer = FALSE;
    PADAPTERCB pAdapter = pVc->Hdr.pAF->pAdapter;
    PNDIS_SEND_CONTEXT  pSendContext = (PNDIS_SEND_CONTEXT)(pPacket->MiniportReservedEx) ;


    do
    {
        extern ULONG TotSends;
        TotSends++;

        //
        // Store the pvc in the Miniport Reserved
        //
        pSendContext->pVc = pVc;
                
        ADAPTER_ACQUIRE_LOCK (pAdapter);

        //
        // Find out if this thread needs to fire the timer
        //

        if (pAdapter->SerSend.bTimerAlreadySet == FALSE)
        {
            fSetTimer = TRUE;
            pAdapter->SerSend.bTimerAlreadySet = TRUE;

        }
                
        InsertTailList(
                &pAdapter->SerSend.Queue,
                &pSendContext->Link
                );
        pAdapter->SerSend.PktsInQueue++;

        nicReferenceCall (pVc, "nicQueueSendPacket ");

        ADAPTER_RELEASE_LOCK (pAdapter);

        //
        // Now queue the timer
        //
        
        if (fSetTimer == TRUE)
        {
            PNDIS_MINIPORT_TIMER pSendTimer;
            //
            //  Initialize the timer
            //
            pSendTimer = &pAdapter->SerSend.Timer;      

            
            TRACE( TL_V, TM_Recv, ( "   Set Timer "));
            
            NdisMSetTimer ( pSendTimer, 0);

        }


        Status = NDIS_STATUS_SUCCESS;

    } while (FALSE);

    ASSERT (Status == NDIS_STATUS_SUCCESS);
    return Status;
}



NDIS_STATUS
nicInitSerializedSendStruct(
    PADAPTERCB pAdapter
    )
/*++

Routine Description:
    Used to initialize the Send Serialization Struct

Arguments:



Return Value:


--*/
{


    NdisZeroMemory (&pAdapter->SerSend, sizeof(pAdapter->SerSend));
    InitializeListHead(&pAdapter->SerSend.Queue);

    NdisMInitializeTimer (&pAdapter->SerSend.Timer,
                      pAdapter->MiniportAdapterHandle,
                      nicSendTimer ,
                      pAdapter);


    return NDIS_STATUS_SUCCESS;

}


VOID
nicDeInitSerializedSendStruct(
    PADAPTERCB pAdapter
    )
/*++

Routine Description:

    Deinit's the Serialize send struct. Does nothing for now

Arguments:


Return Value:


--*/
{





}

VOID
nicSendTimer (
    IN  PVOID                   SystemSpecific1,
    IN  PVOID                   FunctionContext,
    IN  PVOID                   SystemSpecific2,
    IN  PVOID                   SystemSpecific3
    )
/*++

Routine Description:
   This function dequeues a packet and invokes the appropriate send handler
   to fire this packet off to the 1394 bus driver

Arguments:

    Function context - Adapter structure, which has the Packet queue. 
    Each packet has the VC embedded in it.

Return Value:


--*/
{
    

    PADAPTERCB      pAdapter = (PADAPTERCB) FunctionContext;
    BOOLEAN         fVcCorrupted = FALSE;

    TRACE( TL_T, TM_Recv, ( "==>nicSendTimer  Context %x", FunctionContext));

    nicIncrementSendTimerCount();

    ADAPTER_ACQUIRE_LOCK (pAdapter);
    

    //
    // Get the stats out
    //
    nicSetCountInHistogram(pAdapter->SerSend.PktsInQueue, SendStats);   

    nicSetMax(nicMaxSend, pAdapter->SerSend.PktsInQueue);
    


    //
    // Empty the Queue indicating as many packets as possible
    //
    while (IsListEmpty(&pAdapter->SerSend.Queue)==FALSE)
    {
        PNDIS_SEND_CONTEXT      pSendContext;
        PNDIS_PACKET            pPacket;
        PVCCB                   pVc;
        PLIST_ENTRY             pLink;
        NDIS_STATUS             NdisStatus;

        pAdapter->SerSend.PktsInQueue--;

        pLink = RemoveHeadList(&pAdapter->SerSend.Queue);

        ADAPTER_RELEASE_LOCK (pAdapter);

        //
        // Extract the send context
        //
        pSendContext = CONTAINING_RECORD(
                                           pLink,
                                           NDIS_SEND_CONTEXT,
                                           Link);


        pVc = pSendContext->pVc;

        if (pVc->Hdr.ulTag != MTAG_VCCB)
        {
            ASSERT (pVc->Hdr.ulTag == MTAG_VCCB);
            fVcCorrupted = TRUE;
            break;
    
        }

        //
        // Now get the packet
        //
        pPacket = CONTAINING_RECORD ( pSendContext,
                                       NDIS_PACKET,
                                       MiniportReservedEx);


        //
        // Call the send handler for the Vc, packet
        //
        nicUpdatePacketState (pPacket, NIC1394_TAG_IN_SEND);

        NdisStatus = pVc->Hdr.VcHandlers.SendPackets(pVc, pPacket);

        //
        // Reference was made before queueing the packet
        //
        nicDereferenceCall (pVc, "nicSendTimer ");
        //
        // Complete the packet , if the send was synchronous
        //
        if (NT_SUCCESS(NdisStatus) == FALSE) // can pend
        {
            nicMpCoSendComplete( NdisStatus,pVc,pPacket);
        }
        

        ADAPTER_ACQUIRE_LOCK (pAdapter);

    }
    
    //
    // clear the flag
    //

    ASSERT (pAdapter->SerSend.PktsInQueue==0);
    ASSERT (IsListEmpty(&pAdapter->SerSend.Queue));

    pAdapter->SerSend.bTimerAlreadySet = FALSE;


    ADAPTER_RELEASE_LOCK (pAdapter);

    
    TRACE( TL_T, TM_Recv, ( "<==nicSendTimer  "));

    


}






VOID
nicMpCoSendComplete (
    NDIS_STATUS NdisStatus,
    PVCCB pVc,
    PNDIS_PACKET pPacket
    )
/*++

Routine Description:
  Wrapper function around NdisMCoSendComplete

Arguments:


Return Value:


--*/
{

        nicIncrementSendCompletes (pVc);
        
        if (NdisStatus == NDIS_STATUS_SUCCESS)
        {
            nicIncrementVcSendPktCount(pVc, pPacket);
        }
        else
        {
            nicIncrementVcSendFailures (pVc, pPacket);
        }

        nicUpdatePacketState (pPacket, NIC1394_TAG_COMPLETED);

        NdisMCoSendComplete(NdisStatus,
                            pVc->Hdr.NdisVcHandle,
                            pPacket);


}



UINT
nicNumFragmentsNeeded (
    UINT PacketLength ,
    UINT MaxPayload,
    UINT FragmentOverhead
    )
/*++

Routine Description:

     Now account for the Fragment headers as well. A fragment header will be added 
     at the head of each fragment.  The Unfragmented header at the head of the data
     will be removed
                
                


Arguments:

    FragmentOverhead - the size of the fragment header, in the asyncstream it includes the gasp header+fragment header.
                      for asyncwrite it is just the fragmentation header

Return Value:


--*/

                
    
{

        UINT NewPacketSize; 
        UINT TotalCapacitySoFar;
        UINT NumFragmentsNeeded ;

        ASSERT (PacketLength  != 0 );
        ASSERT (MaxPayload != 0) ;
        ASSERT (FragmentOverhead != 0);

        //
        // This division takes care of the case where PacketLength 
        // is an integral multiple of the MaxPayload.  Since we add 1 to the fragment
        // it takes care of the overhead added by the fragment headers
        //
        NumFragmentsNeeded = (PacketLength / MaxPayload) + 1;

         

        //
        // If we add the fragment and gasp header to our fragments, we
        // might need another fragment due to an overflow
        //

        //
        // Calculate the new packet size after fragmentation 
        //
        {
            //
            // Add the length of the fragment headers 
            //
            NewPacketSize = PacketLength + (NumFragmentsNeeded * FragmentOverhead);

            //
            // Now remove the default non-fragment header
            //
            NewPacketSize -= sizeof (NDIS1394_UNFRAGMENTED_HEADER)   ;
        }

        //
        // 
        //
        
        TotalCapacitySoFar = NumFragmentsNeeded * MaxPayload;
        
        if ( NewPacketSize > TotalCapacitySoFar)
        {
            //
            // We'll need one more fragment
            //
            NumFragmentsNeeded ++;
        }

        return NumFragmentsNeeded ; 

}





VOID
nicCheckForEthArps (
    IN PNDIS_PACKET pPkt
    )
/*++

Routine Description:

    It will print the pkt if an eth arp or arp response goes 
    through nic1394
                
Arguments:

Return Value:


--*/
{

    PNDIS_BUFFER pBuffer;
    ULONG Len;
    ENetHeader* pENetHeader = NULL;
    PETH_ARP_PKT pArp = NULL;
    USHORT PacketType;
    USHORT opcode;
    extern ULONG g_ulDumpEthPacket ;
    do
    {

        if (g_ulDumpEthPacket == 0)
        {
            break;
        }

        pBuffer = pPkt->Private.Head;
        Len = NdisBufferLength (pBuffer);

        if (Len < sizeof (ENetHeader) )
        {
            ASSERT (Len >= sizeof (ENetHeader) );
            break;
        }

        pENetHeader = (ENetHeader*) NdisBufferVirtualAddress (pBuffer);

        if (pENetHeader == NULL)
        {
            ASSERT ( pENetHeader != NULL);
            break;
        }

        PacketType = ntohs (pENetHeader->eh_type);

        
        if (PacketType == ARP_ETH_ETYPE_IP)
        {
            break;
        }

        if (PacketType == ARP_ETH_ETYPE_ARP)
        {
            DbgPrint ("Arp Pkt - ");
        }

        pArp = (ETH_ARP_PKT*)pENetHeader;

        opcode = ntohs(pArp->opcode);

        if (opcode == ARP_ETH_REQUEST )
        {
            DbgPrint ("Request ");
        }
        else if (opcode == ARP_ETH_RESPONSE )
        {
            DbgPrint ("Response ");
        }
        else
        {
            break;
        }

        // Print the packet
        DbgPrint("\n");
 
        {

            ENetAddr    Addr;

            Addr = pArp->sender_hw_address;

            DbgPrint ("Sender Hw Addr %x %x %x %x %x %x \n",
                        Addr.addr[0],
                        Addr.addr[1],
                        Addr.addr[2],
                        Addr.addr[3],
                        Addr.addr[4],
                        Addr.addr[5]);
                        
            DbgPrint ("Ip Addr %x\n",pArp->sender_IP_address);

            Addr = pArp->target_hw_address;

            DbgPrint ("Target Hw Addr %x %x %x %x %x %x \n",
                        Addr.addr[0],
                        Addr.addr[1],
                        Addr.addr[2],
                        Addr.addr[3],
                        Addr.addr[4],
                        Addr.addr[5]);
                        
            DbgPrint ("Ip Addr %x\n",pArp->target_IP_address);

        }
        

    } while (FALSE);


}