// Copyright (c) 1998-1999, Microsoft Corporation, all rights reserved
//
// send.c
//
// IEEE1394 mini-port/call-manager driver
//
// Mini-port Send routines
//
// 05/15/1999 ADube Created, 
//




#include <precomp.h>




//-----------------------------------------------------------------------------
// Local prototypes (alphabetically)
//-----------------------------------------------------------------------------

extern ULONG           NdisBufferAllocated[NoMoreCodePaths];
extern ULONG           NdisBufferFreed[NoMoreCodePaths];


NDIS_STATUS
nicGetIsochDescriptors(
    IN UINT Num,
    OUT PPISOCH_DESCRIPTOR  ppIsochDescriptor
    );



NDIS_STATUS
nicInitializeIsochDescriptor (
    IN PCHANNEL_VCCB pChannelVc,
    IN PMDL pMdl,
    IN PISOCH_DESCRIPTOR pIsochDesriptor
    );


VOID
nicIsochRxCallback (
    IN PVOID Context1,
    IN PVOID Context2
    );

ULONG           ReassemblyCompleted = 0;

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





NDIS_STATUS
nicAllocateAndInitializeIsochDescriptors (
    IN PCHANNEL_VCCB pChannelVc,
    IN UINT NumDescriptors,
    IN UINT BufferLength,
    IN OUT PPISOCH_DESCRIPTOR  ppIsochDescriptor
    )

    
    // Function Description:
    //   This function allocate and initializes a 
    //   a numer of isoch descriptors all allocated 
    //   together.
    //   This should allocate isoch desc, 
    //   get local memory, get mdls 
    //   initialize mdl, isoch _desc and then return
    //
    //
    // Arguments
    //  ChannelVc- this contains all the other argumernts, size
    //  num of descriptors,etc
    //  ppIsochDescriptor- will contain the pointer to the allocated 
    //  array of IsochDescriptor
    //
    //
    // Return Value:
    //  Success if all the memory allocations succeeded 
    //
    //
    //
{
    NDIS_STATUS NdisStatus = NDIS_STATUS_FAILURE;
    UINT Size;
    UINT index =0;
    PMDL pMdl = NULL;
    PVOID pLocalBuffer = NULL;

    TRACE( TL_T, TM_Recv, ( "==>nicAllocateAndInitializeIsochDescriptors Vc %.8x, Num %.8x", 
                                 pChannelVc, NumDescriptors) );
    TRACE( TL_V, TM_Recv, ( "Max BuffferSize %.8x", pChannelVc, BufferLength) );

    do
    {
        
        NdisStatus = nicGetIsochDescriptors (NumDescriptors, ppIsochDescriptor);

    
        if (NdisStatus != NDIS_STATUS_SUCCESS)
        {
            BREAK ( TM_Recv, "nicGetIsochDescriptors FAILED" );
        }

        ASSERT (*ppIsochDescriptor != NULL);

        while (index < NumDescriptors)
        {
            //
            // Get a local that point to the isoch descriptor that needs to be initialized
            //
            PISOCH_DESCRIPTOR pIsochDescriptor = NULL;
            pIsochDescriptor = (PISOCH_DESCRIPTOR)((ULONG_PTR)(*ppIsochDescriptor)+ index*sizeof(ISOCH_DESCRIPTOR));
            //
            // Get Locally owned memory for the data
            // 
            NdisStatus = nicGetLocalBuffer (BufferLength, &pLocalBuffer);

            if (NdisStatus != NDIS_STATUS_SUCCESS)
            {
                BREAK ( TM_Recv, ( "nicGetLocalBuffer FAILED " ) );
            }
            
            //
            // Get an MDL and initialze the MDL with the buffer 
            //
            NdisStatus = nicGetMdl ( BufferLength,
                                    pLocalBuffer,
                                    &pMdl);

            if (NdisStatus != NDIS_STATUS_SUCCESS)
            {
                nicFreeLocalBuffer(BufferLength, pLocalBuffer);
                pLocalBuffer = NULL;
                BREAK ( TM_Recv, ( "nicGetMdl FAILED " ) );
            }

            //
            // this reference will be removed when the MDL will be freed 
            // in FreeSingleIsochDescriptor
            //
            nicReferenceCall ((PVCCB)pChannelVc, "nicAllocateAndInitializeIsochDescriptors " );

            NdisStatus = nicInitializeIsochDescriptor (pChannelVc,
                                                  pMdl,
                                                  pIsochDescriptor);

            if (NdisStatus != NDIS_STATUS_SUCCESS)
            {
                BREAK ( TM_Recv, ( "nicInitializeIsochDescriptor FAILED " ) );
            }
                               
            index++;
        }


    } while (FALSE);

    TRACE( TL_T, TM_Recv, ( "==>nicAllocateAndInitializeIsochDescriptors Status %.8x", NdisStatus) );

    return NdisStatus;

}


NDIS_STATUS
nicFreeIsochDescriptors(
    IN UINT Num,
    IN PISOCH_DESCRIPTOR  pIsochDescriptor,
    IN PVCCB pVc
    )
    // Function Description:
    //   Free Num number of IsochDescriptors
    //
    // Arguments
    //  Num number of descriptors
    //  Start of the allocated memory
    //
    //
    // Return Value:
    //  Success if all the memory allocations succeeded 
    //
{
    NDIS_STATUS NdisStatus = NDIS_STATUS_FAILURE;
    PISOCH_DESCRIPTOR pCurrDesc = pIsochDescriptor;
    UINT i=0;

    
    TRACE( TL_T, TM_Recv, ( "==>nicFreeIsochDescriptors Num %.8x, pIsochDescriptor %x", Num, pIsochDescriptor ) );
    do 
    {
        if (pIsochDescriptor == NULL)
        {
            break;
        }
        
        pCurrDesc = pIsochDescriptor;

        while (i < Num)
        {
            nicFreeSingleIsochDescriptor( pCurrDesc, pVc);

            pCurrDesc  = (PISOCH_DESCRIPTOR)((PUCHAR)pCurrDesc + sizeof(ISOCH_DESCRIPTOR));

            i++;

        }       
        
        FREE_NONPAGED (pIsochDescriptor);   

    } while (FALSE);

    NdisStatus = NDIS_STATUS_SUCCESS;

    TRACE( TL_T, TM_Recv, ( "<==nicGetIsochDescriptors Status %.8x", NdisStatus ) );
    return NdisStatus;
    
}



NDIS_STATUS
nicFreeSingleIsochDescriptor(
    IN PISOCH_DESCRIPTOR  pIsochDescriptor,
    IN PVCCB pVc
    )
    // Function Description:
    //   Free IsochDescriptor and its MDL
    //
    // Arguments
    //  Start of the isochDesc
    //
    //
    // Return Value:
    //  Success if all the memory allocations succeeded 
    //
{
    NDIS_STATUS NdisStatus = NDIS_STATUS_FAILURE;
    PCHANNEL_VCCB pChannelVc = pIsochDescriptor->Context1;

    ASSERT (pVc == (PVCCB)pChannelVc);
    
    TRACE( TL_T, TM_Recv, ( "==>nicFreeSingleIsochDescriptor pIsochDescriptor %x", pIsochDescriptor ) );

    //
    // Lets make sure we have a VC and Channel VC 
    //
    ASSERT (pChannelVc->Hdr.ulTag == MTAG_VCCB );

    //
    // Just to protect ourselves against a bad isoch descriptor
    //
    if (pIsochDescriptor->Mdl != NULL)
    {
        PVOID pVa = MmGetMdlVirtualAddress(pIsochDescriptor->Mdl);
        ULONG Length = MmGetMdlByteCount(pIsochDescriptor->Mdl);
            
        nicDereferenceCall ((PVCCB)pChannelVc, "nicFreeSingleIsochDescriptors ");

        nicFreeMdl (pIsochDescriptor->Mdl);

        nicFreeLocalBuffer(Length, pVa);
    }
    else
    {
        ASSERT (pIsochDescriptor->Mdl != NULL);
    }
    
    
    NdisStatus = NDIS_STATUS_SUCCESS;
    
    TRACE( TL_T, TM_Recv, ( "<==nicFreeSingleIsochDescriptors Status %.8x", NdisStatus ) );
    return NdisStatus;
    
}




NDIS_STATUS
nicGetIsochDescriptors(
    IN UINT Num,
    OUT PPISOCH_DESCRIPTOR  ppIsochDescriptor
    )
    // Function Description:
    //   The function returns a block of contigious memory of size
    //   Num * sizeof (ISOCH_DESCRIPTOR)
    //
    //
    // Arguments
    //  Num number of descriptors
    //  Start of the allocated memory
    //
    //
    // Return Value:
    //  Success if all the memory allocations succeeded 
    //
{
    NDIS_STATUS NdisStatus = NDIS_STATUS_FAILURE;
    PVOID pMemory = NULL; 
    
    TRACE( TL_T, TM_Recv, ( "==>nicGetIsochDescriptors Num %.8x", Num) );

    pMemory = ALLOC_NONPAGED (Num*sizeof(ISOCH_DESCRIPTOR),  MTAG_CBUF  );  

    if (pMemory != NULL)
    {
        NdisStatus = NDIS_STATUS_SUCCESS;
        *ppIsochDescriptor = (PISOCH_DESCRIPTOR)pMemory;
    
    }
    else
    {
        *ppIsochDescriptor  = NULL;
        TRACE( TL_A, TM_Recv, ( "<==nicGetIsochDescriptors About to Fail") );
        ASSERT (*ppIsochDescriptor  != NULL);

        
    }

    TRACE( TL_T, TM_Recv, ( "<==nicGetIsochDescriptors Status %.8x, StartAddress %.8x", NdisStatus , *ppIsochDescriptor) );
    return NdisStatus;
    
}



NDIS_STATUS
nicInitializeIsochDescriptor (
    IN PCHANNEL_VCCB pChannelVc,
    IN PMDL pMdl,
    IN OUT PISOCH_DESCRIPTOR pIsochDescriptor
    )
    // Function Description:
    //   Initializes the Isoch_desciptor using the fields in
    //   the Channel Vc Block and the Mdl
    //
    // Arguments
    //   ChannelVc- Pointer to the Channel Vc which will own 
    //              Descriptor, also contains information
    //   pMdl- The IsochDescriptor will get a pointer to this Mdl
    //   pIsochDescriptor - is the descriptor whose fields will get 
    //                    initialized
    //
    // Return Value:
    //  Success if all the memory allocations succeeded 
    //


{
    NDIS_STATUS NdisStatus = NDIS_STATUS_FAILURE;

    TRACE( TL_T, TM_Recv, ( "==>nicInitializeIsochDescriptor Vc %.8x, Mdl ", pChannelVc, pMdl) );

    pIsochDescriptor->fulFlags = DESCRIPTOR_SYNCH_ON_TAG;

    //
    // Mdl pointing to buffer
    //
    pIsochDescriptor->Mdl = pMdl;

    //
    // Length of combined buffer(s) as represented by the Mdl
    //
    pIsochDescriptor->ulLength = MmGetMdlByteCount(pMdl);

    //
    // Payload size of each Isoch packet to be used in this descriptor
    //
    pIsochDescriptor->nMaxBytesPerFrame = MmGetMdlByteCount(pMdl);

    //
    // Synchronization field; equivalent to Sy in the Isoch packet
    //
    pIsochDescriptor->ulSynch= 0;

    //
    // Synchronization field; equivalent to Tag in the Isoch packet. 
    //
    pIsochDescriptor->ulTag = g_IsochTag;

    //
    // Cycle time field; returns time to be sent/received or when finished
    //
    //    (ULONG)pIsochDescriptor->CycleTime=0;

    //
    // Callback routine (if any) to be called when this descriptor completes
    //
    pIsochDescriptor->Callback = nicIsochRxCallback;

    //
    // First context (if any) parameter to be passed when doing callbacks
    //
    pIsochDescriptor->Context1 = pChannelVc;

    //
    // Second context (if any) parameter to be passed when doing callbacks
    //
    pIsochDescriptor->Context2 = pIsochDescriptor;

    //
    // Holds the final status of this descriptor. Assign a bogus value
    //
    pIsochDescriptor->status = STATUS_INFO_LENGTH_MISMATCH;

    NdisStatus = NDIS_STATUS_SUCCESS;

    TRACE( TL_T, TM_Recv, ( "<==nicInitializeIsochDescriptors  Status %.8x, IsochDesc %.8x", NdisStatus, pIsochDescriptor) );
    return NdisStatus;
}




VOID
nicIsochRxCallback (
    PVOID Context1,
    PVOID Context2
    )
/*++

Routine Description:
    This is the callback routine passed to bus driver
    This function does some channel specific work and calls the common callback routine

Arguments:
    Context1 - pChannel Vc
    Context2 - pIsochDescriptor2

Return Value:


--*/
    
{
    PISOCH_DESCRIPTOR pIsochDescriptor = (PISOCH_DESCRIPTOR)Context2;
    PVCCB pVc = (PVCCB) Context1;

    MARK_ISOCH_DESCRIPTOR_INDICATED (pIsochDescriptor);
    STORE_CHANNELVC_IN_DESCRIPTOR (pIsochDescriptor, pVc );
    NdisInterlockedIncrement (&((PCHANNEL_VCCB)pVc)->NumIndicatedIsochDesc);
    CLEAR_DESCRIPTOR_NEXT (pIsochDescriptor);
    pIsochDescriptor->Mdl->Next = NULL;
    

    nicReceiveCommonCallback (pIsochDescriptor ,
                            pVc,
                            IsochReceive,
                            pIsochDescriptor->Mdl);
}



VOID
nicReceiveCommonCallback (
    IN PVOID pIndicatedStruct,
    IN PVCCB pVc,
    BUS_OPERATION RecvOp,
    PMDL pMdl
    )
/*++

Routine Description:

    This function is the common receive code for Isoch and Fifo Recv

    It allocate an NdisBuffer that points to the Ip1394 data
    If packet is complete (i.e reassembly is complete) , then it allocates an ndis packet
        and indicates it up to Ndis


Arguments:
    pIndicatedStruct - Isoch Desc. or Address fifo,
    pVc - VC on which data was indicated, 
    RecvOp - Isoch or Fifo,
    pMdl - Mdl associated with the Isoch Desc or Fifo. 
           Passed in seperately for ease of use (debugging)


Return Value:
    None

--*/



{
    NDIS_STATUS                         NdisStatus = NDIS_STATUS_FAILURE;
    PNDIS1394_REASSEMBLY_STRUCTURE      pReassembly = NULL;
    NIC_RECV_DATA_INFO                  RcvInfo;
    ULONG                               DataLength = 0;
    BOOLEAN                             fVcActive = FALSE;
    ULONG                               ulValidDataLength = 0;
    PVOID                               pStartValidData = NULL;
    PNDIS_BUFFER                        pHeadNdisBuffer = NULL;
    PADAPTERCB                          pAdapter = NULL;
    BOOLEAN                             fPacketCompleted  = FALSE;
    PNDIS_PACKET                        pMyPacket = NULL;
    ULONG                               PacketLength = 0;
    PPKT_CONTEXT                        pPktContext = NULL;
    NDIS_STATUS                         IndicatedStatus = NDIS_STATUS_FAILURE;
    PVOID                               pIndicatedChain = NULL;

    
    STORE_CURRENT_IRQL;

    ASSERT (RecvOp == IsochReceive || RecvOp == AddressRange);

    TRACE( TL_T, TM_Recv, ( "==>nicReceiveCommonCallback pVc %x, IndicatedStruc %x, Mdl %x", 
                              pVc, pIndicatedStruct , pMdl ) );



    do 
    {
    

        if (pVc->Hdr.ulTag != MTAG_VCCB)
        {
            ASSERT (pVc->Hdr.ulTag == MTAG_VCCB);
            NdisStatus = NDIS_STATUS_VC_NOT_ACTIVATED;

            break;
        }
        //
        // First, we need to check if we still have an active make call. VC will be present will bus driver
        // has buffers
        //

        fVcActive = VC_ACTIVE (pVc) ;

        if (fVcActive == TRUE)
        {
            pAdapter = pVc->Hdr.pAF->pAdapter;
        }

        
        
        if (fVcActive == FALSE ) 
        {
            TRACE( TL_N, TM_Recv, ( "We do not have a valid VC Block  %x ",pVc) );   

            NdisStatus = NDIS_STATUS_VC_NOT_ACTIVATED;

            break;
        }


        NdisStatus = nicValidateRecvData ( pMdl,
                                      RecvOp,
                                      pIndicatedStruct,
                                      pVc  ,
                                      &RcvInfo);
        
        if (NdisStatus != NDIS_STATUS_SUCCESS)
        {
            TRACE (TL_A, TM_Recv, ("Invalid Data Received pMdl %x, RecvOp %x, pVc %x", 
                                    pMdl, RecvOp, pVc) );
            break;
        }

                                        
        nicDumpMdl (pMdl, RcvInfo.Length1394, "Received Mdl\n");


#ifdef PKT_LOG
            
        {
            

            if (RecvOp == IsochReceive)
            {
                PPACKET_FORMAT pPacketFormat = (PPACKET_FORMAT)RcvInfo.p1394Data; 
                GASP_HEADER GaspHeader = pPacketFormat->IsochReceiveNonFragmented.GaspHeader;
                GaspHeader.FirstQuadlet.GaspHeaderHigh = SWAPBYTES_ULONG (GaspHeader.FirstQuadlet.GaspHeaderHigh);


                NIC1394_LOG_PKT(
                    pVc->Hdr.pAF->pAdapter,
                    NIC1394_LOGFLAGS_RECV_CHANNEL,
                    GaspHeader.FirstQuadlet.u.GH_Source_ID, // SourceID
                    ((PCHANNEL_VCCB)pVc)->Channel, // DestID
                    RcvInfo.p1394Data,
                    RcvInfo.DataLength );
            }
            else
            {
                PNOTIFICATION_INFO pNotificationInfo = (PNOTIFICATION_INFO)pIndicatedStruct;
                ULONG NodeNumber =  ((PASYNC_PACKET)pNotificationInfo->RequestPacket)->AP_Source_ID.NA_Node_Number;
                
                    NIC1394_LOG_PKT(
                    pVc->Hdr.pAF->pAdapter,
                    NIC1394_LOGFLAGS_RECV_FIFO  ,
                    NodeNumber , // SourceID
                    pAdapter->BCRData.LocalNodeNumber,
                    RcvInfo.p1394Data,
                    RcvInfo.DataLength );


            }
        }

#endif
        //
        // Sanity check 
        //
        
        if (RcvInfo.fFragmented == FALSE)
        {
            //
            // Pack the mdl into an ndispacket and indicate it to Ndis.
            // SHOULD BE NDIS_BUFFERS
            //

            
            ulValidDataLength = RcvInfo.DataLength;

        
            pStartValidData  = (PVOID)RcvInfo.pEncapHeader;
    
            NdisStatus = nicGetNdisBuffer ( ulValidDataLength,
                                            pStartValidData,
                                            &pHeadNdisBuffer);

            if (NdisStatus != NDIS_STATUS_SUCCESS)
            {
                pHeadNdisBuffer = NULL;
                break;
            }

            nicIncRecvBuffer(pVc->Hdr.VcType == NIC1394_RecvFIFO);

            pIndicatedChain = RcvInfo.NdisPktContext.pCommon;


            fPacketCompleted = TRUE;



        }
        else  // Recv Data is fragemented
        {
            PREMOTE_NODE    pRemoteNode= NULL;
            USHORT          SourceNodeAddress = 0xff;

            //
            // First extract all the useful information from the indicated structure, rem node, dgl, first frag, etc
            //
            nicInitRecvDataFragmented (pMdl, RecvOp, pIndicatedStruct, &RcvInfo);

            
            RcvInfo.pRemoteNode = nicGetRemoteNodeFromTable (RcvInfo.SourceID,pAdapter);


            TRACE( TL_T, TM_Recv, ( "     pRemoteNode %x", RcvInfo.pRemoteNode) );
            //
            // If there is no remote node present , break out
            //

            if (RcvInfo.pRemoteNode == NULL)
            {
                NdisStatus = NDIS_STATUS_FAILURE;

                nicRecvNoRemoteNode (pAdapter);
                BREAK (TM_Recv, (" Rx - Did not find a remote Node for reassembly" ) );
            }

            //
            // Try and use the indicated data to reassemble 
            //
            NdisStatus = nicDoReassembly (&RcvInfo, &pReassembly, &fPacketCompleted  );

            if (NdisStatus != NDIS_STATUS_SUCCESS)
            {
                TRACE (TL_N, TM_Recv, ("Generic  RX - Reassembly Failed") );
                break;
            }
            
            ASSERT (pReassembly != NULL);
            
            if (fPacketCompleted  == TRUE && 
              REASSEMBLY_ACTIVE (pReassembly))
            {
                
                TRACE( TL_V, TM_Recv, ( " pReassembly->ReassemblyComplete %x ", pReassembly->fReassemblyComplete )  );

                pHeadNdisBuffer = pReassembly->pHeadNdisBuffer;

                pIndicatedChain = pReassembly->Head.pCommon;

                nicReassemblyCompleted(pAdapter);

            }
            else
            {
                //
                // Do not call the return packet as reassembly is in progress. Dereference the ref made 
                // when the reaassembly was found in nicFindReassembly...
                //
                ASSERT (pHeadNdisBuffer == NULL);

                NdisStatus = NDIS_STATUS_SUCCESS;

            }

            ASSERT (NdisStatus == NDIS_STATUS_SUCCESS);

                
            //
            // Dereference the ref that was made nicFindReassembly. After this only one thread
            // should have the reassembly struct. And there should be one ref on it.
            //
            nicDereferenceReassembly (pReassembly, "nicIsochRxCallback Reassembly Incomplete");


        }  // Completed the ifFragmented code


        if (RcvInfo.pRemoteNode != NULL)
        {
            //
            // Deref the ref made earlier in the if fragmented code path within this function
            //
            nicDereferenceRemoteNode(RcvInfo.pRemoteNode , GetRemoteNodeFromTable );
            RcvInfo.pRemoteNode = NULL;
        }

        
        if (fPacketCompleted  == FALSE)
        {
            //
            // If  the  packet is not completely reassembled, then there is no 
            // more work to be done
            //
            pReassembly = NULL;

            NdisStatus  = NDIS_STATUS_SUCCESS;
            break;
        }

        //
        // Now we package the data into an NdisPacket
        //

        nicAllocatePacket (&NdisStatus,
                        &pMyPacket,
                        RcvInfo.pPacketPool);


        if (NdisStatus != NDIS_STATUS_SUCCESS)
        {
            pMyPacket = NULL;
            ASSERT(NdisStatus == NDIS_STATUS_SUCCESS);
            break;
        }

        
        //
        // Set the NdisBuffer as Head and Tail of the Packet
        //
        NdisChainBufferAtFront (pMyPacket, pHeadNdisBuffer);


        NdisQueryPacket( pMyPacket,
                       NULL,
                       NULL,
                       NULL,
                       &PacketLength);

        TRACE( TL_V, TM_Recv, ( "Packet %x Length %x", pMyPacket, PacketLength )  ); 

    
        NDIS_SET_PACKET_STATUS (pMyPacket, NDIS_STATUS_SUCCESS);

        // Insert the node address if we are in bridge mode
        //
        if (ADAPTER_TEST_FLAG(pAdapter,fADAPTER_BridgeMode)== TRUE)
        {
            nicInsertNodeAddressAtHead (pMyPacket, &RcvInfo);
        }
        //
        // Set up the context in the ndis packet . Contains the Vc, and 
        // the Buffer registered with the bus driver
        //

        pPktContext = (PPKT_CONTEXT)&pMyPacket->MiniportReserved;   
        pPktContext->Receive.pVc = pVc;
        pPktContext->Receive.IndicatedStruct.pCommon = pIndicatedChain ;

        
        //
        // Its time to indicate the packet to NDIS
        //

        IndicatedStatus = NDIS_GET_PACKET_STATUS(pMyPacket);

        nicIndicateNdisPacketToNdis (pMyPacket,
                                     pVc,
                                     pVc->Hdr.pAF->pAdapter);
                                     
        //
        // Dereference the reassembly here . so that pReassembly is valid and it will catch all 
        // failures prior this 
        //

        if (pReassembly != NULL)
        {
            //
            // This should free the reassembly
            //
            nicFreeReassemblyStructure (pReassembly);
            pReassembly = NULL;
        }
        
    } while (FALSE);

    ASSERT (IndicatedStatus != NDIS_STATUS_RESOURCES);

    if (IndicatedStatus == NDIS_STATUS_RESOURCES)
    {

        //
        // The packet was completed with  a status of resources.
        // It is gauranteed to be valid and we need to call the 
        // return packet handler
        //
        nicInternalReturnPacket (pVc, pMyPacket);

    }
    //
    // Failure code path
    //
    if (NdisStatus != NDIS_STATUS_SUCCESS)
    {
        //
        // if there was a valid reassembly structure and the failure occured after that
        // then we need to abort the reaasembly
        //
        if (pMyPacket)
        {
            
            nicFreePacket (pMyPacket, RcvInfo.pPacketPool);
        }

        //
        // There could still be NdisBuffers that are allocated.
        //
        if (pReassembly != NULL)
        {
            nicDereferenceReassembly (pReassembly, "Indication failure");
            nicAbortReassembly(pReassembly);

        }
        else
        {
            //
            // We need to return this descriptor or fifo back to the bus driver
            //
            
            if (pVc->Hdr.VcType == NIC1394_SendRecvChannel || 
              pVc->Hdr.VcType == NIC1394_RecvChannel )
            {
                nicReturnDescriptorChain ((PISOCH_DESCRIPTOR)pIndicatedStruct,
                                      (PCHANNEL_VCCB) pVc);
            }
            else
            {
                ASSERT (pVc->Hdr.VcType == NIC1394_RecvFIFO);
                ASSERT (((PNOTIFICATION_INFO)pIndicatedStruct)->Fifo != NULL);
                
                nicReturnFifoChain ( ((PNOTIFICATION_INFO)pIndicatedStruct)->Fifo, 
                                   (PRECVFIFO_VCCB) pVc);

            }

            if (pHeadNdisBuffer != NULL)
            {   
                nicReturnNdisBufferChain(pHeadNdisBuffer, pVc);
            }

        }

    }

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

}






VOID 
nicChannelReturnPacket (
    IN PVCCB pVc,
    IN PNDIS_PACKET pMyPacket
    )

    // Function Description:
    //  Return Packets Handler -
    //   For FIFO's will reinsert the buffer (MDL) into  the Fifo SList
    //   Check for the VC Active  and then return it ot the SList . Free it otherwise
    //
    // Arguments
    //  RecvFIFOVc - Vc of the packet
    //  pPacket = Packet in question
    //
    // Return Value:
    //
    //
    //
    
{
    PCHANNEL_VCCB       pChannelVc = (PCHANNEL_VCCB)pVc;
    PNDIS_BUFFER        pMyNdisBuffer;
    PPKT_CONTEXT        pPktContext  = (PPKT_CONTEXT)&pMyPacket->MiniportReserved; 
    PISOCH_DESCRIPTOR   pIsochDescriptor = NULL;
    
    
    TRACE( TL_T, TM_Recv, ( "==>nicChannelReturnPacket  pVc %x, pPacket %x", 
                             pChannelVc, pMyPacket) );

    ASSERT (pMyPacket != NULL);                          

    NdisUnchainBufferAtFront (pMyPacket,
                              &pMyNdisBuffer);
 
    while (pMyNdisBuffer != NULL)
    {

        NdisFreeBuffer(pMyNdisBuffer);
        nicDecChannelRecvBuffer();
        
        pMyNdisBuffer = NULL;
        
        NdisUnchainBufferAtFront (pMyPacket,
                                 &pMyNdisBuffer);
    
    } 


    //
    // Return the Descriptors to the bus driver
    //
    
    pIsochDescriptor = pPktContext->IsochListen.pIsochDescriptor;
                    

    //
    // temporary sanity check asserts
    //
    ASSERT (pIsochDescriptor != NULL);


    nicReturnDescriptorChain (pIsochDescriptor, pChannelVc);

    //
    // Free The packet 
    //
    nicFreePacket(pMyPacket, &pChannelVc->PacketPool);
    //
    // Update Count
    //
    //NdisInterlockedDecrement (&pChannelVc->OutstandingPackets ) ;

    

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

}



VOID
nicReturnDescriptorChain ( 
    IN PISOCH_DESCRIPTOR pIsochDescriptor,
    IN PCHANNEL_VCCB pChannelVc
    )
{
    //
    // Walk the list of isoch descriptors and mark them as returned
    //

    PISOCH_DESCRIPTOR pCurr = NULL;
    PVOID       pNext = NULL;
    PMDL        pMdl = NULL;
    PVOID       pVa = NULL;
    ULONG       Len = 0;

    TRACE( TL_T, TM_Recv, ( "==> nicReturnDescriptorChain pIsochDescriptor %x",pIsochDescriptor  ) );
    
    pCurr = pIsochDescriptor ;
    


    while (pCurr != NULL)
    {   
        //ASSERT (pChannelVc == (PCHANNEL_VCCB)GET_CHANNELVC_FROM_DESCRIPTOR(pCurr));

        if (pNext != NULL)
        {
            pCurr = CONTAINING_RECORD (pNext, 
                                       ISOCH_DESCRIPTOR,
                                       DeviceReserved[IsochNext] );

            
        }
        
        pNext = (PVOID)NEXT_ISOCH_DESCRIPTOR(pCurr);

        TRACE (TL_V, TM_Recv ,(" Isoch Descriptors Curr %x, next %x" , pCurr, pNext) );
        
        CLEAR_DESCRIPTOR_OF_NDIS_TAG(pCurr);

        CLEAR_DESCRIPTOR_NEXT(pCurr);

        //
        // Zero out the data that is being returned to the bus driver
        //
#if 0       
        pMdl = pCurr->Mdl;
        pVa = NIC_GET_SYSTEM_ADDRESS_FOR_MDL (pMdl);

        if (pVa != NULL)
        {
            Len = NIC_GET_BYTE_COUNT_FOR_MDL(pMdl); 
            NdisZeroMemory(pVa, Len);

        }
#endif      
        pCurr = pNext;

        //
        // Update the count, The close call will wait for this count to go to zero
        //

        NdisInterlockedDecrement (&pChannelVc->NumIndicatedIsochDesc);

        //
        // Clear the Variables
        //
        pMdl = NULL;
        pVa = NULL;
        Len = 0;


    }

    TRACE( TL_T, TM_Recv, ( "<== nicReturnDescriptorChain  pIsochDescriptor %x",pIsochDescriptor  ) );


}










VOID
nicRecvNoRemoteNode(
    PADAPTERCB pAdapter
    )

    

/*++

Routine Description:

 Queues a work item to go and update the node addresses of all the remote node

Arguments:


Return Value:


--*/

{
    NDIS_STATUS NdisStatus  = NDIS_STATUS_FAILURE;
    
    TRACE( TL_T, TM_Send, ( "==>nicRecvNoRemoteNode ") );

    ASSERT (pAdapter != NULL);
    
    do
    {
        PNDIS_WORK_ITEM pUpdateTableWorkItem  = NULL;
        BOOLEAN fWorkItemAlreadyLaunched  = FALSE;

        //
        // We need to update the generation count
        //
        pUpdateTableWorkItem   = ALLOC_NONPAGED (sizeof(NDIS_WORK_ITEM), MTAG_WORKITEM); 

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

            break;
        }

        
        
        ADAPTER_ACQUIRE_LOCK (pAdapter);
        
        if (ADAPTER_TEST_FLAGS (pAdapter, fADAPTER_UpdateNodeTable))
        {
            fWorkItemAlreadyLaunched = TRUE;
        }
        else
        {
            ADAPTER_SET_FLAG (pAdapter, fADAPTER_UpdateNodeTable);
            //
            // Ref the adapter. Deref in workitem
            //
            nicReferenceAdapter (pAdapter, "nicRecvNoRemoteNode" );
            fWorkItemAlreadyLaunched = FALSE;

        }
        
        ADAPTER_RELEASE_LOCK (pAdapter);

        if (fWorkItemAlreadyLaunched == TRUE)
        {
            FREE_NONPAGED(pUpdateTableWorkItem);
            break;
        }


        //
        // Set the Workitem
        //

        NdisInitializeWorkItem ( pUpdateTableWorkItem, 
                              (NDIS_PROC)nicUpdateNodeTable,
                              (PVOID)pAdapter );

        NdisInterlockedIncrement(&pAdapter->OutstandingWorkItems);
        
        NdisScheduleWorkItem (pUpdateTableWorkItem);

        NdisStatus = NDIS_STATUS_SUCCESS;
        
    } while (FALSE);




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



}




VOID
nicUpdateNodeTable(
    NDIS_WORK_ITEM* pUpdateTable,
    IN PVOID Context 
    )

/*++

Routine Description:
 
    This is called when the reassembly cannot find 
    a remote node for reassembly

    For simplicity's sake, we go through all 63 entries

    We find a remote node, get its address. now the address' entry
    in the Node Table may already have another node there, so we take that 
    node out put the new remote Node in that location.  Then we go
    and update the node address of that remote node

    This is the WorkItem version of nicUpdateRemoteNodeTable

Arguments:
   pAdapter Local host

Return Value:


--*/
{

    ULONG               i = 0;
    ULONG               NumNodes = 0;
    PREMOTE_NODE        pRemoteNode = NULL;
    NODE_ADDRESS        NodeAddress ;
    NDIS_STATUS         NdisStatus = NDIS_STATUS_SUCCESS;
    PADAPTERCB          pAdapter = (PADAPTERCB)Context;

    NdisZeroMemory (&NodeAddress, sizeof(NODE_ADDRESS));

    do
    {
        ADAPTER_ACQUIRE_LOCK (pAdapter);

        //
        // Get a remote node
        //
        pRemoteNode = NULL;
        
        while (i<NIC1394_MAX_NUMBER_NODES)
        {
            pRemoteNode = pAdapter->NodeTable.RemoteNode[i];
            
            if (pRemoteNode != NULL)
            {
                break;
            }
            
            i++;
        }

        if (pRemoteNode != NULL)
        {
            nicReferenceRemoteNode (pRemoteNode, UpdateNodeTable);
        }           

        ADAPTER_RELEASE_LOCK (pAdapter);

        //
        //  break out if we are the end of the loop
        //

        if  (i>=NIC1394_MAX_NUMBER_NODES)
        {
            ASSERT (pRemoteNode == NULL)
            break;
        }
        
        if (pRemoteNode == NULL)
        {
            break;
        }

        i++;

    
        //
        // Now update the table. The new location of the Node in the
        // NodeTable may already have a Node, so we we will need to
        // update that remote node's address as well
        // 
        do
        {
            PREMOTE_NODE pRemNodeOld = NULL;


            nicGet1394AddressOfRemoteNode (pRemoteNode,
                                           &NodeAddress,
                                           0);
                                           

            if (NdisStatus != NDIS_STATUS_SUCCESS)
            {
                //
                // Just use the old node address, if it fails
                //
                NodeAddress  = pRemoteNode->RemoteAddress;
            }

            
            ADAPTER_ACQUIRE_LOCK (pAdapter);

            //
            // Extract the occupant of the location
            //
            pRemNodeOld = pAdapter->NodeTable.RemoteNode[NodeAddress.NA_Node_Number];

            //
            // Update RemoteNode Address
            //
        
            pRemoteNode->RemoteAddress = NodeAddress  ;
    
            if (pRemNodeOld == pRemoteNode)
            {
                //
                //  The remote nodes are the same, so we do not
                //  care about the old remote node
                //
                pRemNodeOld = NULL;
            }

            //
            // Only Reference And Update the Old remoteNode'address
            // if it is not our current remote node
            //

            if ( pRemNodeOld != NULL )
            {
                //
                // This location is already occupied. 
                // Take it out, reference it and update its address
                //
                nicReferenceRemoteNode (pRemNodeOld, UpdateNodeTable);
                
                
            }


            //
            //  Update the Master Node Table
            //
            pAdapter->NodeTable.RemoteNode[NodeAddress.NA_Node_Number] = pRemoteNode;

            //
            // This either derefs' the remote node or pRemNodeOld from a previous interation
            //
            nicDereferenceRemoteNode (pRemoteNode, UpdateNodeTable);

            //
            // Update pRemoteNode so that it loops again 
            // and we can update the node addres of this pRemNodeOld
            //  else it will be null and we exit this loop
            // 
            pRemoteNode = pRemNodeOld;
            
            ADAPTER_RELEASE_LOCK (pAdapter);


        } while (pRemoteNode != NULL);

    //
    // If we have not gone through all the nodes . 
    //  get the next remote node
    // 

    }while (i<NIC1394_MAX_NUMBER_NODES);
    

    ADAPTER_ACQUIRE_LOCK (pAdapter);

    ADAPTER_CLEAR_FLAG (pAdapter, fADAPTER_UpdateNodeTable);

    ADAPTER_RELEASE_LOCK (pAdapter);

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

    nicDereferenceAdapter(pAdapter, "nicRecvNoRemoteNode Derefed in  nicUpdateNodeTable" );
    
}


VOID
nicInsertNodeAddressAtHead (
    IN PNDIS_PACKET pPacket, 
    IN PNIC_RECV_DATA_INFO pRcvInfo
    )
/*++

Routine Description:
    Adds the Node Address to the first 16 bits in the packet. 
    It fails quietly if it is not able to do so

Arguments:
    pPacket  pPacket in Question
    pRcvInfo -> With the correct Gasp Header
Return Value:


--*/
{
    PNDIS1394_UNFRAGMENTED_HEADER pHeader = NULL;

    TRACE( TL_T, TM_Recv, ( "nicInsertNodeAddressAtHead , pPacket %x pRecvInfo %x",pPacket, pRcvInfo  ) );

    do
    {
        pHeader = nicNdisBufferVirtualAddress (pPacket->Private.Head);

        if (pHeader == NULL)
        {
            break;
        }

        //
        // Assert for now. Will not work if there are 1394 bridges with bus number != 0;
        //
        ASSERT (pRcvInfo->SourceID < 64);
        pHeader->u1.SourceAddress= (UCHAR)(pRcvInfo->SourceID); 

        pHeader->u1.fHeaderHasSourceAddress = TRUE;
        
    } while (FALSE);

    TRACE( TL_T, TM_Recv, ( "nicInsertNodeAddressAtHead , pHeader %x =  *pHeader %x", pHeader, pHeader->HeaderUlong  ) );

}