|
|
/*++
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_MININT
/////////////////////////////////////////////////////////////////////
//
// HALT / CLOSE CODE
//
/////////////////////////////////////////////////////////////////////
BOOLEAN FASTCALL ndisMKillOpen( IN PNDIS_OPEN_BLOCK Open )
/*++
Routine Description:
Closes an open. Used when NdisCloseAdapter is called.
Arguments:
Open - The open to be closed.
Return Value:
TRUE if the open finished, FALSE if it pended.
Comments: called at passive level -without- miniport's lock held.
--*/ { PNDIS_MINIPORT_BLOCK Miniport = Open->MiniportHandle; PNDIS_OPEN_BLOCK tmpOpen; ULONG newWakeUpEnable; BOOLEAN rc = TRUE; NDIS_STATUS Status; UINT OpenRef; KIRQL OldIrql; DBGPRINT_RAW(DBG_COMP_INIT, DBG_LEVEL_INFO, ("==>ndisMKillOpen: Open %p\n", Open));
ASSERT(ndisPkgs[NPNP_PKG].ReferenceCount > 0); PnPReferencePackage();
KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); NDIS_ACQUIRE_MINIPORT_SPIN_LOCK_DPC(Miniport); //
// Find the Miniport open block
//
for (tmpOpen = Miniport->OpenQueue; tmpOpen != NULL; tmpOpen = tmpOpen->MiniportNextOpen) { if (tmpOpen == Open) { break; } } NDIS_RELEASE_MINIPORT_SPIN_LOCK_DPC(Miniport);
do { ASSERT(tmpOpen != NULL); if (tmpOpen == NULL) break; //
// See if this open is already closing.
//
ACQUIRE_SPIN_LOCK_DPC(&Open->SpinLock); if (MINIPORT_TEST_FLAG(Open, fMINIPORT_OPEN_CLOSING)) { RELEASE_SPIN_LOCK_DPC(&Open->SpinLock); break; }
//
// Indicate to others that this open is closing.
//
MINIPORT_SET_FLAG(Open, fMINIPORT_OPEN_CLOSING); RELEASE_SPIN_LOCK_DPC(&Open->SpinLock);
NDIS_ACQUIRE_MINIPORT_SPIN_LOCK_DPC(Miniport); BLOCK_LOCK_MINIPORT_DPC_L(Miniport);
//
// Remove us from the filter package
//
switch (Miniport->MediaType) { #if ARCNET
case NdisMediumArcnet878_2: if (!MINIPORT_TEST_FLAG(Open, fMINIPORT_OPEN_USING_ETH_ENCAPSULATION)) { Status = ArcDeleteFilterOpenAdapter(Miniport->ArcDB, Open->FilterHandle, NULL); break; }
//
// If we're using encapsulation then we
// didn't open an arcnet filter but rather
// an ethernet filter.
//
#endif
case NdisMedium802_3: Status = EthDeleteFilterOpenAdapter(Miniport->EthDB, Open->FilterHandle); break;
case NdisMedium802_5: Status = TrDeleteFilterOpenAdapter(Miniport->TrDB, Open->FilterHandle); break;
case NdisMediumFddi: Status = FddiDeleteFilterOpenAdapter(Miniport->FddiDB, Open->FilterHandle); break;
default: Status = nullDeleteFilterOpenAdapter(Miniport->NullDB, Open->FilterHandle); break; }
//
// Fix up flags that are dependant on all opens.
//
//
// preserve the state of NDIS_PNP_WAKE_UP_MAGIC_PACKET and NDIS_PNP_WAKE_UP_LINK_CHANGE flag
//
newWakeUpEnable = Miniport->WakeUpEnable & (NDIS_PNP_WAKE_UP_MAGIC_PACKET | NDIS_PNP_WAKE_UP_LINK_CHANGE);
for (tmpOpen = Miniport->OpenQueue; tmpOpen != NULL; tmpOpen = tmpOpen->MiniportNextOpen) { //
// We don't want to include the open that is closing.
//
if (tmpOpen != Open) { newWakeUpEnable |= tmpOpen->WakeUpEnable; } }
//
// Reset the filter settings. Just to be sure that we remove the
// opens settings at the adapter.
//
switch (Miniport->MediaType) { case NdisMedium802_3: case NdisMedium802_5: case NdisMediumFddi: #if ARCNET
case NdisMediumArcnet878_2: #endif
if (!MINIPORT_PNP_TEST_FLAG(Miniport, fMINIPORT_REMOVE_IN_PROGRESS | fMINIPORT_PM_HALTED)) { ndisMRestoreFilterSettings(Miniport, Open, FALSE); } break; }
DBGPRINT_RAW(DBG_COMP_BIND, DBG_LEVEL_INFO, ("!=0 Open 0x%x References 0x%x\n", Open, Open->References));
if (Status != NDIS_STATUS_CLOSING_INDICATING) { //
// Otherwise the close action routine will fix this up.
//
DBGPRINT_RAW(DBG_COMP_BIND, DBG_LEVEL_INFO, ("- Open 0x%x Reference 0x%x\n", Open, Open->References));
M_OPEN_DECREMENT_REF_INTERLOCKED(Open, OpenRef); }
rc = FALSE; if (OpenRef != 0) { ndisMDoRequests(Miniport); UNLOCK_MINIPORT_L(Miniport); } else { UNLOCK_MINIPORT_L(Miniport); ndisMFinishClose(Open); }
NDIS_RELEASE_MINIPORT_SPIN_LOCK_DPC(Miniport);
} while (FALSE);
DBGPRINT_RAW(DBG_COMP_INIT, DBG_LEVEL_INFO, ("<==ndisMKillOpen: Open %p, rc %ld\n", Open, rc));
KeLowerIrql(OldIrql); PnPDereferencePackage();
return rc; }
VOID FASTCALL ndisMFinishClose( IN PNDIS_OPEN_BLOCK Open ) /*++
Routine Description:
Finishes off a close adapter call. it is called when the ref count on the open drops to zero.
CALLED WITH LOCK HELD!!
Arguments:
Miniport - The mini-port the open is queued on.
Open - The open to close
Return Value:
None.
Comments: Called at DPC with Miniport's SpinLock held
--*/ { PNDIS_MINIPORT_BLOCK Miniport = Open->MiniportHandle; PKEVENT pAllOpensClosedEvent; KIRQL OldIrql;
DBGPRINT_RAW(DBG_COMP_INIT, DBG_LEVEL_INFO, ("==>ndisMFinishClose: MOpen %p\n", Open));
ASSERT(MINIPORT_TEST_FLAG(Open, fMINIPORT_OPEN_CLOSING));
MINIPORT_INCREMENT_REF(Miniport);
//
// free any memory allocated to Vcs
//
if (MINIPORT_TEST_FLAG(Miniport, fMINIPORT_IS_CO)) { ndisMCoFreeResources(Open); }
ndisDeQueueOpenOnProtocol(Open, Open->ProtocolHandle);
if (Open->Flags & fMINIPORT_OPEN_PMODE) { Miniport->PmodeOpens --; Open->Flags &= ~fMINIPORT_OPEN_PMODE; NDIS_CHECK_PMODE_OPEN_REF(Miniport); ndisUpdateCheckForLoopbackFlag(Miniport); } ndisDeQueueOpenOnMiniport(Open, Miniport); Open->QC.Status = NDIS_STATUS_SUCCESS; INITIALIZE_WORK_ITEM(&Open->QC.WorkItem, ndisMQueuedFinishClose, Open); QUEUE_WORK_ITEM(&Open->QC.WorkItem, DelayedWorkQueue); MINIPORT_DECREMENT_REF(Miniport); DBGPRINT_RAW(DBG_COMP_INIT, DBG_LEVEL_INFO, ("<==ndisMFinishClose: Mopen %p\n", Open)); }
VOID ndisMQueuedFinishClose( IN PNDIS_OPEN_BLOCK Open ) /*++
Routine Description:
Finishes off a close adapter call.
Arguments:
Miniport - The mini-port the open is queued on.
Open - The open to close
Return Value:
None.
--*/ { PNDIS_MINIPORT_BLOCK Miniport = Open->MiniportHandle; PKEVENT pAllOpensClosedEvent; KIRQL OldIrql; BOOLEAN FreeOpen; DBGPRINT_RAW(DBG_COMP_INIT, DBG_LEVEL_INFO, ("==>ndisMQueuedFinishClose: Open %p, Miniport %p\n", Open, Miniport));
ASSERT(ndisPkgs[NPNP_PKG].ReferenceCount > 0);
MINIPORT_INCREMENT_REF(Miniport);
(Open->ProtocolHandle->ProtocolCharacteristics.CloseAdapterCompleteHandler) ( Open->ProtocolBindingContext, NDIS_STATUS_SUCCESS);
MINIPORT_DECREMENT_REF(Miniport); ndisDereferenceProtocol(Open->ProtocolHandle); if (Open->CloseCompleteEvent != NULL) { SET_EVENT(Open->CloseCompleteEvent); }
NDIS_ACQUIRE_MINIPORT_SPIN_LOCK(Miniport, &OldIrql);
if ((Miniport->AllOpensClosedEvent != NULL) && (Miniport->OpenQueue == NULL)) { pAllOpensClosedEvent = Miniport->AllOpensClosedEvent; Miniport->AllOpensClosedEvent = NULL; SET_EVENT(pAllOpensClosedEvent); }
ACQUIRE_SPIN_LOCK_DPC(&Open->SpinLock); if (MINIPORT_TEST_FLAG(Open, fMINIPORT_OPEN_DONT_FREE)) { //
// there is an unbind attempt in progress
// do not free the Open block and let unbind know that
// you've seen its message
//
MINIPORT_SET_FLAG(Open, fMINIPORT_OPEN_CLOSE_COMPLETE); FreeOpen = FALSE; } else { FreeOpen = TRUE; } RELEASE_SPIN_LOCK_DPC(&Open->SpinLock); NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql);
if (FreeOpen) { ndisRemoveOpenFromGlobalList(Open); FREE_POOL(Open); } //
// finaly decrement the ref count we added for miniport
//
MINIPORT_DECREMENT_REF(Miniport);
//
// decrement the ref count for PnP package that we added when noticed
// close is going to pend.
//
PnPDereferencePackage();
DBGPRINT_RAW(DBG_COMP_INIT, DBG_LEVEL_INFO, ("<==ndisMQueuedFinishClose: Open %p, Miniport %p\n", Open, Miniport)); }
VOID FASTCALL ndisDeQueueOpenOnMiniport( IN PNDIS_OPEN_BLOCK OpenP, IN PNDIS_MINIPORT_BLOCK Miniport )
/*++
Routine Description:
Arguments:
Return Value:
Note: Called with Miniport lock held.
--*/ { KIRQL OldIrql; DBGPRINT_RAW(DBG_COMP_INIT, DBG_LEVEL_INFO, ("==>ndisDeQueueOpenOnMiniport: MOpen %p, Miniport %p\n", OpenP, Miniport));
//
// we can not reference the package here because this routine can
// be called at raised IRQL.
// make sure the PNP package has been referenced already
//
ASSERT(ndisPkgs[NPNP_PKG].ReferenceCount > 0);
//
// Find the open on the queue, and remove it.
//
if (Miniport->OpenQueue == OpenP) { Miniport->OpenQueue = OpenP->MiniportNextOpen; Miniport->NumOpens--; } else { PNDIS_OPEN_BLOCK PP = Miniport->OpenQueue;
while ((PP != NULL) && (PP->MiniportNextOpen != OpenP)) { PP = PP->MiniportNextOpen; } if (PP == NULL) { #if TRACK_MOPEN_REFCOUNTS
DbgPrint("Ndis:ndisDeQueueOpenOnMiniport Open %p is -not- on Miniport %p\n", OpenP, Miniport); DbgBreakPoint(); #endif
} else { PP->MiniportNextOpen = PP->MiniportNextOpen->MiniportNextOpen; Miniport->NumOpens--; } } ndisUpdateCheckForLoopbackFlag(Miniport);
DBGPRINT_RAW(DBG_COMP_INIT, DBG_LEVEL_INFO, ("<==ndisDeQueueOpenOnMiniport: MOpen %p, Miniport %p\n", OpenP, Miniport)); }
BOOLEAN FASTCALL ndisQueueMiniportOnDriver( IN PNDIS_MINIPORT_BLOCK Miniport, IN PNDIS_M_DRIVER_BLOCK MiniBlock )
/*++
Routine Description:
Adds an mini-port to a list of mini-port for a driver.
Arguments:
Miniport - The mini-port block to queue. MiniBlock - The driver block to queue it to.
Return Value:
FALSE if the driver is closing. TRUE otherwise.
--*/
{ KIRQL OldIrql; BOOLEAN rc = TRUE; DBGPRINT_RAW(DBG_COMP_INIT, DBG_LEVEL_INFO, ("==>ndisQueueMiniportOnDriver: Miniport %p, MiniBlock %p\n", Miniport, MiniBlock));
PnPReferencePackage();
do { ACQUIRE_SPIN_LOCK(&MiniBlock->Ref.SpinLock, &OldIrql);
//
// Make sure the driver is not closing.
//
if (MiniBlock->Ref.Closing) { RELEASE_SPIN_LOCK(&MiniBlock->Ref.SpinLock, OldIrql); rc = FALSE; break; }
//
// Add this adapter at the head of the queue
//
Miniport->NextMiniport = MiniBlock->MiniportQueue; MiniBlock->MiniportQueue = Miniport; RELEASE_SPIN_LOCK(&MiniBlock->Ref.SpinLock, OldIrql); } while (FALSE);
PnPDereferencePackage(); DBGPRINT_RAW(DBG_COMP_INIT, DBG_LEVEL_INFO, ("<==ndisQueueMiniportOnDriver: Miniport %p, MiniBlock %p, rc %ld\n", Miniport, MiniBlock, rc)); return rc; }
VOID FASTCALL FASTCALL ndisDeQueueMiniportOnDriver( IN PNDIS_MINIPORT_BLOCK Miniport, IN PNDIS_M_DRIVER_BLOCK MiniBlock )
/*++
Routine Description:
Removes an mini-port from a list of mini-port for a driver.
Arguments:
Miniport - The mini-port block to dequeue. MiniBlock - The driver block to dequeue it from.
Return Value:
None.
--*/
{ PNDIS_MINIPORT_BLOCK *ppQ; KIRQL OldIrql; DBGPRINT_RAW(DBG_COMP_INIT, DBG_LEVEL_INFO, ("==>ndisDeQueueMiniportOnDriver, Miniport %p, MiniBlock %p\n", Miniport, MiniBlock));
PnPReferencePackage();
ACQUIRE_SPIN_LOCK(&MiniBlock->Ref.SpinLock, &OldIrql);
//
// Find the driver on the queue, and remove it.
//
for (ppQ = &MiniBlock->MiniportQueue; *ppQ != NULL; ppQ = &(*ppQ)->NextMiniport) { if (*ppQ == Miniport) { *ppQ = Miniport->NextMiniport; break; } }
ASSERT(*ppQ == Miniport->NextMiniport);
//
// the same miniport can be queued on the driver again without all the fields
// getting re-initialized so zero out the linkage
//
Miniport->NextMiniport = NULL;
RELEASE_SPIN_LOCK(&MiniBlock->Ref.SpinLock, OldIrql);
PnPDereferencePackage();
DBGPRINT_RAW(DBG_COMP_INIT, DBG_LEVEL_INFO, ("<==ndisDeQueueMiniportOnDriver: Miniport %p, MiniBlock %p\n", Miniport, MiniBlock)); }
VOID FASTCALL ndisDereferenceDriver( IN PNDIS_M_DRIVER_BLOCK MiniBlock, IN BOOLEAN fGlobalLockHeld ) /*++
Routine Description:
Removes a reference from the mini-port driver, deleting it if the count goes to 0.
Arguments:
Miniport - The mini-port block to dereference.
Return Value:
None.
--*/ { KIRQL OldIrql;
DBGPRINT_RAW(DBG_COMP_REF, DBG_LEVEL_INFO, ("==>ndisDereferenceDriver: MiniBlock %p\n", MiniBlock)); if (ndisDereferenceRef(&(MiniBlock)->Ref)) { PNDIS_M_DRIVER_BLOCK *ppMB; PNDIS_PENDING_IM_INSTANCE ImInstance, NextImInstance; //
// Remove it from the global list.
//
ASSERT (IsListEmpty(&MiniBlock->DeviceList));
if (!fGlobalLockHeld) { ACQUIRE_SPIN_LOCK(&ndisMiniDriverListLock, &OldIrql); }
for (ppMB = &ndisMiniDriverList; *ppMB != NULL; ppMB = &(*ppMB)->NextDriver) { if (*ppMB == MiniBlock) { *ppMB = MiniBlock->NextDriver; DEREF_NDIS_DRIVER_OBJECT(); break; } }
if (!fGlobalLockHeld) { RELEASE_SPIN_LOCK(&ndisMiniDriverListLock, OldIrql); }
//
// Free the wrapper handle allocated during NdisInitializeWrapper
//
if (MiniBlock->NdisDriverInfo != NULL) { FREE_POOL(MiniBlock->NdisDriverInfo); MiniBlock->NdisDriverInfo = NULL; }
//
// Free any queued device-instance blocks
//
for (ImInstance = MiniBlock->PendingDeviceList; ImInstance != NULL; ImInstance = NextImInstance) { NextImInstance = ImInstance->Next; FREE_POOL(ImInstance); }
//
// set the event holding unload to go through
//
SET_EVENT(&MiniBlock->MiniportsRemovedEvent); }
DBGPRINT_RAW(DBG_COMP_REF, DBG_LEVEL_INFO, ("<==ndisDereferenceDriver: MiniBlock %p\n", MiniBlock)); }
#if DBG
BOOLEAN FASTCALL ndisReferenceMiniport( IN PNDIS_MINIPORT_BLOCK Miniport ) { BOOLEAN rc;
DBGPRINT(DBG_COMP_REF, DBG_LEVEL_INFO,("==>ndisReferenceMiniport: Miniport %p\n", Miniport));
rc = ndisReferenceULongRef(&(Miniport->Ref));
DBGPRINT_RAW(DBG_COMP_REF, DBG_LEVEL_INFO, (" ndisReferenceMiniport: Miniport %p, Ref = %lx\n", Miniport, Miniport->Ref.ReferenceCount));
DBGPRINT(DBG_COMP_REF, DBG_LEVEL_INFO,("<==ndisReferenceMiniport: Miniport %p\n", Miniport));
return(rc); } #endif
#ifdef TRACK_MINIPORT_REFCOUNTS
BOOLEAN ndisReferenceMiniportAndLog( IN PNDIS_MINIPORT_BLOCK Miniport, IN UINT Line, IN UINT Module ) { BOOLEAN rc; rc = ndisReferenceMiniport(Miniport); M_LOG_MINIPORT_INCREMENT_REF(Miniport, Line, Module); return rc; }
BOOLEAN ndisReferenceMiniportAndLogCreate( IN PNDIS_MINIPORT_BLOCK Miniport, IN UINT Line, IN UINT Module, IN PIRP Irp ) { BOOLEAN rc; rc = ndisReferenceMiniport(Miniport); M_LOG_MINIPORT_INCREMENT_REF_CREATE(Miniport, Line, Module); return rc; } #endif
VOID FASTCALL ndisDereferenceMiniport( IN PNDIS_MINIPORT_BLOCK Miniport ) /*++
Routine Description:
Removes a reference from the mini-port driver, deleting it if the count goes to 0.
Arguments:
Miniport - The mini-port block to dereference.
Return Value:
None.
--*/ { PSINGLE_LIST_ENTRY Link; PNDIS_MINIPORT_WORK_ITEM WorkItem; UINT c; PKEVENT RemoveReadyEvent = NULL; KEVENT RequestsCompletedEvent; KIRQL OldIrql; BOOLEAN fTimerCancelled; BOOLEAN rc; DBGPRINT_RAW(DBG_COMP_REF, DBG_LEVEL_INFO, ("==>ndisDereferenceMiniport: Miniport %p\n", Miniport)); rc = ndisDereferenceULongRef(&(Miniport)->Ref); DBGPRINT_RAW(DBG_COMP_REF, DBG_LEVEL_INFO, (" ndisDereferenceMiniport:Miniport %p, Ref = %lx\n", Miniport, Miniport->Ref.ReferenceCount)); if (rc) { DBGPRINT_RAW(DBG_COMP_INIT, DBG_LEVEL_INFO, (" ndisDereferenceMiniport:Miniport %p, Ref = %lx\n", Miniport, Miniport->Ref.ReferenceCount));
RemoveReadyEvent = Miniport->RemoveReadyEvent; if (ndisIsMiniportStarted(Miniport) && (Miniport->Ref.ReferenceCount == 0)) { ASSERT (Miniport->Interrupt == NULL);
if (Miniport->EthDB) { EthDeleteFilter(Miniport->EthDB); Miniport->EthDB = NULL; }
if (Miniport->TrDB) { TrDeleteFilter(Miniport->TrDB); Miniport->TrDB = NULL; }
if (Miniport->FddiDB) { FddiDeleteFilter(Miniport->FddiDB); Miniport->FddiDB = NULL; }
#if ARCNET
if (Miniport->ArcDB) { ArcDeleteFilter(Miniport->ArcDB); Miniport->ArcDB = NULL; } #endif
if (Miniport->AllocatedResources) { FREE_POOL(Miniport->AllocatedResources); } //
// Free the work items that are currently on the work queue that are
// allocated outside of the miniport block
//
for (c = NUMBER_OF_SINGLE_WORK_ITEMS; c < NUMBER_OF_WORK_ITEM_TYPES; c++) { //
// Free all work items on the current queue.
//
while (Miniport->WorkQueue[c].Next != NULL) { Link = PopEntryList(&Miniport->WorkQueue[c]); WorkItem = CONTAINING_RECORD(Link, NDIS_MINIPORT_WORK_ITEM, Link); FREE_POOL(WorkItem); } } if (Miniport->OidList != NULL) { FREE_POOL(Miniport->OidList); Miniport->OidList = NULL; }
//
// Did we set a timer for the link change power down?
//
if (MINIPORT_PNP_TEST_FLAG(Miniport, fMINIPORT_MEDIA_DISCONNECT_WAIT)) { MINIPORT_PNP_CLEAR_FLAG(Miniport, fMINIPORT_MEDIA_DISCONNECT_WAIT); MINIPORT_PNP_SET_FLAG(Miniport, fMINIPORT_MEDIA_DISCONNECT_CANCELLED); NdisCancelTimer(&Miniport->MediaDisconnectTimer, &fTimerCancelled); if (!fTimerCancelled) { NdisStallExecution(Miniport->MediaDisconnectTimeOut * 1000000); } } #if ARCNET
//
// Is there an arcnet lookahead buffer allocated?
//
if ((Miniport->MediaType == NdisMediumArcnet878_2) && (Miniport->ArcBuf != NULL)) { if (Miniport->ArcBuf->ArcnetLookaheadBuffer != NULL) { FREE_POOL(Miniport->ArcBuf->ArcnetLookaheadBuffer); } FREE_POOL(Miniport->ArcBuf); Miniport->ArcBuf = NULL; } #endif
//
// if the adapter uses SG DMA, we have to dereference the DMA adapter
// to get it freed
//
if (MINIPORT_TEST_FLAG(Miniport, fMINIPORT_SG_LIST)) { ndisDereferenceDmaAdapter(Miniport); }
INITIALIZE_EVENT(&RequestsCompletedEvent);
NDIS_ACQUIRE_MINIPORT_SPIN_LOCK(Miniport, &OldIrql); Miniport->DmaResourcesReleasedEvent = &RequestsCompletedEvent; if (Miniport->SystemAdapterObject != NULL) { LARGE_INTEGER TimeoutValue;
TimeoutValue.QuadPart = Int32x32To64(30000, -10000); // Make it 30 second
NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql); if (!NT_SUCCESS(WAIT_FOR_OBJECT(&RequestsCompletedEvent, &TimeoutValue))) { #if DBG
ASSERTMSG("Ndis: Miniport is going away without releasing all resources.\n", (Miniport->DmaAdapterRefCount == 0)); #endif
} } else { NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql); }
Miniport->DmaResourcesReleasedEvent = NULL; //
// Free the map of custom GUIDs to OIDs.
//
if (NULL != Miniport->pNdisGuidMap) { FREE_POOL(Miniport->pNdisGuidMap); Miniport->pNdisGuidMap = NULL; } if (Miniport->FakeMac != NULL) { FREE_POOL(Miniport->FakeMac); Miniport->FakeMac = NULL; }
if (MINIPORT_TEST_FLAG(Miniport, fMINIPORT_IS_CO)) { CoDereferencePackage(); }
ndisDeQueueMiniportOnDriver(Miniport, Miniport->DriverHandle); ndisDereferenceDriver(Miniport->DriverHandle, FALSE); NdisMDeregisterAdapterShutdownHandler(Miniport); IoUnregisterShutdownNotification(Miniport->DeviceObject);
if (Miniport->SymbolicLinkName.Buffer != NULL) { RtlFreeUnicodeString(&Miniport->SymbolicLinkName); Miniport->SymbolicLinkName.Buffer = NULL; } MiniportDereferencePackage(); } if (RemoveReadyEvent) { SET_EVENT(RemoveReadyEvent); } } DBGPRINT_RAW(DBG_COMP_REF, DBG_LEVEL_INFO, ("<==ndisDereferenceMiniport: Miniport %p\n", Miniport)); }
VOID FASTCALL ndisMCommonHaltMiniport( IN PNDIS_MINIPORT_BLOCK Miniport ) /*++
Routine Description:
This is common code for halting a miniport. There are two different paths that will call this routine: 1) from a normal unload. 2) from an adapter being transitioned to a low power state.
Arguments:
Return Value:
--*/ { KIRQL OldIrql; BOOLEAN Canceled; PNDIS_AF_LIST MiniportAfList, pNext; KEVENT RequestsCompletedEvent; FILTER_PACKET_INDICATION_HANDLER PacketIndicateHandler;
DBGPRINT_RAW(DBG_COMP_INIT, DBG_LEVEL_INFO, ("==>ndisMCommonHaltMiniport: Miniport %p\n", Miniport));
PnPReferencePackage(); //
// wait for outstanding resets to complete
//
BLOCK_LOCK_MINIPORT_LOCKED(Miniport, OldIrql); MINIPORT_PNP_SET_FLAG(Miniport, fMINIPORT_HALTING | fMINIPORT_REJECT_REQUESTS); if (MINIPORT_TEST_FLAG(Miniport, fMINIPORT_RESET_IN_PROGRESS)) { INITIALIZE_EVENT(&RequestsCompletedEvent); Miniport->ResetCompletedEvent = &RequestsCompletedEvent; } UNLOCK_MINIPORT_L(Miniport); NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql);
if (Miniport->ResetCompletedEvent) WAIT_FOR_OBJECT(&RequestsCompletedEvent, NULL); Miniport->ResetCompletedEvent = NULL;
//
// if we have an outstanding queued workitem to initialize the bindings
// wait for it to fire
//
BLOCK_LOCK_MINIPORT_LOCKED(Miniport, OldIrql); if (MINIPORT_PNP_TEST_FLAG(Miniport, fMINIPORT_QUEUED_BIND_WORKITEM)) { INITIALIZE_EVENT(&RequestsCompletedEvent); Miniport->QueuedBindingCompletedEvent = &RequestsCompletedEvent; } UNLOCK_MINIPORT_L(Miniport); NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql);
if (Miniport->QueuedBindingCompletedEvent) WAIT_FOR_OBJECT(&RequestsCompletedEvent, NULL); Miniport->QueuedBindingCompletedEvent = NULL; IoSetDeviceInterfaceState(&Miniport->SymbolicLinkName, FALSE); //
// Deregister with WMI
//
IoWMIRegistrationControl(Miniport->DeviceObject, WMIREG_ACTION_DEREGISTER);
NdisCancelTimer(&Miniport->WakeUpDpcTimer, &Canceled); if (!Canceled) { NdisStallExecution(NDIS_MINIPORT_WAKEUP_TIMEOUT * 1000); }
BLOCK_LOCK_MINIPORT_LOCKED(Miniport, OldIrql); if (Miniport->PendingRequest != NULL) { INITIALIZE_EVENT(&RequestsCompletedEvent); Miniport->AllRequestsCompletedEvent = &RequestsCompletedEvent; } UNLOCK_MINIPORT_L(Miniport); NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql);
if (Miniport->AllRequestsCompletedEvent) WAIT_FOR_OBJECT(&RequestsCompletedEvent, NULL); Miniport->AllRequestsCompletedEvent = NULL;
if (MINIPORT_TEST_FLAG(Miniport, fMINIPORT_INTERMEDIATE_DRIVER)) { PacketIndicateHandler = Miniport->PacketIndicateHandler; Miniport->PacketIndicateHandler = ndisMDummyIndicatePacket; while (Miniport->IndicatedPacketsCount != 0) { NdisMSleep(1000); } } (Miniport->DriverHandle->MiniportCharacteristics.HaltHandler)(Miniport->MiniportAdapterContext);
if (MINIPORT_TEST_FLAG(Miniport, fMINIPORT_INTERMEDIATE_DRIVER)) { Miniport->PacketIndicateHandler = PacketIndicateHandler; } MINIPORT_PNP_CLEAR_FLAG(Miniport, fMINIPORT_HALTING); ASSERT(Miniport->TimerQueue == NULL); ASSERT (Miniport->Interrupt == NULL); ASSERT(Miniport->MapRegisters == NULL);
//
// check for memory leak
//
if (Miniport == ndisMiniportTrackAlloc) { ASSERT(IsListEmpty(&ndisMiniportTrackAllocList)); ndisMiniportTrackAlloc = NULL; }
//
// zero out statistics
//
ZeroMemory(&Miniport->NdisStats, sizeof(Miniport->NdisStats)); if ((Miniport->TimerQueue != NULL) || (Miniport->Interrupt != NULL)) { if (Miniport->Interrupt != NULL) { BAD_MINIPORT(Miniport, "Unloading without deregistering interrupt"); } else { BAD_MINIPORT(Miniport, "Unloading without deregistering timer"); } KeBugCheckEx(BUGCODE_ID_DRIVER, (ULONG_PTR)Miniport, (ULONG_PTR)Miniport->TimerQueue, (ULONG_PTR)Miniport->Interrupt, 0); }
BLOCK_LOCK_MINIPORT_LOCKED(Miniport, OldIrql);
ndisMAbortPackets(Miniport, NULL, NULL);
//
// Dequeue any request work items that are queued
//
NDISM_DEQUEUE_WORK_ITEM(Miniport, NdisWorkItemRequest, NULL); ndisMAbortRequests(Miniport);
//
// Free up any AFs registered by this miniport
//
for (MiniportAfList = Miniport->CallMgrAfList, Miniport->CallMgrAfList = NULL; MiniportAfList != NULL; MiniportAfList = pNext) { pNext = MiniportAfList->NextAf; FREE_POOL(MiniportAfList); }
UNLOCK_MINIPORT_L(Miniport); NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql); PnPDereferencePackage();
DBGPRINT_RAW(DBG_COMP_INIT, DBG_LEVEL_INFO, ("<==ndisMCommonHaltMiniport: Miniport %p\n", Miniport)); }
VOID FASTCALL ndisMHaltMiniport( IN PNDIS_MINIPORT_BLOCK Miniport )
/*++
Routine Description:
Does all the clean up for a mini-port.
Arguments:
Miniport - pointer to the mini-port to halt
Return Value:
None.
--*/
{ DBGPRINT_RAW(DBG_COMP_INIT, DBG_LEVEL_INFO, ("==>ndisMHaltMiniport: Miniport %p\n", Miniport));
do { //
// If the Miniport is already closing, return.
//
if (!ndisCloseULongRef(&Miniport->Ref)) { break; } //
// if the miniport is not already halted becuase of a PM event
// halt it here
//
if (!MINIPORT_PNP_TEST_FLAG(Miniport, fMINIPORT_PM_HALTED)) { //
// Common halt code.
//
ndisMCommonHaltMiniport(Miniport);
//
// If a shutdown handler was registered then deregister it.
//
NdisMDeregisterAdapterShutdownHandler(Miniport); } MINIPORT_DECREMENT_REF(Miniport); } while (FALSE); DBGPRINT_RAW(DBG_COMP_INIT, DBG_LEVEL_INFO, ("<==ndisMHaltMiniport: Miniport %p\n", Miniport)); }
VOID ndisMUnload( IN PDRIVER_OBJECT DriverObject ) /*++
Routine Description:
This routine is called when a driver is supposed to unload.
Arguments:
DriverObject - the driver object for the mac that is to unload.
Return Value:
None.
--*/ { PNDIS_M_DRIVER_BLOCK MiniBlock, Tmp, IoMiniBlock; PNDIS_MINIPORT_BLOCK Miniport, NextMiniport; KIRQL OldIrql; #if TRACK_UNLOAD
DbgPrint("ndisMUnload: DriverObject %p\n", DriverObject); #endif
DBGPRINT_RAW(DBG_COMP_INIT, DBG_LEVEL_INFO, ("==>ndisMUnload: DriverObject %p\n", DriverObject));
PnPReferencePackage(); do { //
// Search for the driver
//
IoMiniBlock = (PNDIS_M_DRIVER_BLOCK)IoGetDriverObjectExtension(DriverObject, (PVOID)NDIS_PNP_MINIPORT_DRIVER_ID); if (IoMiniBlock && !(IoMiniBlock->Flags & fMINIBLOCK_TERMINATE_WRAPPER_UNLOAD)) { IoMiniBlock->Flags |= fMINIBLOCK_IO_UNLOAD; }
ACQUIRE_SPIN_LOCK(&ndisMiniDriverListLock, &OldIrql);
MiniBlock = ndisMiniDriverList;
while (MiniBlock != (PNDIS_M_DRIVER_BLOCK)NULL) { if (MiniBlock->NdisDriverInfo->DriverObject == DriverObject) { break; }
MiniBlock = MiniBlock->NextDriver; }
RELEASE_SPIN_LOCK(&ndisMiniDriverListLock, OldIrql);
#if TRACK_UNLOAD
DbgPrint("ndisMUnload: MiniBlock %p\n", MiniBlock); #endif
if (MiniBlock == (PNDIS_M_DRIVER_BLOCK)NULL) { //
// It is already gone. Just return.
//
break; } ASSERT(MiniBlock == IoMiniBlock); DBGPRINT_RAW(DBG_COMP_INIT, DBG_LEVEL_INFO, (" ndisMUnload: MiniBlock %p\n", MiniBlock));
MiniBlock->Flags |= fMINIBLOCK_UNLOADING;
//
// Now remove the last reference (this will remove it from the list)
//
// ASSERT(MiniBlock->Ref.ReferenceCount == 1);
//
// If this is an intermediate driver and wants to be called to do unload handling, allow him
//
if (MiniBlock->UnloadHandler != NULL) { (*MiniBlock->UnloadHandler)(DriverObject); }
if (MiniBlock->AssociatedProtocol != NULL) { MiniBlock->AssociatedProtocol->AssociatedMiniDriver = NULL; MiniBlock->AssociatedProtocol = NULL; } ndisDereferenceDriver(MiniBlock, FALSE);
//
// Wait for all adapters to be gonzo.
//
WAIT_FOR_OBJECT(&MiniBlock->MiniportsRemovedEvent, NULL); RESET_EVENT(&MiniBlock->MiniportsRemovedEvent);
#if TRACK_UNLOAD
ACQUIRE_SPIN_LOCK(&ndisMiniDriverListLock, &OldIrql);
for (Tmp = ndisMiniDriverList; Tmp != NULL; Tmp = Tmp->NextDriver) { ASSERT (Tmp != MiniBlock); if (Tmp == MiniBlock) { DbgPrint("NdisMUnload: MiniBlock %p is getting unloaded but it is still on ndisMiniDriverList\n", MiniBlock); KeBugCheckEx(BUGCODE_ID_DRIVER, (ULONG_PTR)MiniBlock, (ULONG_PTR)MiniBlock->Ref.ReferenceCount, 0, 0);
} }
RELEASE_SPIN_LOCK(&ndisMiniDriverListLock, OldIrql); #endif
//
// check to make sure that the driver has freed all the memory it allocated
//
if (MiniBlock == ndisDriverTrackAlloc) { ASSERT(IsListEmpty(&ndisDriverTrackAllocList)); ndisDriverTrackAlloc = NULL; }
} while (FALSE); PnPDereferencePackage();
DBGPRINT_RAW(DBG_COMP_INIT, DBG_LEVEL_INFO, ("<==ndisMUnload: DriverObject %p, MiniBlock %p\n", DriverObject, MiniBlock)); }
/////////////////////////////////////////////////////////////////////
//
// PLUG-N-PLAY CODE
//
/////////////////////////////////////////////////////////////////////
NDIS_STATUS FASTCALL ndisCloseMiniportBindings( IN PNDIS_MINIPORT_BLOCK Miniport ) /*++
Routine Description:
Unbind all protocols from this miniport and finally unload it.
Arguments:
Miniport - The Miniport to unload.
Return Value:
None.
--*/ { KIRQL OldIrql; PNDIS_OPEN_BLOCK Open, TmpOpen; NDIS_BIND_CONTEXT UnbindContext; NDIS_STATUS UnbindStatus; KEVENT CloseCompleteEvent; KEVENT AllOpensClosedEvent; PKEVENT pAllOpensClosedEvent; DBGPRINT_RAW(DBG_COMP_INIT, DBG_LEVEL_INFO, ("==>ndisCloseMiniportBindings, Miniport %p\n", Miniport));
PnPReferencePackage();
//
// if we have an outstanding queued workitem to initialize the bindings
// wait for it to fire
//
BLOCK_LOCK_MINIPORT_LOCKED(Miniport, OldIrql); if (MINIPORT_PNP_TEST_FLAG(Miniport, fMINIPORT_QUEUED_BIND_WORKITEM)) { INITIALIZE_EVENT(&AllOpensClosedEvent); Miniport->QueuedBindingCompletedEvent = &AllOpensClosedEvent; } UNLOCK_MINIPORT_L(Miniport); NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql);
if (Miniport->QueuedBindingCompletedEvent) WAIT_FOR_OBJECT(&AllOpensClosedEvent, NULL); Miniport->QueuedBindingCompletedEvent = NULL; INITIALIZE_EVENT(&AllOpensClosedEvent); INITIALIZE_EVENT(&CloseCompleteEvent);
NDIS_ACQUIRE_MINIPORT_SPIN_LOCK(Miniport, &OldIrql);
if ((Miniport->OpenQueue != NULL) && (Miniport->AllOpensClosedEvent == NULL)) { Miniport->AllOpensClosedEvent = &AllOpensClosedEvent; }
pAllOpensClosedEvent = Miniport->AllOpensClosedEvent; next:
//
// Walk the list of open bindings on this miniport and ask the protocols to
// unbind from them.
//
for (Open = Miniport->OpenQueue; Open != NULL; Open = Open->MiniportNextOpen) { ACQUIRE_SPIN_LOCK_DPC(&Open->SpinLock); if (!MINIPORT_TEST_FLAG(Open, (fMINIPORT_OPEN_CLOSING | fMINIPORT_OPEN_PROCESSING))) { MINIPORT_SET_FLAG(Open, fMINIPORT_OPEN_PROCESSING); if (!MINIPORT_TEST_FLAG(Open, fMINIPORT_OPEN_UNBINDING)) { MINIPORT_SET_FLAG(Open, fMINIPORT_OPEN_UNBINDING | fMINIPORT_OPEN_DONT_FREE); Open->CloseCompleteEvent = &CloseCompleteEvent; RELEASE_SPIN_LOCK_DPC(&Open->SpinLock); break; } #if DBG
else { DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_INFO, ("ndisCloseMiniportBindings: Open %p is already Closing, Flags %lx\n", Open, Open->Flags));
} #endif
} RELEASE_SPIN_LOCK_DPC(&Open->SpinLock); }
if (Open != NULL) { NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql);
ndisUnbindProtocol(Open, FALSE); NDIS_ACQUIRE_MINIPORT_SPIN_LOCK(Miniport, &OldIrql);
goto next; }
//
// if we reached the end of the list but there are still some opens
// that are not marked for closing (can happen if we skip an open only because of
// processign flag being set) release the spinlocks, give whoever set the
// processing flag time to release the open. then go back and try again
// ultimately, all opens should either be marked for Unbinding or be gone
// by themselves
//
for (TmpOpen = Miniport->OpenQueue; TmpOpen != NULL; TmpOpen = TmpOpen->MiniportNextOpen) { if (!MINIPORT_TEST_FLAG(TmpOpen, fMINIPORT_OPEN_UNBINDING)) break; }
if (TmpOpen != NULL) { NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql); NDIS_INTERNAL_STALL(50); NDIS_ACQUIRE_MINIPORT_SPIN_LOCK(Miniport, &OldIrql);
goto next; } NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql);
if (pAllOpensClosedEvent) { WAIT_FOR_OBJECT(pAllOpensClosedEvent, NULL); } DBGPRINT_RAW(DBG_COMP_INIT, DBG_LEVEL_INFO, ("<==ndisCloseMiniportBindings, Miniport %p\n", Miniport)); PnPDereferencePackage();
return NDIS_STATUS_SUCCESS; }
VOID NdisMSetPeriodicTimer( IN PNDIS_MINIPORT_TIMER Timer, IN UINT MillisecondsPeriod ) /*++
Routine Description:
Sets up a periodic timer.
Arguments:
Timer - The timer to Set.
MillisecondsPeriod - The timer will fire once every so often.
Return Value:
--*/ { LARGE_INTEGER FireUpTime;
FireUpTime.QuadPart = Int32x32To64((LONG)MillisecondsPeriod, -10000);
#if CHECK_TIMER
if ((Timer->Dpc.DeferredRoutine != ndisMWakeUpDpc) && (Timer->Dpc.DeferredRoutine != ndisMWakeUpDpcX) && (Timer->Miniport->DriverHandle->Flags & fMINIBLOCK_VERIFYING)) { KIRQL OldIrql; PNDIS_MINIPORT_TIMER pTimer;
ACQUIRE_SPIN_LOCK(&Timer->Miniport->TimerQueueLock, &OldIrql); //
// check to see if the timer is already set
//
for (pTimer = Timer->Miniport->TimerQueue; pTimer != NULL; pTimer = pTimer->NextTimer) { if (pTimer == Timer) break; } if (pTimer == NULL) { Timer->NextTimer = Timer->Miniport->TimerQueue; Timer->Miniport->TimerQueue = Timer; } RELEASE_SPIN_LOCK(&Timer->Miniport->TimerQueueLock, OldIrql); } #endif
//
// Set the timer
//
SET_PERIODIC_TIMER(&Timer->Timer, FireUpTime, MillisecondsPeriod, &Timer->Dpc); }
VOID NdisMSleep( IN ULONG MicrosecondsToSleep ) /*++
Routine Description:
Blocks the caller for specified duration of time. Callable at Irql < DISPATCH_LEVEL.
Arguments:
MicrosecondsToSleep - The caller will be blocked for this much time.
Return Value:
NONE
--*/ { KTIMER SleepTimer; LARGE_INTEGER TimerValue;
ASSERT (KeGetCurrentIrql() == LOW_LEVEL);
INITIALIZE_TIMER_EX(&SleepTimer, SynchronizationTimer);
TimerValue.QuadPart = Int32x32To64(MicrosecondsToSleep, -10); SET_TIMER(&SleepTimer, TimerValue, NULL);
WAIT_FOR_OBJECT(&SleepTimer, NULL); }
|