diff options
Diffstat (limited to '')
-rw-r--r-- | private/ntos/ndis/ndis40/miniport.c | 3180 |
1 files changed, 3180 insertions, 0 deletions
diff --git a/private/ntos/ndis/ndis40/miniport.c b/private/ntos/ndis/ndis40/miniport.c new file mode 100644 index 000000000..859c55be1 --- /dev/null +++ b/private/ntos/ndis/ndis40/miniport.c @@ -0,0 +1,3180 @@ +/*++ + +Copyright (c) 1990-1995 Microsoft Corporation + +Module Name: + + miniport.c + +Abstract: + + NDIS miniport wrapper functions + +Author: + + Sean Selitrennikoff (SeanSe) 05-Oct-93 + Jameel Hyder (JameelH) Re-organization 01-Jun-95 + +Environment: + + Kernel mode, FSD + +Revision History: + +--*/ + +#include <precomp.h> +#pragma hdrstop + + +// +// Define the module number for debug code. +// +#define MODULE_NUMBER MODULE_MINIPORT + +///////////////////////////////////////////////////////////////////// +// +// WORK-ITEM CODE +// +///////////////////////////////////////////////////////////////////// + + +BOOLEAN +NdisIMSwitchToMiniport( + IN NDIS_HANDLE MiniportAdapterHandle, + OUT PNDIS_HANDLE SwitchHandle + ) +/*++ + +Routine Description: + + This routine will attempt to synchronously grab the miniport's (specified + by MiniportAdapterHandle) spin-lock and local lock. If it succeeds + it will return TRUE, otherwise it will return FALSE. + +Arguments: + + MiniportAdapterHandle - Pointer to the NDIS_MINIPORT_BLOCK whose + context we should nail down. + SwitchHandle - Pointer to storage for the current irql. + This is returned to the caller as a handle, + need-to-know basis baby. + +Return Value: + + TRUE if we obtain both locks, FALSE otherwise. + +--*/ +{ + PNDIS_MINIPORT_BLOCK Miniport = (PNDIS_MINIPORT_BLOCK)MiniportAdapterHandle; + BOOLEAN LocalLock; + LONG Thread; + + // + // Did we already acuqire the lock with this thread? + // + Thread = InterlockedExchangeAdd(&Miniport->MiniportThread, 0); + if (CURRENT_THREAD == Thread) + { + // + // We've already acquired the lock... + // + ASSERT(Miniport->LockAcquired); + + *SwitchHandle = (NDIS_HANDLE)-1; + + return(TRUE); + } + + NDIS_ACQUIRE_MINIPORT_SPIN_LOCK(Miniport, (PKIRQL)SwitchHandle); + + LOCK_MINIPORT(Miniport, LocalLock); + if (!LocalLock) + { + NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, (KIRQL)*SwitchHandle); + + return(FALSE); + } + + return(TRUE); +} + +VOID +NdisIMRevertBack( + IN NDIS_HANDLE MiniportAdapterHandle, + IN NDIS_HANDLE SwitchHandle + ) +/*++ + +Routine Description: + + This routine will undo what NdisMLockMiniport did. It will release the + local lock and free the spin lock. + +Arguments: + + MiniportAdapterHandle - Pointer to the NDIS_MINIPORT_BLOCK whose + context we are releasing. + SwitchHandle - This is the original irql from the NdisMLockMiniport + call. + +Return Value: + +--*/ +{ + PNDIS_MINIPORT_BLOCK Miniport = (PNDIS_MINIPORT_BLOCK)MiniportAdapterHandle; + + // + // Before we unlock the miniport's context we need to pick up any + // stray workitems for this miniport that may have been generated by + // the caller. + // + +#if _SEND_PRIORITY + // + // If we are not reseting and not halting then give priority to sends + // at this point. + // + if (!MINIPORT_TEST_FLAG(Miniport, fMINIPORT_RESET_REQUESTED) && + !MINIPORT_TEST_FLAG(Miniport, fMINIPORT_RESET_IN_PROGRESS) && + !MINIPORT_TEST_FLAG(Miniport, fMINIPORT_HALTING)) + { + if (MINIPORT_TEST_FLAG(Miniport, fMINIPORT_FULL_DUPLEX)) + { + ndisMProcessDeferredFullDuplexPrioritySends(Miniport); + } + else + { + ndisMProcessDeferredPrioritySends(Miniport); + } + } + else +#endif + { + NDISM_PROCESS_DEFERRED(Miniport); + } + + if ((NDIS_HANDLE)-1 == SwitchHandle) + { + return; + } + + UNLOCK_MINIPORT(Miniport, TRUE); + + NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, (KIRQL)SwitchHandle); +} + +NDIS_STATUS +NdisIMQueueMiniportCallback( + IN NDIS_HANDLE MiniportAdapterHandle, + IN W_MINIPORT_CALLBACK CallbackRoutine, + IN PVOID CallbackContext + ) +/*++ + +Routine Description: + + This routine will attempt to acquire the specified MiniportAdapterHandle's + miniport lock and local lock and call the callback routine with the context + information. If it cannot do so then it will queue a workitem to do it + later. + +Arguments: + + MiniportAdapterHandle - PNDIS_MINIPORT_BLOCK of the miniport whose + context we are attempting to acquire. + CallbackRoutine - Pointer to the routine that we are to call. + CallbackContext - Context information for the callback routine. + +Return Value: + + NDIS_STATUS_SUCCESS - If we were able to do this synchronously. + NDIS_STATUS_PENDING - If it will be called at a later time. + NDIS_STATUS_FAILURE - If the work item could not be queue'd. + +--*/ +{ + PNDIS_MINIPORT_BLOCK Miniport = (PNDIS_MINIPORT_BLOCK)MiniportAdapterHandle; + BOOLEAN LocalLock; + KIRQL OldIrql; + LONG Thread; + BOOLEAN LockAcquired; + + // + // Did we already acuqire the lock with this thread? + // + Thread = InterlockedExchangeAdd(&Miniport->MiniportThread, 0); + if (CURRENT_THREAD == Thread) + { + // + // We've already acquired the lock... + // + ASSERT(Miniport->LockAcquired); + LockAcquired = FALSE; + LocalLock = TRUE; + } + else + { + NDIS_ACQUIRE_MINIPORT_SPIN_LOCK(Miniport, &OldIrql); + LOCK_MINIPORT(Miniport, LocalLock); + LockAcquired = TRUE; + } + + if (!LocalLock) + { + NDIS_STATUS Status; + + // + // Queue the work item to do this later. + // + Status = NDISM_QUEUE_NEW_WORK_ITEM( + Miniport, + NdisWorkItemMiniportCallback, + CallbackRoutine, + CallbackContext); + + if (LockAcquired) + { + NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql); + } + + return((NDIS_STATUS_SUCCESS == Status) ? NDIS_STATUS_PENDING : NDIS_STATUS_FAILURE); + } + + // + // Call the callback routine. + // + (*CallbackRoutine)(Miniport->MiniportAdapterContext, CallbackContext); + +#if _SEND_PRIORITY + // + // If we are not reseting and not halting then give priority to sends + // at this point. + // + if (!MINIPORT_TEST_FLAG(Miniport, fMINIPORT_RESET_REQUESTED) && + !MINIPORT_TEST_FLAG(Miniport, fMINIPORT_RESET_IN_PROGRESS) && + !MINIPORT_TEST_FLAG(Miniport, fMINIPORT_HALTING)) + { + if (MINIPORT_TEST_FLAG(Miniport, fMINIPORT_FULL_DUPLEX)) + { + ndisMProcessDeferredFullDuplexPrioritySends(Miniport); + } + else + { + ndisMProcessDeferredPrioritySends(Miniport); + } + } + else +#endif + { + NDISM_PROCESS_DEFERRED(Miniport); + } + + if (LockAcquired) + { + UNLOCK_MINIPORT(Miniport, LocalLock); + NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql); + } + + return(NDIS_STATUS_SUCCESS); +} + +///////////////////////////////////////////////////////////////////// +// +// WORKITEM CODE FOR INTERMEDIATE MINIPORTS +// +///////////////////////////////////////////////////////////////////// + + +NDIS_STATUS +FASTCALL +ndisIMQueueWorkItem( + IN PNDIS_MINIPORT_BLOCK Miniport, + IN NDIS_WORK_ITEM_TYPE WorkItemType, + IN PVOID WorkItemContext1, + IN PVOID WorkItemContext2 + ) +/*++ + +Routine Description: + + Queue's the specific workitem on the work queue. + + NOTE!!!!! + + This routine assumes that you have the correct lock acquire to + touch the SingleWorkItem that you requested to be queued. + + NdisWorkItemSend - SendLock + NdisWorkItemResetRequested - Miniport lock + NdisWorkItemRequest - Miniport lock + NdisWorkItemDpc - Miniport lock + NdisWorkItemHalt - Miniport lock + +Arguments: + + Miniport - Pointer to the miniport block. + WorkItemType - Type of work item to queue. + +Return Value: + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; +#ifdef NDIS_NT + + NDISM_QUEUE_WORK_ITEM_FULL_DUPLEX_MACRO( + Miniport, + WorkItemType, + WorkItemContext1, + WorkItemContext2, + &Status); + + // + // If the status is not accepted then it means that there is already + // a workitem of this type queued and there should be a timer fired + // to process it. + // + if (Status != NDIS_STATUS_NOT_ACCEPTED) + { + NDISM_DEFER_PROCESS_DEFERRED(Miniport); + } + +#endif + return(Status); +} + + +NDIS_STATUS +FASTCALL +ndisIMQueueNewWorkItem( + IN PNDIS_MINIPORT_BLOCK Miniport, + IN NDIS_WORK_ITEM_TYPE WorkItemType, + IN PVOID WorkItemContext1, + IN PVOID WorkItemContext2 + ) +/*++ + +Routine Description: + + This routine will queue a workitem in the work queue even if there + are already work items queue for it. + + THE WORK QUEUE LOCK MUST BE HELD WHEN CALLING THIS ROUTINE!!!! + +Arguments: + + Miniport - Miniport block to queue the workitem to. + WorkItem - Workitem to place on the queue. + +Return Value: + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; +#ifdef NDIS_NT + + NDISM_QUEUE_NEW_WORK_ITEM_FULL_DUPLEX_MACRO( + Miniport, + WorkItemType, + WorkItemContext1, + WorkItemContext2, + &Status); + + // + // Since we can have any number of these work items queued we + // need to be sure that they all get processed. + // + NDISM_DEFER_PROCESS_DEFERRED(Miniport); + +#endif + return(Status); +} + + +///////////////////////////////////////////////////////////////////// +// +// WORKITEM CODE FOR HARDWARE MINIPORTS +// +///////////////////////////////////////////////////////////////////// + + +VOID +FASTCALL +ndisMDeQueueWorkItem( + IN PNDIS_MINIPORT_BLOCK Miniport, + IN NDIS_WORK_ITEM_TYPE WorkItemType, + OUT PVOID *WorkItemContext1, + OUT PVOID *WorkItemContext2 + ) +/*++ + +Routine Description: + + This routine will dequeue a workitem of the given type and return any context + information that is associated with it. + + NOTE: + + FOR FULL-DUPLEX USAGE THE WORK LOCK MUST BE ACQUIRED BEFORE THIS ROUTINE IS CALLED !!!! + +Arguments: + + Miniport - Pointer to the miniport block. + WorkItemType - Type of workitem to dequeue. + WorkItemContext1 - Pointer to storage space for first piece of context information. + WorkItemContext2 - Pointer to storage space for second piece of context information. + +Return Value: + + None. + +--*/ +{ + PSINGLE_LIST_ENTRY Link; + PNDIS_MINIPORT_WORK_ITEM WorkItem; + + // + // Grab the first workitem of the given type. + // + Link = PopEntryList(&Miniport->WorkQueue[WorkItemType]); + if (Link != NULL) + { + // + // Get a pointer to the context information. + // + WorkItem = CONTAINING_RECORD(Link, NDIS_MINIPORT_WORK_ITEM, Link); + + if (WorkItemContext1 != NULL) + { + *WorkItemContext1 = WorkItem->WorkItemContext1; + } + + if (WorkItemContext2 != NULL) + { + *WorkItemContext2 = WorkItem->WorkItemContext2; + } + + switch (WorkItemType) + { + case NdisWorkItemMiniportCallback: + case NdisWorkItemTimer: + case NdisWorkItemPendingOpen: + + ASSERT(*WorkItemContext1 != NULL); + PushEntryList(&Miniport->WorkItemFreeQueue, Link); + break; + + case NdisWorkItemResetInProgress: + + PushEntryList(&Miniport->SingleWorkItems[NdisWorkItemResetRequested], Link); + break; + + case NdisWorkItemResetRequested: + + WorkItem->WorkItemType = NdisWorkItemResetInProgress; + PushEntryList(&Miniport->WorkQueue[NdisWorkItemResetInProgress], Link); + break; + + default: + PushEntryList(&Miniport->SingleWorkItems[WorkItemType], Link); + break; + } + } +} + +NDIS_STATUS +FASTCALL +ndisMQueueWorkItem( + IN PNDIS_MINIPORT_BLOCK Miniport, + IN NDIS_WORK_ITEM_TYPE WorkItemType, + IN PVOID WorkItemContext1, + IN PVOID WorkItemContext2 + ) +/*++ + +Routine Description: + +Arguments: + +Return Value: + +--*/ +{ + NDIS_STATUS Status; + + NDISM_QUEUE_WORK_ITEM_MACRO( + Miniport, + WorkItemType, + WorkItemContext1, + WorkItemContext2, + &Status); + + return(Status); +} + +NDIS_STATUS +FASTCALL +ndisMQueueWorkItemFullDuplex( + IN PNDIS_MINIPORT_BLOCK Miniport, + IN NDIS_WORK_ITEM_TYPE WorkItemType, + IN PVOID WorkItemContext1, + IN PVOID WorkItemContext2 + ) +/*++ + +Routine Description: + + Queue's the specific workitem on the work queue. + + NOTE!!!!! + + This routine assumes that you have the correct lock acquire to + touch the SingleWorkItem that you requested to be queued. + + NdisWorkItemSend - SendLock + NdisWorkItemResetRequested - Miniport lock + NdisWorkItemRequest - Miniport lock + NdisWorkItemDpc - Miniport lock + NdisWorkItemHalt - Miniport lock + +Arguments: + + Miniport - Pointer to the miniport block. + WorkItemType - Type of work item to queue. + +Return Value: + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; +#ifdef NDIS_NT + + NDISM_QUEUE_WORK_ITEM_FULL_DUPLEX_MACRO( + Miniport, + WorkItemType, + WorkItemContext1, + WorkItemContext2, + &Status); + +#endif + return(Status); +} + + +NDIS_STATUS +FASTCALL +ndisMQueueNewWorkItem( + IN PNDIS_MINIPORT_BLOCK Miniport, + IN NDIS_WORK_ITEM_TYPE WorkItemType, + IN PVOID WorkItemContext1, + IN PVOID WorkItemContext2 + ) +/*++ + +Routine Description: + + This routine will queue a workitem in the work queue even if there + are already work items queue for it. + + THE WORK QUEUE LOCK MUST BE HELD WHEN CALLING THIS ROUTINE!!!! + +Arguments: + + Miniport - Miniport block to queue the workitem to. + WorkItem - Workitem to place on the queue. + +Return Value: + +--*/ +{ + NDIS_STATUS Status; + + NDISM_QUEUE_NEW_WORK_ITEM_MACRO( + Miniport, + WorkItemType, + WorkItemContext1, + WorkItemContext2, + &Status); + + return(Status); +} + +NDIS_STATUS +FASTCALL +ndisMQueueNewWorkItemFullDuplex( + IN PNDIS_MINIPORT_BLOCK Miniport, + IN NDIS_WORK_ITEM_TYPE WorkItemType, + IN PVOID WorkItemContext1, + IN PVOID WorkItemContext2 + ) +/*++ + +Routine Description: + + This routine will queue a workitem in the work queue even if there + are already work items queue for it. + + THE WORK QUEUE LOCK MUST BE HELD WHEN CALLING THIS ROUTINE!!!! + +Arguments: + + Miniport - Miniport block to queue the workitem to. + WorkItem - Workitem to place on the queue. + +Return Value: + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; +#ifdef NDIS_NT + + NDISM_QUEUE_NEW_WORK_ITEM_FULL_DUPLEX_MACRO( + Miniport, + WorkItemType, + WorkItemContext1, + WorkItemContext2, + &Status); + +#endif + return(Status); +} + + +///////////////////////////////////////////////////////////////////// +// +// DEFERRED PROCESSING CODE +// +///////////////////////////////////////////////////////////////////// + + +#if _SEND_PRIORITY + +VOID +FASTCALL +ndisMProcessDeferredFullDuplexPrioritySends( + PNDIS_MINIPORT_BLOCK Miniport + ) + +/*++ + +Routine Description: + + Processes all outstanding operations. + + CALLED WITH THE LOCK HELD!! + +Arguments: + + Miniport - Miniport to send to. + +Return Value: + + None. + +--*/ + +{ +#ifdef NDIS_NT + NDIS_STATUS Status; + BOOLEAN ProcessWorkItems; + PNDIS_MINIPORT_WORK_ITEM WorkItem; + PSINGLE_LIST_ENTRY Link; + NDIS_WORK_ITEM_TYPE WorkItemType; + BOOLEAN AddressingReset; + PKDPC Dpc; + PMINIPORT_PENDING_OPEN PendingOpen; + + ASSERT(MINIPORT_AT_DPC_LEVEL); + + DBGPRINT(DBG_COMP_WORK_ITEM, DBG_LEVEL_INFO, + ("==>ndisMProcessDeferredFullDuplexPrioritySends\n")); + + ACQUIRE_SPIN_LOCK_DPC(&Miniport->WorkLock); + + // + // Are there any sends to process? + // + if (Miniport->WorkQueue[NdisWorkItemSend].Next != NULL) + { + BOOLEAN fMoreSends; + + // + // Process the sends. + // + RELEASE_SPIN_LOCK_DPC(&Miniport->WorkLock); + NDIS_RELEASE_MINIPORT_SPIN_LOCK_DPC(Miniport); + + NDISM_START_SENDS(Miniport); + + NDIS_ACQUIRE_MINIPORT_SPIN_LOCK_DPC(Miniport); + ACQUIRE_SPIN_LOCK_DPC(&Miniport->WorkLock); + + NDISM_DEQUEUE_WORK_ITEM(Miniport, NdisWorkItemSend, NULL, NULL); + } + + RELEASE_SPIN_LOCK_DPC(&Miniport->WorkLock); + + // + // Are there any loopback packets to indicate? + // + if ((Miniport->WorkQueue[NdisWorkItemMiniportCallback].Next != NULL) || + (Miniport->WorkQueue[NdisWorkItemSendLoopback].Next != NULL) || + (Miniport->WorkQueue[NdisWorkItemDpc].Next != NULL) || + (Miniport->WorkQueue[NdisWorkItemTimer].Next != NULL) || + (Miniport->WorkQueue[NdisWorkItemPendingOpen].Next != NULL) || + (Miniport->WorkQueue[NdisWorkItemRequest].Next != NULL)) + { + + NDISM_PROCESS_DEFERRED(Miniport); + } + + DBGPRINT(DBG_COMP_WORK_ITEM, DBG_LEVEL_INFO, + ("<==ndisMProcessDeferredFullDuplexPrioritySends\n")); +#endif +} + +#endif + +VOID +FASTCALL +ndisMProcessDeferredFullDuplex( + PNDIS_MINIPORT_BLOCK Miniport + ) + +/*++ + +Routine Description: + + Processes all outstanding operations. + + CALLED WITH THE LOCK HELD!! + +Arguments: + + Miniport - Miniport to send to. + +Return Value: + + None. + +--*/ + +{ +#ifdef NDIS_NT + NDIS_STATUS Status; + BOOLEAN ProcessWorkItems; + PNDIS_MINIPORT_WORK_ITEM WorkItem; + PSINGLE_LIST_ENTRY Link; + NDIS_WORK_ITEM_TYPE WorkItemType; + BOOLEAN AddressingReset; + PKDPC Dpc; + PMINIPORT_PENDING_OPEN PendingOpen; + + ASSERT(MINIPORT_AT_DPC_LEVEL); + + DBGPRINT(DBG_COMP_WORK_ITEM, DBG_LEVEL_INFO, + ("==>ndisMProcessDeferredFullDuplex\n")); + + + ACQUIRE_SPIN_LOCK_DPC(&Miniport->WorkLock); + + // + // DO NOT CHANGE THE ORDER THAT THE WORKITEMS ARE PROCESSED!!!!! + // + do + { + ProcessWorkItems = FALSE; + + // + // Is there a reset currently in progress? + // + if (Miniport->WorkQueue[NdisWorkItemResetInProgress].Next != NULL) + { + // + // The only thing that can run during a reset in progress + // are the timers. + // + if (Miniport->WorkQueue[NdisWorkItemTimer].Next != NULL) + { + DBGPRINT(DBG_COMP_WORK_ITEM, DBG_LEVEL_INFO, + ("Queuing dpc timer\n")); + + // + // Grab the timer workitem. + // + NDISM_DEQUEUE_WORK_ITEM(Miniport, NdisWorkItemTimer, &Dpc, NULL); + + // + // Queue the timer's dpc to fire. + // + QUEUE_DPC(Dpc); + } + else if (Miniport->WorkQueue[NdisWorkItemRequest].Next != NULL) + { + // + // We have requests to process that set up the packet + // filters. + // + RELEASE_SPIN_LOCK_DPC(&Miniport->WorkLock); + ndisMDoRequests(Miniport); + ACQUIRE_SPIN_LOCK_DPC(&Miniport->WorkLock); + + NDISM_DEQUEUE_WORK_ITEM(Miniport, NdisWorkItemRequest, NULL, NULL); + } + + break; + } + + // + // If the adapter is halting then get out of here. + // + if (Miniport->WorkQueue[NdisWorkItemHalt].Next != NULL) + { + DBGPRINT(DBG_COMP_WORK_ITEM, DBG_LEVEL_INFO, + ("Miniport is halting\n")); + + break; + } + + // + // If an intermediate miniport wants a call back do it now... + // + if (Miniport->WorkQueue[NdisWorkItemMiniportCallback].Next != NULL) + { + W_MINIPORT_CALLBACK CallbackRoutine; + PVOID CallbackContext; + + // + // Get the callback routine and the context information for it. + // + NDISM_DEQUEUE_WORK_ITEM( + Miniport, + NdisWorkItemMiniportCallback, + (PVOID *)&CallbackRoutine, + &CallbackContext); + + // + // Call the intermediate drivers callback routine. + // + RELEASE_SPIN_LOCK_DPC(&Miniport->WorkLock); + + (*CallbackRoutine)(Miniport->MiniportAdapterContext, CallbackContext); + + ACQUIRE_SPIN_LOCK_DPC(&Miniport->WorkLock); + + ProcessWorkItems = TRUE; + } + + // + // Does a deferred dpc need to run? + // + if (Miniport->WorkQueue[NdisWorkItemDpc].Next != NULL) + { + DBGPRINT(DBG_COMP_WORK_ITEM, DBG_LEVEL_INFO, + ("Queuing DPC\n")); + + // + // Queue the dpc to run. + // + NDISM_DEQUEUE_WORK_ITEM(Miniport, NdisWorkItemDpc, &Dpc, NULL); + QUEUE_DPC(Dpc); + + break; + } + + // + // Is there a timer to fire? + // + if (Miniport->WorkQueue[NdisWorkItemTimer].Next != NULL) + { + DBGPRINT(DBG_COMP_WORK_ITEM, DBG_LEVEL_INFO, + ("Queuing dpc timer\n")); + + // + // Queue the timer's dpc to fire. + // + NDISM_DEQUEUE_WORK_ITEM(Miniport, NdisWorkItemTimer, &Dpc, NULL); + QUEUE_DPC(Dpc); + + break; + } + + // + // Finish any pending opens? + // + if (Miniport->WorkQueue[NdisWorkItemPendingOpen].Next != NULL) + { + // + // Grab the pending open block. + // + NDISM_DEQUEUE_WORK_ITEM(Miniport, NdisWorkItemPendingOpen, &PendingOpen, NULL); + + RELEASE_SPIN_LOCK_DPC(&Miniport->WorkLock); + + // + // Finish the pending open. + // + Status = ndisMFinishPendingOpen(PendingOpen); + + ACQUIRE_SPIN_LOCK_DPC(&Miniport->WorkLock); + + // + // Did it pend again? + // + if (NDIS_STATUS_PENDING == Status) + { + NDISM_QUEUE_NEW_WORK_ITEM( + Miniport, + NdisWorkItemPendingOpen, + PendingOpen, + NULL); + } + + + // + // Process more work items. + // + ProcessWorkItems = TRUE; + } + + // + // Was there a reset requested? + // + if (Miniport->WorkQueue[NdisWorkItemResetRequested].Next != NULL) + { + DBGPRINT(DBG_COMP_WORK_ITEM, DBG_LEVEL_INFO, + ("Reset requested\n")); + + RELEASE_SPIN_LOCK_DPC(&Miniport->WorkLock); + + // + // We need to release the work item lock to + // indicate the status to the bindings + // and to call down to the miniport driver. + // + Status = ndisMProcessResetRequested(Miniport, + &AddressingReset); + if (NDIS_STATUS_PENDING == Status) + { + DBGPRINT(DBG_COMP_WORK_ITEM, DBG_LEVEL_INFO, + ("Reset is pending\n")); + + ACQUIRE_SPIN_LOCK_DPC(&Miniport->WorkLock); + + // + // Do we need to run a dpc? + // + if (Miniport->WorkQueue[NdisWorkItemDpc].Next != NULL) + { + // + // Queue the dpc to run. + // + NDISM_DEQUEUE_WORK_ITEM(Miniport, NdisWorkItemDpc, &Dpc, NULL); + QUEUE_DPC(Dpc); + } + + // + // The reset is still in progress so we need to stop + // processing workitems and wait for the completion. + // + break; + } + else + { + // + // Do step1 of the reset complete. + // + ndisMResetCompleteCommonStep1(Miniport, + Status, + AddressingReset); + if (!AddressingReset || (Status != NDIS_STATUS_SUCCESS)) + { + // + // If there is no addressing reset to be done or + // the reset failed in some way then we tell the + // bindings now. + // + ndisMResetCompleteCommonStep2(Miniport); + } + + ACQUIRE_SPIN_LOCK_DPC(&Miniport->WorkLock); + + continue; + } + } + + // + // Process any requests? + // + if (Miniport->WorkQueue[NdisWorkItemRequest].Next != NULL) + { + // + // Process the requests. + // + RELEASE_SPIN_LOCK_DPC(&Miniport->WorkLock); + ndisMDoRequests(Miniport); + ACQUIRE_SPIN_LOCK_DPC(&Miniport->WorkLock); + + NDISM_DEQUEUE_WORK_ITEM(Miniport, NdisWorkItemRequest, NULL, NULL); + + ProcessWorkItems = TRUE; + } + + // + // Are there any loopback packets to indicate? + // + if (Miniport->WorkQueue[NdisWorkItemSendLoopback].Next != NULL) + { + BOOLEAN fMoreLoopback; + + // + // Process the loopback packets. + // + RELEASE_SPIN_LOCK_DPC(&Miniport->WorkLock); + fMoreLoopback = ndisMIndicateLoopback(Miniport); + ACQUIRE_SPIN_LOCK_DPC(&Miniport->WorkLock); + + if (!fMoreLoopback) + { + NDISM_DEQUEUE_WORK_ITEM(Miniport, NdisWorkItemSendLoopback, NULL, NULL); + } + + ProcessWorkItems = TRUE; + } + + // + // Are there any sends to process? + // + if (Miniport->WorkQueue[NdisWorkItemSend].Next != NULL) + { + BOOLEAN fMoreSends; + + // + // Process the sends. + // + RELEASE_SPIN_LOCK_DPC(&Miniport->WorkLock); + NDIS_RELEASE_MINIPORT_SPIN_LOCK_DPC(Miniport); + + NDISM_START_SENDS(Miniport); + + NDIS_ACQUIRE_MINIPORT_SPIN_LOCK_DPC(Miniport); + ACQUIRE_SPIN_LOCK_DPC(&Miniport->WorkLock); + + NDISM_DEQUEUE_WORK_ITEM(Miniport, NdisWorkItemSend, NULL, NULL); + + ProcessWorkItems = TRUE; + } + } while (ProcessWorkItems); + + RELEASE_SPIN_LOCK_DPC(&Miniport->WorkLock); + + MINIPORT_CLEAR_FLAG(Miniport, fMINIPORT_PROCESS_DEFERRED); + + DBGPRINT(DBG_COMP_WORK_ITEM, DBG_LEVEL_INFO, + ("<==ndisMProcessDeferredFullDuplex\n")); +#endif +} + +#if _SEND_PRIORITY + +VOID +FASTCALL +ndisMProcessDeferredPrioritySends( + PNDIS_MINIPORT_BLOCK Miniport + ) + +/*++ + +Routine Description: + + Processes all outstanding operations. + + CALLED WITH THE LOCK HELD!! + +Arguments: + + Miniport - Miniport to send to. + +Return Value: + + None. + +--*/ + +{ + ASSERT(MINIPORT_AT_DPC_LEVEL); + + DBGPRINT(DBG_COMP_WORK_ITEM, DBG_LEVEL_INFO, + ("==>ndisMProcessDeferredPrioritySends\n")); + + // + // Are there any sends to process? + // + if (Miniport->WorkQueue[NdisWorkItemSend].Next != NULL) + { + // + // Process the sends. + // + NDISM_START_SENDS(Miniport); + + NDISM_DEQUEUE_WORK_ITEM(Miniport, NdisWorkItemSend, NULL, NULL); + } + + // + // Are there any loopback packets to indicate? + // + if ((Miniport->WorkQueue[NdisWorkItemMiniportCallback].Next != NULL) || + (Miniport->WorkQueue[NdisWorkItemSendLoopback].Next != NULL) || + (Miniport->WorkQueue[NdisWorkItemDpc].Next != NULL) || + (Miniport->WorkQueue[NdisWorkItemTimer].Next != NULL) || + (Miniport->WorkQueue[NdisWorkItemPendingOpen].Next != NULL) || + (Miniport->WorkQueue[NdisWorkItemRequest].Next != NULL)) + { + NDISM_PROCESS_DEFERRED(Miniport); + } + + DBGPRINT(DBG_COMP_WORK_ITEM, DBG_LEVEL_INFO, + ("<==ndisMProcessDeferredPrioritySends\n")); +} + +#endif + +VOID +FASTCALL +ndisMProcessDeferred( + PNDIS_MINIPORT_BLOCK Miniport + ) + +/*++ + +Routine Description: + + Processes all outstanding operations. + + CALLED WITH THE LOCK HELD!! + +Arguments: + + Miniport - Miniport to send to. + +Return Value: + + None. + +--*/ + +{ + NDIS_STATUS Status; + BOOLEAN ProcessWorkItems; + PSINGLE_LIST_ENTRY Link; + NDIS_WORK_ITEM_TYPE WorkItemType; + BOOLEAN AddressingReset; + PKDPC Dpc; + PNDIS_MINIPORT_WORK_ITEM WorkItem; + PMINIPORT_PENDING_OPEN PendingOpen; + + ASSERT(MINIPORT_AT_DPC_LEVEL); + + DBGPRINT(DBG_COMP_WORK_ITEM, DBG_LEVEL_INFO, + ("==>ndisMProcessDeferred\n")); + + // + // DO NOT CHANGE THE ORDER THAT THE WORKITEMS ARE PROCESSED!!!!! + // + do + { + ProcessWorkItems = FALSE; + + // + // Is there a reset currently in progress? + // + if (Miniport->WorkQueue[NdisWorkItemResetInProgress].Next != NULL) + { + // + // The only thing that can run during a reset in progress + // are the timers. + // + if (Miniport->WorkQueue[NdisWorkItemTimer].Next != NULL) + { + DBGPRINT(DBG_COMP_WORK_ITEM, DBG_LEVEL_INFO, + ("Queuing dpc timer\n")); + + // + // Grab the timer workitem. + // + NDISM_DEQUEUE_WORK_ITEM(Miniport, NdisWorkItemTimer, &Dpc, NULL); + + // + // Queue the timer's dpc to fire. + // + QUEUE_DPC(Dpc); + } + else if (Miniport->WorkQueue[NdisWorkItemRequest].Next != NULL) + { + // + // We have requests to process that set up the packet + // filters. + // + ndisMDoRequests(Miniport); + + NDISM_DEQUEUE_WORK_ITEM(Miniport, NdisWorkItemRequest, NULL, NULL); + } + + break; + } + + // + // If the adapter is halting then get out of here. + // + if (Miniport->WorkQueue[NdisWorkItemHalt].Next != NULL) + { + DBGPRINT(DBG_COMP_WORK_ITEM, DBG_LEVEL_INFO, + ("Miniport is halting\n")); + + break; + } + + // + // If an intermediate miniport wants a call back do it now... + // + if (Miniport->WorkQueue[NdisWorkItemMiniportCallback].Next != NULL) + { + W_MINIPORT_CALLBACK CallbackRoutine; + PVOID CallbackContext; + + // + // Get the callback routine and the context information for it. + // + NDISM_DEQUEUE_WORK_ITEM( + Miniport, + NdisWorkItemMiniportCallback, + (PVOID *)&CallbackRoutine, + &CallbackContext); + + // + // Call the intermediate drivers callback routine. + // + (*CallbackRoutine)(Miniport->MiniportAdapterContext, CallbackContext); + + ProcessWorkItems = TRUE; + } + + // + // Does a deferred dpc need to run? + // + if (Miniport->WorkQueue[NdisWorkItemDpc].Next != NULL) + { + DBGPRINT(DBG_COMP_WORK_ITEM, DBG_LEVEL_INFO, + ("Queuing DPC\n")); + + // + // Queue the dpc to run. + // + NDISM_DEQUEUE_WORK_ITEM(Miniport, NdisWorkItemDpc, &Dpc, NULL); + QUEUE_DPC(Dpc); + + break; + } + + // + // Is there a timer to fire? + // + if (Miniport->WorkQueue[NdisWorkItemTimer].Next != NULL) + { + DBGPRINT(DBG_COMP_WORK_ITEM, DBG_LEVEL_INFO, + ("Queuing dpc timer\n")); + + // + // Queue the timer's dpc to fire. + // + NDISM_DEQUEUE_WORK_ITEM(Miniport, NdisWorkItemTimer, &Dpc, NULL); + QUEUE_DPC(Dpc); + + break; + } + + // + // Finish any pending opens? + // + if (Miniport->WorkQueue[NdisWorkItemPendingOpen].Next != NULL) + { + NDISM_DEQUEUE_WORK_ITEM(Miniport, NdisWorkItemPendingOpen, &PendingOpen, NULL); + + // + // Finish the pending open. + // + Status = ndisMFinishPendingOpen(PendingOpen); + if (NDIS_STATUS_PENDING == Status) + { + NDISM_QUEUE_NEW_WORK_ITEM( + Miniport, + NdisWorkItemPendingOpen, + PendingOpen, + NULL); + } + + // + // Process more work items. + // + ProcessWorkItems = TRUE; + } + + // + // Was there a reset requested? + // + if (Miniport->WorkQueue[NdisWorkItemResetRequested].Next != NULL) + { + DBGPRINT(DBG_COMP_WORK_ITEM, DBG_LEVEL_INFO, + ("Reset requested\n")); + // + // We need to release the work item lock to + // indicate the status to the bindings + // and to call down to the miniport driver. + // + Status = ndisMProcessResetRequested(Miniport, + &AddressingReset); + if (NDIS_STATUS_PENDING == Status) + { + DBGPRINT(DBG_COMP_WORK_ITEM, DBG_LEVEL_INFO, + ("Reset is pending\n")); + // + // Do we need to run a dpc? + // + if (Miniport->WorkQueue[NdisWorkItemDpc].Next != NULL) + { + // + // Dequeue the dpc. + // + NDISM_DEQUEUE_WORK_ITEM(Miniport, NdisWorkItemDpc, &Dpc, NULL); + + // + // Queue the dpc to run. + // + QUEUE_DPC(Dpc); + } + + // + // The reset is still in progress so we need to stop + // processing workitems and wait for the completion. + // + break; + } + else + { + // + // Do step1 of the reset complete. + // + ndisMResetCompleteCommonStep1(Miniport, + Status, + AddressingReset); + if (!AddressingReset || (Status != NDIS_STATUS_SUCCESS)) + { + // + // If there is no addressing reset to be done or + // the reset failed in some way then we tell the + // bindings now. + // + ndisMResetCompleteCommonStep2(Miniport); + } + else + { + // + // We MUST complete the filter requests within + // the reset in progress workitem. Mainly because + // we don't want to do any sends at this time. + // + ProcessWorkItems = TRUE; + + continue; + } + } + } + + // + // Process any requests? + // + if (Miniport->WorkQueue[NdisWorkItemRequest].Next != NULL) + { + // + // Process the requests. + // + ndisMDoRequests(Miniport); + + NDISM_DEQUEUE_WORK_ITEM(Miniport, NdisWorkItemRequest, NULL, NULL); + + ProcessWorkItems = TRUE; + } + + // + // Are there any loopback packets to indicate? + // + if (Miniport->WorkQueue[NdisWorkItemSendLoopback].Next != NULL) + { + BOOLEAN fMoreLoopback; + + // + // Process the loopback packets. + // + fMoreLoopback = ndisMIndicateLoopback(Miniport); + + if (!fMoreLoopback) + { + NDISM_DEQUEUE_WORK_ITEM(Miniport, NdisWorkItemSendLoopback, NULL, NULL); + } + + ProcessWorkItems = TRUE; + } + + // + // Are there any sends to process? + // + if (Miniport->WorkQueue[NdisWorkItemSend].Next != NULL) + { + // + // Process the sends. + // + NDISM_START_SENDS(Miniport); + + NDISM_DEQUEUE_WORK_ITEM(Miniport, NdisWorkItemSend, NULL, NULL); + + ProcessWorkItems = TRUE; + } + } while (ProcessWorkItems); + + MINIPORT_CLEAR_FLAG(Miniport, fMINIPORT_PROCESS_DEFERRED); + + DBGPRINT(DBG_COMP_WORK_ITEM, DBG_LEVEL_INFO, + ("<==ndisMProcessDeferred\n")); +} + +///////////////////////////////////////////////////////////////////// +// +// INDICATE CODE +// +///////////////////////////////////////////////////////////////////// + +VOID +NdisMIndicateStatus( + IN NDIS_HANDLE MiniportAdapterHandle, + IN NDIS_STATUS GeneralStatus, + IN PVOID StatusBuffer, + IN UINT StatusBufferSize + ) +/*++ + +Routine Description: + + This function indicates a new status of the media/mini-port. + +Arguments: + + MiniportAdapterHandle - points to the adapter block. + + GeneralStatus - The status to indicate. + + StatusBuffer - Additional information. + + StatusBufferSize - Length of the buffer. + +Return Value: + + None. + + +--*/ +{ + PNDIS_MINIPORT_BLOCK Miniport = (PNDIS_MINIPORT_BLOCK)MiniportAdapterHandle; + PNDIS_M_OPEN_BLOCK Open; + NDIS_STATUS Status; + + ASSERT(MINIPORT_AT_DPC_LEVEL); + ASSERT(MINIPORT_LOCK_ACQUIRED(Miniport)); + + if ((GeneralStatus == NDIS_STATUS_RING_STATUS) && + (StatusBufferSize == sizeof(NDIS_STATUS))) + { + + Status = *((PNDIS_STATUS)StatusBuffer); + + if (Status & (NDIS_RING_LOBE_WIRE_FAULT | + NDIS_RING_HARD_ERROR | + NDIS_RING_SIGNAL_LOSS)) + { + Miniport->TrResetRing = NDIS_MINIPORT_TR_RESET_TIMEOUT; + } + } + + Open = Miniport->OpenQueue; + + while (Open != NULL) + { + // + // Call Protocol to indicate status + // + + NDIS_RELEASE_MINIPORT_SPIN_LOCK_DPC(Miniport); + + (Open->ProtocolHandle->ProtocolCharacteristics.StatusHandler) ( + Open->ProtocolBindingContext, + GeneralStatus, + StatusBuffer, + StatusBufferSize + ); + + NDIS_ACQUIRE_MINIPORT_SPIN_LOCK_DPC(Miniport); + + Open = Open->MiniportNextOpen; + } +} + +VOID +NdisMIndicateStatusComplete( + IN NDIS_HANDLE MiniportAdapterHandle + ) +/*++ + +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 + // + + NDIS_RELEASE_MINIPORT_SPIN_LOCK_DPC(Miniport); + + (Open->ProtocolHandle->ProtocolCharacteristics.StatusCompleteHandler) ( + Open->ProtocolBindingContext); + + NDIS_ACQUIRE_MINIPORT_SPIN_LOCK_DPC(Miniport); + + Open = Open->MiniportNextOpen; + } +} + + +VOID +NdisMWanIndicateReceive( + OUT PNDIS_STATUS Status, + IN NDIS_HANDLE MiniportAdapterHandle, + IN NDIS_HANDLE NdisLinkContext, + IN PUCHAR Packet, + IN ULONG PacketSize + ) +/*++ + +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 + // + + NDIS_RELEASE_MINIPORT_SPIN_LOCK_DPC(Miniport); + + *Status = ((WAN_RECEIVE_HANDLER)(Open->ProtocolHandle->ProtocolCharacteristics.ReceiveHandler)) ( + NdisLinkContext, + Packet, + PacketSize); + + NDIS_ACQUIRE_MINIPORT_SPIN_LOCK_DPC(Miniport); + + Open = Open->MiniportNextOpen; + } +} + + + +VOID +NdisMWanIndicateReceiveComplete( + IN NDIS_HANDLE MiniportAdapterHandle, + IN NDIS_HANDLE NdisLinkContext + ) +/*++ + +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 + // + + NDIS_RELEASE_MINIPORT_SPIN_LOCK_DPC(Miniport); + + (Open->ProtocolHandle->ProtocolCharacteristics.ReceiveCompleteHandler)(NdisLinkContext); + + NDIS_ACQUIRE_MINIPORT_SPIN_LOCK_DPC(Miniport); + + Open = Open->MiniportNextOpen; + } +} + + + +NDIS_STATUS +NdisQueryReceiveInformation( + IN NDIS_HANDLE NdisBindingHandle, + IN NDIS_HANDLE MacContext, + OUT PLONGLONG TimeSent OPTIONAL, + OUT PLONGLONG TimeReceived OPTIONAL, + IN PUCHAR Buffer, + IN UINT BufferSize, + OUT PUINT SizeNeeded + ) +{ + PNDIS_OPEN_BLOCK OpenBlock = ((PNDIS_OPEN_BLOCK)NdisBindingHandle); + PNDIS_M_OPEN_BLOCK MOpenBlock = (PNDIS_M_OPEN_BLOCK)(OpenBlock->MacBindingHandle); + PNDIS_MINIPORT_BLOCK Miniport = (PNDIS_MINIPORT_BLOCK)(MOpenBlock->MiniportHandle); + PNDIS_PACKET Packet = (PNDIS_PACKET)MacContext; + NDIS_STATUS Status = NDIS_STATUS_NOT_SUPPORTED; + + + ASSERT(MINIPORT_LOCK_ACQUIRED(Miniport)); + + DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO, + ("NdisQueryReceiveInformation - Miniort %lx, Packet %lx\n", + Miniport, Packet)); + + // + // The following tests whether this is a mac or a miniport and also if we + // came here via a IndicatePacket or IndicateRecieve + // + if ((MOpenBlock->FakeOpen == OpenBlock) && + (MINIPORT_TEST_FLAG(Miniport, fMINIPORT_PACKET_ARRAY_VALID))) + { + PNDIS_PACKET_OOB_DATA pOob; + + pOob = NDIS_OOB_DATA_FROM_PACKET(Packet); + *SizeNeeded = pOob->SizeMediaSpecificInfo; + if (BufferSize < *SizeNeeded) + { + Status = NDIS_STATUS_BUFFER_TOO_SHORT; + } + else + { + CopyMemory(Buffer, pOob->MediaSpecificInformation, *SizeNeeded); + Status = NDIS_STATUS_SUCCESS; + } + if (ARGUMENT_PRESENT(TimeSent)) + { + *TimeSent = pOob->TimeSent; + } + if (ARGUMENT_PRESENT(TimeReceived)) + { + *TimeReceived = pOob->TimeReceived; + } + } + + return Status; +} + + +VOID +NdisReturnPackets( + IN PNDIS_PACKET *PacketsToReturn, + IN UINT NumberOfPackets + ) +/*++ + +Routine Description: + + Decrement the refcount for the packet and return back to the miniport if 0. + We take the Miniport lock here and hence are protected against other receives. + +Arguments: + + NdisBindingHandle - Handle to the open binding + PacketsToReturn - Pointer to the set of packets to return to the miniport + NumberOfPackets - self descriptive + +Return Value: + + None. + +--*/ +{ + UINT i; + + for (i = 0; i < NumberOfPackets; i++) + { + KIRQL OldIrql; + PNDIS_MINIPORT_BLOCK Miniport; + W_RETURN_PACKET_HANDLER Handler; + PNDIS_PACKET Packet; + BOOLEAN QueueWorkItem = FALSE; + + Packet = PacketsToReturn[i]; + ASSERT (Packet != NULL); + + Miniport = PNDIS_REFERENCE_FROM_PNDIS_PACKET(Packet)->Miniport; + ASSERT (Miniport != NULL); + + NDIS_ACQUIRE_MINIPORT_SPIN_LOCK(Miniport, &OldIrql); + + PNDIS_REFERENCE_FROM_PNDIS_PACKET(Packet)->RefCount --; + if (PNDIS_REFERENCE_FROM_PNDIS_PACKET(Packet)->RefCount == 0) + { + if (!MINIPORT_TEST_FLAG(Miniport, fMINIPORT_PACKET_ARRAY_VALID)) + { + Handler = Miniport->DriverHandle->MiniportCharacteristics.ReturnPacketHandler; + (*Handler)(Miniport->MiniportAdapterContext, Packet); + } + else + { + ASSERT(NDIS_GET_PACKET_STATUS(Packet) != NDIS_STATUS_RESOURCES); + PNDIS_REFERENCE_FROM_PNDIS_PACKET(Packet)->NextPacket = + Miniport->ReturnPacketsQueue; + Miniport->ReturnPacketsQueue = Packet; + if (!MINIPORT_TEST_FLAG(Miniport, fMINIPORT_RP_WORK_ITEM_QUEUED)) + { + MINIPORT_SET_FLAG(Miniport, fMINIPORT_RP_WORK_ITEM_QUEUED); + QueueWorkItem = TRUE; + } + } + } + + NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql); + + if (QueueWorkItem) + { + ndisReferenceMiniport(Miniport); + QUEUE_WORK_ITEM(&Miniport->WorkItem, HyperCriticalWorkQueue); + } + } +} + + +VOID +ndisMLazyReturnPackets( + IN PNDIS_MINIPORT_BLOCK Miniport + ) +{ + KIRQL OldIrql; + PNDIS_PACKET Packet, NextPacket; + W_RETURN_PACKET_HANDLER Handler; + + NDIS_ACQUIRE_MINIPORT_SPIN_LOCK(Miniport, &OldIrql); + + ASSERT(MINIPORT_TEST_FLAG(Miniport, fMINIPORT_RP_WORK_ITEM_QUEUED)); + + Packet = Miniport->ReturnPacketsQueue; + Miniport->ReturnPacketsQueue = NULL; + + for (NOTHING; + Packet != NULL; + Packet = NextPacket) + { + NextPacket = PNDIS_REFERENCE_FROM_PNDIS_PACKET(Packet)->NextPacket; + Handler = Miniport->DriverHandle->MiniportCharacteristics.ReturnPacketHandler; + (*Handler)(Miniport->MiniportAdapterContext, Packet); + } + + MINIPORT_CLEAR_FLAG(Miniport, fMINIPORT_RP_WORK_ITEM_QUEUED); + + NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql); + + ndisDereferenceMiniport(Miniport); +} + + +VOID +ndisMIndicatePacket( + IN PNDIS_MINIPORT_BLOCK Miniport, + IN PPNDIS_PACKET PacketArray, + IN UINT NumberOfPackets + ) +/*++ + +Routine Description: + + This routine is called by the Miniport to indicate packets to + all bindings. This is the code path for ndis 4.0 miniport drivers. + +Arguments: + + Miniport - The Miniport block. + + PacketArray - An array of Packets indicated by the miniport. Each packet consists of a + single buffer. + + NumberOfPackets - Self-explanatory. + +Return Value: + + None. + +--*/ +{ + // + // The private structure to indicate up with + // + PPNDIS_PACKET pPktArray = PacketArray; + + // + // Pointer to the buffer in the ndispacket + // + PNDIS_BUFFER Buffer; + + // + // Pointer to the 1st segment of the buffer, points to dest address + // + PUCHAR Address; + + // + // Total packet length + // + UINT i, LASize,PacketSize, NumIndicates = 0; + + // + // Decides whether we use the protocol's revpkt handler or fall + // back to old rcvindicate handler + // + BOOLEAN fFallBack, FixRef; + + // + // The current open block + // + PNDIS_M_OPEN_BLOCK pOpenBlock, NextOpen; + + NDIS_STATUS StatusOfReceive; + + // + // Define a NULL Fiter structure so that the IndicateToProtocol macro can be used. + // + struct _NullFilter + { + PNDIS_SPIN_LOCK Lock; + } Filter; + + Filter.Lock = &Miniport->Lock; + MINIPORT_SET_FLAG(Miniport, fMINIPORT_PACKET_ARRAY_VALID); + + // Walk all the packets + for (i = 0; i < NumberOfPackets; i++, pPktArray++) + { + PNDIS_PACKET Packet = *pPktArray; + PNDIS_PACKET_OOB_DATA pOob; + + ASSERT(Packet != NULL); + pOob = NDIS_OOB_DATA_FROM_PACKET(Packet); + + NdisGetFirstBufferFromPacket(Packet, + &Buffer, + &Address, + &LASize, + &PacketSize); + ASSERT(Buffer != NULL); + + // + // Set the status here that nobody is holding the packet. This will get + // overwritten by the real status from the protocol. Pay heed to what + // the miniport is saying. + // + PNDIS_REFERENCE_FROM_PNDIS_PACKET(Packet)->RefCount = 0; + PNDIS_REFERENCE_FROM_PNDIS_PACKET(Packet)->Miniport = Miniport; + + if (pOob->Status != NDIS_STATUS_RESOURCES) + { + PNDIS_REFERENCE_FROM_PNDIS_PACKET(Packet)->RefCount --; + pOob->Status = NDIS_STATUS_SUCCESS; + fFallBack = FALSE; + FixRef = TRUE; + } + else + { + FixRef = FALSE; + fFallBack = TRUE; + } + + // + // Ensure that we force re-calculation. + // + Packet->Private.ValidCounts = FALSE; + + for (pOpenBlock = Miniport->OpenQueue; + pOpenBlock != NULL; + pOpenBlock = NextOpen) + { + // + // Get the next open to look at. + // + NextOpen = pOpenBlock->MiniportNextOpen; + pOpenBlock->ReceivedAPacket = TRUE; + pOpenBlock->References++; + NumIndicates ++; + + IndicateToProtocol(Miniport, + &Filter, + pOpenBlock->FakeOpen, + Packet, + Address, + PacketSize, + pOob->HeaderSize, + &fFallBack, + FALSE, + NdisMediumMax); // A dummy medium since it is unknown + + pOpenBlock->References--; + if (pOpenBlock->References == 0) + { + // Anything to do here ? + } + } + + if (FixRef) + { + PNDIS_REFERENCE_FROM_PNDIS_PACKET(Packet)->RefCount++; + if (PNDIS_REFERENCE_FROM_PNDIS_PACKET(Packet)->RefCount != 0) + { + NDIS_SET_PACKET_STATUS(Packet, NDIS_STATUS_PENDING); + } + } + } + + if (NumIndicates > 0) + { + for (pOpenBlock = Miniport->OpenQueue; + pOpenBlock != NULL; + pOpenBlock = NextOpen) + { + if (pOpenBlock->ReceiveCompleteHandler) + { + pOpenBlock->References++; + NDIS_RELEASE_MINIPORT_SPIN_LOCK_DPC(Miniport); + + (*pOpenBlock->ReceiveCompleteHandler)(pOpenBlock->ProtocolBindingContext); + + NDIS_ACQUIRE_MINIPORT_SPIN_LOCK_DPC(Miniport); + if ((--(pOpenBlock->References)) == 0) + { + // Anything to do here ? + } + } + } + } + + MINIPORT_CLEAR_FLAG(Miniport, fMINIPORT_PACKET_ARRAY_VALID); +} + + +///////////////////////////////////////////////////////////////////// +// +// TRANSFER DATA CODE +// +///////////////////////////////////////////////////////////////////// + +VOID +NdisMTransferDataComplete( + IN NDIS_HANDLE MiniportAdapterHandle, + IN PNDIS_PACKET Packet, + IN NDIS_STATUS Status, + IN UINT BytesTransferred + ) +/*++ + +Routine Description: + + This function indicates the completion of a transfer data request. + +Arguments: + + MiniportAdapterHandle - points to the adapter block. + + Packet - The packet the data was copied into. + + Status - Status of the operation. + + BytesTransferred - Total number of bytes transferred. + +Return Value: + + None. + + +--*/ +{ + PNDIS_MINIPORT_BLOCK Miniport = (PNDIS_MINIPORT_BLOCK)MiniportAdapterHandle; + PNDIS_M_OPEN_BLOCK Open; + PNDIS_PACKET PrevPacket; + + ASSERT(MINIPORT_AT_DPC_LEVEL); + ASSERT(MINIPORT_LOCK_ACQUIRED(Miniport)); + ASSERT(Miniport->FirstTDPacket != NULL); + + // + // Find the packet + // + + if (Packet == Miniport->FirstTDPacket) + { + Miniport->FirstTDPacket = PNDIS_RESERVED_FROM_PNDIS_PACKET(Packet)->Next; + } + else + { + PrevPacket = Miniport->FirstTDPacket; + + while (PNDIS_RESERVED_FROM_PNDIS_PACKET(PrevPacket)->Next != Packet) + { + PrevPacket = PNDIS_RESERVED_FROM_PNDIS_PACKET(PrevPacket)->Next; + + ASSERT(PrevPacket != NULL); + } + + PNDIS_RESERVED_FROM_PNDIS_PACKET(PrevPacket)->Next = + PNDIS_RESERVED_FROM_PNDIS_PACKET(Packet)->Next; + + if (Packet == Miniport->LastTDPacket) + { + Miniport->LastTDPacket = PrevPacket; + } + } + + // + // Indicate to Protocol; + // + + Open = PNDIS_RESERVED_FROM_PNDIS_PACKET(Packet)->Open; + + NDIS_RELEASE_MINIPORT_SPIN_LOCK_DPC(Miniport); + + (Open->ProtocolHandle->ProtocolCharacteristics.TransferDataCompleteHandler) ( + Open->ProtocolBindingContext, + Packet, + Status, + BytesTransferred); + + NDIS_ACQUIRE_MINIPORT_SPIN_LOCK_DPC(Miniport); +} + +NDIS_STATUS +ndisMTransferDataSync( + IN NDIS_HANDLE NdisBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN UINT ByteOffset, + IN UINT BytesToTransfer, + IN OUT PNDIS_PACKET Packet, + OUT PUINT BytesTransferred + ) +{ + PNDIS_MINIPORT_BLOCK Miniport = ((PNDIS_M_OPEN_BLOCK)NdisBindingHandle)->MiniportHandle; + PNDIS_PACKET_RESERVED Reserved = PNDIS_RESERVED_FROM_PNDIS_PACKET(Packet); + PNDIS_PACKET_OOB_DATA pOob; + NDIS_STATUS Status; + + ASSERT(MINIPORT_AT_DPC_LEVEL); + + NDIS_ACQUIRE_MINIPORT_SPIN_LOCK_DPC(Miniport); + + ASSERT(MINIPORT_LOCK_ACQUIRED(Miniport)); + ASSERT((Miniport->MacOptions & NDIS_MAC_OPTION_TRANSFERS_NOT_PEND) != 0); + + // + // Handle non-loopback as the default case. + // + + if (Miniport->LoopbackPacket == NULL) + { + Reserved->Next = NULL; + Reserved->Open = (PNDIS_M_OPEN_BLOCK)NdisBindingHandle; + + // + // Call Miniport. + // + + Status = (Reserved->Open->TransferDataHandler)( + Packet, + BytesTransferred, + Reserved->Open->MiniportAdapterContext, + MacReceiveContext, + ByteOffset, + BytesToTransfer); + // + // This miniport better not pend this send. + // + + ASSERT(Status != NDIS_STATUS_PENDING); + + NDIS_RELEASE_MINIPORT_SPIN_LOCK_DPC(Miniport); + + return Status; + } + + // + // This packet is a loopback packet! + // + + NDIS_RELEASE_MINIPORT_SPIN_LOCK_DPC(Miniport); + + pOob = NDIS_OOB_DATA_FROM_PACKET((PNDIS_PACKET)MacReceiveContext); + NdisCopyFromPacketToPacket(Packet, + 0, + BytesToTransfer, + Miniport->LoopbackPacket, + ByteOffset + pOob->HeaderSize, + BytesTransferred); + + if (*BytesTransferred == BytesToTransfer) + { + return NDIS_STATUS_SUCCESS; + } + + return NDIS_STATUS_FAILURE; +} + + +NDIS_STATUS +ndisMTransferData( + IN NDIS_HANDLE NdisBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN UINT ByteOffset, + IN UINT BytesToTransfer, + IN OUT PNDIS_PACKET Packet, + OUT PUINT BytesTransferred + ) +{ + PNDIS_MINIPORT_BLOCK Miniport = ((PNDIS_M_OPEN_BLOCK)NdisBindingHandle)->MiniportHandle; + PNDIS_PACKET_RESERVED Reserved = PNDIS_RESERVED_FROM_PNDIS_PACKET(Packet); + PNDIS_PACKET_OOB_DATA pOob; + PNDIS_PACKET PrevLast; + NDIS_STATUS Status; + + ASSERT(MINIPORT_AT_DPC_LEVEL); + + NDIS_ACQUIRE_MINIPORT_SPIN_LOCK_DPC(Miniport); + ASSERT(MINIPORT_LOCK_ACQUIRED(Miniport)); + + // + // Handle non-loopback as the default case. + // + + if (Miniport->LoopbackPacket == NULL) + { + Reserved->Next = NULL; + Reserved->Open = (PNDIS_M_OPEN_BLOCK)NdisBindingHandle; + + // + // Put this guy on the transfer data queue. + // + + PrevLast = Miniport->LastTDPacket; + + if (Miniport->FirstTDPacket == NULL) + { + Miniport->FirstTDPacket = Packet; + } + else + { + PNDIS_RESERVED_FROM_PNDIS_PACKET(Miniport->LastTDPacket)->Next = Packet; + } + + Miniport->LastTDPacket = Packet; + + // + // Call Miniport + // + + Status = (Reserved->Open->TransferDataHandler)( + Packet, + BytesTransferred, + Reserved->Open->MiniportAdapterContext, + MacReceiveContext, + ByteOffset, + BytesToTransfer); + + // + // If it didn't pend then we won't get a transfer data complte call + // so we need to remove this guy now. + // + + if (Status != NDIS_STATUS_PENDING) + { + // + // Remove from queue + // + + if (Miniport->FirstTDPacket != Packet) + { + PNDIS_RESERVED_FROM_PNDIS_PACKET(PrevLast)->Next = NULL; + Miniport->LastTDPacket = PrevLast; + } + else + { + Miniport->FirstTDPacket = NULL; + Miniport->LastTDPacket = NULL; + } + } + + NDIS_RELEASE_MINIPORT_SPIN_LOCK_DPC(Miniport); + + return Status; + } + + // + // This packet is a loopback packet! + // + + NDIS_RELEASE_MINIPORT_SPIN_LOCK_DPC(Miniport); + + pOob = NDIS_OOB_DATA_FROM_PACKET((PNDIS_PACKET)MacReceiveContext); + NdisCopyFromPacketToPacket(Packet, + 0, + BytesToTransfer, + Miniport->LoopbackPacket, + ByteOffset + pOob->HeaderSize, + BytesTransferred); + + if (*BytesTransferred == BytesToTransfer) + { + return NDIS_STATUS_SUCCESS; + } + + return NDIS_STATUS_FAILURE; +} + +NDIS_STATUS +ndisMDummyTransferData( + IN NDIS_HANDLE NdisBindingHandle, + IN NDIS_HANDLE MacReceiveContext, + IN UINT ByteOffset, + IN UINT BytesToTransfer, + IN OUT PNDIS_PACKET Packet, + OUT PUINT BytesTransferred + ) +{ + PNDIS_PACKET_OOB_DATA pOob; + + pOob = NDIS_OOB_DATA_FROM_PACKET((PNDIS_PACKET)MacReceiveContext); + NdisCopyFromPacketToPacket(Packet, + 0, + BytesToTransfer, + (PNDIS_PACKET)MacReceiveContext, + ByteOffset + pOob->HeaderSize, + BytesTransferred); + + return ((*BytesTransferred == BytesToTransfer) ? + NDIS_STATUS_SUCCESS : NDIS_STATUS_FAILURE); +} + + + + +///////////////////////////////////////////////////////////////////// +// +// RESET CODE +// +///////////////////////////////////////////////////////////////////// + +VOID +ndisMAbortPacketsAndRequests( + PNDIS_MINIPORT_BLOCK Miniport + ) + +/*++ + +Routine Description: + + Aborts all outstanding requests on a mini-port. + + CALLED WITH THE LOCK HELD!! + +Arguments: + + Miniport - Miniport to abort. + +Return Value: + + None. + +--*/ +{ + PNDIS_PACKET Packet; + PNDIS_PACKET NextPacket; + PNDIS_REQUEST MiniportRequest; + PNDIS_REQUEST PendingRequest; + PNDIS_REQUEST NextRequest; + PNDIS_M_OPEN_BLOCK Open; + PNDIS_PACKET ArcnetLimitPacket; + PNDIS_MINIPORT_WORK_ITEM WorkItem; + BOOLEAN fRestoreDeferredState = FALSE; + PSINGLE_LIST_ENTRY Link; + + DBGPRINT(DBG_COMP_WORK_ITEM, DBG_LEVEL_INFO, + ("Enter abort packets and requests\n")); + + ASSERT(MINIPORT_AT_DPC_LEVEL); + + // + // Dequeue any send workitems and acquire the send spin lock + // if we are full duplex.. + // + if (MINIPORT_TEST_FLAG(Miniport, fMINIPORT_FULL_DUPLEX)) + { + // + // Since we are full duplex we need to wrap the dequeue operation + // with spin lock acquire and release. + // + ACQUIRE_SPIN_LOCK_DPC(&Miniport->WorkLock); + + NDISM_DEQUEUE_WORK_ITEM(Miniport, NdisWorkItemSend, NULL, NULL); + + RELEASE_SPIN_LOCK_DPC(&Miniport->WorkLock); + + // + // Acquire the spin lock. + // + NDIS_ACQUIRE_SEND_SPIN_LOCK_DPC(Miniport); + } + else + { + // + // non-full duplex miniports can just dequeue any send work items + // that are queued + // + NDISM_DEQUEUE_WORK_ITEM(Miniport, NdisWorkItemSend, NULL, NULL); + } + + // + // Clear out the packet queues. + // + Packet = Miniport->FirstPacket; + ArcnetLimitPacket = Miniport->FirstPendingPacket; + + Miniport->LastMiniportPacket = NULL; + Miniport->FirstPendingPacket = NULL; + Miniport->FirstPacket = NULL; + Miniport->LastPacket = NULL; + + NDISM_LOG_PACKET(Miniport, NULL, NULL, 'a'); + + // + // Go through the list of packets an return them to the + // bindings + // + while (Packet != NULL) + { + // + // Get a pointer to the next packet before we kill + // the current one. + // + NextPacket = PNDIS_RESERVED_FROM_PNDIS_PACKET(Packet)->Next; + + // + // Get the open that the packet came from. + // + Open = PNDIS_RESERVED_FROM_PNDIS_PACKET(Packet)->Open; + + // + // Set flag that we've reached the packets that are + // not on the mini-port. + // + + if (Packet == ArcnetLimitPacket) + { + ArcnetLimitPacket = NULL; + } + + // + // Now free the arcnet header. + // + + if (Miniport->MediaType == NdisMediumArcnet878_2 && ArcnetLimitPacket) + { + ndisMFreeArcnetHeader(Miniport, Packet); + } + + NDISM_LOG_PACKET(Miniport, Packet, NULL, 'C'); + + if (MINIPORT_TEST_FLAG(Miniport, fMINIPORT_FULL_DUPLEX)) + { + NDIS_RELEASE_SEND_SPIN_LOCK_DPC(Miniport); + } + + NDIS_RELEASE_MINIPORT_SPIN_LOCK_DPC(Miniport); + + (Open->ProtocolHandle->ProtocolCharacteristics.SendCompleteHandler) ( + Open->ProtocolBindingContext, + Packet, + NDIS_STATUS_REQUEST_ABORTED); + + NDIS_ACQUIRE_MINIPORT_SPIN_LOCK_DPC(Miniport); + + // + // Re-acquire the send lock if we are full duplex. + // Note that the wrapper does NOT keep track of open + // references with regard to sends for full duplex miniports. + // + if (MINIPORT_TEST_FLAG(Miniport, fMINIPORT_FULL_DUPLEX)) + { + NDIS_ACQUIRE_SEND_SPIN_LOCK_DPC(Miniport); + } + else + { + 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); + } + } + + // + // Get the next packet. + // + Packet = NextPacket; + } + + NDISM_LOG_PACKET(Miniport, NULL, NULL, 'A'); + + Miniport->SendResourcesAvailable = 0x00ffffff; + + // + // Dequeue any request workitems. + // + if (MINIPORT_TEST_FLAG(Miniport, fMINIPORT_FULL_DUPLEX)) + { + // + // Release the send lock. + // + NDIS_RELEASE_SEND_SPIN_LOCK_DPC(Miniport); + + // + // Since we are full duplex we need to wrap the dequeue operation + // with spin lock acquire and release. + // + ACQUIRE_SPIN_LOCK_DPC(&Miniport->WorkLock); + + NDISM_DEQUEUE_WORK_ITEM(Miniport, NdisWorkItemRequest, NULL, NULL); + + RELEASE_SPIN_LOCK_DPC(&Miniport->WorkLock); + } + else + { + // + // non-full duplex miniports can just dequeue any send work items + // that are queued + // + NDISM_DEQUEUE_WORK_ITEM(Miniport, NdisWorkItemRequest, NULL, NULL); + } + + + // + // Clear the request timeout flag. + // + MINIPORT_CLEAR_FLAG(Miniport, fMINIPORT_REQUEST_TIMEOUT); + + // + // We need to clear out the pending request queue before the currently in + // progress one so that when we complete the in-progress request we + // won't think that we have to process more requests. + // + PendingRequest = Miniport->PendingRequest; + Miniport->PendingRequest = NULL; + + MiniportRequest = Miniport->MiniportRequest; + + // + // If there is one then abort it. + // + if (MiniportRequest != NULL) + { + // + // Get a pointer to the open that the request came from. + // + Open = PNDIS_RESERVED_FROM_PNDIS_REQUEST(MiniportRequest)->Open; + + // + // Was this a statistics request? + // + if ((Open != NULL) && + (MiniportRequest->RequestType == NdisRequestQueryStatistics)) + { + ndisMAbortQueryStatisticsRequest(MiniportRequest, + NDIS_STATUS_REQUEST_ABORTED); + + Miniport->MiniportRequest = NULL; + } + else + { + if (MiniportRequest->RequestType == NdisRequestSetInformation) + { + ndisMSyncSetInformationComplete((NDIS_HANDLE)Miniport, + NDIS_STATUS_REQUEST_ABORTED); + } + else + { + ndisMSyncQueryInformationComplete((NDIS_HANDLE)Miniport, + NDIS_STATUS_REQUEST_ABORTED); + } + } + } + + // + // Go through the pending request queue and clear it out. + // + while (PendingRequest != NULL) + { + // + // Get a pointer to the next request before we kill the + // current one. + // + NextRequest = PNDIS_RESERVED_FROM_PNDIS_REQUEST(PendingRequest)->Next; + + // + // Get a pointer to the open that the request belongs to. + // + Open = PNDIS_RESERVED_FROM_PNDIS_REQUEST(PendingRequest)->Open; + + if (PendingRequest->RequestType == NdisRequestQueryStatistics) + { + ndisMAbortQueryStatisticsRequest( + PendingRequest, + NDIS_STATUS_REQUEST_ABORTED); + } + else + { + // + // Make this request the request in progress. + // + Miniport->MiniportRequest = PendingRequest; + + if (PendingRequest->RequestType == NdisRequestSetInformation) + { + ndisMSyncSetInformationComplete(Miniport, + NDIS_STATUS_REQUEST_ABORTED); + } + else + { + ndisMSyncQueryInformationComplete(Miniport, + NDIS_STATUS_REQUEST_ABORTED); + } + } + + // + // Get the next request. + // + PendingRequest = NextRequest; + } + + DBGPRINT(DBG_COMP_WORK_ITEM, DBG_LEVEL_INFO, + ("Exit abort packets and requests\n")); +} + +VOID +ndisMResetCompleteCommonStep2( + IN PNDIS_MINIPORT_BLOCK Miniport + ) +/*++ + +Routine Description: + +Arguments: + +Return Value: + +--*/ +{ + PSINGLE_LIST_ENTRY Link; + PNDIS_M_OPEN_BLOCK Open; + PNDIS_M_OPEN_BLOCK tmpOpen; + PNDIS_MINIPORT_WORK_ITEM WorkItem; + + + // + // Dequeue the reset in progress work item. + // + if (MINIPORT_TEST_FLAG(Miniport, fMINIPORT_FULL_DUPLEX)) + { + // + // Acquire the work lock and dequeue the reset in progress work item. + // + ACQUIRE_SPIN_LOCK_DPC(&Miniport->WorkLock); + NDISM_DEQUEUE_WORK_ITEM(Miniport, NdisWorkItemResetInProgress, &Open, NULL); + RELEASE_SPIN_LOCK_DPC(&Miniport->WorkLock); + + // + // Grab the send lock so that we can clear the reset in progress flag. + // + NDIS_ACQUIRE_SEND_SPIN_LOCK_DPC(Miniport); + + MINIPORT_CLEAR_FLAG(Miniport, fMINIPORT_RESET_IN_PROGRESS); + + for (tmpOpen = Miniport->OpenQueue; + tmpOpen != NULL; + tmpOpen = tmpOpen->MiniportNextOpen) + { + // + // Restore the send handler + // + tmpOpen->FakeOpen->SendHandler = Miniport->FakeMac->MacCharacteristics.SendHandler; + + // + // Restore the send packets handler. + // + if (MINIPORT_TEST_SEND_FLAG(Miniport, fMINIPORT_SEND_PACKET_ARRAY)) + { + tmpOpen->FakeOpen->SendPacketsHandler = ndisMSendPacketsFullDuplex; + } + else + { + tmpOpen->FakeOpen->SendPacketsHandler = ndisMSendPacketsFullDuplexToSend; + } + } + + NDIS_RELEASE_SEND_SPIN_LOCK_DPC(Miniport); + } + else + { + // + // For non full duplex miniports we don't need to acquire the work lock. + // + NDISM_DEQUEUE_WORK_ITEM(Miniport, NdisWorkItemResetInProgress, &Open, NULL); + + // + // Clear the reset in progress flag. + // + MINIPORT_CLEAR_FLAG(Miniport, fMINIPORT_RESET_IN_PROGRESS); + + for (tmpOpen = Miniport->OpenQueue; + tmpOpen != NULL; + tmpOpen = tmpOpen->MiniportNextOpen) + { + // + // Restore the send handler + // + tmpOpen->FakeOpen->SendHandler = Miniport->FakeMac->MacCharacteristics.SendHandler; + + // + // Restore the send packets handler. + // + if (MINIPORT_TEST_SEND_FLAG(Miniport, fMINIPORT_SEND_PACKET_ARRAY)) + { + tmpOpen->FakeOpen->SendPacketsHandler = ndisMSendPackets; + } + else + { + tmpOpen->FakeOpen->SendPacketsHandler = ndisMSendPacketsToSend; + } + } + } + + ASSERT(Open != NULL); + + // + // Indicate to Protocols the reset is complete + // + NdisMIndicateStatus(Miniport, + NDIS_STATUS_RESET_END, + &Miniport->ResetStatus, + sizeof(Miniport->ResetStatus)); + + NdisMIndicateStatusComplete(Miniport); + + // + // If a protocol initiated the reset then notify it of the + // completion. + // + if (Open != (PNDIS_M_OPEN_BLOCK)Miniport) + { + NDIS_RELEASE_MINIPORT_SPIN_LOCK_DPC(Miniport); + + (Open->ProtocolHandle->ProtocolCharacteristics.ResetCompleteHandler)( + Open->ProtocolBindingContext, + Miniport->ResetStatus); + + 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", Open, Open->References)); + + if (Open->References == 0) + { + ndisMFinishClose(Miniport,Open); + } + } +} + +VOID +ndisMResetCompleteCommonStep1( + IN PNDIS_MINIPORT_BLOCK Miniport, + IN NDIS_STATUS Status, + IN BOOLEAN AddressingReset + ) +/*++ + +Routine Description: + +Arguments: + +Return Value: + +--*/ +{ + ASSERT(MINIPORT_AT_DPC_LEVEL); + ASSERT(MINIPORT_LOCK_ACQUIRED(Miniport)); + + // + // Destroy all outstanding packets and requests. + // + ndisMAbortPacketsAndRequests(Miniport); + + // + // Check if we are going to have to reset the + // adapter again. This happens when we are doing + // the reset because of a ring failure. + // + if (!MINIPORT_TEST_FLAG(Miniport, fMINIPORT_IGNORE_TOKEN_RING_ERRORS)) + { + if (Miniport->TrResetRing == 1) + { + if (Status == NDIS_STATUS_SUCCESS) + { + Miniport->TrResetRing = 0; + } + else + { + Miniport->TrResetRing = NDIS_MINIPORT_TR_RESET_TIMEOUT; + } + } + } + + // + // If we need to reset the miniports filter settings then + // queue the necessary requests & work items. + // + if (AddressingReset && + (Status == NDIS_STATUS_SUCCESS) && + ((Miniport->EthDB != NULL) || (Miniport->TrDB != NULL) || + (Miniport->FddiDB != NULL) || (Miniport->ArcDB != NULL))) + { + ndisMRestoreFilterSettings(Miniport, NULL); + } + + // + // Save the reset status as it is now. + // + Miniport->ResetStatus = Status; +} + + +NDIS_STATUS +ndisMProcessResetRequested( + IN PNDIS_MINIPORT_BLOCK Miniport, + OUT PBOOLEAN pAddressingReset + ) +/*++ + +Routine Description: + +Arguments: + +Return Value: + +--*/ +{ + NDIS_STATUS Status; + PNDIS_M_OPEN_BLOCK Open; + + // + // + if (MINIPORT_TEST_FLAG(Miniport, fMINIPORT_FULL_DUPLEX)) + { + // + // We need to acquire the work lock to dequeue the reset requsted work item. + // + ACQUIRE_SPIN_LOCK_DPC(&Miniport->WorkLock); + + NDISM_DEQUEUE_WORK_ITEM(Miniport, NdisWorkItemResetRequested, NULL, NULL); + + RELEASE_SPIN_LOCK_DPC(&Miniport->WorkLock); + + // + // We need to acquire the send lock to modify the reset requested and + // reset in progress flags. + // + NDIS_ACQUIRE_SEND_SPIN_LOCK_DPC(Miniport); + + // + // Set the reset in progress bit so that the send path can see it. + // + MINIPORT_CLEAR_FLAG(Miniport, fMINIPORT_RESET_REQUESTED); + MINIPORT_SET_FLAG(Miniport, fMINIPORT_RESET_IN_PROGRESS); + + // + // Replace the send handler for the open's + // + for (Open = Miniport->OpenQueue; + Open != NULL; + Open = Open->MiniportNextOpen) + { + if (NdisMediumWan == Miniport->MediaType) + { + Open->FakeOpen->SendHandler = (PVOID)ndisMResetWanSend; + } + else + { + Open->FakeOpen->SendHandler = ndisMResetSend; + } + + Open->FakeOpen->SendPacketsHandler = ndisMResetSendPackets; + } + + NDIS_RELEASE_SEND_SPIN_LOCK_DPC(Miniport); + } + else + { + // + // Dequeue the reset requested work item. this dequeuing will automatically + // queue the reset in progress work item. + // + NDISM_DEQUEUE_WORK_ITEM(Miniport, NdisWorkItemResetRequested, NULL, NULL); + + // + // Set the reset in progress bit so that the send path can see it. + // + MINIPORT_CLEAR_FLAG(Miniport, fMINIPORT_RESET_REQUESTED); + MINIPORT_SET_FLAG(Miniport, fMINIPORT_RESET_IN_PROGRESS); + + // + // Replace the send handler for the open's + // + for (Open = Miniport->OpenQueue; + Open != NULL; + Open = Open->MiniportNextOpen) + { + if (NdisMediumWan == Miniport->MediaType) + { + Open->FakeOpen->SendHandler = (PVOID)ndisMResetWanSend; + } + else + { + Open->FakeOpen->SendHandler = ndisMResetSend; + } + + Open->FakeOpen->SendPacketsHandler = ndisMResetSendPackets; + } + } + + + // + // Indicate the reset status to the protocols. + // + NdisMIndicateStatus(Miniport, NDIS_STATUS_RESET_START, NULL, 0); + NdisMIndicateStatusComplete(Miniport); + + DBGPRINT(DBG_COMP_WORK_ITEM, DBG_LEVEL_INFO, + ("Calling miniport reset\n")); + // + // Call the miniport's reset handler. + // + Status = (Miniport->DriverHandle->MiniportCharacteristics.ResetHandler)( + pAddressingReset, + Miniport->MiniportAdapterContext); + + return(Status); +} + + +VOID +ndisMResetCompleteFullDuplex( + IN NDIS_HANDLE MiniportAdapterHandle, + IN NDIS_STATUS Status, + IN BOOLEAN AddressingReset + ) +{ +#ifdef NDIS_NT + PNDIS_MINIPORT_BLOCK Miniport = (PNDIS_MINIPORT_BLOCK)MiniportAdapterHandle; + + ASSERT(MINIPORT_AT_DPC_LEVEL); + ASSERT(MINIPORT_LOCK_ACQUIRED(Miniport)); + + DBGPRINT(DBG_COMP_ALL, DBG_LEVEL_INFO, + ("Enter reset complete\n")); + + // + // Code that is common for synchronous and async resets. + // + ndisMResetCompleteCommonStep1(Miniport, Status, AddressingReset); + if (!AddressingReset || (Status != NDIS_STATUS_SUCCESS)) + { + // + // If there is no addressing reset to be done or + // the reset failed in some way then we tell the + // bindings now. + // + ndisMResetCompleteCommonStep2(Miniport); + } + + DBGPRINT(DBG_COMP_ALL, DBG_LEVEL_INFO, + ("Exit reset complete\n")); +#endif +} + +VOID +NdisMResetComplete( + IN NDIS_HANDLE MiniportAdapterHandle, + IN NDIS_STATUS Status, + IN BOOLEAN AddressingReset + ) +/*++ + +Routine Description: + + This function indicates the completion of a reset. + +Arguments: + + MiniportAdapterHandle - points to the adapter block. + + Status - Status of the reset. + + AddressingReset - Do we have to submit a request to reload the address + information. This includes packet filter, and multicast/functional addresses. + +Return Value: + + None. + + +--*/ +{ + PNDIS_MINIPORT_BLOCK Miniport = (PNDIS_MINIPORT_BLOCK)MiniportAdapterHandle; + + ASSERT(MINIPORT_AT_DPC_LEVEL); + ASSERT(MINIPORT_LOCK_ACQUIRED(Miniport)); + + DBGPRINT(DBG_COMP_ALL, DBG_LEVEL_INFO, + ("Enter reset complete\n")); + + // + // Code that is common for synchronous and async resets. + // + ndisMResetCompleteCommonStep1(Miniport, Status, AddressingReset); + if (!AddressingReset || (Status != NDIS_STATUS_SUCCESS)) + { + // + // If there is no addressing reset to be done or + // the reset failed in some way then we tell the + // bindings now. + // + ndisMResetCompleteCommonStep2(Miniport); + } + + DBGPRINT(DBG_COMP_ALL, DBG_LEVEL_INFO, + ("Exit reset complete\n")); +} + +NDIS_STATUS +ndisMResetFullDuplex( + IN NDIS_HANDLE NdisBindingHandle + ) +/*++ + +Routine Description: + +Arguments: + +Return Value: + +--*/ +{ + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; +#ifdef NDIS_NT + PNDIS_MINIPORT_WORK_ITEM WorkItem; + PNDIS_MINIPORT_BLOCK Miniport = ((PNDIS_M_OPEN_BLOCK)NdisBindingHandle)->MiniportHandle; + KIRQL OldIrql; + BOOLEAN LocalLock; + PSINGLE_LIST_ENTRY Link; + + NDIS_ACQUIRE_MINIPORT_SPIN_LOCK(Miniport, &OldIrql); + + // + // Is there already a reset in progress? + // + Status = NDISM_QUEUE_WORK_ITEM(Miniport, + NdisWorkItemResetRequested, + NdisBindingHandle, + NULL); + if (Status != NDIS_STATUS_SUCCESS) + { + NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql); + + return(NDIS_STATUS_RESET_IN_PROGRESS); + } + + // + // Update the open's references. + // + ((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)); + + // + // The reset requested flag is used by both the send path and the + // dpc path. + // + NDIS_ACQUIRE_SEND_SPIN_LOCK_DPC(Miniport); + + MINIPORT_SET_FLAG(Miniport, fMINIPORT_RESET_REQUESTED); + + NDIS_RELEASE_SEND_SPIN_LOCK_DPC(Miniport); + + // + // Grab the local lock. + // + LOCK_MINIPORT(Miniport, LocalLock); + if (LocalLock) + { + // + // If we did not lock down the mini-port, then some other routine will + // do this processing for us. Otherwise we need to do this processing. + // + NDISM_PROCESS_DEFERRED(Miniport); + } + + UNLOCK_MINIPORT(Miniport, LocalLock); + + NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql); +#endif + return(NDIS_STATUS_PENDING); +} + +NDIS_STATUS +ndisMReset( + IN NDIS_HANDLE NdisBindingHandle + ) +{ + PNDIS_MINIPORT_BLOCK Miniport = ((PNDIS_M_OPEN_BLOCK)NdisBindingHandle)->MiniportHandle; + PNDIS_MINIPORT_WORK_ITEM WorkItem; + KIRQL OldIrql; + BOOLEAN LocalLock; + PSINGLE_LIST_ENTRY Link; + NDIS_STATUS Status; + + NDIS_ACQUIRE_MINIPORT_SPIN_LOCK(Miniport, &OldIrql); + + // + // Is there already a reset in progress? + // + Status = NDISM_QUEUE_WORK_ITEM(Miniport, + NdisWorkItemResetRequested, + NdisBindingHandle, + NULL); + if (Status != NDIS_STATUS_SUCCESS) + { + NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql); + return(NDIS_STATUS_RESET_IN_PROGRESS); + } + + // + // Update the open's references. + // + ((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)); + + // + // Set the reset requested flag. + // + MINIPORT_SET_FLAG(Miniport, fMINIPORT_RESET_REQUESTED); + + // + // Grab the local lock. + // + LOCK_MINIPORT(Miniport, LocalLock); + if (LocalLock) + { + // + // If we did not lock down the mini-port, then some other routine will + // do this processing for us. Otherwise we need to do this processing. + // + NDISM_PROCESS_DEFERRED(Miniport); + } + + UNLOCK_MINIPORT(Miniport, LocalLock); + + NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql); + + return(NDIS_STATUS_PENDING); +} + + |