/*++ Copyright (c) 1992 Microsoft Corporation Module Name: ltrecv.c Abstract: This module contains the receive processing routines. Author: Stephen Hou (stephh@microsoft.com) Nikhil Kamkolkar (nikhilk@microsoft.com) Revision History: 19 Jun 1992 Initial Version (dch@pacvax.pacersoft.com) Notes: Tab stop: 4 --*/ #define LTRECV_H_LOCALS #include "ltmain.h" // Define file id for errorlogging #define FILENUM LTRECV VOID LtRecvProcessQueue( IN PLT_ADAPTER Adapter ) /*++ Routine Description: This routine is called by the timer poll routine to process the receive queue. Note that the actual receives from the card happen in the timer poll routine itself. Arguments: Adapter : Pointer to the Adapter on which receive completion needs to happen. Return Value: None. --*/ { UINT PacketLength, DgramLength; PUCHAR Packet, Dgram; PLIST_ENTRY p; PRECV_DESC RecvDesc; DBGPRINT(DBG_COMP_RECV, DBG_LEVEL_ENTRY, ("LtRecvProcessQueue: Entering...\n")); NdisAcquireSpinLock(&Adapter->Lock); while((!IsListEmpty(&Adapter->Receive)) && ((Adapter->Flags & ADAPTER_RESET_IN_PROGRESS) == 0)) { p = RemoveHeadList(&Adapter->Receive); RecvDesc = CONTAINING_RECORD( p, RECV_DESC, Linkage); // We will always have the link header at minimum PacketLength = RecvDesc->BufferLength; DgramLength = PacketLength - LT_LINK_HEADER_LENGTH; ASSERTMSG("LtRecvProcessQueue: Packet length 0!\n", PacketLength != 0); Packet = (PUCHAR)((PUCHAR)RecvDesc+sizeof(RECV_DESC)); Dgram = Packet + LT_LINK_HEADER_LENGTH; if (IsListEmpty(&Adapter->OpenBindings)) { DBGPRINT(DBG_COMP_RECV, DBG_LEVEL_WARN, ("LtRecvProcessQueue: No Binding! Discarding packet!\n")); // No body to receive this, free up the buffer; NdisFreeMemory( RecvDesc, sizeof(RecvDesc)+PacketLength, 0); continue; } // Indicate the packet to all the open bindings on this adapter. // After return from this routine, we should be able to free up // the packet. LtRecvIndicatePacket( Adapter, Packet, Dgram, DgramLength, DgramLength, (NDIS_HANDLE)Packet); NdisFreeMemory( RecvDesc, sizeof(RecvDesc)+PacketLength, 0); } // Check if we need to do any receive completes. LtRecvQueueCompletion(Adapter); NdisReleaseSpinLock(&Adapter->Lock); DBGPRINT(DBG_COMP_RECV, DBG_LEVEL_ENTRY, ("LTProcessReceiveQueue: Leaving...\n")); return; } NDIS_STATUS LtRecvTransferData( IN NDIS_HANDLE MacBindingHandle, IN NDIS_HANDLE MacReceiveContext, IN UINT ByteOffset, IN UINT BytesToTransfer, OUT PNDIS_PACKET Packet, OUT PUINT BytesTransferred ) /*++ Routine Description: This is called by ndis to transfer previously indicated data. A MacReceiveContext of NULL is used to transfer data from the current loopback packet. Arguments: As described in NDIS 3.0. MacReceiveContext : NULL - Use current loopback packet Otherwise it is a pointer to a RECV_DESC. Return Value: NDIS_STATUS_SUCCESS : If successful, error otherwise. --*/ { BOOLEAN DerefAdapter = FALSE; BOOLEAN DerefBinding = FALSE; PLT_OPEN Binding = (PLT_OPEN)MacBindingHandle; PLT_ADAPTER Adapter = Binding->LtAdapter; NDIS_STATUS Status = NDIS_STATUS_SUCCESS; DBGPRINT(DBG_COMP_RECV, DBG_LEVEL_ENTRY, ("LtRecvTransferData: Entered\n")); NdisAcquireSpinLock(&Adapter->Lock); do { LtReferenceAdapterNonInterlock(Adapter, &Status); if (Status != NDIS_STATUS_SUCCESS) { Status = NDIS_STATUS_REQUEST_ABORTED; break; } else { DerefAdapter = TRUE; LtReferenceBindingNonInterlock(Binding, &Status); if (Status != NDIS_STATUS_SUCCESS) { Status = NDIS_STATUS_REQUEST_ABORTED; break; } DerefBinding = TRUE; } if (Adapter->Flags & ADAPTER_RESET_IN_PROGRESS) { Status = NDIS_STATUS_RESET_IN_PROGRESS; break; } } while (FALSE); NdisReleaseSpinLock(&Adapter->Lock); if (Status == NDIS_STATUS_SUCCESS) { if (MacReceiveContext == NULL) { DBGPRINT(DBG_COMP_RECV, DBG_LEVEL_INFO, ("LtRecvTransferData: CurrentLookXfer\n")); NdisCopyFromPacketToPacket( Packet, 0, BytesToTransfer, Adapter->CurrentLoopbackPacket, ByteOffset + LT_LINK_HEADER_LENGTH, BytesTransferred); } else { DBGPRINT(DBG_COMP_RECV, DBG_LEVEL_INFO, ("LtRecvTransferData: NormalXfer\n")); LtUtilsCopyFromBufferToPacket( (PUCHAR)MacReceiveContext, ByteOffset + LT_LINK_HEADER_LENGTH, BytesToTransfer, Packet, BytesTransferred); } } if (DerefAdapter) LtDeReferenceAdapter(Adapter); if (DerefBinding) LtDeReferenceBinding(Binding); return Status; } VOID LtRecvIndicatePacket( IN PLT_ADAPTER Adapter, IN PUCHAR LinkHdr, IN PUCHAR LookAheadBuffer, IN UINT LookAheadSize, IN UINT DgramLength, IN NDIS_HANDLE IndicateCtx ) /*++ Routine Description: This routine is called to indicate a specific packet to all bindings on an adapter. Arguments: Adapter : Pointer to the adapter LinkHdr : Link header, guaranteed to be 3 bytes LookAheadBuffer : Lookahead buffer to indicate LookAheadSize : Size of lookahead buffer (excludes link header) DgramLength : Size of the complete packet (excludes link header) IndicateCtx : Ctx to pass as indicate context to NDIS Return Value: None. --*/ { NDIS_STATUS RefStatus, Status; PLT_OPEN NextBinding, Binding; UINT CurLookAheadSize; NextBinding = NULL; LtReferenceBindingNextNcNonInterlock( Adapter->OpenBindings.Flink, &Adapter->OpenBindings, &Binding, &RefStatus); while (RefStatus == NDIS_STATUS_SUCCESS) { // Reference the next non-closing binding LtReferenceBindingNextNcNonInterlock( Binding->Linkage.Flink, &Adapter->OpenBindings, &NextBinding, &RefStatus); // Never more than one binding usually, remove when not true. ASSERT(RefStatus != NDIS_STATUS_SUCCESS); // Go ahead and do the indicate. CurLookAheadSize = Binding->CurrentLookAheadSize; if (((LtUtilsUcharPacketType(LinkHdr[0], LinkHdr[1]) == LT_BROADCAST) && (Binding->CurrentPacketFilter & NDIS_PACKET_TYPE_BROADCAST)) || ((LtUtilsUcharPacketType(LinkHdr[0], LinkHdr[1]) != LT_BROADCAST) && (Binding->CurrentPacketFilter & NDIS_PACKET_TYPE_DIRECTED))) { DBGPRINT(DBG_COMP_RECV, DBG_LEVEL_INFO, ("LtRecvIndicatePacket: Indicating packet on bind %lx\n", Binding)); NdisReleaseSpinLock(&Adapter->Lock); NdisIndicateReceive( &Status, Binding->NdisBindingContext, IndicateCtx, LinkHdr, LT_LINK_HEADER_LENGTH, LookAheadBuffer, ((LookAheadSize > CurLookAheadSize) ? \ CurLookAheadSize : LookAheadSize), DgramLength); NdisAcquireSpinLock(&Adapter->Lock); if (Status != NDIS_STATUS_SUCCESS) { Adapter->MediaOptional[MO_NO_HANDLERS] ++; } // Since this routine is called within a loop in LtRecvProcessQueue, // and since we need only one reference per binding for receive // completion, we do the following to avoid over-referencing the // binding structure. if (Binding->Flags & BINDING_DO_RECV_COMPLETION) { NdisReleaseSpinLock(&Adapter->Lock); LtDeReferenceBinding(Binding); NdisAcquireSpinLock(&Adapter->Lock); } else { // Remember this binding needs a receive completion Binding->Flags |= BINDING_DO_RECV_COMPLETION; } // Also, make a note in the adapter so that the receive // completion handler is enqueued. Adapter->Flags |= ADAPTER_QUEUE_RECV_COMPLETION; } else { // Remove the reference on this binding. NdisReleaseSpinLock(&Adapter->Lock); LtDeReferenceBinding(Binding); NdisAcquireSpinLock(&Adapter->Lock); } // Never more than one binding usually, remove when not true. ASSERT(RefStatus != NDIS_STATUS_SUCCESS); Binding = NextBinding; } return; } VOID LtRecvQueueCompletion( IN PLT_ADAPTER Adapter ) /*++ Routine Description: This routine is used to queue up a completion handler on the adapter. This handler will then indicate receive completion to all necessary bindings on the adapter. ASSUMES: Adapter->Lock is HELD. Arguments: Adapter : Pointer to the adapter Return Value: None. --*/ { NDIS_STATUS Status; BOOLEAN Queue = FALSE; // !!! ASSUMES Adapter->Lock is held!!! if (Adapter->Flags & ADAPTER_QUEUE_RECV_COMPLETION) { if ((Adapter->Flags & ADAPTER_QUEUED_RECV_COMPLETION) == 0) { Adapter->Flags |= ADAPTER_QUEUED_RECV_COMPLETION; Queue = TRUE; } Adapter->Flags &= ~ADAPTER_QUEUE_RECV_COMPLETION; } if (Queue) { DBGPRINT(DBG_COMP_RECV, DBG_LEVEL_INFO, ("LtRecvQueueCompletion: queing receive complete\n")); // !!! NOTE !!! // This should never fail!! If Queue is set, then a binding // was referenced for receive completion. So it couldnt have // gone away. And so the adapter cannot be in a CLOSING state. // As all binding need to have gone away, before the RemovAdapter // is called by NDIS. LtReferenceAdapterNonInterlock(Adapter, &Status); ASSERTMSG("LtRecvQueueCompletion: Adapter is closing!\n", (Status == NDIS_STATUS_SUCCESS)); if (Status != NDIS_STATUS_SUCCESS) { // !!! KEBUGCHECK() !!! KeBugCheck((ULONG)__LINE__); } NdisReleaseSpinLock(&Adapter->Lock); LtRecvCompletion(Adapter); LtDeReferenceAdapter(Adapter); NdisAcquireSpinLock(&Adapter->Lock); } return; } NTSTATUS LtRecvCompletion( IN PLT_ADAPTER Adapter ) /*++ Routine Description: Called to indicate receive completion on all binding on this adapter. This will loop until all receive completions are done. This might tend to starve bindings towards the end of the list, but we wont worry about that. Arguments: Adapter : Pointer to the Adapter on which receive completion needs to happen. Return Value: STATUS_SUCCESS --*/ { PLIST_ENTRY p; PLT_OPEN Binding; // For each binding, if recv completion is to be called, do it. NdisAcquireSpinLock(&Adapter->Lock); DBGPRINT(DBG_COMP_RECV, DBG_LEVEL_INFO, ("LtRecvCompletion: Indicating receive completion\n")); for (p = Adapter->OpenBindings.Flink; (p != &Adapter->OpenBindings);) { Binding = CONTAINING_RECORD( p, LT_OPEN, Linkage); if (Binding->Flags & BINDING_DO_RECV_COMPLETION) { Binding->Flags &= ~BINDING_DO_RECV_COMPLETION; } else { // !!! Note the continue in here !!! DBGPRINT(DBG_COMP_RECV, DBG_LEVEL_WARN, ("LtRecvCompletion: No recv comp flag on binding\n")); p = p->Flink; // Never more than one binding usually, remove when not true. ASSERT(p == &Adapter->OpenBindings); continue; } NdisReleaseSpinLock(&Adapter->Lock); // Call NdisReceiveCompletion for this binding. NdisIndicateReceiveComplete(Binding->NdisBindingContext); // Dereference the binding, this was added in the process queue // routine. LtDeReferenceBinding(Binding); NdisAcquireSpinLock(&Adapter->Lock); // Restart the search p = Adapter->OpenBindings.Flink; } DBGPRINT(DBG_COMP_RECV, DBG_LEVEL_INFO, ("LtRecvCompletion: Enabling receive queing\n")); // Enable any new queue requests to take effect. Adapter->Flags &= ~ADAPTER_QUEUED_RECV_COMPLETION; NdisReleaseSpinLock(&Adapter->Lock); return STATUS_SUCCESS; }