windows-nt/Source/XPSP1/NT/net/sfm/atalk/sys/atkpnp.c
2020-09-26 16:20:57 +08:00

693 lines
20 KiB
C

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