VOID ReturnTCB( __in PMP_ADAPTER Adapter, __in PTCB Tcb) { TXNblRelease(Adapter, NBL_FROM_SEND_NB(Tcb->NetBuffer), TRUE); Tcb->NetBuffer = NULL; NdisInterlockedInsertTailList( &Adapter->FreeTcbList, &Tcb->TcbLink, &Adapter->FreeTcbListLock); }
VOID MPSendNetBufferLists( _In_ NDIS_HANDLE MiniportAdapterContext, _In_ PNET_BUFFER_LIST NetBufferLists, _In_ NDIS_PORT_NUMBER PortNumber, _In_ ULONG SendFlags) /*++ Routine Description: Send Packet Array handler. Called by NDIS whenever a protocol bound to our miniport sends one or more packets. The input packet descriptor pointers have been ordered according to the order in which the packets should be sent over the network by the protocol driver that set up the packet array. The NDIS library preserves the protocol-determined ordering when it submits each packet array to MiniportSendPackets As a deserialized driver, we are responsible for holding incoming send packets in our internal queue until they can be transmitted over the network and for preserving the protocol-determined ordering of packet descriptors incoming to its MiniportSendPackets function. A deserialized miniport driver must complete each incoming send packet with NdisMSendComplete, and it cannot call NdisMSendResourcesAvailable. Runs at IRQL <= DISPATCH_LEVEL Arguments: MiniportAdapterContext Pointer to our adapter NetBufferLists Head of a list of NBLs to send PortNumber A miniport adapter port. Default is 0. SendFlags Additional flags for the send operation Return Value: None. Write status directly into each NBL with the NET_BUFFER_LIST_STATUS macro. --*/ { PMP_ADAPTER Adapter = MP_ADAPTER_FROM_CONTEXT(MiniportAdapterContext); PNET_BUFFER_LIST Nbl; PNET_BUFFER_LIST NextNbl = NULL; BOOLEAN fAtDispatch = (SendFlags & NDIS_SEND_FLAGS_DISPATCH_LEVEL) ? TRUE:FALSE; NDIS_STATUS Status; ULONG NumNbls=0; DEBUGP(MP_TRACE, "[%p] ---> MPSendNetBufferLists\n", Adapter); UNREFERENCED_PARAMETER(PortNumber); UNREFERENCED_PARAMETER(SendFlags); ASSERT(PortNumber == 0); // Only the default port is supported // // Each NET_BUFFER_LIST has a list of NET_BUFFERs. // Loop over all the NET_BUFFER_LISTs, sending each NET_BUFFER. // for ( Nbl = NetBufferLists; Nbl!= NULL; Nbl = NextNbl, ++NumNbls) { PNET_BUFFER NetBuffer; NextNbl = NET_BUFFER_LIST_NEXT_NBL(Nbl); // // Unlink the NBL and prepare our bookkeeping. // // We use a reference count to make sure that we don't send complete // the NBL until we're done reading each NB on the NBL. // NET_BUFFER_LIST_NEXT_NBL(Nbl) = NULL; SEND_REF_FROM_NBL(Nbl) = 0; Status = TXNblReference(Adapter, Nbl); if(Status == NDIS_STATUS_SUCCESS) { NET_BUFFER_LIST_STATUS(Nbl) = NDIS_STATUS_SUCCESS; // // Queue each NB for transmission. // for ( NetBuffer = NET_BUFFER_LIST_FIRST_NB(Nbl); NetBuffer != NULL; NetBuffer = NET_BUFFER_NEXT_NB(NetBuffer)) { NBL_FROM_SEND_NB(NetBuffer) = Nbl; TXQueueNetBufferForSend(Adapter, NetBuffer); } TXNblRelease(Adapter, Nbl, fAtDispatch); } else { // // We can't send this NBL now. Indicate failure. // if (MP_TEST_FLAG(Adapter, fMP_RESET_IN_PROGRESS)) { NET_BUFFER_LIST_STATUS(Nbl) = NDIS_STATUS_RESET_IN_PROGRESS; } else if (MP_TEST_FLAG(Adapter, fMP_ADAPTER_PAUSE_IN_PROGRESS|fMP_ADAPTER_PAUSED)) { NET_BUFFER_LIST_STATUS(Nbl) = NDIS_STATUS_PAUSED; } else if (MP_TEST_FLAG(Adapter, fMP_ADAPTER_LOW_POWER)) { NET_BUFFER_LIST_STATUS(Nbl) = NDIS_STATUS_LOW_POWER_STATE; } else { NET_BUFFER_LIST_STATUS(Nbl) = Status; } NdisMSendNetBufferListsComplete( Adapter->AdapterHandle, Nbl, fAtDispatch ? NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL:0); continue; } } DEBUGP(MP_TRACE, "[%p] %i NBLs processed.\n", Adapter, NumNbls); // // Now actually go send each of the queued NBs. // TXTransmitQueuedSends(Adapter, fAtDispatch); DEBUGP(MP_TRACE, "[%p] <--- MPSendNetBufferLists\n", Adapter); }
VOID TXFlushSendQueue( _In_ PMP_ADAPTER Adapter, _In_ NDIS_STATUS CompleteStatus) /*++ Routine Description: This routine is called by the Halt or Reset handler to fail all the queued up Send NBLs because the device is either gone, being stopped for resource rebalance, or reset. Arguments: Adapter Pointer to our adapter CompleteStatus The status code with which to complete each NBL Return Value: None. --*/ { PTCB Tcb; DEBUGP(MP_TRACE, "[%p] ---> TXFlushSendQueue Status = 0x%08x\n", Adapter, CompleteStatus); // // First, free anything queued in the driver. // while (TRUE) { PLIST_ENTRY pEntry; PNET_BUFFER NetBuffer; PNET_BUFFER_LIST NetBufferList; pEntry = NdisInterlockedRemoveHeadList( &Adapter->SendWaitList, &Adapter->SendWaitListLock); if (!pEntry) { // End of list -- nothing left to free. break; } NetBuffer = NB_FROM_SEND_WAIT_LIST(pEntry); NetBufferList = NBL_FROM_SEND_NB(NetBuffer); DEBUGP(MP_TRACE, "[%p] Dropping Send NB: 0x%p.\n", Adapter, NetBuffer); NET_BUFFER_LIST_STATUS(NetBufferList) = CompleteStatus; TXNblRelease(Adapter, NetBufferList, FALSE); } // // Next, cancel anything queued in the hardware. // while (NULL != (Tcb = TXGetNextTcbToSend(Adapter))) { NET_BUFFER_LIST_STATUS(NBL_FROM_SEND_NB(Tcb->NetBuffer)) = CompleteStatus; ReturnTCB(Adapter, Tcb); } DEBUGP(MP_TRACE, "[%p] <--- TXFlushSendQueue\n", Adapter); }
VOID TXSendComplete( _In_ PMP_ADAPTER Adapter) /*++ Routine Description: This routine completes pending sends for the given adapter. Each busy TCB is popped from the BusyTcbList and its corresponding NB is released. If there was an error sending the frame, the NB's NBL's status is updated. --*/ { BOOLEAN fRescheduleThisDpcAgain = TRUE; ULONG NumFramesSent = 0; DEBUGP(MP_TRACE, "[%p] ---> TXSendComplete.\n", Adapter); for (NumFramesSent = 0; NumFramesSent < NIC_MAX_SENDS_PER_DPC; NumFramesSent++) { ULONG BytesSent; PTCB Tcb = TXGetNextTcbToSend(Adapter); if (!Tcb) { // // There are no more TCBs remaining to send. We're all done. // fRescheduleThisDpcAgain = FALSE; break; } // // Finish the transmit operation. For our hardware, that means the // frame is pushed onto the RecvWaitLists of each other adapter. // BytesSent = HWGetBytesSent(Adapter, Tcb); if (BytesSent == 0) { // // Failed to send the frame. // Adapter->TransmitFailuresOther++; NET_BUFFER_LIST_STATUS(NBL_FROM_SEND_NB(Tcb->NetBuffer)) = NDIS_STATUS_RESOURCES; } else { // // We've finished sending this NB successfully; update the stats. // switch (Tcb->FrameType) { case NDIS_PACKET_TYPE_BROADCAST: Adapter->FramesTxBroadcast++; Adapter->BytesTxBroadcast += BytesSent; break; case NDIS_PACKET_TYPE_MULTICAST: Adapter->FramesTxMulticast++; Adapter->BytesTxMulticast += BytesSent; break; case NDIS_PACKET_TYPE_DIRECTED: default: Adapter->FramesTxDirected++; Adapter->BytesTxDirected += BytesSent; } } // // Now that we've finished using the TCB and its associated NET_BUFFER, // we can release the NET_BUFFER back to the protocol and the TCB back // to the free list. // ReturnTCB(Adapter, Tcb); } TXTransmitQueuedSends(Adapter, TRUE); if (fRescheduleThisDpcAgain) { TXScheduleTheSendComplete(Adapter); } DEBUGP(MP_TRACE, "[%p] <--- TXSendComplete.\n", Adapter); }
VOID TXQueueNetBufferForSend( _In_ PMP_ADAPTER Adapter, _In_ PNET_BUFFER NetBuffer) /*++ Routine Description: This routine inserts the NET_BUFFER into the SendWaitList, then calls TXTransmitQueuedSends to start sending data from the list. We use this indirect queue to send data because the miniport should try to send frames in the order in which the protocol gave them. If we just sent the NET_BUFFER immediately, then it would be out-of-order with any data on the SendWaitList. Runs at IRQL <= DISPATCH_LEVEL Arguments: Adapter Adapter that is transmitting this NB NetBuffer NB to be transfered Return Value: None. --*/ { NDIS_STATUS Status; UCHAR DestAddress[NIC_MACADDR_SIZE]; DEBUGP(MP_TRACE, "[%p] ---> TXQueueNetBufferForSend, NB= 0x%p\n", Adapter, NetBuffer); do { // // First, do a sanity check on the frame data. // Status = HWGetDestinationAddress(NetBuffer, DestAddress); if (Status != NDIS_STATUS_SUCCESS) { NET_BUFFER_LIST_STATUS(NBL_FROM_SEND_NB(NetBuffer)) = NDIS_STATUS_INVALID_DATA; break; } // // Stash away the frame type. We'll use that later, when updating // our send statistics (since we don't have NIC hardware to compute the // send statistics for us). // FRAME_TYPE_FROM_SEND_NB(NetBuffer) = NICGetFrameTypeFromDestination(DestAddress); // // Pin the original NBL with a reference, so it isn't completed until // we're done with its NB. // Status = TXNblReference(Adapter, NBL_FROM_SEND_NB(NetBuffer)); if(Status == NDIS_STATUS_SUCCESS) { // // Insert the NB into the queue. The caller will flush the queue when // it's done adding items to the queue. // NdisInterlockedInsertTailList( &Adapter->SendWaitList, SEND_WAIT_LIST_FROM_NB(NetBuffer), &Adapter->SendWaitListLock); } } while (FALSE); DEBUGP(MP_TRACE, "[%p] <--- TXQueueNetBufferForSend\n", Adapter); }