Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1633 lines
41 KiB

/*++
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);
}