diff options
Diffstat (limited to '')
-rw-r--r-- | private/ntos/ndis/ndis40/sendm.c | 3212 |
1 files changed, 3212 insertions, 0 deletions
diff --git a/private/ntos/ndis/ndis40/sendm.c b/private/ntos/ndis/ndis40/sendm.c new file mode 100644 index 000000000..8e3dfc7c4 --- /dev/null +++ b/private/ntos/ndis/ndis40/sendm.c @@ -0,0 +1,3212 @@ +/*++ + +Copyright (c) 1990-1995 Microsoft Corporation + +Module Name: + + sendm.c + +Abstract: + +Author: + + Kyle Brandon (KyleB) + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include <precomp.h> +#pragma hdrstop + +#include "sendm.h" + +// +// Define the module number for debug code. +// +#define MODULE_NUMBER MODULE_SENDM + +VOID +ndisMCopyFromPacketToBuffer( + IN PNDIS_PACKET Packet, + IN UINT Offset, + IN UINT BytesToCopy, + OUT PCHAR Buffer, + OUT PUINT BytesCopied + ) + +/*++ + +Routine Description: + + Copy from an ndis packet into a buffer. + +Arguments: + + Packet - The packet to copy from. + + Offset - The offset from which to start the copy. + + BytesToCopy - The number of bytes to copy from the packet. + + Buffer - The destination of the copy. + + BytesCopied - The number of bytes actually copied. Can be less then + BytesToCopy if the packet is shorter than BytesToCopy. + +Return Value: + + None + +--*/ + +{ + // + // Holds the number of ndis buffers comprising the packet. + // + UINT NdisBufferCount; + + // + // Points to the buffer from which we are extracting data. + // + PNDIS_BUFFER CurrentBuffer; + + // + // Holds the virtual address of the current buffer. + // + PVOID VirtualAddress; + + // + // Holds the length of the current buffer of the packet. + // + UINT CurrentLength; + + // + // Keep a local variable of BytesCopied so we aren't referencing + // through a pointer. + // + UINT LocalBytesCopied = 0; + + // + // Take care of boundary condition of zero length copy. + // + + *BytesCopied = 0; + if (!BytesToCopy) + return; + + // + // Get the first buffer. + // + + NdisQueryPacket(Packet, + NULL, + &NdisBufferCount, + &CurrentBuffer, + NULL); + + // + // Could have a null packet. + // + + if (!NdisBufferCount) + return; + + NdisQueryBuffer(CurrentBuffer, &VirtualAddress, &CurrentLength); + + while (LocalBytesCopied < BytesToCopy) + { + if (CurrentLength == 0) + { + NdisGetNextBuffer(CurrentBuffer, &CurrentBuffer); + + // + // We've reached the end of the packet. We return + // with what we've done so far. (Which must be shorter + // than requested. + // + + if (!CurrentBuffer) + break; + + NdisQueryBuffer(CurrentBuffer, &VirtualAddress, &CurrentLength); + continue; + } + + // + // Try to get us up to the point to start the copy. + // + + if (Offset) + { + if (Offset > CurrentLength) + { + // + // What we want isn't in this buffer. + // + + Offset -= CurrentLength; + CurrentLength = 0; + continue; + } + else + { + VirtualAddress = (PCHAR)VirtualAddress + Offset; + CurrentLength -= Offset; + Offset = 0; + } + } + + // + // Copy the data. + // + { + // + // Holds the amount of data to move. + // + UINT AmountToMove; + + AmountToMove = ((CurrentLength <= (BytesToCopy - LocalBytesCopied)) ? + (CurrentLength): + (BytesToCopy - LocalBytesCopied)); + + MoveMemory(Buffer, VirtualAddress, AmountToMove); + + Buffer = (PCHAR)Buffer + AmountToMove; + VirtualAddress = (PCHAR)VirtualAddress + AmountToMove; + + LocalBytesCopied += AmountToMove; + CurrentLength -= AmountToMove; + } + } + + *BytesCopied = LocalBytesCopied; +} + +BOOLEAN +FASTCALL +ndisMIsLoopbackPacket( + IN PNDIS_MINIPORT_BLOCK Miniport, + IN PNDIS_PACKET Packet + ) +/*++ + +Routine Description: + + This routine will determine if a packet needs to be looped back in + software. if the packet is any kind of loopback packet then it + will get placed on the loopback queue and a workitem will be queued + to process it later. + +Arguments: + + Miniport- Pointer to the miniport block to send the packet on. + Packet - Packet to check for loopback. + +Return Value: + + Returns TRUE if the packet is self-directed. + +--*/ +{ + PNDIS_BUFFER FirstBuffer; + UINT Length; + UINT Offset; + PUCHAR BufferAddress; + BOOLEAN Loopback; + BOOLEAN SelfDirected; + PNDIS_PACKET pNewPacket; + PUCHAR Buffer; + NDIS_STATUS Status; + PNDIS_BUFFER pNdisBuffer; + UINT HdrLength; + BOOLEAN ArcEncap = TRUE; + + // + // We should not be here if the driver handles loopback. + // + ASSERT(Miniport->MacOptions & NDIS_MAC_OPTION_NO_LOOPBACK); + ASSERT(MINIPORT_AT_DPC_LEVEL); + + FirstBuffer = Packet->Private.Head; + BufferAddress = MDL_ADDRESS(FirstBuffer); + + // + // If the card does not do loopback, then we check if + // we need to send it to ourselves, then if that is the + // case we also check for it being self-directed. + // + switch (Miniport->MediaType) + { + case NdisMedium802_3: + + if (MINIPORT_TEST_SEND_FLAG(Miniport, fMINIPORT_SEND_LOOPBACK_DIRECTED)) + { + if (ETH_IS_MULTICAST(BufferAddress)) + { + Loopback = FALSE; + SelfDirected = FALSE; + break; + } + + // + // Packet is of type directed, now make sure that it + // is not self-directed. + // + ETH_COMPARE_NETWORK_ADDRESSES_EQ( + BufferAddress, + Miniport->EthDB->AdapterAddress, + &Loopback); + + SelfDirected = FALSE; + + break; + } + + // + // Check for the miniports that don't do loopback. + // + EthShouldAddressLoopBackMacro(Miniport->EthDB, + BufferAddress, + &Loopback, + &SelfDirected); + break; + + case NdisMedium802_5: + + if (MINIPORT_TEST_SEND_FLAG(Miniport, fMINIPORT_SEND_LOOPBACK_DIRECTED)) + { + BOOLEAN IsNotDirected; + TR_IS_NOT_DIRECTED(BufferAddress + 2, &IsNotDirected); + if (IsNotDirected) + { + Loopback = FALSE; + SelfDirected = FALSE; + break; + } + + // + // Packet is of type directed, now make sure that it + // is not self-directed. + // + TR_COMPARE_NETWORK_ADDRESSES_EQ( + BufferAddress + 2, + Miniport->TrDB->AdapterAddress, + &Loopback); + + SelfDirected = FALSE; + + break; + } + + TrShouldAddressLoopBackMacro(Miniport->TrDB, + BufferAddress +2, + BufferAddress +8, + &Loopback, + &SelfDirected); + + break; + + case NdisMediumFddi: + + if (MINIPORT_TEST_SEND_FLAG(Miniport, fMINIPORT_SEND_LOOPBACK_DIRECTED)) + { + BOOLEAN IsMulticast; + + FDDI_IS_MULTICAST( + BufferAddress + 1, + (BufferAddress[0] & 0x40) ? + FDDI_LENGTH_OF_LONG_ADDRESS : + FDDI_LENGTH_OF_SHORT_ADDRESS, + &IsMulticast); + if (IsMulticast) + { + Loopback = FALSE; + SelfDirected = FALSE; + break; + } + + // + // Packet is of type directed, now make sure that it + // is not self-directed. + // + FDDI_COMPARE_NETWORK_ADDRESSES_EQ( + BufferAddress + 1, + (BufferAddress[0] & 0x40) ? + Miniport->FddiDB->AdapterLongAddress : + Miniport->FddiDB->AdapterShortAddress, + (BufferAddress[0] & 0x40) ? + FDDI_LENGTH_OF_LONG_ADDRESS : + FDDI_LENGTH_OF_SHORT_ADDRESS, + &Loopback); + + SelfDirected = FALSE; + + break; + } + + FddiShouldAddressLoopBackMacro(Miniport->FddiDB, + BufferAddress + 1, // Skip FC byte to dest address. + (BufferAddress[0] & 0x40) ? + FDDI_LENGTH_OF_LONG_ADDRESS : + FDDI_LENGTH_OF_SHORT_ADDRESS, + &Loopback, + &SelfDirected); + break; + + case NdisMediumArcnet878_2: + + // + // We just handle arcnet packets (encapsulated or not) in + // a totally different manner... + // + SelfDirected = ndisMArcnetSendLoopback(Miniport, Packet); + + // + // Mark the packet as having been looped back. + // + MINIPORT_SET_PACKET_FLAG(Packet, fPACKET_HAS_BEEN_LOOPED_BACK); + + return(SelfDirected); + + break; + } + + // + // If it is not a loopback packet then get out of here. + // + if (!Loopback) + { + ASSERT(!SelfDirected); + return FALSE; + } + + // + // Get the buffer length. + // + NdisQueryPacket(Packet, NULL, NULL, NULL, &Length); + Offset = 0; + + // + // Allocate a buffer for the packet. + // + pNewPacket = (PNDIS_PACKET)ALLOC_FROM_POOL(Length + + sizeof(NDIS_PACKET) + + sizeof(NDIS_PACKET_OOB_DATA) + + sizeof(NDIS_PACKET_PRIVATE_EXTENSION) + + PROTOCOL_RESERVED_SIZE_IN_PACKET, + NDIS_TAG_LOOP_PKT); + if (pNewPacket != NULL) + { + // + // Get a pointer to the destination buffer. + // + Buffer = (PUCHAR)pNewPacket + + sizeof(NDIS_PACKET) + + sizeof(NDIS_PACKET_OOB_DATA) + + sizeof(NDIS_PACKET_PRIVATE_EXTENSION) + + PROTOCOL_RESERVED_SIZE_IN_PACKET; + + ZeroMemory(pNewPacket, + sizeof(NDIS_PACKET) + + sizeof(NDIS_PACKET_OOB_DATA) + + sizeof(NDIS_PACKET_PRIVATE_EXTENSION) + + PROTOCOL_RESERVED_SIZE_IN_PACKET); + + // + // Allocate an MDL for the packet. + // + NdisAllocateBuffer(&Status, + &pNdisBuffer, + NULL, + Buffer, + Length); + if (NDIS_STATUS_SUCCESS == Status) + { + // + // NdisChainBufferAtFront() + // + pNewPacket->Private.Head = pNdisBuffer; + pNewPacket->Private.Tail = pNdisBuffer; + + pNewPacket->Private.NdisPacketOobOffset = (USHORT)sizeof(NDIS_PACKET) + PROTOCOL_RESERVED_SIZE_IN_PACKET; + + ndisMCopyFromPacketToBuffer(Packet, // Packet to copy from. + Offset, // Offset from beginning of packet. + Length, // Number of bytes to copy. + Buffer, // The destination buffer. + &HdrLength);// The number of bytes copied. + } + else + { + // + // Clean up the memory allocated for the packet. + // + FREE_POOL(pNewPacket); + pNewPacket = NULL; + } + } + + // + // Do we have a packet built ? + // + if (NULL != pNewPacket) + { + // + // Mark the packet as having been looped back. + // + MINIPORT_SET_PACKET_FLAG(Packet, fPACKET_HAS_BEEN_LOOPED_BACK); + + NDISM_LOG_PACKET(Miniport, Packet, pNewPacket, 'pool'); + + // + // Place the packet on the loopback queue. + // + if (NULL == Miniport->LoopbackHead) + { + Miniport->LoopbackHead = pNewPacket; + + // + // If this is the first one on the loopback queue then we need + // to make sure that we pick it up later. + // + NDISM_DEFER_PROCESS_DEFERRED(Miniport); + } + else + { + PNDIS_RESERVED_FROM_PNDIS_PACKET(Miniport->LoopbackTail)->Next = pNewPacket; + } + + Miniport->LoopbackTail = pNewPacket; + + // + // Packet needs to have a workitem queued. + // + NDISM_QUEUE_WORK_ITEM(Miniport, NdisWorkItemSendLoopback, NULL, NULL); + } + + return(SelfDirected); +} + + +BOOLEAN +FASTCALL +ndisMIndicateLoopback( + IN PNDIS_MINIPORT_BLOCK Miniport + ) + +/*++ + +Routine Description: + + Checks if a packet needs to be loopbacked and does so if necessary. + + NOTE: Must be called at DPC_LEVEL with lock HELD! + +Arguments: + + Miniport - Miniport to send to. + + Packet - Packet to loopback. + +Return Value: + + FALSE if the packet should be sent on the net, TRUE if it is + a self-directed packet. + +--*/ + +{ + UINT Length; + PUCHAR BufferAddress; + PNDIS_PACKET Packet, QueueHead; + PNDIS_PACKET_OOB_DATA pOob; + NDIS_STATUS Status; + BOOLEAN fReturnStatus = FALSE; + + // We should not be here if the driver handles loopback + ASSERT(Miniport->MacOptions & NDIS_MAC_OPTION_NO_LOOPBACK); + ASSERT(MINIPORT_AT_DPC_LEVEL); + + if (MINIPORT_TEST_FLAG(Miniport, fMINIPORT_FULL_DUPLEX)) + { + NDIS_ACQUIRE_SEND_SPIN_LOCK_DPC(Miniport); + } + + // + // Get a local copy of the loopback queue. + // + QueueHead = Miniport->LoopbackHead; + Miniport->LoopbackHead = NULL; + Miniport->LoopbackTail = NULL; + + if (MINIPORT_TEST_FLAG(Miniport, fMINIPORT_FULL_DUPLEX)) + { + NDIS_RELEASE_SEND_SPIN_LOCK_DPC(Miniport); + } + + // + // Loop through any loopback packets that are queue'd. + // + + while (QueueHead != NULL) + { + // + // Grab the first loopback packet to indicate up. + // + Packet = QueueHead; + pOob = NDIS_OOB_DATA_FROM_PACKET(Packet); + pOob->Status = NDIS_STATUS_RESOURCES; + QueueHead = PNDIS_RESERVED_FROM_PNDIS_PACKET(Packet)->Next; + + NDISM_LOG_PACKET(Miniport, Packet, NULL, 'i'); + + // + // Setup the packet references and packet array. + // + Miniport->LoopbackPacket = Packet; + BufferAddress = (PUCHAR)Packet + + sizeof(NDIS_PACKET) + + sizeof(NDIS_PACKET_OOB_DATA) + + sizeof(NDIS_PACKET_PRIVATE_EXTENSION) + + PROTOCOL_RESERVED_SIZE_IN_PACKET; + + // + // For ethernet/token-ring/fddi/encapsulated arc-net, we want to + // indicate the packet using the receivepacket way. + // + switch (Miniport->MediaType) + { + case NdisMedium802_3: + pOob->HeaderSize = 14; + EthFilterDprIndicateReceivePacket(Miniport, &Packet, 1); + break; + + case NdisMedium802_5: + pOob->HeaderSize = 14; + if (BufferAddress[8] & 0x80) + { + pOob->HeaderSize += (BufferAddress[14] & 0x1F); + } + TrFilterDprIndicateReceivePacket(Miniport, &Packet, 1); + break; + + case NdisMediumFddi: + pOob->HeaderSize = (*BufferAddress & 0x40) ? + 2 * FDDI_LENGTH_OF_LONG_ADDRESS + 1: + 2 * FDDI_LENGTH_OF_SHORT_ADDRESS + 1; + + FddiFilterDprIndicateReceivePacket(Miniport, &Packet, 1); + break; + } + + ASSERT(NDIS_GET_PACKET_STATUS(Packet) != NDIS_STATUS_PENDING); + NdisFreeBuffer(Packet->Private.Head); + FREE_POOL(Packet); + } + + Miniport->LoopbackPacket = NULL; + + if (MINIPORT_TEST_FLAG(Miniport, fMINIPORT_FULL_DUPLEX)) + { + NDIS_ACQUIRE_SEND_SPIN_LOCK_DPC(Miniport); + } + + // + // If there are more loopback packets on the loopback + // queue then we need to let process deferred know not to + // dequeue the loopback workitem. + // + if (Miniport->LoopbackHead != NULL) + { + fReturnStatus = TRUE; + } + + if (MINIPORT_TEST_FLAG(Miniport, fMINIPORT_FULL_DUPLEX)) + { + NDIS_RELEASE_SEND_SPIN_LOCK_DPC(Miniport); + } + + return(fReturnStatus); +} + +BOOLEAN +FASTCALL +ndisMStartSendPacketsFullDuplex( + PNDIS_MINIPORT_BLOCK Miniport + ) +{ + BOOLEAN fReturn = FALSE; +#ifdef NDIS_NT + PNDIS_PACKET Packet; + PNDIS_PACKET PrevPacket; + NDIS_STATUS Status; + PNDIS_PACKET_RESERVED Reserved; + PNDIS_M_OPEN_BLOCK Open; + UINT Flags; + PPNDIS_PACKET pPktArray; + + // + // Acquire the send lock. + // + ASSERT(MINIPORT_AT_DPC_LEVEL); + NDIS_ACQUIRE_SEND_SPIN_LOCK_DPC(Miniport); + + NDISM_LOG_PACKET(Miniport, NULL, NULL, 'rfed'); + + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("==>ndisMStartSendPacketsFullDuplex\n")); + + MINIPORT_CLEAR_SEND_FLAG(Miniport, fMINIPORT_SEND_COMPLETE_CALLED); + + // + // Loop and process sends. + // + while ((Miniport->SendResourcesAvailable != 0) && + (Miniport->FirstPendingPacket != NULL)) + { + UINT Count; + UINT ResourcesCount; + UINT NumberOfPackets; + + // + // Initialize the packet array. + // + pPktArray = Miniport->PacketArray; + + // + // Place as many packets as we can in the packet array to send + // to the miniport. + // + for (NumberOfPackets = 0; + (NumberOfPackets < Miniport->MaximumSendPackets) && + (Miniport->FirstPendingPacket != NULL); ) + { + // + // Grab the packet off of the pending queue. + // + Packet = Miniport->FirstPendingPacket; + Reserved = PNDIS_RESERVED_FROM_PNDIS_PACKET(Packet); + + // + // Remove from pending queue + // + Miniport->FirstPendingPacket = Reserved->Next; + + // + // Put on finish queue + // + PrevPacket = Miniport->LastMiniportPacket; + Miniport->LastMiniportPacket = Packet; + + // + // Indicate the packet loopback if necessary. + // + if (((Miniport->MacOptions & NDIS_MAC_OPTION_NO_LOOPBACK) || + MINIPORT_TEST_SEND_FLAG(Miniport, fMINIPORT_SEND_LOOPBACK_DIRECTED)) && + !MINIPORT_TEST_PACKET_FLAG(Packet, fPACKET_HAS_BEEN_LOOPED_BACK) && + ndisMIsLoopbackPacket(Miniport, Packet)) + { + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("Packet 0x%x is self-directed.\n", Packet)); + + // + // Get a pointer to the open block. + // DO NOT USE "Reserved->Open" TO CALL INTO THE MACRO!!!!! + // + Open = Reserved->Open; + + // + // Complete the packet back to the binding. + // + NDISM_COMPLETE_SEND_FULL_DUPLEX( + Miniport, + Open, + Packet, + PrevPacket, + NDIS_STATUS_SUCCESS); + + // + // No, we don't want to increment the counter for the + // miniport's packet array. + // + } + else + { + NDISM_LOG_PACKET(Miniport, Packet, NULL, 'inim'); + + // + // We have to re-initialize this. + // + *pPktArray = Packet; + NDIS_SET_PACKET_STATUS(Packet, NDIS_STATUS_SUCCESS); + + // + // Increment the counter for the packet array index. + // + NumberOfPackets++; + pPktArray++; + } + } + + // + // Are there any packets to send? + // + if (NumberOfPackets != 0) + { + // + // Get a temp pointer to our packet array. + // + pPktArray = Miniport->PacketArray; + Open = PNDIS_RESERVED_FROM_PNDIS_PACKET(*pPktArray)->Open; + + // + // Pass the packet array down to the miniport. + // + (Open->SendPacketsHandler)( + Miniport->MiniportAdapterContext, + Miniport->PacketArray, + NumberOfPackets); + + // + // First check to see if the LastMiniportPacket is NULL. + // If it is then the miniport called NdisMSendComplete() + // in our send context and we don't need to do anything more. + // + if (NULL == Miniport->LastMiniportPacket) + { + // + // We may still have packets pending to be sent down.... + // + continue; + } + + // + // Process the packet completion. + // + for (Count = 0; Count < NumberOfPackets; Count++, pPktArray++) + { + BOOLEAN fFoundPacket = FALSE; + + // + // Try and find the packet on our miniport's packet queue. + // + Packet = Miniport->FirstPacket; + PrevPacket = NULL; + + ASSERT(Packet != NULL); + + // + // We are only going to travers the packet queue from the + // FirstPacket to the LastMiniportPacket. + // Why you ask? Well we need to make sure that the miniport + // didn't complete the packet we are now trying to complete + // with a call to NdisMSendComplete(). + // + while (Packet != PNDIS_RESERVED_FROM_PNDIS_PACKET(Miniport->LastMiniportPacket)->Next) + { + if (Packet == *pPktArray) + { + fFoundPacket = TRUE; + break; + } + + PrevPacket = Packet; + Packet = PNDIS_RESERVED_FROM_PNDIS_PACKET(Packet)->Next; + } + + // + // If we didn't find the packet on our queue then we need to + // go on to the next one since it MUST have been completed + // via NdisMSendComplete.... + // + if (!fFoundPacket) + { + continue; + } + + Status = NDIS_GET_PACKET_STATUS(*pPktArray); + + // + // Process the packet based on it's return status. + // + if (NDIS_STATUS_PENDING == Status) + { + NDISM_LOG_PACKET(Miniport, *pPktArray, NULL, 'dnep'); + + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("Complete is pending\n")); + } + else if (NDIS_STATUS_RESOURCES != Status) + { +#if DBG + if (Status != NDIS_STATUS_SUCCESS) + { + NDISM_LOG_PACKET(Miniport, *pPktArray, NULL, 'liaf'); + } + else + { + NDISM_LOG_PACKET(Miniport, *pPktArray, NULL, 'ccus'); + } +#endif + + ADD_RESOURCE(Miniport, 'F'); + + // + // Remove from the finish queue. + // + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("Completed packet 0x%x with status 0x%x\n", + *pPktArray, + Status)); + + // + // Fix up the packet queues. + // + if (Miniport->FirstPacket == *pPktArray) + { + Miniport->FirstPacket = PNDIS_RESERVED_FROM_PNDIS_PACKET(*pPktArray)->Next; + + if (Miniport->LastMiniportPacket == *pPktArray) + { + Miniport->LastMiniportPacket = NULL; + } + } + else + { + // + // If we just completed the last packet then + // we need to update our last packet pointer. + // + PNDIS_RESERVED_FROM_PNDIS_PACKET(PrevPacket)->Next = PNDIS_RESERVED_FROM_PNDIS_PACKET(*pPktArray)->Next; + if (*pPktArray == Miniport->LastPacket) + { + Miniport->LastPacket = PrevPacket; + } + + // + // If we just complete the last miniport packet then + // the last miniport packet is the previous packet. + // + if (Miniport->LastMiniportPacket == *pPktArray) + { + Miniport->LastMiniportPacket = PrevPacket; + } + } + + // + // We need to save a pointer to the open so that we + // can dereference it after the completion. + // + Open = PNDIS_RESERVED_FROM_PNDIS_PACKET(*pPktArray)->Open; + + NDIS_RELEASE_SEND_SPIN_LOCK_DPC(Miniport); + + (Open->ProtocolHandle->ProtocolCharacteristics.SendCompleteHandler)( + Open->ProtocolBindingContext, + *pPktArray, + Status); + + NDIS_ACQUIRE_SEND_SPIN_LOCK_DPC(Miniport); + } + else + { + // + // Once we hit a return code of NDIS_STATUS_RESOURCES + // for a packet then we must break out and re-queue. + // + CLEAR_RESOURCE(Miniport, 'S'); + MINIPORT_CLEAR_SEND_FLAG(Miniport, fMINIPORT_SEND_COMPLETE_CALLED); + + break; + } + } + + // + // if there are any packets that returned NDIS_STATUS_RESOURCES + // then re-queue them. + // + if (Count != NumberOfPackets) + { + PNDIS_PACKET FinalPrevPacket = PrevPacket; + + for (ResourcesCount = NumberOfPackets - 1, pPktArray = &Miniport->PacketArray[ResourcesCount]; + ResourcesCount > Count; + ResourcesCount--, pPktArray--) + { + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("Packet 0x%x returned resources\n", *pPktArray)); + + NDISM_LOG_PACKET(Miniport, *pPktArray, NULL, 'oser'); + + // + // Float the pointers. + // + Miniport->LastMiniportPacket = *(pPktArray - 1); + Miniport->FirstPendingPacket = *pPktArray; + } + + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("Packet 0x%x returned resources\n", *pPktArray)); + + NDISM_LOG_PACKET(Miniport, *pPktArray, NULL, 'oser'); + + // + // If this is the last packet on the miniport queue + // then NULL the last miniport packet out so we know there + // are no more. + // + if (Miniport->FirstPacket == *pPktArray) + { + Miniport->LastMiniportPacket = NULL; + Miniport->FirstPendingPacket = *pPktArray; + } + else + { + // + // There are other packets that are pending on + // the miniport queue. + // + Miniport->LastMiniportPacket = PrevPacket; + Miniport->FirstPendingPacket = *pPktArray; + } + } + } + } + + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("<==ndisMStartSendPacketsFullDuplex\n")); + + // + // If there are no more resources available but + // there are still packets to send then we need to + // keep the workitem queue'd. + // + if ((0 == Miniport->SendResourcesAvailable) && + (Miniport->FirstPendingPacket != NULL)) + { + fReturn = TRUE; + } + else + { + fReturn = FALSE; + } + + NDISM_LOG_PACKET(Miniport, NULL, NULL, 'RFED'); + + NDIS_RELEASE_SEND_SPIN_LOCK_DPC(Miniport); +#endif + return(fReturn); +} + +BOOLEAN +FASTCALL +ndisMStartSendsFullDuplex( + PNDIS_MINIPORT_BLOCK Miniport + ) +/*++ + +Routine Description: + + This routine will process any pending sends for full-duplex miniports. + + + !!!!!!!NOTE!!!!!!!! + This routine MUST be called with the Miniport Lock NOT held!!!! + + +Arguments: + +Return Value: + +--*/ +{ + BOOLEAN fReturn = FALSE; +#ifdef NDIS_NT + PNDIS_PACKET Packet; + PNDIS_PACKET PrevPacket; + NDIS_STATUS Status; + PNDIS_PACKET_RESERVED Reserved; + PNDIS_M_OPEN_BLOCK Open; + UINT Flags; + KIRQL OldIrql; + + // + // Acquire the send lock. + // + NDIS_ACQUIRE_SEND_SPIN_LOCK_DPC(Miniport); + + NDISM_LOG_PACKET(Miniport, NULL, NULL, 'rfed'); + + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("==>ndisMStartSendsFullDuplex\n")); + + MINIPORT_CLEAR_SEND_FLAG(Miniport, fMINIPORT_SEND_COMPLETE_CALLED); + + // + // Loop and process sends. + // + while ((Miniport->SendResourcesAvailable != 0) && + (Miniport->FirstPendingPacket != NULL)) + { + // + // Grab the packet off of the pending queue. + // + Packet = Miniport->FirstPendingPacket; + Reserved = PNDIS_RESERVED_FROM_PNDIS_PACKET(Packet); + Open = Reserved->Open; + + // + // Remove from pending queue + // + Miniport->FirstPendingPacket = Reserved->Next; + + // + // Put on finish queue + // + PrevPacket = Miniport->LastMiniportPacket; + Miniport->LastMiniportPacket = Packet; + + // + // Send the packet + // + NDISM_SEND_PACKET(Miniport, Open, Packet, &Status); + + // + // Process the completion status of the packet. + // + if (NDIS_STATUS_PENDING == Status) + { + NDISM_LOG_PACKET(Miniport, Packet, NULL, 'dnep'); + + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("Complete is pending\n")); + } + else if (Status != NDIS_STATUS_RESOURCES) + { + NDISM_COMPLETE_SEND_FULL_DUPLEX( + Miniport, + Open, + Packet, + PrevPacket, + Status); + } + else + { + NDISM_COMPLETE_SEND_RESOURCES(Miniport, Packet, PrevPacket); + } + } + + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, ("<==ndisMStartSendsFullDuplex\n")); + + // + // If there are no more resources available but + // there are still packets to send then we need to + // keep the workitem queue'd. + // + if ((0 == Miniport->SendResourcesAvailable) && + (Miniport->FirstPendingPacket != NULL)) + { + fReturn = TRUE; + } + else + { + fReturn = FALSE; + } + + NDISM_LOG_PACKET(Miniport, NULL, NULL, 'RFED'); + + NDIS_RELEASE_SEND_SPIN_LOCK_DPC(Miniport); +#endif + return(fReturn); +} + +BOOLEAN +FASTCALL +ndisMStartSendPackets( + PNDIS_MINIPORT_BLOCK Miniport + ) +{ + PNDIS_PACKET Packet; + PNDIS_PACKET PrevPacket; + NDIS_STATUS Status; + PNDIS_PACKET_RESERVED Reserved; + PNDIS_M_OPEN_BLOCK Open; + UINT Flags; + BOOLEAN fReturn; + PPNDIS_PACKET pPktArray; + + ASSERT(MINIPORT_AT_DPC_LEVEL); + ASSERT(MINIPORT_LOCK_ACQUIRED(Miniport)); + + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, ("==>ndisMStartSendPackets\n")); + NDISM_LOG_PACKET(Miniport, NULL, NULL, 'rfed'); + + MINIPORT_CLEAR_SEND_FLAG(Miniport, fMINIPORT_SEND_COMPLETE_CALLED); + + // + // Loop and process sends. + // + while ((Miniport->SendResourcesAvailable != 0) && + (Miniport->FirstPendingPacket != NULL)) + { + UINT Count; + UINT ResourcesCount; + UINT NumberOfPackets; + PNDIS_PACKET FirstPrevPacket = NULL; + + // + // Initialize the packet array. + // + pPktArray = Miniport->PacketArray; + + // + // Place as many packets as we can in the packet array to send + // to the miniport. + // + for (NumberOfPackets = 0; + (NumberOfPackets < Miniport->MaximumSendPackets) && + (Miniport->FirstPendingPacket != NULL); ) + { + // + // Grab the packet off of the pending queue. + // + Packet = Miniport->FirstPendingPacket; + Reserved = PNDIS_RESERVED_FROM_PNDIS_PACKET(Packet); + + // + // Remove from pending queue + // + Miniport->FirstPendingPacket = Reserved->Next; + + // + // Put on finish queue + // + PrevPacket = Miniport->LastMiniportPacket; + Miniport->LastMiniportPacket = Packet; + + // + // Indicate the packet loopback if necessary. + // + if (((Miniport->MacOptions & NDIS_MAC_OPTION_NO_LOOPBACK) || + MINIPORT_TEST_SEND_FLAG(Miniport, fMINIPORT_SEND_LOOPBACK_DIRECTED)) && + !MINIPORT_TEST_PACKET_FLAG(Packet, fPACKET_HAS_BEEN_LOOPED_BACK) && + ndisMIsLoopbackPacket(Miniport, Packet)) + { + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("Packet 0x%x is self-directed.\n", Packet)); + + // + // Get a pointer to our open block. + // DO NOT USE "Reserved->Open" TO CALL INTO THE MACRO SINCE IT + // WILL NOT BE VALID FOR THE LIFE OF THE MACRO!!!!! + // + Open = Reserved->Open; + + // + // Complete the packet back to the binding. + // + NDISM_COMPLETE_SEND( + Miniport, + Open, + Packet, + PrevPacket, + NDIS_STATUS_SUCCESS); + + // + // No, we don't want to increment the counter for the + // miniport's packet array. + // + } + else + { + NDISM_LOG_PACKET(Miniport, Packet, NULL, 'inim'); + + // + // We have to re-initialize this. + // + *pPktArray = Packet; + NDIS_SET_PACKET_STATUS(Packet, NDIS_STATUS_SUCCESS); + + // + // Increment the counter for the packet array index. + // + NumberOfPackets++; + pPktArray++; + } + } + + // + // Are there any packets to send? + // + if (NumberOfPackets != 0) + { + // + // Get a temp pointer to our packet array. + // + pPktArray = Miniport->PacketArray; + Open = PNDIS_RESERVED_FROM_PNDIS_PACKET(*pPktArray)->Open; + + // + // Pass the packet array down to the miniport. + // + (Open->SendPacketsHandler)( + Miniport->MiniportAdapterContext, + Miniport->PacketArray, + NumberOfPackets); + + // + // First check to see if the LastMiniportPacket is NULL. + // If it is then the miniport called NdisMSendComplete() + // in our send context and we don't need to do anything more. + // + if (NULL == Miniport->LastMiniportPacket) + { + // + // We may still have packets pending to be sent down.... + // + continue; + } + + // + // Process the packet completion. + // + for (Count = 0; Count < NumberOfPackets; Count++, pPktArray++) + { + BOOLEAN fFoundPacket = FALSE; + + // + // Try and find the packet on our miniport's packet queue. + // + Packet = Miniport->FirstPacket; + PrevPacket = NULL; + + ASSERT(Packet != NULL); + + // + // We are only going to travers the packet queue from the + // FirstPacket to the LastMiniportPacket. + // Why you ask? Well we need to make sure that the miniport + // didn't complete the packet we are now trying to complete + // with a call to NdisMSendComplete(). + // + while (Packet != PNDIS_RESERVED_FROM_PNDIS_PACKET(Miniport->LastMiniportPacket)->Next) + { + if (Packet == *pPktArray) + { + fFoundPacket = TRUE; + break; + } + + PrevPacket = Packet; + Packet = PNDIS_RESERVED_FROM_PNDIS_PACKET(Packet)->Next; + } + + // + // If we didn't find the packet on our queue then we need to + // go on to the next one since it MUST have been completed + // via NdisMSendComplete.... + // + if (!fFoundPacket) + { + continue; + } + + Status = NDIS_GET_PACKET_STATUS(*pPktArray); + + // + // Process the packet based on it's return status. + // + if (NDIS_STATUS_PENDING == Status) + { + NDISM_LOG_PACKET(Miniport, *pPktArray, NULL, 'dnep'); + + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("Complete is pending\n")); + } + else if (Status != NDIS_STATUS_RESOURCES) + { +#if DBG + if (Status != NDIS_STATUS_SUCCESS) + { + NDISM_LOG_PACKET(Miniport, *pPktArray, NULL, 'liaf'); + } + else + { + NDISM_LOG_PACKET(Miniport, *pPktArray, NULL, 'ccus'); + } +#endif + + ADD_RESOURCE(Miniport, 'F'); + + // + // Remove from the finish queue. + // + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("Completed packet 0x%x with status 0x%x\n", + *pPktArray, + Status)); + + // + // Fix up the packet queues. + // + if (Miniport->FirstPacket == *pPktArray) + { + Miniport->FirstPacket = PNDIS_RESERVED_FROM_PNDIS_PACKET(*pPktArray)->Next; + + if (Miniport->LastMiniportPacket == *pPktArray) + { + Miniport->LastMiniportPacket = NULL; + } + } + else + { + // + // If we just completed the last packet then + // we need to update our last packet pointer. + // + PNDIS_RESERVED_FROM_PNDIS_PACKET(PrevPacket)->Next = PNDIS_RESERVED_FROM_PNDIS_PACKET(*pPktArray)->Next; + if (*pPktArray == Miniport->LastPacket) + { + Miniport->LastPacket = PrevPacket; + } + + // + // If we just complete the last miniport packet then + // the last miniport packet is the previous packet. + // + if (Miniport->LastMiniportPacket == *pPktArray) + { + Miniport->LastMiniportPacket = PrevPacket; + } + } + + // + // We need to save a pointer to the open so that we + // can dereference it after the completion. + // + Open = PNDIS_RESERVED_FROM_PNDIS_PACKET(*pPktArray)->Open; + + NDIS_RELEASE_MINIPORT_SPIN_LOCK_DPC(Miniport); + + (Open->ProtocolHandle->ProtocolCharacteristics.SendCompleteHandler)( + Open->ProtocolBindingContext, + *pPktArray, + Status); + + NDIS_ACQUIRE_MINIPORT_SPIN_LOCK_DPC(Miniport); + + DBGPRINT(DBG_COMP_OPEN, DBG_LEVEL_INFO, + ("- Open 0x%x Reference 0x%x\n", Open, Open->References)); + + Open->References--; + + DBGPRINT(DBG_COMP_OPEN, DBG_LEVEL_INFO, + ("==0 Open 0x%x Reference 0x%x\n", + PNDIS_RESERVED_FROM_PNDIS_PACKET(*pPktArray)->Open, + PNDIS_RESERVED_FROM_PNDIS_PACKET(*pPktArray)->Open->References)); + + if (Open->References == 0) + { + ndisMFinishClose(Miniport, Open); + } + } + else + { + // + // Once we hit a return code of NDIS_STATUS_RESOURCES + // for a packet then we must break out and re-queue. + // + CLEAR_RESOURCE(Miniport, 'S'); + MINIPORT_CLEAR_SEND_FLAG(Miniport, fMINIPORT_SEND_COMPLETE_CALLED); + + break; + } + } + + // + // if there are any packets that returned NDIS_STATUS_RESOURCES + // then re-queue them. + // + if (Count != NumberOfPackets) + { + PNDIS_PACKET FinalPrevPacket = PrevPacket; + + for (ResourcesCount = NumberOfPackets - 1, pPktArray = &Miniport->PacketArray[ResourcesCount]; + ResourcesCount > Count; + ResourcesCount--, pPktArray--) + { + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("Packet 0x%x returned resources\n", *pPktArray)); + + NDISM_LOG_PACKET(Miniport, *pPktArray, NULL, 'oser'); + + // + // Float the pointers. + // + Miniport->LastMiniportPacket = *(pPktArray - 1); + Miniport->FirstPendingPacket = *pPktArray; + } + + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("Packet 0x%x returned resources\n", *pPktArray)); + + NDISM_LOG_PACKET(Miniport, *pPktArray, NULL, 'oser'); + + // + // If this is the last packet on the miniport queue + // then NULL the last miniport packet out so we know there + // are no more. + // + if (Miniport->FirstPacket == *pPktArray) + { + Miniport->LastMiniportPacket = NULL; + Miniport->FirstPendingPacket = *pPktArray; + } + else + { + // + // There are other packets that are pending on + // the miniport queue. + // + Miniport->LastMiniportPacket = PrevPacket; + Miniport->FirstPendingPacket = *pPktArray; + } + } + } + } + + NDISM_LOG_PACKET(Miniport, NULL, NULL, 'RFED'); + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, ("<==ndisMStartSendPackets\n")); + + return(FALSE); +} + +BOOLEAN +FASTCALL +ndisMStartSends( + PNDIS_MINIPORT_BLOCK Miniport + ) + +/*++ + +Routine Description: + + Submits as many sends as possible to the mini-port. + +Arguments: + + Miniport - Miniport to send to. + +Return Value: + + If there are more packets to send but no resources to do it with + the this is TRUE to keep a workitem queue'd. + +--*/ + +{ + PNDIS_PACKET Packet; + PNDIS_PACKET PrevPacket; + NDIS_STATUS Status; + PNDIS_PACKET_RESERVED Reserved; + PNDIS_M_OPEN_BLOCK Open; + UINT Flags; + BOOLEAN fReturn; + + ASSERT(MINIPORT_AT_DPC_LEVEL); + ASSERT(MINIPORT_LOCK_ACQUIRED(Miniport)); + + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, ("==>ndisMStartSends\n")); + + MINIPORT_CLEAR_SEND_FLAG(Miniport, fMINIPORT_SEND_COMPLETE_CALLED); + NDISM_LOG_PACKET(Miniport, NULL, NULL, 'rfed'); + + // + // Loop and process sends. + // + while ((Miniport->SendResourcesAvailable != 0) && + (Miniport->FirstPendingPacket != NULL)) + { + // + // Grab the packet off of the pending queue. + // + Packet = Miniport->FirstPendingPacket; + Reserved = PNDIS_RESERVED_FROM_PNDIS_PACKET(Packet); + Open = Reserved->Open; + + NDISM_LOG_PACKET(Miniport, Packet, NULL, 's'); + + // + // Is this arcnet using ethernet encapsulation? + // + if (Miniport->MediaType == NdisMediumArcnet878_2) + { + // + // Build the header for arcnet. + // + Status = ndisMBuildArcnetHeader(Miniport, Open, Packet); + if (NDIS_STATUS_PENDING == Status) + { + return(TRUE); + } + } + + // + // Remove from pending queue + // + Miniport->FirstPendingPacket = Reserved->Next; + + // + // Put on finish queue + // + PrevPacket = Miniport->LastMiniportPacket; + Miniport->LastMiniportPacket = Packet; + + // + // Send the packet. + // + NDISM_SEND_PACKET(Miniport, Open, Packet, &Status); + + // + // Process the packet pending completion status. + // + if (NDIS_STATUS_PENDING == Status) + { + NDISM_LOG_PACKET(Miniport, Packet, NULL, 'dnep'); + + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("Complete is pending\n")); + } + else + { + // + // Clean-up arcnet's extra buffers. + // + if (Miniport->MediaType == NdisMediumArcnet878_2) + { + ndisMFreeArcnetHeader(Miniport, Packet); + } + + // + // Handle the completion and resources cases. + // + if (Status != NDIS_STATUS_RESOURCES) + { + NDISM_COMPLETE_SEND( + Miniport, + Open, + Packet, + PrevPacket, + Status); + } + else + { + NDISM_COMPLETE_SEND_RESOURCES(Miniport, Packet, PrevPacket); + } + } + + } + + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, ("<==ndisMStartSends\n")); + + // + // If there are no more resources available but + // there are still packets to send then we need to + // keep the workitem queue'd. + // + if ((0 == Miniport->SendResourcesAvailable) && + (Miniport->FirstPendingPacket != NULL)) + { + fReturn = TRUE; + } + else + { + fReturn = FALSE; + } + + NDISM_LOG_PACKET(Miniport, NULL, NULL, 'RFED'); + + return(fReturn); +} + +NDIS_STATUS FASTCALL +ndisMSyncSend( + IN PNDIS_MINIPORT_BLOCK Miniport, + IN PNDIS_PACKET Packet + ) +/*++ + +Routine Description: + + Submits an immediate send to a miniport. The miniport has + the send on the pending queue, and it is the only element on the send + queue. This routine is also called with the lock held. + +Arguments: + + Miniport - Miniport to send to. + +Return Value: + + None. + +--*/ + +{ + PNDIS_PACKET PrevPacket; + NDIS_STATUS Status; + PNDIS_PACKET_RESERVED Reserved; + PNDIS_M_OPEN_BLOCK Open; + UINT Flags; + + ASSERT(MINIPORT_AT_DPC_LEVEL); + ASSERT(MINIPORT_LOCK_ACQUIRED(Miniport)); + + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("Enter Sync send.\n")); + + NDISM_LOG_PACKET(Miniport, Packet, NULL, 'cnys'); + + // + // Get the Open block that the send is for from the packet. + // + Reserved = PNDIS_RESERVED_FROM_PNDIS_PACKET(Packet); + Open = Reserved->Open; + + // + // Remove from Queue + // + Miniport->FirstPendingPacket = Reserved->Next; + + // + // Put on finish queue + // + PrevPacket = Miniport->LastMiniportPacket; + Miniport->LastMiniportPacket = Packet; + MINIPORT_CLEAR_SEND_FLAG(Miniport, fMINIPORT_SEND_COMPLETE_CALLED); + + // + // Send the packet. + // + NDISM_SEND_PACKET(Miniport, Open, Packet, &Status); + + // + // Process the status of the send. + // + if (NDIS_STATUS_PENDING == Status) + { + NDISM_LOG_PACKET(Miniport, Packet, NULL, 'dnep'); + + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("Complete sync send is pending\n")); + + return(NDIS_STATUS_PENDING); + } + + // + // If send complete was called from the miniport's send handler + // then our local PrevPacket pointer may no longer be valid.... + // + if (MINIPORT_TEST_SEND_FLAG(Miniport, fMINIPORT_SEND_COMPLETE_CALLED)) + { + MINIPORT_CLEAR_SEND_FLAG(Miniport, fMINIPORT_SEND_COMPLETE_CALLED); + MiniportFindPacket(Miniport, Packet, &PrevPacket); + } + + Miniport->LastMiniportPacket = PrevPacket; + + if (Status != NDIS_STATUS_RESOURCES) + { +#if DBG + if (Status != NDIS_STATUS_SUCCESS) + { + NDISM_LOG_PACKET(Miniport, Packet, NULL, 'liaf'); + } + else + { + NDISM_LOG_PACKET(Miniport, Packet, NULL, 'ccus'); + } +#endif + + ADD_RESOURCE(Miniport, 'F'); + + // + // Remove from finish queue + // + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("Completed 0x%x\n", Status)); + + if (PrevPacket == NULL) + { + // + // Set up the next packet to send. + // + Miniport->FirstPacket = Reserved->Next; + } + else + { + PNDIS_RESERVED_FROM_PNDIS_PACKET(PrevPacket)->Next = Reserved->Next; + + if (Packet == Miniport->LastPacket) + { + Miniport->LastPacket = PrevPacket; + } + } + + DBGPRINT(DBG_COMP_OPEN, DBG_LEVEL_INFO, + ("- Open 0x%x Reference 0x%x\n", Open, Open->References)); + + Open->References--; + + DBGPRINT(DBG_COMP_OPEN, DBG_LEVEL_INFO, + ("==0 Open 0x%x Reference 0x%x\n", Open, Open->References)); + + if (Open->References == 0) + ndisMFinishClose(Miniport, Open); + } + else + { + // + // Status == NDIS_STATUS_RESOURCES!!!! + // + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("Deferring send\n")); + + // + // Put on pending queue + // + Miniport->FirstPendingPacket = Packet; + + NDISM_LOG_PACKET(Miniport, Packet, NULL, 'oser'); + + // + // Set flag + // + CLEAR_RESOURCE(Miniport, 'S'); + + Status = NDIS_STATUS_PENDING; + } + + return(Status); +} + + +NDIS_STATUS +ndisMWanSend( + IN NDIS_HANDLE NdisBindingHandle, + IN NDIS_HANDLE NdisLinkHandle, + IN PVOID Packet + ) +{ + PNDIS_MINIPORT_BLOCK Miniport = ((PNDIS_M_OPEN_BLOCK)NdisBindingHandle)->MiniportHandle; + BOOLEAN LocalLock; + NDIS_STATUS Status; + KIRQL OldIrql; + + NDIS_ACQUIRE_MINIPORT_SPIN_LOCK(Miniport, &OldIrql); + LOCK_MINIPORT(Miniport, LocalLock); + + // + // Call MAC to send WAN packet + // + Status = ((PNDIS_M_WAN_SEND)(Miniport->DriverHandle->MiniportCharacteristics.SendHandler))( + Miniport->MiniportAdapterContext, + NdisLinkHandle, + Packet); + + if (LocalLock) + { + // + // Process any changes that may have occured. + // + NDISM_PROCESS_DEFERRED(Miniport); + } + + UNLOCK_MINIPORT(Miniport, LocalLock); + NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql); + + return Status; +} + +VOID +ndisMSendCompleteFullDuplex( + IN NDIS_HANDLE MiniportAdapterHandle, + IN PNDIS_PACKET Packet, + IN NDIS_STATUS Status + ) +{ +#ifdef NDIS_NT + PNDIS_MINIPORT_BLOCK Miniport = (PNDIS_MINIPORT_BLOCK)MiniportAdapterHandle; + PNDIS_M_OPEN_BLOCK Open; + PNDIS_PACKET_RESERVED Reserved; + LONG Thread; + BOOLEAN fAcquireMiniportLock = FALSE; + + ASSERT(MINIPORT_AT_DPC_LEVEL); + + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("==>ndisMSendCompleteFullDuplex\n")); + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("packet 0x%x\n", Packet)); + + // + // Acquire the send lock, unless it has alrady been acquired earlier + // by this thread.... + // + Thread = InterlockedExchangeAdd(&Miniport->SendThread, 0); + if (CURRENT_THREAD != Thread) + { + // + // We need to try and grab the lock... + // + NDIS_ACQUIRE_SEND_SPIN_LOCK_DPC(Miniport); + } + + MINIPORT_SET_SEND_FLAG(Miniport, fMINIPORT_SEND_COMPLETE_CALLED); + + // + // If the packet is not equal to the first packet then we have to find + // it because it may have completed out of order. + // + Reserved = PNDIS_RESERVED_FROM_PNDIS_PACKET(Packet); + + if (Miniport->FirstPacket == Packet) + { + Miniport->FirstPacket = Reserved->Next; + + if (Miniport->LastMiniportPacket == Packet) + { + Miniport->LastMiniportPacket = NULL; + } + } + else + { + PNDIS_PACKET PrevPacket; + + // + // Search for the packet. + // + MiniportFindPacket(Miniport, Packet, &PrevPacket); + + ASSERT(PrevPacket != NULL); + + // + // If we just completed the last packet then + // we need to update our last packet pointer. + // + PNDIS_RESERVED_FROM_PNDIS_PACKET(PrevPacket)->Next = Reserved->Next; + if (Packet == Miniport->LastPacket) + { + Miniport->LastPacket = PrevPacket; + } + + // + // If we just completed the last miniport packet then + // last miniport packet is the previous packet. + // + if (Miniport->LastMiniportPacket == Packet) + { + Miniport->LastMiniportPacket = PrevPacket; + } + } + + // + // Indicate to Protocol; + // + Open = Reserved->Open; + + // + // Do we need to queue another workitem to process more sends? + // + if (Miniport->FirstPendingPacket != NULL) + { + NDISM_QUEUE_WORK_ITEM(Miniport, NdisWorkItemSend, NULL, NULL); + } + + +#if DBG + NDISM_LOG_PACKET(Miniport, NULL, NULL, 'pmoc'); + + if (Status != NDIS_STATUS_SUCCESS) + { + NDISM_LOG_PACKET(Miniport, Packet, NULL, 'liaf'); + } + else + { + NDISM_LOG_PACKET(Miniport, Packet, NULL, 'ccus'); + } +#endif + + ADD_RESOURCE(Miniport, 'P'); + + NDIS_RELEASE_SEND_SPIN_LOCK_DPC(Miniport); + + // + // If the current thread has the miniport lock then we need to + // release it.... + // + if (CURRENT_THREAD == Miniport->MiniportThread) + { + NDIS_RELEASE_MINIPORT_SPIN_LOCK_DPC(Miniport); + fAcquireMiniportLock = TRUE; + } + + (Open->ProtocolHandle->ProtocolCharacteristics.SendCompleteHandler)( + Open->ProtocolBindingContext, + Packet, + Status); + + if (fAcquireMiniportLock) + { + NDIS_ACQUIRE_MINIPORT_SPIN_LOCK_DPC(Miniport); + } + + // + // If the SendLock was acquired on entry then we need to make sure + // that it's acquired on exit... + // + if (CURRENT_THREAD == Thread) + { + NDIS_ACQUIRE_SEND_SPIN_LOCK_DPC(Miniport); + } + + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("<==ndisMSendCompleteFullDuplex\n")); +#endif +} + +VOID +NdisMSendComplete( + IN NDIS_HANDLE MiniportAdapterHandle, + IN PNDIS_PACKET Packet, + IN NDIS_STATUS Status + ) +/*++ + +Routine Description: + + This function indicates the completion of a send. + +Arguments: + + MiniportAdapterHandle - points to the adapter block. + +Return Value: + + None. + +--*/ +{ + PNDIS_MINIPORT_BLOCK Miniport = (PNDIS_MINIPORT_BLOCK)MiniportAdapterHandle; + PNDIS_M_OPEN_BLOCK Open; + PNDIS_PACKET_RESERVED Reserved; + PSINGLE_LIST_ENTRY Link; + + ASSERT(MINIPORT_AT_DPC_LEVEL); + ASSERT(MINIPORT_LOCK_ACQUIRED(Miniport)); + + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("==>ndisMSendComplete\n")); + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("packet 0x%x\n", Packet)); + + MINIPORT_SET_SEND_FLAG(Miniport, fMINIPORT_SEND_COMPLETE_CALLED); + + // + // If the packet is not equal to the first packet then we have to find + // it because it may have completed out of order. + // + Reserved = PNDIS_RESERVED_FROM_PNDIS_PACKET(Packet); + + if (Miniport->FirstPacket == Packet) + { + Miniport->FirstPacket = Reserved->Next; + + if (Miniport->LastMiniportPacket == Packet) + { + Miniport->LastMiniportPacket = NULL; + } + } + else + { + PNDIS_PACKET PrevPacket; + + // + // Search for the packet. + // + MiniportFindPacket(Miniport, Packet, &PrevPacket); + + ASSERT(PrevPacket != NULL); + + // + // If we just completed the last packet then + // we need to update our last packet pointer. + // + PNDIS_RESERVED_FROM_PNDIS_PACKET(PrevPacket)->Next = Reserved->Next; + if (Packet == Miniport->LastPacket) + { + Miniport->LastPacket = PrevPacket; + } + + // + // If we just completed the last miniport packet then + // last miniport packet is the previous packet. + // + + if (Miniport->LastMiniportPacket == Packet) + { + Miniport->LastMiniportPacket = PrevPacket; + } + } + + // + // Indicate to Protocol; + // + Open = Reserved->Open; + + // + // If this is arcnet, then free the appended header. + // + if (Miniport->MediaType == NdisMediumArcnet878_2) + { + ndisMFreeArcnetHeader(Miniport, Packet); + } + +#if DBG + NDISM_LOG_PACKET(Miniport, NULL, NULL, 'pmoc'); + + if (Status != NDIS_STATUS_SUCCESS) + { + NDISM_LOG_PACKET(Miniport, Packet, NULL, 'liaf'); + } + else + { + NDISM_LOG_PACKET(Miniport, Packet, NULL, 'ccus'); + } +#endif + + NDIS_RELEASE_MINIPORT_SPIN_LOCK_DPC(Miniport); + + (Open->ProtocolHandle->ProtocolCharacteristics.SendCompleteHandler)( + Open->ProtocolBindingContext, + Packet, + Status); + + NDIS_ACQUIRE_MINIPORT_SPIN_LOCK_DPC(Miniport); + + ADD_RESOURCE(Miniport, 'P'); + + DBGPRINT(DBG_COMP_OPEN, DBG_LEVEL_INFO, + ("- Open 0x%x Reference 0x%x\n", Open, Open->References)); + + Open->References--; + + DBGPRINT(DBG_COMP_OPEN, DBG_LEVEL_INFO, + ("==0 Open 0x%x Reference 0x%x\n", Open, Open->References)); + + if (Open->References == 0) + { + ndisMFinishClose(Miniport,Open); + } + + // + // Do we need to queue another workitem to process more sends? + // + if (Miniport->FirstPendingPacket != NULL) + { + NDISM_QUEUE_WORK_ITEM(Miniport, NdisWorkItemSend, NULL, NULL); + } + + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("<==ndisMSendComplete\n")); +} + + +VOID +NdisMWanSendComplete( + IN NDIS_HANDLE MiniportAdapterHandle, + IN PNDIS_PACKET Packet, + IN NDIS_STATUS Status + ) + +/*++ + +Routine Description: + + This function indicates the status is complete. + +Arguments: + + MiniportAdapterHandle - points to the adapter block. + +Return Value: + + None. + + +--*/ +{ + PNDIS_MINIPORT_BLOCK Miniport = (PNDIS_MINIPORT_BLOCK)MiniportAdapterHandle; + PNDIS_M_OPEN_BLOCK Open; + + ASSERT(MINIPORT_AT_DPC_LEVEL); + ASSERT(MINIPORT_LOCK_ACQUIRED(Miniport)); + + Open = Miniport->OpenQueue; + + while (Open != NULL) + { + // + // Call Protocol to indicate status + // + + NDISM_LOG_PACKET(Miniport, Packet, NULL, 'C'); + + NDIS_RELEASE_MINIPORT_SPIN_LOCK_DPC(Miniport); + + (Open->ProtocolHandle->ProtocolCharacteristics.SendCompleteHandler) ( + Open->ProtocolBindingContext, + Packet, + Status); + + NDIS_ACQUIRE_MINIPORT_SPIN_LOCK_DPC(Miniport); + + Open = Open->MiniportNextOpen; + } +} + +VOID +ndisMSendResourcesAvailableFullDuplex( + IN NDIS_HANDLE MiniportAdapterHandle + ) +{ +#ifdef NDIS_NT + PNDIS_MINIPORT_BLOCK Miniport = (PNDIS_MINIPORT_BLOCK)MiniportAdapterHandle; + LONG Thread; + + ASSERT(MINIPORT_AT_DPC_LEVEL); + + Thread = InterlockedExchangeAdd(&Miniport->SendThread, 0); + if (CURRENT_THREAD != Thread) + { + NDIS_ACQUIRE_SEND_SPIN_LOCK_DPC(Miniport); + } + + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("==>ndisMSendResourcesAvailableFullDuplex\n")); + + NDISM_LOG_PACKET(Miniport, NULL, NULL, 'aser'); + ADD_RESOURCE(Miniport, 'V'); + + // + // Are there more sends to process? + // + if (Miniport->FirstPendingPacket != NULL) + { + NDISM_QUEUE_WORK_ITEM(Miniport, NdisWorkItemSend, NULL, NULL); + } + + if (CURRENT_THREAD != Thread) + { + NDIS_RELEASE_SEND_SPIN_LOCK_DPC(Miniport); + } + + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("<==ndisMSendResourcesAvailableFullDuplex\n")); +#endif +} + +VOID +NdisMSendResourcesAvailable( + IN NDIS_HANDLE MiniportAdapterHandle + ) +/*++ + +Routine Description: + + This function indicates that some send resources are available and are free for + processing more sends. + +Arguments: + + MiniportAdapterHandle - points to the adapter block. + +Return Value: + + None. + + +--*/ +{ + PNDIS_MINIPORT_BLOCK Miniport = (PNDIS_MINIPORT_BLOCK)MiniportAdapterHandle; + + ASSERT(MINIPORT_AT_DPC_LEVEL); + + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("==>ndisMSendResourcesAvailable\n")); + + ADD_RESOURCE(Miniport, 'V'); + NDISM_LOG_PACKET(Miniport, NULL, NULL, 'aser'); + + // + // Are there more sends to process? + // + if (Miniport->FirstPendingPacket != NULL) + { + NDISM_QUEUE_WORK_ITEM(Miniport, NdisWorkItemSend, NULL, NULL); + } + + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("<==ndisMSendResourcesAvailable\n")); +} + + +/////////////////////////////////////////////////////////////////////////////////////// +// +// UPPER-EDGE SEND HANDLERS +// +/////////////////////////////////////////////////////////////////////////////////////// + + +NDIS_STATUS +ndisMSendFullDuplexToSendPackets( + IN NDIS_HANDLE NdisBindingHandle, + IN PNDIS_PACKET Packet + ) +{ + NDIS_STATUS Status = NDIS_STATUS_PENDING; +#ifdef NDIS_NT + PNDIS_MINIPORT_BLOCK Miniport = ((PNDIS_M_OPEN_BLOCK)NdisBindingHandle)->MiniportHandle; + PNDIS_PACKET_RESERVED Reserved; + KIRQL OldIrql; + BOOLEAN fQueueWorkItem = FALSE; + BOOLEAN fDeferredSend = FALSE; + BOOLEAN LocalLock; + UINT Flags; + + NDIS_ACQUIRE_SEND_SPIN_LOCK(Miniport, &OldIrql); + + NDISM_LOG_PACKET(Miniport, Packet, NULL, 'dnes'); + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("==>ndisMSendFullDuplexToSendPackets\n")); + + // + // Initialize the packet info. + // + Reserved = PNDIS_RESERVED_FROM_PNDIS_PACKET(Packet); + Reserved->Next = NULL; + Reserved->Open = (PNDIS_M_OPEN_BLOCK)NdisBindingHandle; + + // + // Place the packet on the packet queue. + // + if (Miniport->FirstPacket == NULL) + { + // + // Place the packet at the head of the queue. + // + Miniport->FirstPacket = Packet; + } + else + { + CHECK_FOR_DUPLICATE_PACKET(Miniport, Packet); + + PNDIS_RESERVED_FROM_PNDIS_PACKET(Miniport->LastPacket)->Next = Packet; + } + + Miniport->LastPacket = Packet; + + // + // This packet has not been looped back yet! + // + MINIPORT_CLEAR_PACKET_FLAG(Packet, (fPACKET_HAS_BEEN_LOOPED_BACK | fPACKET_HAS_TIMED_OUT)); + + // + // Check to see if a sync send is possible. + // + if (Miniport->FirstPendingPacket == NULL) + { + Miniport->FirstPendingPacket = Packet; + } + + NDISM_QUEUE_WORK_ITEM(Miniport, NdisWorkItemSend, NULL, NULL); + + NDISM_LOG_PACKET(Miniport, Packet, NULL, 'DNES'); + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("<==ndisMSendFullDuplexToSendPackets\n")); + + NDIS_RELEASE_SEND_SPIN_LOCK(Miniport, OldIrql); +#endif + return(Status); +} + +VOID +ndisMSendPacketsFullDuplex( + IN PNDIS_OPEN_BLOCK NdisOpenBlock, + IN PPNDIS_PACKET PacketArray, + IN UINT NumberOfPackets + ) +{ +#ifdef NDIS_NT + PNDIS_M_OPEN_BLOCK NdisBindingHandle = (PNDIS_M_OPEN_BLOCK)NdisOpenBlock->MacBindingHandle; + PNDIS_MINIPORT_BLOCK Miniport = NdisBindingHandle->MiniportHandle; + PPNDIS_PACKET pPktArray; + PNDIS_PACKET_RESERVED Reserved; + BOOLEAN LocalLock; + KIRQL OldIrql; + UINT c; + + NDIS_ACQUIRE_SEND_SPIN_LOCK(Miniport, &OldIrql); + + NDISM_LOG_PACKET(Miniport, NULL, NULL, 'KPES'); + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("==>ndisMSendPacketsFullDuplex\n")); + + // + // Place the packets on the miniport queue. + // + for (c = 0, pPktArray = PacketArray; + c < NumberOfPackets; + c++, pPktArray++) + { + // + // Get the first packet.... + // + NDIS_SET_PACKET_STATUS(*pPktArray, NDIS_STATUS_PENDING); + + // + // Initialize the packet info. + // + Reserved = PNDIS_RESERVED_FROM_PNDIS_PACKET(*pPktArray); + Reserved->Next = NULL; + Reserved->Open = (PNDIS_M_OPEN_BLOCK)NdisBindingHandle; + + // + // Place the packet on the packet queue. + // + if (Miniport->FirstPacket == NULL) + { + // + // Place the packet at the head of the queue. + // + Miniport->FirstPacket = *pPktArray; + } + else + { + CHECK_FOR_DUPLICATE_PACKET(Miniport, *pPktArray); + + PNDIS_RESERVED_FROM_PNDIS_PACKET(Miniport->LastPacket)->Next = *pPktArray; + } + + Miniport->LastPacket = *pPktArray; + + // + // This packet has not been looped back yet! + // + MINIPORT_CLEAR_PACKET_FLAG(*pPktArray, (fPACKET_HAS_BEEN_LOOPED_BACK | fPACKET_HAS_TIMED_OUT)); + + // + // Check to see if a sync send is possible. + // + if (Miniport->FirstPendingPacket == NULL) + { + Miniport->FirstPendingPacket = *pPktArray; + } + } + + // + // Queue a workitem for the new sends. + // + NDISM_QUEUE_WORK_ITEM(Miniport, NdisWorkItemSend, NULL, NULL); + + NDISM_LOG_PACKET(Miniport, NULL, NULL, 'KPES'); + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("<==ndisMSendPacketsFullDuplex\n")); + + NDIS_RELEASE_SEND_SPIN_LOCK(Miniport, OldIrql); + +#endif +} + + +NDIS_STATUS +ndisMSendToSendPackets( + IN NDIS_HANDLE NdisBindingHandle, + IN PNDIS_PACKET Packet + ) +{ + PNDIS_MINIPORT_BLOCK Miniport = ((PNDIS_M_OPEN_BLOCK)NdisBindingHandle)->MiniportHandle; + PNDIS_PACKET_RESERVED Reserved; + BOOLEAN LocalLock; + NDIS_STATUS Status = NDIS_STATUS_PENDING; + KIRQL OldIrql; + + NDIS_ACQUIRE_MINIPORT_SPIN_LOCK(Miniport, &OldIrql); + + NDISM_LOG_PACKET(Miniport, Packet, NULL, 'dnes'); + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("==>ndisMSendToSendPackets\n")); + + // + // Increment the references on this open. + // + ((PNDIS_M_OPEN_BLOCK)NdisBindingHandle)->References++; + + DBGPRINT(DBG_COMP_OPEN, DBG_LEVEL_INFO, + ("+ Open 0x%x Reference 0x%x\n", NdisBindingHandle, ((PNDIS_M_OPEN_BLOCK)NdisBindingHandle)->References)); + + // + // Initialize the packet info. + // + Reserved = PNDIS_RESERVED_FROM_PNDIS_PACKET(Packet); + Reserved->Next = NULL; + Reserved->Open = (PNDIS_M_OPEN_BLOCK)NdisBindingHandle; + + // + // Place the packet on the packet queue. + // + if (Miniport->FirstPacket == NULL) + { + // + // Place the packet at the head of the queue. + // + Miniport->FirstPacket = Packet; + } + else + { + CHECK_FOR_DUPLICATE_PACKET(Miniport, Packet); + + PNDIS_RESERVED_FROM_PNDIS_PACKET(Miniport->LastPacket)->Next = Packet; + } + + Miniport->LastPacket = Packet; + + // + // This packet has not been looped back yet! + // + MINIPORT_CLEAR_PACKET_FLAG(Packet, (fPACKET_HAS_BEEN_LOOPED_BACK | fPACKET_HAS_TIMED_OUT)); + + // + // Check to see if a sync send is possible. + // + if (Miniport->FirstPendingPacket == NULL) + { + Miniport->FirstPendingPacket = Packet; + } + + NDISM_QUEUE_WORK_ITEM(Miniport, NdisWorkItemSend, NULL, NULL); + + // + // Try to grab the local lock on the miniport block. + // + LOCK_MINIPORT(Miniport, LocalLock); + + // + // If we have the local lock and there is not + // a reset in progress then fire off the send. + // + if (LocalLock) + { + // + // Process deferred. + // + NDISM_PROCESS_DEFERRED(Miniport); + } + + UNLOCK_MINIPORT(Miniport, LocalLock); + + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("<==ndisMSendToSendPackets\n")); + + NDISM_LOG_PACKET(Miniport, Packet, NULL, 'DNES'); + + NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql); + + return(NDIS_STATUS_PENDING); +} + + +VOID +ndisMSendPackets( + IN PNDIS_OPEN_BLOCK NdisOpenBlock, + IN PPNDIS_PACKET PacketArray, + IN UINT NumberOfPackets + ) +{ + PNDIS_M_OPEN_BLOCK NdisBindingHandle = (PNDIS_M_OPEN_BLOCK)NdisOpenBlock->MacBindingHandle; + PNDIS_MINIPORT_BLOCK Miniport = NdisBindingHandle->MiniportHandle; + BOOLEAN LocalLock; + KIRQL OldIrql; + UINT c; + PNDIS_PACKET_RESERVED Reserved; + PPNDIS_PACKET pPktArray;; + + NDIS_ACQUIRE_MINIPORT_SPIN_LOCK(Miniport, &OldIrql); + NDISM_LOG_PACKET(Miniport, NULL, NULL, 'KPES'); + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("==>ndisMSendPackets\n")); + + // + // Place the packets on the miniport queue. + // + for (c = 0, pPktArray = PacketArray; + c < NumberOfPackets; + c++, pPktArray++) + { + // + // Get the first packet.... + // + NDIS_SET_PACKET_STATUS(*pPktArray, NDIS_STATUS_PENDING); + + // + // Initialize the packet info. + // + Reserved = PNDIS_RESERVED_FROM_PNDIS_PACKET(*pPktArray); + Reserved->Next = NULL; + Reserved->Open = (PNDIS_M_OPEN_BLOCK)NdisBindingHandle; + + // + // Increment the references on this open. + // + ((PNDIS_M_OPEN_BLOCK)NdisBindingHandle)->References++; + + DBGPRINT(DBG_COMP_OPEN, DBG_LEVEL_INFO, + ("+ Open 0x%x Reference 0x%x\n", NdisBindingHandle, ((PNDIS_M_OPEN_BLOCK)NdisBindingHandle)->References)); + + // + // Place the packet on the packet queue. + // + if (Miniport->FirstPacket == NULL) + { + // + // Place the packet at the head of the queue. + // + Miniport->FirstPacket = *pPktArray; + } + else + { + CHECK_FOR_DUPLICATE_PACKET(Miniport, *pPktArray); + + PNDIS_RESERVED_FROM_PNDIS_PACKET(Miniport->LastPacket)->Next = *pPktArray; + } + + Miniport->LastPacket = *pPktArray; + + // + // This packet has not been looped back yet! + // + MINIPORT_CLEAR_PACKET_FLAG(*pPktArray, (fPACKET_HAS_BEEN_LOOPED_BACK | fPACKET_HAS_TIMED_OUT)); + + // + // Check to see if a sync send is possible. + // + if (Miniport->FirstPendingPacket == NULL) + { + Miniport->FirstPendingPacket = *pPktArray; + } + } + + // + // Queue a workitem for the new sends. + // + NDISM_QUEUE_WORK_ITEM(Miniport, NdisWorkItemSend, NULL, NULL); + + // + // Try to grab the local lock on the miniport block. + // + LOCK_MINIPORT(Miniport, LocalLock); + + // + // If we have the local lock and there is not + // a reset in progress then fire off the send. + // + if (LocalLock) + { + // + // Process deferred. + // + NDISM_PROCESS_DEFERRED(Miniport); + } + + UNLOCK_MINIPORT(Miniport, LocalLock); + + NDISM_LOG_PACKET(Miniport, NULL, NULL, 'KPES'); + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("<==ndisMSendPackets\n")); + + NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql); +} + +VOID +ndisMSendPacketsFullDuplexToSend( + IN PNDIS_OPEN_BLOCK NdisOpenBlock, + IN PPNDIS_PACKET PacketArray, + IN UINT NumberOfPackets + ) +{ +#ifdef NDIS_NT + PNDIS_M_OPEN_BLOCK NdisBindingHandle = (PNDIS_M_OPEN_BLOCK)NdisOpenBlock->MacBindingHandle; + PNDIS_MINIPORT_BLOCK Miniport = NdisBindingHandle->MiniportHandle; + PNDIS_PACKET_RESERVED Reserved; + PPNDIS_PACKET pPktArray; + BOOLEAN LocalLock; + KIRQL OldIrql; + UINT c; + + NDIS_ACQUIRE_SEND_SPIN_LOCK(Miniport, &OldIrql); + + NDISM_LOG_PACKET(Miniport, NULL, NULL, 'kpes'); + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("==>ndisMSendPacketsFullDuplexToSend\n")); + + // + // Place the packets on the miniport queue. + // + for (c = 0, pPktArray = PacketArray; + c < NumberOfPackets; + c++, pPktArray++) + { + // + // Initialize the packet info. + // + Reserved = PNDIS_RESERVED_FROM_PNDIS_PACKET(*pPktArray); + Reserved->Next = NULL; + Reserved->Open = (PNDIS_M_OPEN_BLOCK)NdisBindingHandle; + + // + // Place the packet on the packet queue. + // + if (Miniport->FirstPacket == NULL) + { + // + // Place the packet at the head of the queue. + // + Miniport->FirstPacket = *pPktArray; + } + else + { + CHECK_FOR_DUPLICATE_PACKET(Miniport, *pPktArray); + + PNDIS_RESERVED_FROM_PNDIS_PACKET(Miniport->LastPacket)->Next = *pPktArray; + } + + Miniport->LastPacket = *pPktArray; + + // + // This packet has not been looped back yet. + // + MINIPORT_CLEAR_PACKET_FLAG(*pPktArray, (fPACKET_HAS_BEEN_LOOPED_BACK | fPACKET_HAS_TIMED_OUT)); + + // + // Check to see if a sync send is possible. + // + if (Miniport->FirstPendingPacket == NULL) + { + Miniport->FirstPendingPacket = *pPktArray; + } + } + + // + // Queue a workitem for the new sends. + // + NDISM_QUEUE_WORK_ITEM(Miniport, NdisWorkItemSend, NULL, NULL); + + NDISM_LOG_PACKET(Miniport, NULL, NULL, 'KPES'); + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("<==ndisMSendPacketsFullDuplexToSend\n")); + + NDIS_RELEASE_SEND_SPIN_LOCK(Miniport, OldIrql); +#endif +} + +NDIS_STATUS +ndisMSendFullDuplex( + IN NDIS_HANDLE NdisBindingHandle, + IN PNDIS_PACKET Packet + ) +/*++ + +Routine Description: + +Arguments: + +Return Value: + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_PENDING; +#ifdef NDIS_NT + PNDIS_MINIPORT_BLOCK Miniport = ((PNDIS_M_OPEN_BLOCK)NdisBindingHandle)->MiniportHandle; + PNDIS_PACKET_RESERVED Reserved; + KIRQL OldIrql; + BOOLEAN fQueueWorkItem = FALSE; + BOOLEAN fDeferredSend = FALSE; + BOOLEAN LocalLock; + UINT Flags; + + NDIS_ACQUIRE_SEND_SPIN_LOCK(Miniport, &OldIrql); + + NDISM_LOG_PACKET(Miniport, Packet, NULL, 'dnes'); + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("==>ndisMSendFullDuplex\n")); + + // + // Initialize the packet info. + // + Reserved = PNDIS_RESERVED_FROM_PNDIS_PACKET(Packet); + Reserved->Next = NULL; + Reserved->Open = (PNDIS_M_OPEN_BLOCK)NdisBindingHandle; + + // + // Place the packet on the packet queue. + // + if (Miniport->FirstPacket == NULL) + { + // + // Place the packet at the head of the queue. + // + Miniport->FirstPacket = Packet; + } + else + { + CHECK_FOR_DUPLICATE_PACKET(Miniport, Packet); + + PNDIS_RESERVED_FROM_PNDIS_PACKET(Miniport->LastPacket)->Next = Packet; + } + + Miniport->LastPacket = Packet; + + // + // This packet has not been looped back yet and it does not + // contain any media specific information. + // + MINIPORT_CLEAR_PACKET_FLAG(Packet, (fPACKET_HAS_BEEN_LOOPED_BACK | fPACKET_HAS_TIMED_OUT)); + + if (NULL == Miniport->FirstPendingPacket) + { + Miniport->FirstPendingPacket = Packet; + } + + NDISM_QUEUE_WORK_ITEM(Miniport, NdisWorkItemSend, NULL, NULL); + + NDISM_LOG_PACKET(Miniport, Packet, NULL, 'DNES'); + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("<==ndisMSendFullDuplex\n")); + + NDIS_RELEASE_SEND_SPIN_LOCK(Miniport, OldIrql); +#endif + return(Status); +} + +VOID +ndisMSendPacketsToSend( + IN PNDIS_OPEN_BLOCK NdisOpenBlock, + IN PPNDIS_PACKET PacketArray, + IN UINT NumberOfPackets + ) +/*++ + +Routine Description: + + This routine will take a packet array and "thunk" it down for a + driver that does not support a send packet handler. + This involves queueing the packet array onto the miniport's packet queue + and sending them one at a time. + +Arguments: + + NdisBindingHandle - Pointer to the NDIS_OPEN_BLOCK. + PacketArray - Array of PNDIS_PACKET structures that are + to be sent to the miniport as NDIS_PACKETs. + NumberOfPackets - Number of elements in the PacketArray. + +Return Value: + + None. + +--*/ +{ + PNDIS_M_OPEN_BLOCK NdisBindingHandle = (PNDIS_M_OPEN_BLOCK)NdisOpenBlock->MacBindingHandle; + PNDIS_MINIPORT_BLOCK Miniport = NdisBindingHandle->MiniportHandle; + PNDIS_PACKET Packet; + PNDIS_PACKET_RESERVED Reserved; + PPNDIS_PACKET pPktArray; + BOOLEAN LocalLock; + KIRQL OldIrql; + UINT c; + + NDIS_ACQUIRE_MINIPORT_SPIN_LOCK(Miniport, &OldIrql); + + NDISM_LOG_PACKET(Miniport, NULL, NULL, 'kpes'); + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("==>ndisMSendPacketsToSend\n")); + + // + // Try to grab the local lock on the miniport block. + // + LOCK_MINIPORT(Miniport, LocalLock); + + // + // Place the packets on the miniport queue. + // + for (c = 0, pPktArray = PacketArray; + c < NumberOfPackets; + c++, pPktArray++) + { + // + // Initialize the packet info. + // + Reserved = PNDIS_RESERVED_FROM_PNDIS_PACKET(*pPktArray); + Reserved->Next = NULL; + Reserved->Open = (PNDIS_M_OPEN_BLOCK)NdisBindingHandle; + + // + // Increment the references on this open. + // + ((PNDIS_M_OPEN_BLOCK)NdisBindingHandle)->References++; + + DBGPRINT(DBG_COMP_OPEN, DBG_LEVEL_INFO, + ("+ Open 0x%x Reference 0x%x\n", NdisBindingHandle, ((PNDIS_M_OPEN_BLOCK)NdisBindingHandle)->References)); + + // + // Place the packet on the packet queue. + // + if (Miniport->FirstPacket == NULL) + { + // + // Place the packet at the head of the queue. + // + Miniport->FirstPacket = *pPktArray; + } + else + { + CHECK_FOR_DUPLICATE_PACKET(Miniport, *pPktArray); + + PNDIS_RESERVED_FROM_PNDIS_PACKET(Miniport->LastPacket)->Next = *pPktArray; + } + + Miniport->LastPacket = *pPktArray; + + // + // This packet has not been looped back yet. + // + MINIPORT_CLEAR_PACKET_FLAG(*pPktArray, (fPACKET_HAS_BEEN_LOOPED_BACK | fPACKET_HAS_TIMED_OUT)); + + // + // Check to see if a sync send is possible. + // + if (Miniport->FirstPendingPacket == NULL) + { + Miniport->FirstPendingPacket = *pPktArray; + } + } + + // + // Queue a workitem for the new sends. + // + NDISM_QUEUE_WORK_ITEM(Miniport, NdisWorkItemSend, NULL, NULL); + + // + // If we have the local lock and there is not + // a reset in progress then fire off the send. + // + if (LocalLock) + { + // + // Process deferred. + // + NDISM_PROCESS_DEFERRED(Miniport); + } + + UNLOCK_MINIPORT(Miniport, LocalLock); + + NDISM_LOG_PACKET(Miniport, NULL, NULL, 'KPES'); + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("<==ndisMSendPacketsToSend\n")); + + NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql); +} + +NDIS_STATUS +ndisMSend( + IN NDIS_HANDLE NdisBindingHandle, + IN PNDIS_PACKET Packet + ) +{ + PNDIS_MINIPORT_BLOCK Miniport = ((PNDIS_M_OPEN_BLOCK)NdisBindingHandle)->MiniportHandle; + PNDIS_PACKET_RESERVED Reserved; + BOOLEAN LocalLock; + BOOLEAN SyncSend = FALSE; + NDIS_STATUS Status = NDIS_STATUS_PENDING; + KIRQL OldIrql; + + NDIS_ACQUIRE_MINIPORT_SPIN_LOCK(Miniport, &OldIrql); + + NDISM_LOG_PACKET(Miniport, Packet, NULL, 'dnes'); + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("==>ndisMSend\n")); + + // + // Try to grab the local lock on the miniport block. + // + LOCK_MINIPORT(Miniport, LocalLock); + + // + // Increment the references on this open. + // + ((PNDIS_M_OPEN_BLOCK)NdisBindingHandle)->References++; + + DBGPRINT(DBG_COMP_OPEN, DBG_LEVEL_INFO, + ("+ Open 0x%x Reference 0x%x\n", NdisBindingHandle, ((PNDIS_M_OPEN_BLOCK)NdisBindingHandle)->References)); + + // + // Initialize the packet info. + // + Reserved = PNDIS_RESERVED_FROM_PNDIS_PACKET(Packet); + Reserved->Next = NULL; + Reserved->Open = (PNDIS_M_OPEN_BLOCK)NdisBindingHandle; + + // + // Place the packet on the packet queue. + // + if (Miniport->FirstPacket == NULL) + { + // + // Place the packet at the head of the queue. + // + Miniport->FirstPacket = Packet; + } + else + { + CHECK_FOR_DUPLICATE_PACKET(Miniport, Packet); + + PNDIS_RESERVED_FROM_PNDIS_PACKET(Miniport->LastPacket)->Next = Packet; + } + + Miniport->LastPacket = Packet; + + // + // Mark the packet as not having been looped back yet. + // + MINIPORT_CLEAR_PACKET_FLAG(Packet, (fPACKET_HAS_BEEN_LOOPED_BACK | fPACKET_HAS_TIMED_OUT)); + + if (NULL == Miniport->FirstPendingPacket) + { + Miniport->FirstPendingPacket = Packet; + + SyncSend = TRUE; + } + + // + // If we have the local lock and there is not + // a reset in progress then fire off the send. + // + if (LocalLock) + { + // + // Can we do a sync send? + // + if (SyncSend && (Miniport->WorkQueue[NdisWorkItemHalt].Next == NULL)) + { + // + // TODO: Make the call to sync send inline!!! + // + // Synchronous send. + // + Status = ndisMSyncSend(Miniport, Packet); + } + else + { + // + // Process deferred. + // + NDISM_QUEUE_WORK_ITEM(Miniport, NdisWorkItemSend, NULL, NULL); + NDISM_PROCESS_DEFERRED(Miniport); + } + } + else + { + // + // We can't get the local lock so queue a workitem + // to process the send later. + // + NDISM_QUEUE_WORK_ITEM(Miniport, NdisWorkItemSend, NULL, NULL); + } + + UNLOCK_MINIPORT(Miniport, LocalLock); + + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("<==ndisMSend\n")); + + NDISM_LOG_PACKET(Miniport, Packet, (PVOID)Status, 'DNES'); + + NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql); + + return(Status); +} + + +VOID +ndisMSendPacketsToFullMac( + IN PNDIS_OPEN_BLOCK NdisOpenBlock, + IN PPNDIS_PACKET PacketArray, + IN UINT NumberOfPackets + ) +/*++ + +Routine Description: + +Arguments: + +Return Value: + +--*/ +{ + UINT c; + UINT Flags; + PPNDIS_PACKET pPktArray; + NDIS_STATUS Status; + + // + // Send the packets to the mac one at a time. + // + for (c = 0, pPktArray = PacketArray; + c < NumberOfPackets; + c++, pPktArray++) + { + // + // Send the packet to the mac driver. + // + Status = NdisOpenBlock->SendHandler( + NdisOpenBlock->MacBindingHandle, + *pPktArray); + + // + // If the packet is not pending then complete it back to the protocol. + // + if (NDIS_STATUS_PENDING != Status) + { + // + // Call the protocol's complete handler.... + // + (NdisOpenBlock->ProtocolHandle->ProtocolCharacteristics.SendCompleteHandler)( + NdisOpenBlock->ProtocolBindingContext, + *pPktArray, + Status); + } + } +} + + +NDIS_STATUS +ndisMResetWanSend( + IN NDIS_HANDLE NdisBindingHandle, + IN NDIS_HANDLE NdisLinkHandle, + IN PVOID Packet + ) +{ + PNDIS_MINIPORT_BLOCK Miniport = ((PNDIS_M_OPEN_BLOCK)NdisBindingHandle)->MiniportHandle; + + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("==>ndisMResetWanSend\n")); + + NDISM_LOG_PACKET(Miniport, Packet, NULL, 'dnes'); + NDISM_LOG_PACKET(Miniport, Packet, NULL, ' PIR'); + NDISM_LOG_PACKET(Miniport, Packet, NULL, 'DNES'); + + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("ndisMResetWanSend: Reset in progress or Reset Requested\n")); + + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("<==ndisMResetWanSend\n")); + + return(NDIS_STATUS_RESET_IN_PROGRESS); +} + +NDIS_STATUS +ndisMResetSend( + IN NDIS_HANDLE NdisBindingHandle, + IN PNDIS_PACKET Packet + ) +/*++ + +Routine Description: + +Arguments: + +Return Value: + +--*/ +{ + PNDIS_MINIPORT_BLOCK Miniport = ((PNDIS_M_OPEN_BLOCK)NdisBindingHandle)->MiniportHandle; + + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("==>ndisMResetSend\n")); + + NDISM_LOG_PACKET(Miniport, Packet, NULL, 'dnes'); + NDISM_LOG_PACKET(Miniport, Packet, NULL, ' PIR'); + NDISM_LOG_PACKET(Miniport, Packet, NULL, 'DNES'); + + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("ndisMResetSend: Reset in progress or Reset Requested\n")); + + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("<==ndisMResetSend\n")); + + return(NDIS_STATUS_RESET_IN_PROGRESS); +} + +VOID +ndisMResetSendPackets( + IN PNDIS_OPEN_BLOCK NdisOpenBlock, + IN PPNDIS_PACKET PacketArray, + IN UINT NumberOfPackets + ) +/*++ + +Routine Description: + +Arguments: + +Return Value: + +--*/ +{ + PNDIS_M_OPEN_BLOCK NdisBindingHandle = (PNDIS_M_OPEN_BLOCK)NdisOpenBlock->MacBindingHandle; + PNDIS_MINIPORT_BLOCK Miniport = NdisBindingHandle->MiniportHandle; + UINT c; + + + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("==>ndisMResetSendPackets\n")); + + NDISM_LOG_PACKET(Miniport, NULL, NULL, 'kpes'); + + for (c = 0; c < NumberOfPackets; c++) + { + NDISM_LOG_PACKET(Miniport, PacketArray[c], NULL, ' PIR'); + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("ndisMResetSendPackets: Reset in progress or Reset Requested\n")); + + // + // For send packets we need to call the completion handler.... + // + (NdisBindingHandle->ProtocolHandle->ProtocolCharacteristics.SendCompleteHandler)( + NdisBindingHandle->ProtocolBindingContext, + PacketArray[c], + NDIS_STATUS_RESET_IN_PROGRESS); + } + + NDISM_LOG_PACKET(Miniport, NULL, NULL, 'KPES'); + + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("<==ndisMResetSendPackets\n")); + +} + + |