/*++ Copyright (c) 1997 Microsoft Corporation Module Name: atkpnp.c Abstract: This module contains the support code for handling PnP events Author: Shirish Koti Revision History: 16 Jun 1997 Initial Version --*/ #include #pragma hdrstop #define FILENUM ATKPNP #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, AtalkPnPHandler) #pragma alloc_text(PAGE, AtalkPnPReconfigure) #pragma alloc_text(PAGE, AtalkPnPEnableAdapter) #endif NDIS_STATUS AtalkPnPHandler( IN NDIS_HANDLE NdisBindCtx, IN PNET_PNP_EVENT pPnPEvent ) { NDIS_STATUS Status=STATUS_SUCCESS; PAGED_CODE(); ASSERT(pPnPEvent); ASSERT(KeGetCurrentIrql() == 0); switch (pPnPEvent->NetEvent) { case NetEventReconfigure: DBGPRINT(DBG_COMP_ACTION, DBG_LEVEL_INFO, ("AtalkPnPHandler: NetEventReconfigure event\n")); Status = AtalkPnPReconfigure(NdisBindCtx,pPnPEvent); break; case NetEventCancelRemoveDevice: DBGPRINT(DBG_COMP_ACTION, DBG_LEVEL_ERR, ("AtalkPnPHandler: NetEventCancelRemoveDevice event\n")); break; case NetEventQueryRemoveDevice: DBGPRINT(DBG_COMP_ACTION, DBG_LEVEL_ERR, ("AtalkPnPHandler: NetEventQueryRemoveDevice event\n")); break; case NetEventQueryPower: DBGPRINT(DBG_COMP_ACTION, DBG_LEVEL_ERR, ("AtalkPnPHandler: NetEventQueryPower event\n")); break; case NetEventSetPower: DBGPRINT(DBG_COMP_ACTION, DBG_LEVEL_ERR, ("AtalkPnPHandler: NetEventSetPower event\n")); break; case NetEventBindsComplete: DBGPRINT(DBG_COMP_ACTION, DBG_LEVEL_ERR, ("AtalkPnPHandler: NetEventBindsComplete event\n")); break; case NetEventBindList: DBGPRINT(DBG_COMP_ACTION, DBG_LEVEL_ERR, ("AtalkPnPHandler: NetEventBindList event\n")); break; default: DBGPRINT(DBG_COMP_ACTION, DBG_LEVEL_ERR, ("AtalkPnPHandler: what is this event?, verify if it is valid/new = %ld\n", pPnPEvent->NetEvent)); break; } ASSERT(Status == STATUS_SUCCESS); return(STATUS_SUCCESS); } NDIS_STATUS AtalkPnPReconfigure( IN NDIS_HANDLE NdisBindCtx, IN PNET_PNP_EVENT pPnPEvent ) { NTSTATUS Status=STATUS_SUCCESS; NTSTATUS LocStatus=STATUS_SUCCESS; PPORT_DESCRIPTOR pPortDesc; PPORT_DESCRIPTOR pPrevPortDesc; PPORT_DESCRIPTOR pNextPortDesc; PPORT_DESCRIPTOR pFirstPortDesc; PPORT_DESCRIPTOR pWalkerPortDesc; PATALK_PNP_EVENT pPnpBuf; BOOLEAN fWeFoundOut; pPortDesc = (PPORT_DESCRIPTOR)NdisBindCtx; pPnpBuf = (PATALK_PNP_EVENT)(pPnPEvent->Buffer); // // if it's a global configuration message, just ignore it because we will // be getting (or have already got) specific messages // if (pPnpBuf == NULL) { DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR, ("AtalkPnPReconfigure: ignoring global config message\n")); return(STATUS_SUCCESS); } if ((!pPortDesc) && (pPnpBuf->PnpMessage != AT_PNP_SWITCH_ROUTING) && (pPnpBuf->PnpMessage != AT_PNP_SWITCH_DEFAULT_ADAPTER)) { DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR, ("AtalkPnPReconfigure: ignoring NULL context (pnp msg = %d)\n", pPnpBuf->PnpMessage)); return(STATUS_SUCCESS); } if (AtalkBindnUnloadStates & ATALK_UNLOADING) { DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR, ("AtalkPnPReconfigure: stack is shutting down, ignoring pnp\n")); return(STATUS_SUCCESS); } AtalkBindnUnloadStates |= ATALK_PNP_IN_PROGRESS; DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR, ("\n\nProcessing PnP Event....\n\n")); AtalkLockInitIfNecessary(); ASSERT(pPnpBuf != NULL); switch (pPnpBuf->PnpMessage) { // // user just checked (or unchecked) the router checkbox! If we are // currently not routing, we must start routing. If we are currently // routing, we must stop routing. "Disable" all the adapters, go read // the global config info and "Enable" all the adapters back. // case AT_PNP_SWITCH_ROUTING: DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR, ("AtalkPnPReconfigure: AT_PNP_SWITCH_ROUTING. Currently, routing is %s\n" ,(AtalkRouter)? "ON" : "OFF" )); pPortDesc = pFirstPortDesc = AtalkPortList; pPrevPortDesc = pPortDesc; if (!pPortDesc) { DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR, ("AtalkPnPReconfigure: no adapter configured! no action taken\n")); break; } // // if we are currently running the router, first stop the global // rtmp and zip timers // if (AtalkRouter) { if (AtalkTimerCancelEvent(&atalkRtmpVTimer, NULL)) { DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR, ("AtalkPnPReconfigure: cancelled atalkRtmpValidityTimer\n")); } else { DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR, ("AtalkPnPReconfigure: couldn't cancel atalkRtmpValidityTimer\n")); } if (AtalkTimerCancelEvent(&atalkZipQTimer, NULL)) { DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR, ("AtalkPnPReconfigure: cancelled atalkZipQueryTimer\n")); } else { DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR, ("AtalkPnPReconfigure: couldn't cancel atalkZipQueryTimer\n")); } } atalkRtmpVdtTmrRunning = FALSE; atalkZipQryTmrRunning = FALSE; // // now, disable all the ports in the list one by one. This actually // removes the adapter from the list as well. Link all these adapters // together so we can enable all of them. // (NDIS guaranteed that no ndis event (pnp, unbind etc.) can happen // when one is in progress, so we don't need lock here) // while (pPortDesc != NULL) { DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR, ("AtalkPnPReconfigure: disabling pPortDesc %lx\n",pPortDesc)); Status = AtalkPnPDisableAdapter(pPortDesc); pPortDesc = AtalkPortList; pPrevPortDesc->pd_Next = pPortDesc; pPrevPortDesc = pPortDesc; } // unlock the pages that we locked when router was first started if (AtalkRouter) { AtalkUnlockRouterIfNecessary(); } if (AtalkDefaultPortName.Buffer) { AtalkFreeMemory(AtalkDefaultPortName.Buffer); AtalkDefaultPortName.Buffer = NULL; } if (AtalkDesiredZone) { ASSERT(AtalkDesiredZone->zn_RefCount >= 1); AtalkZoneDereference(AtalkDesiredZone); AtalkDesiredZone = NULL; } // get rid of routing table, if one exists AtalkRtmpInit(FALSE); // go read all the parms again: registry must have changed LocStatus = atalkInitGlobal(); ASSERT(NT_SUCCESS(LocStatus)); // now, enable all the adapters back! pPortDesc = pFirstPortDesc; while (pPortDesc != NULL) { pNextPortDesc = pPortDesc->pd_Next; pPortDesc->pd_Next = NULL; DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR, ("AtalkPnPReconfigure: enabling pPortDesc %lx\n",pPortDesc)); Status = AtalkPnPEnableAdapter(pPortDesc); pPortDesc = pNextPortDesc; } break; // // user has changed the default adapter. First, "disable" our // current default adapter and the wannabe default adapter. Then, // "enable" both the adapters, and that should take care of everything! // case AT_PNP_SWITCH_DEFAULT_ADAPTER: DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR, ("AtalkPnPReconfigure: AT_PNP_SWITCH_DEFAULT_ADAPTER (old=(%lx) new=(%lx)\n", AtalkDefaultPort,pPortDesc)); pPrevPortDesc = AtalkDefaultPort; // check if default adapter exists: it's possible that at this moment there isn't one if (pPrevPortDesc) { Status = AtalkPnPDisableAdapter(pPrevPortDesc); } // release the default adapter name buffer, and desired zone buffer if (AtalkDefaultPortName.Buffer) { AtalkFreeMemory(AtalkDefaultPortName.Buffer); AtalkDefaultPortName.Buffer = NULL; } if (AtalkDesiredZone) { ASSERT(AtalkDesiredZone->zn_RefCount >= 1); AtalkZoneDereference(AtalkDesiredZone); AtalkDesiredZone = NULL; } // go read all the parms again: registry must have changed LocStatus = atalkInitGlobal(); ASSERT(NT_SUCCESS(LocStatus)); fWeFoundOut = FALSE; ASSERT(AtalkDefaultPortName.Buffer != NULL); // if we know who the new default adapter is going to be, disable him now if (pPortDesc != NULL) { Status = AtalkPnPDisableAdapter(pPortDesc); } // // UI doesn't know who the default adapter is, so let's find out // AtalkDefaultPortName.Buffer can not be null, but let's not bugcheck if // there is some problem in how UI does things. // else if (AtalkDefaultPortName.Buffer != NULL) { // // note that we aren't holding AtalkPortLock here. The only way // the list can change is if an adapter binds or unbinds. Since ndis // guarantees that all bind/unbind/pnp operations are serialized, and // since ndis has already called us here, the list can't change. // pPortDesc = AtalkPortList; while (pPortDesc != NULL) { if (RtlEqualUnicodeString(&pPortDesc->pd_AdapterName, &AtalkDefaultPortName, TRUE)) { fWeFoundOut = TRUE; break; } pPortDesc = pPortDesc->pd_Next; } if (pPortDesc == NULL) { DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR, ("AtalkPnPReconfigure: still no default port????\n")); } } // // if there default adapter existed before this, reenable it (to be // a non-default adapter) // if (pPrevPortDesc) { Status = AtalkPnPEnableAdapter(pPrevPortDesc); } // // if we were told who the default adapter is, or if we found out // ourselves and one of the existing adapters is the default adatper, // disable it and reenable // if (pPortDesc) { // disable this guy if we found him out if (fWeFoundOut) { Status = AtalkPnPDisableAdapter(pPortDesc); } // reenable the new adapter so that it is now the default adatper Status = AtalkPnPEnableAdapter(pPortDesc); DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR, ("AtalkPnPReconfigure: %lx is the new default adapter\n",pPortDesc)); ASSERT(AtalkDefaultPort == pPortDesc); } else { DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR, ("AtalkPnPReconfigure: no default adapter configured!\n")); } break; // // user has changed some parameter on the adapter (e.g. the desired zone, // or some seeding info etc.). Just "disable" and then "enable" this // adapter, and everything should just work! // case AT_PNP_RECONFIGURE_PARMS: DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR, ("AtalkPnPReconfigure: AT_PNP_RECONFIGURE_PARMS on pPortDesc %lx\n",pPortDesc)); Status = AtalkPnPDisableAdapter(pPortDesc); // release the default adapter name buffer, and desired zone buffer if (AtalkDefaultPortName.Buffer) { AtalkFreeMemory(AtalkDefaultPortName.Buffer); AtalkDefaultPortName.Buffer = NULL; } if (AtalkDesiredZone) { ASSERT(AtalkDesiredZone->zn_RefCount >= 1); AtalkZoneDereference(AtalkDesiredZone); AtalkDesiredZone = NULL; } // go read all the parms again: registry must have changed LocStatus = atalkInitGlobal(); ASSERT(NT_SUCCESS(LocStatus)); Status = AtalkPnPEnableAdapter(pPortDesc); break; default: DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR, ("AtalkPnPReconfigure: and what msg is this (%ld) ??\n",pPnpBuf->PnpMessage)); ASSERT(0); break; } AtalkUnlockInitIfNecessary(); ASSERT(Status == STATUS_SUCCESS); AtalkBindnUnloadStates &= ~ATALK_PNP_IN_PROGRESS; DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR, ("\n\n.... completed processing PnP Event\n\n")); return(STATUS_SUCCESS); } NTSTATUS AtalkPnPDisableAdapter( IN PPORT_DESCRIPTOR pPortDesc ) { NTSTATUS Status; KIRQL OldIrql; PLIST_ENTRY pList; PARAPCONN pArapConn; PATCPCONN pAtcpConn; BOOLEAN fDllDeref; BOOLEAN fLineDownDeref; if (!pPortDesc) { DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR, ("AtalkPnPDisableAdapter: pPortDesc is NULL!!!\n")); return(STATUS_SUCCESS); } ASSERT(VALID_PORT(pPortDesc)); DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR, ("AtalkPnPDisableAdapter: entered with %lx\n",pPortDesc)); // // we are going to "disable" this port due to PnP: note that fact! // ACQUIRE_SPIN_LOCK(&pPortDesc->pd_Lock, &OldIrql); pPortDesc->pd_Flags |= PD_PNP_RECONFIGURE; RELEASE_SPIN_LOCK(&pPortDesc->pd_Lock, OldIrql); // First and foremost: tell guys above so they can cleanup if (pPortDesc->pd_Flags & PD_DEF_PORT) { ASSERT(pPortDesc == AtalkDefaultPort); if (TdiAddressChangeRegHandle) { TdiDeregisterNetAddress(TdiAddressChangeRegHandle); TdiAddressChangeRegHandle = NULL; DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR, ("AtalkPnPDisableAdapter: TdiDeregisterNetAddress on %Z done\n", &pPortDesc->pd_AdapterName)); } // this will tell AFP if (TdiRegistrationHandle) { DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR, ("AtalkPnPDisableAdapter: telling AFP about PnP\n")); TdiDeregisterDeviceObject(TdiRegistrationHandle); TdiRegistrationHandle = NULL; } // this will take care of informing ARAP and PPP engine above AtalkPnPInformRas(FALSE); } // // if this is RAS port or the Default port, kill all the ARAP and PPP // connections if any are left. // Since we've marked that PnpReconfigure is in progress, no more // new connections will be allowed // if ((pPortDesc == RasPortDesc) || ((pPortDesc->pd_Flags & PD_DEF_PORT) && (RasPortDesc != NULL))) { ACQUIRE_SPIN_LOCK(&RasPortDesc->pd_Lock, &OldIrql); pList = RasPortDesc->pd_ArapConnHead.Flink; // first, the ARAP guys while (pList != &RasPortDesc->pd_ArapConnHead) { pArapConn = CONTAINING_RECORD(pList, ARAPCONN, Linkage); ASSERT(pArapConn->Signature == ARAPCONN_SIGNATURE); // if this connection is already disconnected, skip it ACQUIRE_SPIN_LOCK_DPC(&pArapConn->SpinLock); if (pArapConn->State == MNP_DISCONNECTED) { pList = pArapConn->Linkage.Flink; RELEASE_SPIN_LOCK_DPC(&pArapConn->SpinLock); continue; } RELEASE_SPIN_LOCK_DPC(&pArapConn->SpinLock); RELEASE_SPIN_LOCK(&RasPortDesc->pd_Lock, OldIrql); DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR, ("AtalkPnPDisableAdapter: killing ARAP connection %lx\n",pArapConn)); ArapCleanup(pArapConn); ACQUIRE_SPIN_LOCK(&RasPortDesc->pd_Lock, &OldIrql); pList = RasPortDesc->pd_ArapConnHead.Flink; } // and now, the PPP guys // if there are any ppp guys, remove them from this list and dereference // them. In most cases, they will get freed right away. If someone had // a refcount, it will get freed when that refcount goes away while (!(IsListEmpty(&RasPortDesc->pd_PPPConnHead))) { pList = RasPortDesc->pd_PPPConnHead.Flink; pAtcpConn = CONTAINING_RECORD(pList, ATCPCONN, Linkage); ASSERT(pAtcpConn->Signature == ATCPCONN_SIGNATURE); ACQUIRE_SPIN_LOCK_DPC(&pAtcpConn->SpinLock); RemoveEntryList(&pAtcpConn->Linkage); InitializeListHead(&pAtcpConn->Linkage); fDllDeref = (pAtcpConn->Flags & ATCP_DLL_SETUP_DONE)? TRUE : FALSE; fLineDownDeref = (pAtcpConn->Flags & ATCP_LINE_UP_DONE)? TRUE : FALSE; pAtcpConn->Flags &= ~(ATCP_DLL_SETUP_DONE|ATCP_LINE_UP_DONE); RELEASE_SPIN_LOCK_DPC(&pAtcpConn->SpinLock); RELEASE_SPIN_LOCK(&RasPortDesc->pd_Lock, OldIrql); DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR, ("AtalkPnPDisableAdapter: deref'ing PPP conn %lx (%d+%d times)\n", pAtcpConn,fDllDeref,fLineDownDeref)); // remove the DLL refcount if (fDllDeref) { DerefPPPConn(pAtcpConn); } // remove the NDISWAN refcount if (fLineDownDeref) { DerefPPPConn(pAtcpConn); } ACQUIRE_SPIN_LOCK(&RasPortDesc->pd_Lock, &OldIrql); } RELEASE_SPIN_LOCK(&RasPortDesc->pd_Lock, OldIrql); } // // "Disable" the adapter (basically we do everything except close the // adapter with ndis and freeing up the pPortDesc memory) // Status = AtalkDeinitAdapter(pPortDesc); if (!NT_SUCCESS(Status)) { DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR, ("AtalkPnPDisableAdapter: AtalkDeinitAdapter failed %lx\n",Status)); ASSERT(0); } return(Status); } NTSTATUS AtalkPnPEnableAdapter( IN PPORT_DESCRIPTOR pPortDesc ) { NTSTATUS Status; if (!pPortDesc) { DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR, ("AtalkPnPDisableAdapter: pPortDesc is NULL!!!\n")); return(STATUS_SUCCESS); } DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR, ("AtalkPnPEnableAdapter: entered with %lx\n",pPortDesc)); // // "Enable" the adapter (we do everything except that we don't // allocate memory for pPortDesc - since we didn't free it, and we don't // open the adapter with ndis - since we didn't close it). // Status = AtalkInitAdapter(NULL, pPortDesc); // we are done with the PnPReconfigure evnet: reset that bit AtalkPortSetResetFlag(pPortDesc, TRUE, PD_PNP_RECONFIGURE); // tell ARAP everything is ok if (pPortDesc->pd_Flags & (PD_DEF_PORT | PD_RAS_PORT)) { ASSERT((pPortDesc == AtalkDefaultPort) || (pPortDesc == RasPortDesc)); // this will take care of informing ARAP and PPP engine above AtalkPnPInformRas(TRUE); } if (!NT_SUCCESS(Status)) { DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR, ("AtalkPnPEnableAdapter: AtalkInitAdapter failed %lx\n",Status)); ASSERT(0); } DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_ERR, ("AtalkPnPEnableAdapter: completed PnP on %lx (flag %lx)\n", pPortDesc,pPortDesc->pd_Flags)); return(Status); }