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