1634 lines
41 KiB
C
1634 lines
41 KiB
C
/*++
|
|
|
|
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);
|
|
}
|
|
|