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.
5587 lines
118 KiB
5587 lines
118 KiB
/*++
|
|
|
|
Copyright (c) 1997 FORE Systems, Inc.
|
|
Copyright (c) 1997 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
elanproc.c
|
|
|
|
Abstract:
|
|
|
|
Revision History:
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
|
|
|
|
#include <precomp.h>
|
|
#pragma hdrstop
|
|
|
|
VOID
|
|
AtmLaneEventHandler(
|
|
IN PNDIS_WORK_ITEM pWorkItem,
|
|
IN PVOID pContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Elan state machine event handler.
|
|
|
|
Arguments:
|
|
|
|
pContext - should be pointer to ATMLANE Elan
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PATMLANE_ELAN pElan;
|
|
PATMLANE_MAC_ENTRY pMacEntry;
|
|
PATMLANE_EVENT pEvent;
|
|
ULONG Event;
|
|
NDIS_STATUS EventStatus;
|
|
NDIS_STATUS Status;
|
|
NDIS_HANDLE AdapterHandle;
|
|
ULONG rc;
|
|
BOOLEAN WasCancelled;
|
|
PLIST_ENTRY p;
|
|
#if DEBUG_IRQL
|
|
KIRQL EntryIrql;
|
|
#endif
|
|
|
|
#if DEBUG_IRQL
|
|
GET_ENTRY_IRQL(EntryIrql);
|
|
ASSERT(EntryIrql == PASSIVE_LEVEL);
|
|
#endif
|
|
|
|
TRACEIN(EventHandler);
|
|
|
|
// Get the pointer to the Elan
|
|
|
|
pElan = (PATMLANE_ELAN)pContext;
|
|
STRUCT_ASSERT(pElan, atmlane_elan);
|
|
|
|
// Lock the Elan
|
|
|
|
ACQUIRE_ELAN_LOCK(pElan);
|
|
|
|
// Release the timer reference
|
|
|
|
rc = AtmLaneDereferenceElan(pElan, "eventtimer");
|
|
ASSERT(rc > 0);
|
|
pElan->Flags &= ~ ELAN_EVENT_WORK_ITEM_SET;
|
|
|
|
// Remove the event at the head of the queue
|
|
|
|
pEvent = AtmLaneDequeueElanEvent(pElan);
|
|
|
|
if (pEvent == NULL)
|
|
{
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
CHECK_EXIT_IRQL(EntryIrql);
|
|
return;
|
|
}
|
|
|
|
// Save locally the important stuff
|
|
|
|
Event = pEvent->Event;
|
|
EventStatus = pEvent->EventStatus;
|
|
|
|
// Free the event struct
|
|
|
|
FREE_MEM(pEvent);
|
|
|
|
// Check if the ELAN is gone.
|
|
if (rc == 1)
|
|
{
|
|
DBGP((0, "EventHandler: ELAN %x is gone!\n", pElan));
|
|
CHECK_EXIT_IRQL(EntryIrql);
|
|
return;
|
|
}
|
|
|
|
// If queue isn't empty schedule next event handler
|
|
|
|
if (!IsListEmpty(&pElan->EventQueue))
|
|
{
|
|
NDIS_STATUS NdisStatus;
|
|
|
|
|
|
AtmLaneReferenceElan(pElan, "workitemevent");
|
|
pElan->Flags |= ELAN_EVENT_WORK_ITEM_SET;
|
|
|
|
p = pElan->EventQueue.Flink;
|
|
pEvent = CONTAINING_RECORD(p, ATMLANE_EVENT, Link);
|
|
NdisInitializeWorkItem(&pElan->EventWorkItem, AtmLaneEventHandler, pElan);
|
|
|
|
DBGP((0, " %d EventHandler: Multiple events queued, pElan %x, State %d, Flags %x, Ref %d, processing %d, queued event %d!\n",
|
|
pElan->ElanNumber, pElan, pElan->State, pElan->Flags, pElan->RefCount, Event, pEvent->Event));
|
|
NdisStatus = NdisScheduleWorkItem(&pElan->EventWorkItem);
|
|
ASSERT(NdisStatus == NDIS_STATUS_SUCCESS);
|
|
}
|
|
|
|
switch (pElan->State)
|
|
{
|
|
//
|
|
// INIT STATE ---------------------------------------------------
|
|
//
|
|
case ELAN_STATE_INIT:
|
|
|
|
switch (Event)
|
|
{
|
|
case ELAN_EVENT_START:
|
|
|
|
|
|
DBGP((1, "%d INIT - START\n", pElan->ElanNumber));
|
|
|
|
//
|
|
// Open Call Manager and get ATM address
|
|
//
|
|
|
|
//
|
|
// Make sure that ShutdownElan does not
|
|
// pre-empt us here.
|
|
//
|
|
pElan->Flags |= ELAN_OPENING_AF;
|
|
AtmLaneReferenceElan(pElan, "openaf");
|
|
INIT_BLOCK_STRUCT(&pElan->AfBlock);
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
|
|
Status = AtmLaneOpenCallMgr(pElan);
|
|
if (NDIS_STATUS_SUCCESS == Status)
|
|
{
|
|
AtmLaneGetAtmAddress(pElan);
|
|
}
|
|
|
|
ACQUIRE_ELAN_LOCK(pElan);
|
|
rc = AtmLaneDereferenceElan(pElan, "openaf");
|
|
if (rc != 0)
|
|
{
|
|
pElan->Flags &= ~ELAN_OPENING_AF;
|
|
SIGNAL_BLOCK_STRUCT(&pElan->AfBlock, NDIS_STATUS_SUCCESS);
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
}
|
|
|
|
break;
|
|
|
|
case ELAN_EVENT_NEW_ATM_ADDRESS:
|
|
|
|
DBGP((1, "%d INIT - NEW ATM ADDRESS\n", pElan->ElanNumber));
|
|
|
|
if (!pElan->CfgUseLecs)
|
|
{
|
|
//
|
|
// If configured to NOT use an LECS then
|
|
// set ELAN vars from registry config vars
|
|
// (normally established in ConfigResponseHandler)
|
|
// and advance to the LES CONNECT Phase.
|
|
|
|
pElan->LanType = (UCHAR)pElan->CfgLanType;
|
|
if (pElan->LanType == LANE_LANTYPE_UNSPEC)
|
|
{
|
|
pElan->LanType = LANE_LANTYPE_ETH;
|
|
}
|
|
pElan->MaxFrameSizeCode = (UCHAR)pElan->CfgMaxFrameSizeCode;
|
|
if (pElan->MaxFrameSizeCode == LANE_MAXFRAMESIZE_CODE_UNSPEC)
|
|
{
|
|
pElan->MaxFrameSizeCode = LANE_MAXFRAMESIZE_CODE_1516;
|
|
}
|
|
switch (pElan->MaxFrameSizeCode)
|
|
{
|
|
case LANE_MAXFRAMESIZE_CODE_18190:
|
|
pElan->MaxFrameSize = 18190;
|
|
break;
|
|
case LANE_MAXFRAMESIZE_CODE_9234:
|
|
pElan->MaxFrameSize = 9234;
|
|
break;
|
|
case LANE_MAXFRAMESIZE_CODE_4544:
|
|
pElan->MaxFrameSize = 4544;
|
|
break;
|
|
case LANE_MAXFRAMESIZE_CODE_1516:
|
|
case LANE_MAXFRAMESIZE_CODE_UNSPEC:
|
|
default:
|
|
pElan->MaxFrameSize = 1516;
|
|
break;
|
|
}
|
|
|
|
if (pElan->LanType == LANE_LANTYPE_ETH)
|
|
{
|
|
pElan->MinFrameSize = LANE_MIN_ETHPACKET;
|
|
}
|
|
else
|
|
{
|
|
pElan->MinFrameSize = LANE_MIN_TRPACKET;
|
|
}
|
|
|
|
NdisMoveMemory(
|
|
&pElan->LesAddress,
|
|
&pElan->CfgLesAddress,
|
|
sizeof(ATM_ADDRESS));
|
|
|
|
pElan->State = ELAN_STATE_LES_CONNECT;
|
|
|
|
pElan->RetriesLeft = 4;
|
|
|
|
AtmLaneQueueElanEvent(pElan, ELAN_EVENT_START, 0);
|
|
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// If configured to NOT discover the LECS then
|
|
// advance to the LECS CONNECT CFG state.
|
|
//
|
|
if (!pElan->CfgDiscoverLecs)
|
|
{
|
|
pElan->State = ELAN_STATE_LECS_CONNECT_CFG;
|
|
|
|
pElan->RetriesLeft = 4;
|
|
|
|
AtmLaneQueueElanEvent(pElan, ELAN_EVENT_START, 0);
|
|
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Otherwise, advance to LECS CONNECT ILMI state
|
|
//
|
|
pElan->State = ELAN_STATE_LECS_CONNECT_ILMI;
|
|
|
|
pElan->RetriesLeft = 4;
|
|
|
|
AtmLaneQueueElanEvent(pElan, ELAN_EVENT_START, 0);
|
|
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case ELAN_EVENT_RESTART:
|
|
|
|
DBGP((1, "%d INIT - RESTART\n", pElan->ElanNumber));
|
|
|
|
AtmLaneShutdownElan(pElan, TRUE);
|
|
|
|
break;
|
|
|
|
case ELAN_EVENT_STOP:
|
|
|
|
DBGP((1, "%d INIT - STOP\n", pElan->ElanNumber));
|
|
|
|
AtmLaneShutdownElan(pElan, FALSE);
|
|
|
|
break;
|
|
|
|
default:
|
|
DBGP((0, "%d INIT - UNEXPECTED EVENT %d\n",
|
|
pElan->ElanNumber, Event));
|
|
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
//
|
|
// LECS CONNECT ILMI STATE -------------------------------------------
|
|
//
|
|
case ELAN_STATE_LECS_CONNECT_ILMI:
|
|
|
|
switch (Event)
|
|
{
|
|
case ELAN_EVENT_START:
|
|
|
|
DBGP((1, "%d LECS CONNECT ILMI - START\n", pElan->ElanNumber));
|
|
|
|
SET_FLAG(
|
|
pElan->Flags,
|
|
ELAN_LECS_MASK,
|
|
ELAN_LECS_ILMI
|
|
);
|
|
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
|
|
AtmLaneGetLecsIlmi(pElan);
|
|
|
|
break;
|
|
|
|
case ELAN_EVENT_GOT_ILMI_LECS_ADDR:
|
|
|
|
DBGP((1, "%d LECS CONNECT ILMI - GOT ILMI LECS ADDR (%x)\n",
|
|
pElan->ElanNumber, EventStatus));
|
|
|
|
if (EventStatus == NDIS_STATUS_SUCCESS)
|
|
{
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
//
|
|
// Attempt to connect to the LECS
|
|
//
|
|
AtmLaneConnectToServer(pElan, ATM_ENTRY_TYPE_LECS, FALSE);
|
|
}
|
|
else
|
|
{
|
|
if (EventStatus == NDIS_STATUS_INTERFACE_DOWN)
|
|
{
|
|
//
|
|
// Wait for a while for the interface to come up.
|
|
//
|
|
DBGP((0, "%d LECS CONNECT ILMI - Interface down\n",
|
|
pElan->ElanNumber));
|
|
|
|
AtmLaneQueueElanEventAfterDelay(pElan, ELAN_EVENT_START, NDIS_STATUS_SUCCESS, 2*1000);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Otherwise advance to LECS CONNECT WKA state
|
|
//
|
|
|
|
pElan->State = ELAN_STATE_LECS_CONNECT_WKA;
|
|
|
|
pElan->RetriesLeft = 4;
|
|
|
|
AtmLaneQueueElanEvent(pElan, ELAN_EVENT_START, 0);
|
|
}
|
|
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
|
|
}
|
|
break;
|
|
|
|
case ELAN_EVENT_SVR_CALL_COMPLETE:
|
|
|
|
DBGP((1, "%d LECS CONNECT ILMI - LECS CALL COMPLETE (%x)\n",
|
|
pElan->ElanNumber, EventStatus));
|
|
|
|
switch (EventStatus)
|
|
{
|
|
case NDIS_STATUS_SUCCESS:
|
|
|
|
//
|
|
// advance to the CONFIGURE Phase
|
|
//
|
|
pElan->State = ELAN_STATE_CONFIGURE;
|
|
|
|
pElan->RetriesLeft = 4;
|
|
|
|
AtmLaneQueueElanEvent(pElan, ELAN_EVENT_START, 0);
|
|
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
|
|
break;
|
|
|
|
case NDIS_STATUS_INTERFACE_DOWN:
|
|
|
|
if (pElan->RetriesLeft--)
|
|
{
|
|
//
|
|
// retry in a little while
|
|
//
|
|
AtmLaneQueueElanEventAfterDelay(
|
|
pElan,
|
|
ELAN_EVENT_GOT_ILMI_LECS_ADDR,
|
|
NDIS_STATUS_SUCCESS,
|
|
2*1000);
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Restart the Elan
|
|
//
|
|
AtmLaneShutdownElan(pElan, TRUE);
|
|
//
|
|
// lock released in above
|
|
//
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
//
|
|
// Call failed, advance to LECS CONNECT WKA state
|
|
//
|
|
pElan->State = ELAN_STATE_LECS_CONNECT_WKA;
|
|
|
|
pElan->RetriesLeft = 4;
|
|
|
|
AtmLaneQueueElanEvent(pElan, ELAN_EVENT_START, 0);
|
|
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
case ELAN_EVENT_RESTART:
|
|
|
|
DBGP((1, "%d LECS CONNECT ILMI - RESTART\n", pElan->ElanNumber));
|
|
|
|
AtmLaneShutdownElan(pElan, TRUE);
|
|
|
|
break;
|
|
|
|
case ELAN_EVENT_STOP:
|
|
|
|
DBGP((1, "%d LECS CONNECT ILMI - STOP\n", pElan->ElanNumber));
|
|
|
|
AtmLaneShutdownElan(pElan, FALSE);
|
|
|
|
break;
|
|
|
|
default:
|
|
DBGP((0, "%d LECS CONNECT ILMI - UNEXPECTED EVENT %d\n",
|
|
pElan->ElanNumber, Event));
|
|
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
|
|
break;
|
|
}
|
|
break;
|
|
|
|
//
|
|
// LECS CONNECT WKA STATE -------------------------------------------
|
|
//
|
|
case ELAN_STATE_LECS_CONNECT_WKA:
|
|
|
|
switch (Event)
|
|
{
|
|
case ELAN_EVENT_START:
|
|
|
|
DBGP((1, "%d LECS CONNECT WKA - START\n", pElan->ElanNumber));
|
|
|
|
//
|
|
// Attempt to connect to the LECS with Well-Known Address
|
|
//
|
|
SET_FLAG(
|
|
pElan->Flags,
|
|
ELAN_LECS_MASK,
|
|
ELAN_LECS_WKA);
|
|
|
|
NdisMoveMemory(
|
|
&pElan->LecsAddress,
|
|
&gWellKnownLecsAddress,
|
|
sizeof(ATM_ADDRESS));
|
|
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
|
|
AtmLaneConnectToServer(pElan, ATM_ENTRY_TYPE_LECS, FALSE);
|
|
|
|
break;
|
|
|
|
case ELAN_EVENT_SVR_CALL_COMPLETE:
|
|
|
|
DBGP((1, "%d LECS CONNECT WKA - LECS CALL COMPLETE (%x)\n",
|
|
pElan->ElanNumber, EventStatus));
|
|
|
|
switch (EventStatus)
|
|
{
|
|
case NDIS_STATUS_SUCCESS:
|
|
|
|
//
|
|
// advance to the CONFIGURE Phase
|
|
//
|
|
pElan->State = ELAN_STATE_CONFIGURE;
|
|
|
|
pElan->RetriesLeft = 4;
|
|
|
|
AtmLaneQueueElanEvent(pElan, ELAN_EVENT_START, 0);
|
|
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
|
|
break;
|
|
|
|
case NDIS_STATUS_INTERFACE_DOWN:
|
|
|
|
if (pElan->RetriesLeft--)
|
|
{
|
|
//
|
|
// retry in a little while
|
|
//
|
|
AtmLaneQueueElanEventAfterDelay(
|
|
pElan,
|
|
ELAN_EVENT_START,
|
|
NDIS_STATUS_SUCCESS,
|
|
2*1000);
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Return to the Init State in a little while
|
|
//
|
|
AtmLaneShutdownElan(pElan, TRUE);
|
|
//
|
|
// lock released in above
|
|
//
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
//
|
|
// Call failed, advance to LECS CONNECT PVC state
|
|
//
|
|
pElan->State = ELAN_STATE_LECS_CONNECT_PVC;
|
|
|
|
pElan->RetriesLeft = 2;
|
|
|
|
AtmLaneQueueElanEvent(pElan, ELAN_EVENT_START, 0);
|
|
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
case ELAN_EVENT_RESTART:
|
|
|
|
DBGP((1, "%d LECS CONNECT WKA - RESTART\n", pElan->ElanNumber));
|
|
|
|
AtmLaneShutdownElan(pElan, TRUE);
|
|
|
|
break;
|
|
|
|
case ELAN_EVENT_STOP:
|
|
|
|
DBGP((1, "%d LECS CONNECT WKA - STOP\n", pElan->ElanNumber));
|
|
|
|
AtmLaneShutdownElan(pElan, FALSE);
|
|
|
|
break;
|
|
|
|
default:
|
|
DBGP((0, "%d LECS CONNECT WKA - UNEXPECTED EVENT %d\n",
|
|
pElan->ElanNumber, Event));
|
|
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
|
|
break;
|
|
}
|
|
break;
|
|
|
|
//
|
|
// LECS CONNECT PVC STATE -------------------------------------------
|
|
//
|
|
case ELAN_STATE_LECS_CONNECT_PVC:
|
|
|
|
switch (Event)
|
|
{
|
|
case ELAN_EVENT_START:
|
|
|
|
DBGP((1, "%d LECS CONNECT PVC - START\n", pElan->ElanNumber));
|
|
|
|
//
|
|
// Attempt to connect to the LECS using PVC (0,17)
|
|
//
|
|
SET_FLAG(
|
|
pElan->Flags,
|
|
ELAN_LECS_MASK,
|
|
ELAN_LECS_PVC);
|
|
|
|
NdisZeroMemory(
|
|
&pElan->LecsAddress,
|
|
sizeof(ATM_ADDRESS));
|
|
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
|
|
AtmLaneConnectToServer(pElan, ATM_ENTRY_TYPE_LECS, TRUE);
|
|
|
|
break;
|
|
|
|
case ELAN_EVENT_SVR_CALL_COMPLETE:
|
|
|
|
DBGP((1, "%d LECS CONNECT PVC - LECS CALL COMPLETE (%x)\n",
|
|
pElan->ElanNumber, EventStatus));
|
|
|
|
switch (EventStatus)
|
|
{
|
|
case NDIS_STATUS_SUCCESS:
|
|
|
|
//
|
|
// advance to the CONFIGURE Phase
|
|
//
|
|
pElan->State = ELAN_STATE_CONFIGURE;
|
|
|
|
pElan->RetriesLeft = 2;
|
|
|
|
AtmLaneQueueElanEvent(pElan, ELAN_EVENT_START, 0);
|
|
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
|
|
break;
|
|
|
|
case NDIS_STATUS_INTERFACE_DOWN:
|
|
|
|
if (pElan->RetriesLeft--)
|
|
{
|
|
//
|
|
// retry in a little while
|
|
//
|
|
AtmLaneQueueElanEventAfterDelay(
|
|
pElan,
|
|
ELAN_EVENT_START,
|
|
NDIS_STATUS_SUCCESS,
|
|
2*1000);
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Return to the Init State in a little while
|
|
//
|
|
AtmLaneShutdownElan(pElan, TRUE);
|
|
//
|
|
// lock released in above
|
|
//
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
//
|
|
// Call failed, Return to the Init State in a little while
|
|
//
|
|
AtmLaneShutdownElan(pElan, TRUE);
|
|
//
|
|
// lock released in above
|
|
//
|
|
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
case ELAN_EVENT_RESTART:
|
|
|
|
DBGP((1, "%d LECS CONNECT PVC - RESTART\n", pElan->ElanNumber));
|
|
|
|
AtmLaneShutdownElan(pElan, TRUE);
|
|
|
|
break;
|
|
|
|
case ELAN_EVENT_STOP:
|
|
|
|
DBGP((1, "%d LECS CONNECT PVC - STOP\n", pElan->ElanNumber));
|
|
|
|
AtmLaneShutdownElan(pElan, FALSE);
|
|
|
|
break;
|
|
|
|
default:
|
|
DBGP((0, "%d LECS CONNECT PVC - UNEXPECTED %d\n",
|
|
pElan->ElanNumber, Event));
|
|
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
|
|
break;
|
|
}
|
|
break;
|
|
|
|
//
|
|
// LECS CONNECT CFG STATE -------------------------------------------
|
|
//
|
|
case ELAN_STATE_LECS_CONNECT_CFG:
|
|
|
|
switch (Event)
|
|
{
|
|
case ELAN_EVENT_START:
|
|
|
|
DBGP((1, "%d LECS CONNECT CFG - START\n", pElan->ElanNumber));
|
|
|
|
//
|
|
// Attempt to connect to the LECS with configured Address
|
|
//
|
|
SET_FLAG(
|
|
pElan->Flags,
|
|
ELAN_LECS_MASK,
|
|
ELAN_LECS_CFG);
|
|
|
|
NdisMoveMemory(
|
|
&pElan->LecsAddress,
|
|
&pElan->CfgLecsAddress,
|
|
sizeof(ATM_ADDRESS));
|
|
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
|
|
AtmLaneConnectToServer(pElan, ATM_ENTRY_TYPE_LECS, FALSE);
|
|
|
|
break;
|
|
|
|
case ELAN_EVENT_SVR_CALL_COMPLETE:
|
|
|
|
DBGP((1, "%d LECS CONNECT CFG - LECS CALL COMPLETE (%x)\n",
|
|
pElan->ElanNumber, EventStatus));
|
|
|
|
switch (EventStatus)
|
|
{
|
|
case NDIS_STATUS_SUCCESS:
|
|
|
|
//
|
|
// advance to the CONFIGURE Phase
|
|
//
|
|
pElan->State = ELAN_STATE_CONFIGURE;
|
|
|
|
pElan->RetriesLeft = 4;
|
|
|
|
AtmLaneQueueElanEvent(pElan, ELAN_EVENT_START, 0);
|
|
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
|
|
break;
|
|
|
|
case NDIS_STATUS_INTERFACE_DOWN:
|
|
|
|
if (pElan->RetriesLeft--)
|
|
{
|
|
//
|
|
// retry in a little while
|
|
//
|
|
AtmLaneQueueElanEventAfterDelay(
|
|
pElan,
|
|
ELAN_EVENT_START,
|
|
NDIS_STATUS_SUCCESS,
|
|
2*1000);
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Return to the Init State in a little while
|
|
//
|
|
AtmLaneShutdownElan(pElan, TRUE);
|
|
//
|
|
// lock released in above
|
|
//
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
//
|
|
// Call failed, XXX What to do ?? Shutdown ?? Log ??
|
|
//
|
|
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
case ELAN_EVENT_RESTART:
|
|
|
|
DBGP((1, "%d LECS CONNECT CFG - RESTART\n", pElan->ElanNumber));
|
|
|
|
AtmLaneShutdownElan(pElan, TRUE);
|
|
|
|
break;
|
|
|
|
case ELAN_EVENT_STOP:
|
|
|
|
DBGP((1, "%d LECS CONNECT CFG - STOP\n", pElan->ElanNumber));
|
|
|
|
AtmLaneShutdownElan(pElan, FALSE);
|
|
|
|
break;
|
|
|
|
default:
|
|
DBGP((0, "%d LECS CONNECT CFG - UNEXPECTED EVENT %d\n",
|
|
pElan->ElanNumber, Event));
|
|
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
|
|
break;
|
|
}
|
|
break;
|
|
|
|
//
|
|
// CONFIGURE STATE -------------------------------------------
|
|
//
|
|
case ELAN_STATE_CONFIGURE:
|
|
|
|
switch (Event)
|
|
{
|
|
case ELAN_EVENT_START:
|
|
|
|
DBGP((1, "%d CONFIGURE - START\n", pElan->ElanNumber));
|
|
|
|
//
|
|
// Start configure request timer
|
|
//
|
|
AtmLaneReferenceElan(pElan, "timer"); // timer reference
|
|
AtmLaneStartTimer(
|
|
pElan,
|
|
&pElan->Timer,
|
|
AtmLaneConfigureResponseTimeout,
|
|
pElan->ControlTimeout,
|
|
pElan);
|
|
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
|
|
//
|
|
// Send a configure request
|
|
//
|
|
AtmLaneSendConfigureRequest(pElan);
|
|
|
|
break;
|
|
|
|
case ELAN_EVENT_CONFIGURE_RESPONSE:
|
|
|
|
DBGP((1, "%d CONFIGURE - CONFIGURE RESPONSE (%x)\n",
|
|
pElan->ElanNumber, EventStatus));
|
|
|
|
switch (EventStatus)
|
|
{
|
|
case NDIS_STATUS_SUCCESS:
|
|
//
|
|
// Stop configure request timer
|
|
//
|
|
if (AtmLaneStopTimer(&pElan->Timer, pElan))
|
|
{
|
|
rc = AtmLaneDereferenceElan(pElan, "timer");
|
|
ASSERT(rc > 0);
|
|
}
|
|
|
|
//
|
|
// Close the LECS Connection
|
|
//
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
ACQUIRE_ATM_ENTRY_LOCK(pElan->pLecsAtmEntry);
|
|
AtmLaneInvalidateAtmEntry(pElan->pLecsAtmEntry);
|
|
|
|
//
|
|
// Advance to LES CONNECT phase.
|
|
//
|
|
ACQUIRE_ELAN_LOCK(pElan);
|
|
|
|
pElan->State = ELAN_STATE_LES_CONNECT;
|
|
|
|
AtmLaneQueueElanEvent(pElan, ELAN_EVENT_START, 0);
|
|
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
|
|
break;
|
|
|
|
case NDIS_STATUS_TIMEOUT:
|
|
|
|
//
|
|
// Return to the Init State in a little while
|
|
//
|
|
AtmLaneShutdownElan(pElan, TRUE);
|
|
//
|
|
// lock released in above
|
|
//
|
|
|
|
break;
|
|
|
|
case NDIS_STATUS_FAILURE:
|
|
|
|
//
|
|
// Return to the Init State in a little while
|
|
//
|
|
AtmLaneShutdownElan(pElan, TRUE);
|
|
//
|
|
// lock released in above
|
|
//
|
|
|
|
break;
|
|
} // switch (EventStatus)
|
|
|
|
break;
|
|
|
|
case ELAN_EVENT_RESTART:
|
|
|
|
DBGP((1, "%d CONFIGURE - RESTART\n", pElan->ElanNumber));
|
|
|
|
AtmLaneShutdownElan(pElan, TRUE);
|
|
|
|
break;
|
|
|
|
case ELAN_EVENT_STOP:
|
|
|
|
DBGP((1, "%d CONFIGURE - STOP\n", pElan->ElanNumber));
|
|
|
|
AtmLaneShutdownElan(pElan, FALSE);
|
|
|
|
break;
|
|
|
|
default:
|
|
DBGP((1, "%d CONFIGURE - UNEXPECTED EVENT %d\n",
|
|
pElan->ElanNumber, Event));
|
|
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
//
|
|
// LES CONNECT STATE --------------------------------------------
|
|
//
|
|
case ELAN_STATE_LES_CONNECT:
|
|
|
|
switch (Event)
|
|
{
|
|
case ELAN_EVENT_START:
|
|
|
|
DBGP((1, "%d LES CONNECT - START\n", pElan->ElanNumber));
|
|
|
|
//
|
|
// Register our SAPs
|
|
//
|
|
AtmLaneRegisterSaps(pElan);
|
|
//
|
|
// Elan lock is released in above.
|
|
//
|
|
break;
|
|
|
|
case ELAN_EVENT_SAPS_REGISTERED:
|
|
|
|
DBGP((1, "%d LES CONNECT - SAPS REGISTERED (%x)\n",
|
|
pElan->ElanNumber, EventStatus));
|
|
|
|
if (NDIS_STATUS_SUCCESS == EventStatus)
|
|
{
|
|
pElan->RetriesLeft = 4;
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
//
|
|
// Connect to the LES
|
|
//
|
|
AtmLaneConnectToServer(pElan, ATM_ENTRY_TYPE_LES, FALSE);
|
|
//
|
|
// Elan lock is released in above.
|
|
//
|
|
}
|
|
else
|
|
{
|
|
// XXX - What to do?
|
|
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
}
|
|
break;
|
|
|
|
case ELAN_EVENT_SVR_CALL_COMPLETE:
|
|
|
|
DBGP((1, "%d LES CONNECT - LES CALL COMPLETE (%x)\n",
|
|
pElan->ElanNumber, EventStatus));
|
|
|
|
switch (EventStatus)
|
|
{
|
|
case NDIS_STATUS_SUCCESS:
|
|
|
|
//
|
|
// Advance to Join state
|
|
//
|
|
pElan->State = ELAN_STATE_JOIN;
|
|
|
|
pElan->RetriesLeft = 4;
|
|
|
|
AtmLaneQueueElanEvent(pElan, ELAN_EVENT_START, 0);
|
|
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
|
|
break;
|
|
|
|
case NDIS_STATUS_INTERFACE_DOWN:
|
|
|
|
if (pElan->RetriesLeft--)
|
|
{
|
|
//
|
|
// retry in a little while
|
|
//
|
|
AtmLaneQueueElanEventAfterDelay(
|
|
pElan,
|
|
ELAN_EVENT_SAPS_REGISTERED,
|
|
NDIS_STATUS_SUCCESS,
|
|
2*1000);
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Return to the Init State in a little while
|
|
//
|
|
AtmLaneShutdownElan(pElan, TRUE);
|
|
//
|
|
// lock released in above
|
|
//
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
//
|
|
// Call failed, return to the Init State in a little while
|
|
//
|
|
AtmLaneShutdownElan(pElan, TRUE);
|
|
//
|
|
// lock released in above
|
|
//
|
|
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
case ELAN_EVENT_RESTART:
|
|
|
|
DBGP((1, "%d LES CONNECT - RESTART\n", pElan->ElanNumber));
|
|
|
|
AtmLaneShutdownElan(pElan, TRUE);
|
|
|
|
break;
|
|
|
|
case ELAN_EVENT_STOP:
|
|
|
|
DBGP((1, "%d LES CONNECT - STOP\n", pElan->ElanNumber));
|
|
|
|
AtmLaneShutdownElan(pElan, FALSE);
|
|
|
|
break;
|
|
|
|
default:
|
|
DBGP((0, "%d LES CONNECT - UNEXPECTED EVENT %d\n",
|
|
pElan->ElanNumber, Event));
|
|
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
|
|
break;
|
|
}
|
|
break;
|
|
|
|
//
|
|
// JOIN STATE ---------------------------------------------------
|
|
//
|
|
case ELAN_STATE_JOIN:
|
|
|
|
switch (Event)
|
|
{
|
|
case ELAN_EVENT_START:
|
|
|
|
DBGP((1, "%d JOIN - START\n", pElan->ElanNumber));
|
|
|
|
//
|
|
// Start join request timer
|
|
//
|
|
AtmLaneReferenceElan(pElan, "timer"); // timer reference
|
|
AtmLaneStartTimer(
|
|
pElan,
|
|
&pElan->Timer,
|
|
AtmLaneJoinResponseTimeout,
|
|
pElan->ControlTimeout,
|
|
pElan);
|
|
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
|
|
//
|
|
// Send a Join request
|
|
//
|
|
AtmLaneSendJoinRequest(pElan);
|
|
break;
|
|
|
|
case ELAN_EVENT_JOIN_RESPONSE:
|
|
|
|
DBGP((1, "%d JOIN - JOIN RESPONSE (%x)\n",
|
|
pElan->ElanNumber, EventStatus));
|
|
|
|
switch (EventStatus)
|
|
{
|
|
case NDIS_STATUS_SUCCESS:
|
|
//
|
|
// Stop join request timer
|
|
//
|
|
if (AtmLaneStopTimer(&pElan->Timer, pElan))
|
|
{
|
|
rc = AtmLaneDereferenceElan(pElan, "timer");
|
|
ASSERT(rc > 0);
|
|
}
|
|
|
|
//
|
|
// Advance to BUS CONNECT phase.
|
|
//
|
|
pElan->State = ELAN_STATE_BUS_CONNECT;
|
|
|
|
AtmLaneQueueElanEvent(pElan, ELAN_EVENT_START, 0);
|
|
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
|
|
break;
|
|
|
|
case NDIS_STATUS_TIMEOUT:
|
|
//
|
|
// restart the Elan
|
|
//
|
|
AtmLaneShutdownElan(pElan, TRUE);
|
|
//
|
|
// lock released in above
|
|
//
|
|
|
|
break;
|
|
|
|
case NDIS_STATUS_FAILURE:
|
|
//
|
|
// restart the Elan
|
|
//
|
|
AtmLaneShutdownElan(pElan, TRUE);
|
|
//
|
|
// lock released in above
|
|
//
|
|
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
case ELAN_EVENT_LES_CALL_CLOSED:
|
|
|
|
DBGP((1, "%d JOIN - LES CALL CLOSED\n", pElan->ElanNumber));
|
|
|
|
//
|
|
// restart the Elan
|
|
//
|
|
AtmLaneShutdownElan(pElan, TRUE);
|
|
//
|
|
// lock released in above
|
|
//
|
|
break;
|
|
|
|
case ELAN_EVENT_RESTART:
|
|
|
|
DBGP((1, "%d JOIN - RESTART\n", pElan->ElanNumber));
|
|
|
|
AtmLaneShutdownElan(pElan, TRUE);
|
|
|
|
break;
|
|
|
|
case ELAN_EVENT_STOP:
|
|
|
|
DBGP((1, "%d JOIN - STOP\n", pElan->ElanNumber));
|
|
|
|
AtmLaneShutdownElan(pElan, FALSE);
|
|
|
|
break;
|
|
|
|
default:
|
|
DBGP((0, "%d JOIN - UNEXPECTED %d\n",
|
|
pElan->ElanNumber, Event));
|
|
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
|
|
break;
|
|
|
|
}
|
|
break;
|
|
|
|
//
|
|
// BUS CONNECT STATE ---------------------------------------------------
|
|
//
|
|
case ELAN_STATE_BUS_CONNECT:
|
|
|
|
switch (Event)
|
|
{
|
|
case ELAN_EVENT_START:
|
|
|
|
DBGP((1, "%d BUS CONNECT - START\n", pElan->ElanNumber));
|
|
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
|
|
//
|
|
// Find or create a MAC entry for the Broadcast MAC Addr
|
|
//
|
|
ACQUIRE_ELAN_MAC_TABLE_LOCK(pElan);
|
|
pMacEntry = AtmLaneSearchForMacAddress(
|
|
pElan,
|
|
LANE_MACADDRTYPE_MACADDR,
|
|
&gMacBroadcastAddress,
|
|
TRUE);
|
|
RELEASE_ELAN_MAC_TABLE_LOCK(pElan);
|
|
|
|
if (pMacEntry == NULL_PATMLANE_MAC_ENTRY)
|
|
{
|
|
break;
|
|
}
|
|
|
|
ACQUIRE_MAC_ENTRY_LOCK(pMacEntry);
|
|
|
|
SET_FLAG(
|
|
pMacEntry->Flags,
|
|
MAC_ENTRY_STATE_MASK,
|
|
MAC_ENTRY_ARPING);
|
|
|
|
pMacEntry->Flags |= MAC_ENTRY_BROADCAST;
|
|
|
|
|
|
//
|
|
// Send ARP Request
|
|
//
|
|
AtmLaneStartTimer(
|
|
pElan,
|
|
&pMacEntry->Timer,
|
|
AtmLaneArpTimeout,
|
|
pElan->ArpResponseTime,
|
|
(PVOID)pMacEntry
|
|
);
|
|
|
|
pMacEntry->RetriesLeft = pElan->MaxRetryCount;
|
|
AtmLaneReferenceMacEntry(pMacEntry, "timer");
|
|
pMacEntry->Flags |= MAC_ENTRY_ARPING;
|
|
|
|
AtmLaneSendArpRequest(pElan, pMacEntry);
|
|
//
|
|
// MAC Entry lock released in above
|
|
//
|
|
|
|
break;
|
|
|
|
case ELAN_EVENT_ARP_RESPONSE:
|
|
|
|
DBGP((1, "%d BUS CONNECT - ARP RESPONSE (%x)\n",
|
|
pElan->ElanNumber, EventStatus));
|
|
|
|
if (NDIS_STATUS_SUCCESS == EventStatus)
|
|
{
|
|
pElan->RetriesLeft = 4;
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
|
|
//
|
|
// Connect to the BUS
|
|
//
|
|
AtmLaneConnectToServer(pElan, ATM_ENTRY_TYPE_BUS, FALSE);
|
|
}
|
|
else
|
|
{
|
|
DBGP((2, "ELAN %d: NO ARP RESPONSE for BUS, restarting\n"));
|
|
|
|
//
|
|
// restart the Elan
|
|
//
|
|
AtmLaneShutdownElan(pElan, TRUE);
|
|
//
|
|
// lock released in above
|
|
//
|
|
}
|
|
break;
|
|
|
|
case ELAN_EVENT_SVR_CALL_COMPLETE:
|
|
|
|
DBGP((1, "%d BUS CONNECT - BUS CALL COMPLETE (%x)\n",
|
|
pElan->ElanNumber, EventStatus));
|
|
|
|
switch (EventStatus)
|
|
{
|
|
case NDIS_STATUS_SUCCESS:
|
|
|
|
//
|
|
// Now connected to BUS, start the Operational phase
|
|
//
|
|
pElan->State = ELAN_STATE_OPERATIONAL;
|
|
|
|
pElan->RetriesLeft = 4;
|
|
|
|
AdapterHandle = pElan->MiniportAdapterHandle;
|
|
|
|
AtmLaneQueueElanEvent(pElan, ELAN_EVENT_START, 0);
|
|
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
|
|
//
|
|
// Indicate media connect status if our miniport is up:
|
|
//
|
|
if (AdapterHandle != NULL)
|
|
{
|
|
NdisMIndicateStatus(
|
|
AdapterHandle,
|
|
NDIS_STATUS_MEDIA_CONNECT,
|
|
(PVOID)NULL,
|
|
0);
|
|
|
|
NdisMIndicateStatusComplete(AdapterHandle);
|
|
}
|
|
|
|
break;
|
|
|
|
case NDIS_STATUS_INTERFACE_DOWN:
|
|
|
|
if (pElan->RetriesLeft--)
|
|
{
|
|
//
|
|
// retry in a little while
|
|
//
|
|
AtmLaneQueueElanEventAfterDelay(
|
|
pElan,
|
|
ELAN_EVENT_ARP_RESPONSE,
|
|
NDIS_STATUS_SUCCESS,
|
|
2*1000);
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// return to the Init State in a little while
|
|
//
|
|
AtmLaneShutdownElan(pElan, TRUE);
|
|
//
|
|
// lock released in above
|
|
//
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
//
|
|
// Call failed,
|
|
// Close the LES connection and
|
|
// return to the Init State in a little while.
|
|
//
|
|
AtmLaneShutdownElan(pElan, TRUE);
|
|
//
|
|
// lock released in above
|
|
//
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
case ELAN_EVENT_LES_CALL_CLOSED:
|
|
|
|
DBGP((1, "%d BUS CONNECT - LES CALL CLOSED\n", pElan->ElanNumber));
|
|
|
|
//
|
|
// restart the Elan
|
|
//
|
|
AtmLaneShutdownElan(pElan, TRUE);
|
|
//
|
|
// lock released in above
|
|
//
|
|
break;
|
|
|
|
case ELAN_EVENT_RESTART:
|
|
|
|
DBGP((1, "%d BUS CONNECT - RESTART\n", pElan->ElanNumber));
|
|
|
|
AtmLaneShutdownElan(pElan, TRUE);
|
|
|
|
break;
|
|
|
|
case ELAN_EVENT_STOP:
|
|
|
|
DBGP((1, "%d BUS CONNECT - STOP\n", pElan->ElanNumber));
|
|
|
|
AtmLaneShutdownElan(pElan, FALSE);
|
|
|
|
break;
|
|
|
|
default:
|
|
DBGP((0, "%d BUS CONNECT - UNEXPECTED EVENT %d\n",
|
|
pElan->ElanNumber, Event));
|
|
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
//
|
|
// OPERATIONAL STATE ---------------------------------------------------
|
|
//
|
|
case ELAN_STATE_OPERATIONAL:
|
|
|
|
switch (Event)
|
|
{
|
|
case ELAN_EVENT_START:
|
|
|
|
DBGP((1, "%d OPERATIONAL - START\n", pElan->ElanNumber));
|
|
|
|
// Initialize the miniport if not already
|
|
|
|
if ((pElan->Flags & ELAN_MINIPORT_INITIALIZED) == 0)
|
|
{
|
|
// Only if we have a device name
|
|
|
|
if (pElan->CfgDeviceName.Length > 0)
|
|
{
|
|
|
|
pElan->Flags |= ELAN_MINIPORT_INITIALIZED;
|
|
|
|
//
|
|
// Schedule a PASSIVE_LEVEL thread to call
|
|
// NdisIMInitializeDeviceInstance
|
|
//
|
|
NdisInitializeWorkItem(
|
|
&pElan->NdisWorkItem,
|
|
AtmLaneInitializeMiniportDevice,
|
|
pElan);
|
|
|
|
AtmLaneReferenceElan(pElan, "workitem");
|
|
|
|
NdisScheduleWorkItem(&pElan->NdisWorkItem);
|
|
|
|
}
|
|
else
|
|
{
|
|
DBGP((0, "EventHandler: No miniport device name configured\n"));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Clear last event log code
|
|
//
|
|
pElan->LastEventCode = 0;
|
|
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
|
|
break;
|
|
|
|
case ELAN_EVENT_LES_CALL_CLOSED:
|
|
|
|
DBGP((1, "%d OPERATIONAL - LES CALL CLOSED\n", pElan->ElanNumber));
|
|
|
|
//
|
|
// Tear everything down and restart
|
|
//
|
|
AtmLaneShutdownElan(pElan, TRUE);
|
|
//
|
|
// lock released in above
|
|
//
|
|
break;
|
|
|
|
case ELAN_EVENT_BUS_CALL_CLOSED:
|
|
|
|
DBGP((1, "%d OPERATIONAL - BUS CALL CLOSED\n", pElan->ElanNumber));
|
|
|
|
//
|
|
// Tear everything down and restart
|
|
//
|
|
AtmLaneShutdownElan(pElan, TRUE);
|
|
//
|
|
// lock released in above
|
|
//
|
|
break;
|
|
|
|
break;
|
|
|
|
case ELAN_EVENT_RESTART:
|
|
|
|
DBGP((1, "%d OPERATIONAL - RESTART\n", pElan->ElanNumber));
|
|
|
|
AtmLaneShutdownElan(pElan, TRUE);
|
|
|
|
break;
|
|
|
|
case ELAN_EVENT_STOP:
|
|
|
|
DBGP((1, "%d OPERATIONAL - STOP\n", pElan->ElanNumber));
|
|
|
|
AtmLaneShutdownElan(pElan, FALSE);
|
|
|
|
break;
|
|
|
|
default:
|
|
DBGP((0, "%d OPERATIONAL - UNEXPECTED EVENT%d\n",
|
|
pElan->ElanNumber, Event));
|
|
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
|
DBGP((0, "%d UNKOWN STATE %d EVENT %d\n",
|
|
pElan->ElanNumber, pElan->State, Event));
|
|
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
|
|
break;
|
|
|
|
|
|
} // switch (pElan->State)
|
|
|
|
TRACEOUT(EventHandler);
|
|
CHECK_EXIT_IRQL(EntryIrql);
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
AtmLaneBootStrapElans(
|
|
IN PATMLANE_ADAPTER pAdapter
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Start up the ELANs configured for an adapter.
|
|
Done upon receipt of AF notify from call manager.
|
|
|
|
Arguments:
|
|
|
|
pAdapter - Pointer to ATMLANE Adapter structure
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
NDIS_STATUS Status;
|
|
NDIS_HANDLE AdapterConfigHandle;
|
|
NDIS_STRING ElanListKeyName;
|
|
NDIS_HANDLE ElanListConfigHandle;
|
|
NDIS_HANDLE ElanConfigHandle;
|
|
NDIS_STRING ElanKeyName;
|
|
UINT Index;
|
|
PLIST_ENTRY p;
|
|
PATMLANE_ELAN pElan;
|
|
PATMLANE_NAME pName;
|
|
BOOLEAN bBootStrapping;
|
|
|
|
TRACEIN(BootStrapElans);
|
|
|
|
//
|
|
// Initialize.
|
|
//
|
|
Status = NDIS_STATUS_SUCCESS;
|
|
AdapterConfigHandle = NULL_NDIS_HANDLE;
|
|
ElanListConfigHandle = NULL_NDIS_HANDLE;
|
|
ElanConfigHandle = NULL_NDIS_HANDLE;
|
|
bBootStrapping = TRUE;
|
|
|
|
do
|
|
{
|
|
DBGP((1, "BootStrapElans: Starting ELANs on adapter %p\n", pAdapter));
|
|
ACQUIRE_ADAPTER_LOCK(pAdapter);
|
|
|
|
if (pAdapter->Flags & (ADAPTER_FLAGS_BOOTSTRAP_IN_PROGRESS|
|
|
ADAPTER_FLAGS_UNBINDING))
|
|
{
|
|
DBGP((0, "Skipping bootstrap on adapter %x/%x\n", pAdapter, pAdapter->Flags));
|
|
RELEASE_ADAPTER_LOCK(pAdapter);
|
|
bBootStrapping = FALSE;
|
|
break;
|
|
}
|
|
|
|
pAdapter->Flags |= ADAPTER_FLAGS_BOOTSTRAP_IN_PROGRESS;
|
|
INIT_BLOCK_STRUCT(&pAdapter->UnbindBlock);
|
|
RELEASE_ADAPTER_LOCK(pAdapter);
|
|
|
|
//
|
|
// Open the AtmLane protocol configuration section for this adapter.
|
|
//
|
|
|
|
NdisOpenProtocolConfiguration(
|
|
&Status,
|
|
&AdapterConfigHandle,
|
|
&pAdapter->ConfigString
|
|
);
|
|
|
|
if (NDIS_STATUS_SUCCESS != Status)
|
|
{
|
|
AdapterConfigHandle = NULL_NDIS_HANDLE;
|
|
DBGP((0, "BootStrapElans: OpenProtocolConfiguration failed\n"));
|
|
Status = NDIS_STATUS_OPEN_FAILED;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Get the protocol specific configuration
|
|
//
|
|
AtmLaneGetProtocolConfiguration(AdapterConfigHandle, pAdapter);
|
|
|
|
//
|
|
// We bootstrap the ELANs differently on NT5 and Memphis(Win98)
|
|
//
|
|
if (!pAdapter->RunningOnMemphis)
|
|
{
|
|
//
|
|
// This is the NT5 ELAN bootstrap case
|
|
//
|
|
do
|
|
{
|
|
//
|
|
// Open the Elan List configuration key.
|
|
//
|
|
NdisInitUnicodeString(&ElanListKeyName, ATMLANE_ELANLIST_STRING);
|
|
|
|
NdisOpenConfigurationKeyByName(
|
|
&Status,
|
|
AdapterConfigHandle,
|
|
&ElanListKeyName,
|
|
&ElanListConfigHandle);
|
|
|
|
if (NDIS_STATUS_SUCCESS != Status)
|
|
{
|
|
ElanListConfigHandle = NULL_NDIS_HANDLE;
|
|
DBGP((0, "BootStrapElans: Failed open of ElanList key\n"));
|
|
Status = NDIS_STATUS_FAILURE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Iterate thru the configured Elans
|
|
//
|
|
for (Index = 0;
|
|
; // Stop only on error or no more Elans
|
|
Index++)
|
|
{
|
|
//
|
|
// Open the "next" Elan key
|
|
//
|
|
NdisOpenConfigurationKeyByIndex(
|
|
&Status,
|
|
ElanListConfigHandle,
|
|
Index,
|
|
&ElanKeyName,
|
|
&ElanConfigHandle
|
|
);
|
|
|
|
if (NDIS_STATUS_SUCCESS != Status)
|
|
{
|
|
ElanConfigHandle = NULL_NDIS_HANDLE;
|
|
}
|
|
|
|
//
|
|
// If NULL handle, assume no more ELANs.
|
|
//
|
|
if (NULL_NDIS_HANDLE == ElanConfigHandle)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Create this Elan
|
|
//
|
|
DBGP((2, "Bootstrap ELANs: Adapter %x, KeyName: len %d, max %d, name: [%ws]\n",
|
|
pAdapter,
|
|
ElanKeyName.Length,
|
|
ElanKeyName.MaximumLength,
|
|
ElanKeyName.Buffer));
|
|
(VOID)AtmLaneCreateElan(pAdapter, &ElanKeyName, &pElan);
|
|
}
|
|
|
|
} while (FALSE);
|
|
|
|
Status = NDIS_STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// This is the Memphis/Win98 ELAN bootstrap case
|
|
//
|
|
// Create Elans for each entry in the UpperBindings List
|
|
//
|
|
pName = pAdapter->UpperBindingsList;
|
|
|
|
while (pName != NULL)
|
|
{
|
|
(VOID)AtmLaneCreateElan(pAdapter, NULL, &pElan);
|
|
|
|
pName = pName->pNext;
|
|
}
|
|
}
|
|
|
|
|
|
} while (FALSE);
|
|
|
|
|
|
//
|
|
// Close config handles
|
|
//
|
|
if (NULL_NDIS_HANDLE != ElanConfigHandle)
|
|
{
|
|
NdisCloseConfiguration(ElanConfigHandle);
|
|
ElanConfigHandle = NULL_NDIS_HANDLE;
|
|
}
|
|
if (NULL_NDIS_HANDLE != ElanListConfigHandle)
|
|
{
|
|
NdisCloseConfiguration(ElanListConfigHandle);
|
|
ElanListConfigHandle = NULL_NDIS_HANDLE;
|
|
}
|
|
if (NULL_NDIS_HANDLE != AdapterConfigHandle)
|
|
{
|
|
NdisCloseConfiguration(AdapterConfigHandle);
|
|
AdapterConfigHandle = NULL_NDIS_HANDLE;
|
|
}
|
|
|
|
|
|
//
|
|
// Loop thru the ELANs and kickstart them - only the newly
|
|
// allocated ones.
|
|
//
|
|
if (!IsListEmpty(&pAdapter->ElanList))
|
|
{
|
|
p = pAdapter->ElanList.Flink;
|
|
while (p != &pAdapter->ElanList)
|
|
{
|
|
pElan = CONTAINING_RECORD(p, ATMLANE_ELAN, Link);
|
|
STRUCT_ASSERT(pElan, atmlane_elan);
|
|
|
|
ACQUIRE_ELAN_LOCK(pElan);
|
|
if (ELAN_STATE_ALLOCATED == pElan->State)
|
|
{
|
|
pElan->AdminState = ELAN_STATE_OPERATIONAL;
|
|
pElan->State = ELAN_STATE_INIT;
|
|
AtmLaneQueueElanEvent(pElan, ELAN_EVENT_START, Status);
|
|
}
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
|
|
p = p->Flink;
|
|
}
|
|
}
|
|
|
|
if (bBootStrapping)
|
|
{
|
|
ACQUIRE_ADAPTER_LOCK(pAdapter);
|
|
pAdapter->Flags &= ~ADAPTER_FLAGS_BOOTSTRAP_IN_PROGRESS;
|
|
RELEASE_ADAPTER_LOCK(pAdapter);
|
|
|
|
SIGNAL_BLOCK_STRUCT(&pAdapter->UnbindBlock, NDIS_STATUS_SUCCESS);
|
|
}
|
|
|
|
TRACEOUT(BootStrapElans);
|
|
return;
|
|
}
|
|
|
|
|
|
NDIS_STATUS
|
|
AtmLaneCreateElan(
|
|
IN PATMLANE_ADAPTER pAdapter,
|
|
IN PNDIS_STRING pElanKey,
|
|
OUT PATMLANE_ELAN * ppElan
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Create and start an ELAN.
|
|
|
|
Arguments:
|
|
|
|
pAdapter - Pointer to ATMLANE Adapter structure
|
|
pElanKey - Points to a Unicode string naming the elan to create.
|
|
ppElan - Pointer to pointer to ATMLANE_ELAN (output)
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
NDIS_STATUS Status;
|
|
PATMLANE_ELAN pElan;
|
|
|
|
|
|
TRACEIN(CreateElan);
|
|
|
|
|
|
Status = NDIS_STATUS_SUCCESS;
|
|
pElan = NULL_PATMLANE_ELAN;
|
|
|
|
DBGP((1, "CreateElan: Adapter %p, ElanKey %ws\n", pAdapter, pElanKey->Buffer));
|
|
|
|
do
|
|
{
|
|
//
|
|
// Weed out duplicates.
|
|
//
|
|
if (pElanKey != NULL)
|
|
{
|
|
|
|
pElan = AtmLaneFindElan(pAdapter, pElanKey);
|
|
|
|
if (NULL_PATMLANE_ELAN != pElan)
|
|
{
|
|
// Duplicate
|
|
DBGP((0, "CreateElan: found duplicate pElan %p\n", pElan));
|
|
|
|
Status = NDIS_STATUS_FAILURE;
|
|
pElan = NULL_PATMLANE_ELAN;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Allocate an ELAN data structure.
|
|
//
|
|
Status = AtmLaneAllocElan(pAdapter, &pElan);
|
|
if (NDIS_STATUS_SUCCESS != Status)
|
|
{
|
|
DBGP((0, "CreateElan: Failed allocate of elan data\n"));
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Put initial reference on the Elan struct.
|
|
//
|
|
AtmLaneReferenceElan(pElan, "adapter");
|
|
|
|
//
|
|
// Store in bind name (NT only, not supplied on Win98)
|
|
//
|
|
if (pElanKey != NULL)
|
|
{
|
|
if (!AtmLaneCopyUnicodeString(&pElan->CfgBindName, pElanKey, TRUE, TRUE))
|
|
{
|
|
DBGP((0, "CreateElan: Failed allocate of bind name string\n"));
|
|
Status = NDIS_STATUS_RESOURCES;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get the ELAN's configuration.
|
|
//
|
|
AtmLaneGetElanConfiguration(pElanKey, pElan);
|
|
|
|
|
|
//
|
|
// Allocate protocol buffers for this Elan.
|
|
//
|
|
Status = AtmLaneInitProtoBuffers(pElan);
|
|
if (Status != NDIS_STATUS_SUCCESS)
|
|
{
|
|
DBGP((0,
|
|
"CreateElan: bad status (%x) from InitBufs\n",
|
|
Status));
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Allocate transmit packet descriptors for this Elan.
|
|
//
|
|
NdisAllocatePacketPool(
|
|
&Status,
|
|
&pElan->TransmitPacketPool,
|
|
pElan->MaxHeaderBufs,
|
|
sizeof(SEND_PACKET_RESERVED)
|
|
);
|
|
#if PKT_HDR_COUNTS
|
|
pElan->XmitPktCount = pElan->MaxHeaderBufs;
|
|
DBGP((1, "XmitPktCount %d\n", pElan->XmitPktCount));
|
|
#endif
|
|
|
|
if (Status != NDIS_STATUS_SUCCESS)
|
|
{
|
|
DBGP((0,
|
|
"CreateElan: bad status (%x)"
|
|
" from NdisAllocatePacketPool (xmit)\n",
|
|
Status));
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Allocate receive packet descriptors for this Elan.
|
|
//
|
|
NdisAllocatePacketPool(
|
|
&Status,
|
|
&pElan->ReceivePacketPool,
|
|
pElan->MaxHeaderBufs,
|
|
sizeof(RECV_PACKET_RESERVED)
|
|
);
|
|
#if PKT_HDR_COUNTS
|
|
pElan->RecvPktCount = pElan->MaxHeaderBufs;
|
|
DBGP((1, "RecvPktCount %d\n", pElan->RecvPktCount));
|
|
#endif
|
|
if (Status != NDIS_STATUS_SUCCESS)
|
|
{
|
|
DBGP((0,
|
|
"CreateElan: bad status (%x)"
|
|
" from NdisAllocatePacketPool (xmit)\n",
|
|
Status));
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Allocate receive buffer descriptors for this Elan.
|
|
//
|
|
NdisAllocateBufferPool(&Status,
|
|
&pElan->ReceiveBufferPool,
|
|
pElan->MaxHeaderBufs
|
|
);
|
|
if (Status != NDIS_STATUS_SUCCESS)
|
|
{
|
|
DBGP((0,
|
|
"AfRegisterNotifyHandler: bad status (%x)"
|
|
" from NdisAllocateBufferPool\n",
|
|
Status));
|
|
break;
|
|
}
|
|
|
|
}
|
|
while (FALSE);
|
|
|
|
if (Status != NDIS_STATUS_SUCCESS)
|
|
{
|
|
//
|
|
// There was a failure in processing this Elan.
|
|
//
|
|
if (NULL_PATMLANE_ELAN != pElan)
|
|
{
|
|
ACQUIRE_ELAN_LOCK(pElan);
|
|
AtmLaneDereferenceElan(pElan, "adapter");
|
|
}
|
|
|
|
*ppElan = NULL_PATMLANE_ELAN;
|
|
}
|
|
else
|
|
{
|
|
// Output created Elan
|
|
|
|
*ppElan = pElan;
|
|
}
|
|
|
|
|
|
TRACEOUT(CreateElan);
|
|
|
|
return Status;
|
|
}
|
|
|
|
NDIS_STATUS
|
|
AtmLaneReconfigureHandler(
|
|
IN PATMLANE_ADAPTER pAdapter,
|
|
IN PNET_PNP_EVENT pNetPnPEvent
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Handler for PnP Reconfigure events.
|
|
|
|
Arguments:
|
|
|
|
pAdapter - Pointer to our adapter struct.
|
|
|
|
pNetPnPEvent - Pointer to PnP Event structure describing the event.
|
|
|
|
Return Value:
|
|
|
|
Status of handling the reconfigure event.
|
|
|
|
--*/
|
|
{
|
|
NDIS_STATUS Status;
|
|
PATMLANE_PNP_RECONFIG_REQUEST pReconfig;
|
|
NDIS_STRING ElanKey;
|
|
PATMLANE_ELAN pElan;
|
|
|
|
TRACEIN(ReconfigureHandler);
|
|
|
|
do
|
|
{
|
|
DBGP((1, "Reconfigurehandler: Buffer 0x%x BufferLength %d\n",
|
|
pNetPnPEvent->Buffer, pNetPnPEvent->BufferLength));
|
|
|
|
Status = NDIS_STATUS_SUCCESS;
|
|
|
|
if (pAdapter == NULL_PATMLANE_ADAPTER)
|
|
{
|
|
//
|
|
// Either a global reconfig notification or this is
|
|
// NDIS itself asking us to re-evaluate our ELANs.
|
|
// We go through the list of configured ELANs on all
|
|
// adapters and start any that haven't been started.
|
|
//
|
|
{
|
|
PLIST_ENTRY pEnt, pNextEnt;
|
|
|
|
ACQUIRE_GLOBAL_LOCK(pAtmLaneGlobalInfo);
|
|
|
|
for (pEnt = pAtmLaneGlobalInfo->AdapterList.Flink;
|
|
pEnt != &pAtmLaneGlobalInfo->AdapterList;
|
|
pEnt = pNextEnt)
|
|
{
|
|
pNextEnt = pEnt->Flink;
|
|
RELEASE_GLOBAL_LOCK(pAtmLaneGlobalInfo);
|
|
|
|
pAdapter = CONTAINING_RECORD(pEnt, ATMLANE_ADAPTER, Link);
|
|
|
|
if (pAdapter->Flags & ADAPTER_FLAGS_AF_NOTIFIED)
|
|
{
|
|
DBGP((1, "Reconfig: Will bootstrap ELANs on Adapter %x\n",
|
|
pAdapter));
|
|
|
|
AtmLaneBootStrapElans(pAdapter);
|
|
}
|
|
|
|
ACQUIRE_GLOBAL_LOCK(pAtmLaneGlobalInfo);
|
|
}
|
|
|
|
RELEASE_GLOBAL_LOCK(pAtmLaneGlobalInfo);
|
|
}
|
|
|
|
|
|
break;
|
|
}
|
|
|
|
// Get pointer to LANE reconfig struct inside the generic PnP struct
|
|
|
|
pReconfig = (PATMLANE_PNP_RECONFIG_REQUEST)(pNetPnPEvent->Buffer);
|
|
|
|
// Check for null pointer
|
|
|
|
if (pReconfig == NULL)
|
|
{
|
|
DBGP((0, "ReconfigureHandler: NULL pointer to event buffer!\n"));
|
|
Status = NDIS_STATUS_INVALID_DATA;
|
|
break;
|
|
}
|
|
|
|
// Validate the version
|
|
|
|
if (pReconfig->Version != 1)
|
|
{
|
|
DBGP((0, "ReconfigureHandler: Version not 1\n"));
|
|
Status = NDIS_STATUS_BAD_VERSION;
|
|
break;
|
|
}
|
|
|
|
// Build a UNICODE string containing the ELAN's key
|
|
|
|
NdisInitUnicodeString(&ElanKey, pReconfig->ElanKey);
|
|
|
|
// First find the Elan
|
|
|
|
pElan = AtmLaneFindElan(pAdapter, &ElanKey);
|
|
|
|
DBGP((0, "ReconfigHandler: Adapter %x, ELAN %x, OpType %d\n",
|
|
pAdapter, pElan, pReconfig->OpType));
|
|
|
|
// Sanity check: don't add an existing ELAN
|
|
|
|
if (ATMLANE_RECONFIG_OP_ADD_ELAN == pReconfig->OpType &&
|
|
NULL_PATMLANE_ELAN != pElan)
|
|
{
|
|
DBGP((0, "ReconfigureHandler: Ignoring ADD existing Elan %x\n", pElan));
|
|
Status = NDIS_STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
// If MOD or DEL first shutdown the existing ELAN
|
|
|
|
if (ATMLANE_RECONFIG_OP_MOD_ELAN == pReconfig->OpType ||
|
|
ATMLANE_RECONFIG_OP_DEL_ELAN == pReconfig->OpType)
|
|
{
|
|
|
|
if (NULL_PATMLANE_ELAN == pElan)
|
|
{
|
|
DBGP((0, "ReconfigureHandler: No existing Elan found for Modify/Delete\n"));
|
|
Status = NDIS_STATUS_FAILURE;
|
|
break;
|
|
}
|
|
|
|
// Shut down the existing Elan
|
|
|
|
ACQUIRE_ELAN_LOCK(pElan);
|
|
AtmLaneShutdownElan(pElan, FALSE);
|
|
}
|
|
|
|
// If ADD or MOD startup the new ELAN
|
|
|
|
if (ATMLANE_RECONFIG_OP_ADD_ELAN == pReconfig->OpType ||
|
|
ATMLANE_RECONFIG_OP_MOD_ELAN == pReconfig->OpType)
|
|
{
|
|
Status = AtmLaneCreateElan(pAdapter, &ElanKey, &pElan);
|
|
if (NDIS_STATUS_SUCCESS == Status)
|
|
{
|
|
pElan->AdminState = ELAN_STATE_OPERATIONAL;
|
|
pElan->State = ELAN_STATE_INIT;
|
|
AtmLaneQueueElanEventAfterDelay(pElan, ELAN_EVENT_START, Status, 1*1000);
|
|
}
|
|
}
|
|
|
|
} while (FALSE);
|
|
|
|
TRACEOUT(ReconfigureHandler);
|
|
DBGP((0, "Reconfigure: pAdapter %x, returning %x\n", pAdapter, Status));
|
|
return Status;
|
|
}
|
|
|
|
PATMLANE_ELAN
|
|
AtmLaneFindElan(
|
|
IN PATMLANE_ADAPTER pAdapter,
|
|
IN PNDIS_STRING pElanKey
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Find an ELAN by bind name/key
|
|
|
|
Arguments:
|
|
|
|
pAdapter - Pointer to an adapter struct.
|
|
|
|
pElanKey - Pointer to NDIS_STRING containing Elan's bind name.
|
|
|
|
Return Value:
|
|
|
|
Pointer to matching Elan or NULL if not found.
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY p;
|
|
PATMLANE_ELAN pElan;
|
|
BOOLEAN Found;
|
|
NDIS_STRING ElanKeyName;
|
|
|
|
TRACEIN(FindElan);
|
|
DBGP((1, "FindElan: Adapter %p, ElanKey %ws\n", pAdapter, pElanKey->Buffer));
|
|
|
|
Found = FALSE;
|
|
ElanKeyName.Buffer = NULL;
|
|
pElan = NULL_PATMLANE_ELAN;
|
|
|
|
do
|
|
{
|
|
//
|
|
// Make an up-cased copy of the given string.
|
|
//
|
|
ALLOC_MEM(&ElanKeyName.Buffer, pElanKey->MaximumLength);
|
|
if (ElanKeyName.Buffer == NULL)
|
|
{
|
|
break;
|
|
}
|
|
|
|
ElanKeyName.Length = pElanKey->Length;
|
|
ElanKeyName.MaximumLength = pElanKey->MaximumLength;
|
|
|
|
#ifndef LANE_WIN98
|
|
(VOID)NdisUpcaseUnicodeString(&ElanKeyName, pElanKey);
|
|
#else
|
|
memcpy(ElanKeyName.Buffer, pElanKey->Buffer, ElanKeyName.Length);
|
|
#endif // LANE_WIN98
|
|
|
|
ACQUIRE_ADAPTER_LOCK(pAdapter);
|
|
|
|
p = pAdapter->ElanList.Flink;
|
|
while (p != &pAdapter->ElanList)
|
|
{
|
|
pElan = CONTAINING_RECORD(p, ATMLANE_ELAN, Link);
|
|
STRUCT_ASSERT(pElan, atmlane_elan);
|
|
|
|
// compare the key
|
|
|
|
if ((ElanKeyName.Length == pElan->CfgBindName.Length) &&
|
|
(memcmp(ElanKeyName.Buffer, pElan->CfgBindName.Buffer, ElanKeyName.Length) == 0))
|
|
{
|
|
//
|
|
// Skip ELANs that are shutting down and not restarting
|
|
//
|
|
if ((pElan->AdminState != ELAN_STATE_SHUTDOWN) ||
|
|
((pElan->Flags & ELAN_NEEDS_RESTART) != 0))
|
|
{
|
|
Found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// get next link
|
|
|
|
p = p->Flink;
|
|
}
|
|
|
|
RELEASE_ADAPTER_LOCK(pAdapter);
|
|
}
|
|
while (FALSE);
|
|
|
|
if (!Found)
|
|
{
|
|
DBGP((2, "FindElan: No match found!\n"));
|
|
|
|
pElan = NULL_PATMLANE_ELAN;
|
|
}
|
|
|
|
if (ElanKeyName.Buffer)
|
|
{
|
|
FREE_MEM(ElanKeyName.Buffer);
|
|
}
|
|
|
|
TRACEOUT(FindElan);
|
|
return pElan;
|
|
}
|
|
|
|
VOID
|
|
AtmLaneConnectToServer(
|
|
IN PATMLANE_ELAN pElan,
|
|
IN ULONG ServerType,
|
|
IN BOOLEAN UsePvc
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Setup and make call to a LANE Server.
|
|
|
|
|
|
Arguments:
|
|
|
|
pElan - Pointer to ATMLANE Elan structure
|
|
|
|
ServerType - LECS, LES, or BUS
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
NDIS_STATUS Status;
|
|
ULONG rc;
|
|
PATMLANE_ATM_ENTRY pAtmEntry;
|
|
BOOLEAN bAtmEntryAlloced = FALSE;
|
|
|
|
TRACEIN(ConnectToServer);
|
|
|
|
do
|
|
{
|
|
switch (ServerType)
|
|
{
|
|
case ATM_ENTRY_TYPE_LECS:
|
|
|
|
//
|
|
// Create the ATM Entry
|
|
//
|
|
pAtmEntry = AtmLaneAllocateAtmEntry(pElan);
|
|
if (NULL_PATMLANE_ATM_ENTRY != pAtmEntry)
|
|
{
|
|
bAtmEntryAlloced = TRUE;
|
|
//
|
|
// Init ATM Entry
|
|
//
|
|
pAtmEntry->Type = ServerType;
|
|
NdisMoveMemory(
|
|
&pAtmEntry->AtmAddress,
|
|
&pElan->LecsAddress,
|
|
sizeof(ATM_ADDRESS));
|
|
|
|
//
|
|
// Add it to the Elan's list
|
|
//
|
|
ACQUIRE_ELAN_ATM_LIST_LOCK(pElan);
|
|
pElan->pLecsAtmEntry = pAtmEntry;
|
|
pAtmEntry->pNext = pElan->pAtmEntryList;
|
|
pElan->pAtmEntryList = pAtmEntry;
|
|
pElan->NumAtmEntries++;
|
|
RELEASE_ELAN_ATM_LIST_LOCK(pElan);
|
|
|
|
ACQUIRE_ATM_ENTRY_LOCK(pAtmEntry);
|
|
|
|
}
|
|
break;
|
|
|
|
case ATM_ENTRY_TYPE_LES:
|
|
//
|
|
// Create the ATM Entry
|
|
//
|
|
pAtmEntry = AtmLaneAllocateAtmEntry(pElan);
|
|
if (NULL_PATMLANE_ATM_ENTRY != pAtmEntry)
|
|
{
|
|
bAtmEntryAlloced = TRUE;
|
|
//
|
|
// Init ATM Entry
|
|
//
|
|
pAtmEntry->Type = ServerType;
|
|
|
|
NdisMoveMemory(
|
|
&pAtmEntry->AtmAddress,
|
|
&pElan->LesAddress,
|
|
sizeof(ATM_ADDRESS));
|
|
//
|
|
// Add it to the Elan's list
|
|
//
|
|
ACQUIRE_ELAN_ATM_LIST_LOCK(pElan);
|
|
pElan->pLesAtmEntry = pAtmEntry;
|
|
pAtmEntry->pNext = pElan->pAtmEntryList;
|
|
pElan->pAtmEntryList = pAtmEntry;
|
|
pElan->NumAtmEntries++;
|
|
RELEASE_ELAN_ATM_LIST_LOCK(pElan);
|
|
|
|
ACQUIRE_ATM_ENTRY_LOCK(pAtmEntry);
|
|
}
|
|
break;
|
|
|
|
case ATM_ENTRY_TYPE_BUS:
|
|
|
|
ASSERT(NULL_PATMLANE_ATM_ENTRY != pElan->pBusAtmEntry);
|
|
|
|
NdisMoveMemory(
|
|
&pElan->BusAddress,
|
|
&pElan->pBusAtmEntry->AtmAddress,
|
|
sizeof(ATM_ADDRESS));
|
|
|
|
pAtmEntry = pElan->pBusAtmEntry;
|
|
|
|
ACQUIRE_ATM_ENTRY_LOCK(pAtmEntry);
|
|
|
|
break;
|
|
}
|
|
|
|
if (NULL_PATMLANE_ATM_ENTRY == pAtmEntry)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Call the Server
|
|
//
|
|
DBGP((1, "%d: ConnectToServer: pElan %x/ref %d, Type %d, pAtmEnt %x, Ref %d\n",
|
|
pElan->ElanNumber,
|
|
pElan,
|
|
pElan->RefCount,
|
|
ServerType,
|
|
pAtmEntry,
|
|
pAtmEntry->RefCount));
|
|
|
|
|
|
Status = AtmLaneMakeCall(pElan, pAtmEntry, UsePvc);
|
|
|
|
if (NDIS_STATUS_SUCCESS == Status)
|
|
{
|
|
//
|
|
// Call completed synchronously.
|
|
//
|
|
AtmLaneQueueElanEvent(pElan, ELAN_EVENT_SVR_CALL_COMPLETE, Status);
|
|
break;
|
|
}
|
|
|
|
if (NDIS_STATUS_PENDING != Status)
|
|
{
|
|
//
|
|
// Call failed.
|
|
// Dereference Atm Entry (should delete it).
|
|
// Signal the elan state machine.
|
|
//
|
|
DBGP((1, "ConnectToServer: MakeCall Failed: Elan %p, Type %d, AtmEnt %p, Ref %d\n",
|
|
pElan,
|
|
ServerType,
|
|
pAtmEntry,
|
|
pAtmEntry->RefCount));
|
|
|
|
if (bAtmEntryAlloced)
|
|
{
|
|
ACQUIRE_ATM_ENTRY_LOCK(pAtmEntry);
|
|
rc = AtmLaneDereferenceAtmEntry(pAtmEntry, "elan"); // Elan list reference
|
|
ASSERT(0 == rc);
|
|
}
|
|
|
|
AtmLaneQueueElanEvent(pElan, ELAN_EVENT_SVR_CALL_COMPLETE, Status);
|
|
|
|
break;
|
|
}
|
|
}
|
|
while (FALSE);
|
|
|
|
TRACEOUT(ConnectToServer);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
AtmLaneInvalidateAtmEntry(
|
|
IN PATMLANE_ATM_ENTRY pAtmEntry LOCKIN NOLOCKOUT
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Invalidate an ATM Entry by unlinking it from MAC entries and
|
|
closing VC's on it.
|
|
|
|
Arguments:
|
|
|
|
pAtmEntry - The ATM Entry needing invalidating.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PATMLANE_MAC_ENTRY pMacEntry;
|
|
PATMLANE_MAC_ENTRY pNextMacEntry;
|
|
ULONG rc; // Ref Count of ATM Entry
|
|
INT MacEntriesUnlinked;
|
|
|
|
TRACEIN(InvalidateAtmEntry);
|
|
|
|
DBGP((1, "%d Del ATM %x: %s\n",
|
|
pAtmEntry->pElan->ElanNumber,
|
|
pAtmEntry,
|
|
AtmAddrToString(pAtmEntry->AtmAddress.Address)));
|
|
|
|
DBGP((3,
|
|
"InvalidateAtmEntry: pAtmEntry %x, pMacEntryList %x\n",
|
|
pAtmEntry,
|
|
pAtmEntry->pMacEntryList));
|
|
//
|
|
// Initialize.
|
|
//
|
|
MacEntriesUnlinked = 0;
|
|
|
|
//
|
|
// Take the MAC Entry list out of the ATM Entry.
|
|
//
|
|
pMacEntry = pAtmEntry->pMacEntryList;
|
|
pAtmEntry->pMacEntryList = NULL_PATMLANE_MAC_ENTRY;
|
|
|
|
//
|
|
// We let go of the ATM Entry lock here because we'll need
|
|
// to lock each MAC Entry in the above list, and we need to make
|
|
// sure that we don't deadlock.
|
|
//
|
|
// However, we make sure that the ATM Entry doesn't go away
|
|
// by adding a reference to it.
|
|
//
|
|
AtmLaneReferenceAtmEntry(pAtmEntry, "temp"); // Temp ref
|
|
RELEASE_ATM_ENTRY_LOCK(pAtmEntry);
|
|
|
|
//
|
|
// Now unlink all MAC entries.
|
|
//
|
|
while (pMacEntry != NULL_PATMLANE_MAC_ENTRY)
|
|
{
|
|
ACQUIRE_MAC_ENTRY_LOCK(pMacEntry);
|
|
pNextMacEntry = pMacEntry->pNextToAtm;
|
|
|
|
//
|
|
// Remove the mapping.
|
|
//
|
|
pMacEntry->Flags = MAC_ENTRY_NEW;
|
|
pMacEntry->pAtmEntry = NULL_PATMLANE_ATM_ENTRY;
|
|
pMacEntry->pNextToAtm = NULL_PATMLANE_MAC_ENTRY;
|
|
|
|
//
|
|
// Remove the ATM Entry linkage reference.
|
|
//
|
|
if (AtmLaneDereferenceMacEntry(pMacEntry, "atm") != 0)
|
|
{
|
|
RELEASE_MAC_ENTRY_LOCK(pMacEntry);
|
|
}
|
|
//
|
|
// else the MAC Entry is gone
|
|
//
|
|
|
|
MacEntriesUnlinked++;
|
|
|
|
pMacEntry = pNextMacEntry;
|
|
}
|
|
|
|
ACQUIRE_ATM_ENTRY_LOCK(pAtmEntry);
|
|
|
|
//
|
|
// Now dereference the ATM Entry as many times as we unliked
|
|
// MAC Entries from it.
|
|
//
|
|
while (MacEntriesUnlinked-- > 0)
|
|
{
|
|
rc = AtmLaneDereferenceAtmEntry(pAtmEntry, "mac"); // MAC Entry reference
|
|
ASSERT(rc != 0);
|
|
}
|
|
|
|
//
|
|
// Take out the reference we added at the beginning of
|
|
// this routine.
|
|
//
|
|
rc = AtmLaneDereferenceAtmEntry(pAtmEntry, "temp"); // Temp ref
|
|
|
|
//
|
|
// Close the SVCs attached to the ATM Entry.
|
|
// But do all this only if the ATM Entry
|
|
// hasn't been dereferenced away already.
|
|
//
|
|
if (rc != 0)
|
|
{
|
|
//
|
|
// The ATM Entry still exists.
|
|
// Close the VCs.
|
|
//
|
|
AtmLaneCloseVCsOnAtmEntry(pAtmEntry);
|
|
//
|
|
// The ATM Entry lock is released within the above.
|
|
//
|
|
}
|
|
//
|
|
// else the ATM Entry is gone
|
|
//
|
|
TRACEOUT(InvalidateAtmEntry);
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
AtmLaneCloseVCsOnAtmEntry(
|
|
IN PATMLANE_ATM_ENTRY pAtmEntry LOCKIN NOLOCKOUT
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Close the (potentially two) VCs attached to an ATM Entry.
|
|
|
|
Arguments:
|
|
|
|
pAtmEntry - Pointer to ATM Entry on which we want to close SVCs.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PATMLANE_VC pVcList; // List of "Main" VCs on the ATM Entry
|
|
PATMLANE_VC pVc; // Main VC on the ATM Entry
|
|
PATMLANE_VC pVcIncomingList; // List of Optional incoming VCs on ATM Entry
|
|
PATMLANE_VC pVcIncoming; // Optional incoming VC on ATM Entry
|
|
PATMLANE_VC pNextVc; // Temp, for traversing VC lists.
|
|
ULONG rc; // Ref count on ATM Entry
|
|
|
|
TRACEIN(CloseVCsOnAtmEntry);
|
|
|
|
//
|
|
// Initialize.
|
|
//
|
|
rc = pAtmEntry->RefCount;
|
|
|
|
//
|
|
// Take out the Main VC list from the ATM Entry.
|
|
//
|
|
pVcList = pAtmEntry->pVcList;
|
|
pAtmEntry->pVcList = NULL_PATMLANE_VC;
|
|
|
|
//
|
|
// Deref the ATM Entry for each of the Main VCs.
|
|
//
|
|
for (pVc = pVcList;
|
|
NULL_PATMLANE_VC != pVc;
|
|
pVc = pNextVc)
|
|
{
|
|
ASSERT(rc != 0);
|
|
|
|
ACQUIRE_VC_LOCK(pVc);
|
|
pNextVc = pVc->pNextVc;
|
|
|
|
//
|
|
// Unlink this VC from the ATM Entry.
|
|
//
|
|
pVc->pAtmEntry = NULL_PATMLANE_ATM_ENTRY;
|
|
|
|
//
|
|
// Leave AtmEntry Reference on VC so it doesn't go away
|
|
//
|
|
RELEASE_VC_LOCK(pVc);
|
|
|
|
DBGP((1, "%d unlink VC %x/%x, Ref %d from ATM Entry %x\n",
|
|
pAtmEntry->pElan->ElanNumber,
|
|
pVc, pVc->Flags, pVc->RefCount,
|
|
pAtmEntry));
|
|
|
|
//
|
|
// Dereference the ATM Entry.
|
|
//
|
|
rc = AtmLaneDereferenceAtmEntry(pAtmEntry, "vc"); // VC reference
|
|
}
|
|
|
|
if (rc != 0)
|
|
{
|
|
//
|
|
// Take out the Incoming VC list from the ATM Entry.
|
|
//
|
|
pVcIncomingList = pAtmEntry->pVcIncoming;
|
|
pAtmEntry->pVcIncoming = NULL_PATMLANE_VC;
|
|
|
|
//
|
|
// Deref the ATM Entry for each of the Incoming VCs.
|
|
//
|
|
for (pVcIncoming = pVcIncomingList;
|
|
NULL_PATMLANE_VC != pVcIncoming;
|
|
pVcIncoming = pNextVc)
|
|
{
|
|
ASSERT(rc != 0);
|
|
|
|
ACQUIRE_VC_LOCK(pVcIncoming);
|
|
pNextVc = pVcIncoming->pNextVc;
|
|
|
|
//
|
|
// Unlink this VC from the ATM Entry.
|
|
//
|
|
pVcIncoming->pAtmEntry = NULL_PATMLANE_ATM_ENTRY;
|
|
|
|
//
|
|
// Leave AtmEntry Reference on VC so it doesn't go away
|
|
//
|
|
RELEASE_VC_LOCK(pVcIncoming);
|
|
|
|
DBGP((1, "%d unlink Incoming VC %x from ATM Entry %x\n",
|
|
pAtmEntry->pElan->ElanNumber,
|
|
pVcIncoming,
|
|
pAtmEntry));
|
|
|
|
//
|
|
// Dereference the ATM Entry.
|
|
//
|
|
rc = AtmLaneDereferenceAtmEntry(pAtmEntry, "vc"); // VC reference
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pVcIncomingList = NULL_PATMLANE_VC;
|
|
}
|
|
|
|
if (rc != 0)
|
|
{
|
|
//
|
|
// The ATM Entry lives on. We don't need a lock to it anymore.
|
|
//
|
|
RELEASE_ATM_ENTRY_LOCK(pAtmEntry);
|
|
}
|
|
|
|
//
|
|
// Now close the VC(s).
|
|
//
|
|
|
|
for (pVc = pVcList;
|
|
NULL_PATMLANE_VC != pVc;
|
|
pVc = pNextVc)
|
|
{
|
|
ACQUIRE_VC_LOCK(pVc);
|
|
pNextVc = pVc->pNextVc;
|
|
|
|
rc = AtmLaneDereferenceVc(pVc, "atm"); // ATM Entry reference
|
|
if (rc != 0)
|
|
{
|
|
AtmLaneCloseCall(pVc);
|
|
//
|
|
// The VC lock is released within the above.
|
|
//
|
|
}
|
|
//
|
|
// else the VC is gone.
|
|
//
|
|
}
|
|
|
|
for (pVcIncoming = pVcIncomingList;
|
|
NULL_PATMLANE_VC != pVcIncoming;
|
|
pVcIncoming = pNextVc)
|
|
{
|
|
ACQUIRE_VC_LOCK(pVcIncoming);
|
|
pNextVc = pVcIncoming->pNextVc;
|
|
|
|
rc = AtmLaneDereferenceVc(pVcIncoming, "atm"); // ATM Entry reference
|
|
if (rc != 0)
|
|
{
|
|
AtmLaneCloseCall(pVcIncoming);
|
|
//
|
|
// The VC lock is released within the above.
|
|
//
|
|
}
|
|
//
|
|
// else the VC is gone.
|
|
//
|
|
}
|
|
|
|
TRACEOUT(CloseVCsOnAtmEntry);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
AtmLaneGenerateMacAddr(
|
|
PATMLANE_ELAN pElan
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Generates a "virtual" MAC address for Elans after the first
|
|
Elan on an ATM interface.
|
|
|
|
Arguments:
|
|
|
|
pElan - Pointer to ATMLANE elan structure
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
|
|
TRACEIN(GenerateMacAddress);
|
|
|
|
//
|
|
// Start by using the real ATM card's MAC address
|
|
//
|
|
|
|
NdisMoveMemory(
|
|
&pElan->MacAddressEth,
|
|
&pElan->pAdapter->MacAddress,
|
|
sizeof(MAC_ADDRESS)
|
|
);
|
|
|
|
if (pElan->ElanNumber != 0)
|
|
{
|
|
//
|
|
// Not Elan number zero so generate a locally
|
|
// administered address by manipulating the first two bytes.
|
|
//
|
|
pElan->MacAddressEth.Byte[0] =
|
|
0x02 | (((UCHAR)pElan->ElanNumber & 0x3f) << 2);
|
|
pElan->MacAddressEth.Byte[1] =
|
|
(pElan->pAdapter->MacAddress.Byte[1] & 0x3f) |
|
|
((UCHAR)pElan->ElanNumber & 0x3f);
|
|
}
|
|
|
|
//
|
|
// Create the Token Ring version of the MAC Address
|
|
//
|
|
NdisMoveMemory(
|
|
&pElan->MacAddressTr,
|
|
&pElan->MacAddressEth,
|
|
sizeof(MAC_ADDRESS)
|
|
);
|
|
|
|
AtmLaneBitSwapMacAddr((PUCHAR)&pElan->MacAddressTr);
|
|
|
|
DBGP((1, "%d MacAddrEth %s\n",
|
|
pElan->ElanNumber, MacAddrToString(&pElan->MacAddressEth)));
|
|
DBGP((1, "%d MacAddrTr %s\n",
|
|
pElan->ElanNumber, MacAddrToString(&pElan->MacAddressTr)));
|
|
|
|
TRACEOUT(GenerateMacAddress);
|
|
return;
|
|
}
|
|
|
|
PATMLANE_MAC_ENTRY
|
|
AtmLaneSearchForMacAddress(
|
|
PATMLANE_ELAN pElan,
|
|
ULONG MacAddrType,
|
|
PMAC_ADDRESS pMacAddress,
|
|
BOOLEAN CreateNew
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Search for an MAC Address in the MAC Table. Optionally, create one
|
|
if a match is not found.
|
|
|
|
The caller is assumed to hold a lock to the MAC Table.
|
|
|
|
Arguments:
|
|
|
|
pElan - Pointer to ATMLANE Elan
|
|
MacAddrType - Type of MAC Addr (MAC vs RD)
|
|
pMacAddress - what we are looking for
|
|
CreateNew - Should a new entry be created if no match?
|
|
|
|
Return Value:
|
|
|
|
Pointer to a matching Mac Entry if found (or created anew).
|
|
|
|
--*/
|
|
{
|
|
ULONG HashIndex;
|
|
PATMLANE_MAC_ENTRY pMacEntry;
|
|
BOOLEAN Found;
|
|
|
|
TRACEIN(SearchForMacAddress);
|
|
|
|
HashIndex = ATMLANE_HASH(pMacAddress);
|
|
Found = FALSE;
|
|
|
|
pMacEntry = pElan->pMacTable[HashIndex];
|
|
|
|
//
|
|
// Go through the addresses in this hash list.
|
|
//
|
|
while (pMacEntry != NULL_PATMLANE_MAC_ENTRY)
|
|
{
|
|
if (!IS_FLAG_SET(
|
|
pMacEntry->Flags,
|
|
MAC_ENTRY_STATE_MASK,
|
|
MAC_ENTRY_ABORTING) &&
|
|
(MAC_ADDR_EQUAL(&pMacEntry->MacAddress, pMacAddress)) &&
|
|
(pMacEntry->MacAddrType == MacAddrType))
|
|
{
|
|
Found = TRUE;
|
|
break;
|
|
}
|
|
pMacEntry = pMacEntry->pNextEntry;
|
|
}
|
|
|
|
if (!Found && CreateNew && (pElan->AdminState != ELAN_STATE_SHUTDOWN))
|
|
{
|
|
pMacEntry = AtmLaneAllocateMacEntry(pElan);
|
|
|
|
if (pMacEntry != NULL_PATMLANE_MAC_ENTRY)
|
|
{
|
|
//
|
|
// Fill in this new entry.
|
|
//
|
|
NdisMoveMemory(&pMacEntry->MacAddress, pMacAddress, sizeof(MAC_ADDRESS));
|
|
pMacEntry->MacAddrType = MacAddrType;
|
|
AtmLaneReferenceMacEntry(pMacEntry, "table"); // Mac Table linkage
|
|
|
|
//
|
|
// Link it to the hash table.
|
|
//
|
|
pMacEntry->pNextEntry = pElan->pMacTable[HashIndex];
|
|
pElan->pMacTable[HashIndex] = pMacEntry;
|
|
pElan->NumMacEntries++;
|
|
|
|
DBGP((1, "%d New MAC %x: %s\n",
|
|
pElan->ElanNumber,
|
|
pMacEntry,
|
|
MacAddrToString(pMacAddress)));
|
|
}
|
|
else
|
|
{
|
|
DBGP((0, "SearchForMacAddress: alloc of new mac entry failed\n"));
|
|
}
|
|
}
|
|
|
|
TRACEOUT(SearchForMacAddress);
|
|
return (pMacEntry);
|
|
}
|
|
|
|
PATMLANE_ATM_ENTRY
|
|
AtmLaneSearchForAtmAddress(
|
|
IN PATMLANE_ELAN pElan,
|
|
IN PUCHAR pAtmAddress,
|
|
IN ULONG Type,
|
|
IN BOOLEAN CreateNew
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Search for an ATM Entry that matches the given ATM address and type.
|
|
Optionally, create one if there is no match.
|
|
|
|
NOTE: this routine references the ATM entry it returns. The caller
|
|
should deref it.
|
|
|
|
Arguments:
|
|
|
|
pElan - Pointer to ATMLANE Elan
|
|
pAtmAddress - ATM Address
|
|
Type - ATM Entry Type (Peer, LECS, LES, BUS)
|
|
CreateNew - Do we create a new entry if we don't find one?
|
|
|
|
Return Value:
|
|
|
|
Pointer to a matching ATM Entry if found (or created anew).
|
|
|
|
--*/
|
|
{
|
|
PATMLANE_ATM_ENTRY pAtmEntry;
|
|
BOOLEAN Found;
|
|
|
|
TRACEIN(SearchForAtmAddress);
|
|
|
|
ACQUIRE_ELAN_ATM_LIST_LOCK(pElan);
|
|
|
|
//
|
|
// Go through the list of ATM Entries on this interface.
|
|
//
|
|
Found = FALSE;
|
|
for (pAtmEntry = pElan->pAtmEntryList;
|
|
pAtmEntry != NULL_PATMLANE_ATM_ENTRY;
|
|
pAtmEntry = pAtmEntry->pNext)
|
|
{
|
|
//
|
|
// Compare the ATM Address and Type
|
|
//
|
|
if ((ATM_ADDR_EQUAL(pAtmAddress, pAtmEntry->AtmAddress.Address)) &&
|
|
(pAtmEntry->Type == Type) &&
|
|
((pAtmEntry->Flags & ATM_ENTRY_WILL_ABORT) == 0))
|
|
{
|
|
Found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!Found && CreateNew && (pElan->State != ELAN_STATE_SHUTDOWN))
|
|
{
|
|
pAtmEntry = AtmLaneAllocateAtmEntry(pElan);
|
|
|
|
if (pAtmEntry != NULL_PATMLANE_ATM_ENTRY)
|
|
{
|
|
//
|
|
// Fill in this new entry.
|
|
//
|
|
pAtmEntry->Flags = ATM_ENTRY_VALID;
|
|
|
|
//
|
|
// The ATM Address.
|
|
//
|
|
pAtmEntry->AtmAddress.AddressType = ATM_NSAP;
|
|
pAtmEntry->AtmAddress.NumberOfDigits = ATM_ADDRESS_LENGTH;
|
|
NdisMoveMemory(
|
|
pAtmEntry->AtmAddress.Address,
|
|
pAtmAddress,
|
|
ATM_ADDRESS_LENGTH);
|
|
|
|
//
|
|
// The Type.
|
|
//
|
|
pAtmEntry->Type = Type;
|
|
|
|
//
|
|
// Link in this entry to the Elan
|
|
//
|
|
pAtmEntry->pNext = pElan->pAtmEntryList;
|
|
pElan->pAtmEntryList = pAtmEntry;
|
|
pElan->NumAtmEntries++;
|
|
|
|
|
|
DBGP((1, "%d New ATM %x: %s\n",
|
|
pElan->ElanNumber,
|
|
pAtmEntry,
|
|
AtmAddrToString(pAtmAddress)));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Reference this ATM entry so that it won't be derefed away
|
|
// before the caller gets to use this.
|
|
//
|
|
if (NULL_PATMLANE_ATM_ENTRY != pAtmEntry)
|
|
{
|
|
ACQUIRE_ATM_ENTRY_LOCK_DPC(pAtmEntry);
|
|
AtmLaneReferenceAtmEntry(pAtmEntry, "search");
|
|
RELEASE_ATM_ENTRY_LOCK_DPC(pAtmEntry);
|
|
}
|
|
|
|
RELEASE_ELAN_ATM_LIST_LOCK(pElan);
|
|
|
|
TRACEOUT(SearchForAtmAddress);
|
|
return (pAtmEntry);
|
|
}
|
|
|
|
ULONG
|
|
AtmLaneMacAddrEqual(
|
|
PMAC_ADDRESS pMacAddr1,
|
|
PMAC_ADDRESS pMacAddr2
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Compares two 48bit(6 Byte) MAC Addresses.
|
|
|
|
Arguments:
|
|
|
|
pMacAddr1 - First MAC Address.
|
|
pMacAddr2 - Second MAC Address.
|
|
|
|
Return Value:
|
|
|
|
1 if equal, 0 if not equal.
|
|
|
|
--*/
|
|
{
|
|
ULONG Result;
|
|
|
|
TRACEIN(MacAddrEqual);
|
|
|
|
//
|
|
// Assume not equal
|
|
//
|
|
Result = 0;
|
|
|
|
do
|
|
{
|
|
//
|
|
// Studies have shown the fifth byte to be
|
|
// the most unique on a network.
|
|
//
|
|
if (pMacAddr1->Byte[4] != pMacAddr2->Byte[4])
|
|
break;
|
|
if (pMacAddr1->Byte[5] != pMacAddr2->Byte[5])
|
|
break;
|
|
if (pMacAddr1->Byte[3] != pMacAddr2->Byte[3])
|
|
break;
|
|
if (pMacAddr1->Byte[2] != pMacAddr2->Byte[2])
|
|
break;
|
|
if (pMacAddr1->Byte[1] != pMacAddr2->Byte[1])
|
|
break;
|
|
if (pMacAddr1->Byte[0] != pMacAddr2->Byte[0])
|
|
break;
|
|
|
|
Result = 1;
|
|
break;
|
|
}
|
|
while (FALSE);
|
|
|
|
TRACEOUT(MacAddrEqual);
|
|
return Result;
|
|
}
|
|
|
|
VOID
|
|
AtmLaneAbortMacEntry(
|
|
IN PATMLANE_MAC_ENTRY pMacEntry
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Clean up and delete an Mac entry.
|
|
|
|
The caller is assumed to hold a lock to the Mac Entry,
|
|
which will be released here.
|
|
|
|
Arguments:
|
|
|
|
pMacEntry - Pointer to Mac Entry to be deleted.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PATMLANE_ELAN pElan;
|
|
PATMLANE_MAC_ENTRY * ppNextMacEntry;
|
|
ULONG rc;
|
|
BOOLEAN Found;
|
|
BOOLEAN TimerWasRunning;
|
|
ULONG HashIndex;
|
|
PNDIS_PACKET pNdisPacket;
|
|
|
|
TRACEIN(AbortMacEntry);
|
|
|
|
DBGP((1, "%d Del MAC %p: AtmEntry %p, NextToAtm %p, %s\n",
|
|
pMacEntry->pElan->ElanNumber,
|
|
pMacEntry, pMacEntry->pAtmEntry, pMacEntry->pNextToAtm,
|
|
MacAddrToString(&pMacEntry->MacAddress)));
|
|
|
|
//
|
|
// Initialize.
|
|
//
|
|
rc = pMacEntry->RefCount;
|
|
pElan = pMacEntry->pElan;
|
|
|
|
do
|
|
{
|
|
if (IS_FLAG_SET(
|
|
pMacEntry->Flags,
|
|
MAC_ENTRY_STATE_MASK,
|
|
MAC_ENTRY_ABORTING))
|
|
{
|
|
DBGP((1, "%d MAC %x already aborting\n",
|
|
pMacEntry->pElan->ElanNumber,
|
|
pMacEntry));
|
|
|
|
RELEASE_MAC_ENTRY_LOCK(pMacEntry);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Set State to ABORTING
|
|
//
|
|
SET_FLAG(
|
|
pMacEntry->Flags,
|
|
MAC_ENTRY_STATE_MASK,
|
|
MAC_ENTRY_ABORTING);
|
|
|
|
//
|
|
// Put temp reference on mac entry
|
|
//
|
|
AtmLaneReferenceMacEntry(pMacEntry, "temp");
|
|
|
|
//
|
|
// Reacquire the desired locks in the right order.
|
|
//
|
|
RELEASE_MAC_ENTRY_LOCK(pMacEntry);
|
|
ACQUIRE_ELAN_MAC_TABLE_LOCK(pElan);
|
|
ACQUIRE_MAC_ENTRY_LOCK_DPC(pMacEntry);
|
|
|
|
//
|
|
// Unlink this MAC Entry from the MAC Table
|
|
//
|
|
Found = FALSE;
|
|
|
|
HashIndex = ATMLANE_HASH(&pMacEntry->MacAddress);
|
|
ppNextMacEntry = &(pElan->pMacTable[HashIndex]);
|
|
while (*ppNextMacEntry != NULL_PATMLANE_MAC_ENTRY)
|
|
{
|
|
if (*ppNextMacEntry == pMacEntry)
|
|
{
|
|
//
|
|
// Make the predecessor point to the next
|
|
// in the list.
|
|
//
|
|
*ppNextMacEntry = pMacEntry->pNextEntry;
|
|
Found = TRUE;
|
|
pElan->NumMacEntries--;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
ppNextMacEntry = &((*ppNextMacEntry)->pNextEntry);
|
|
}
|
|
}
|
|
|
|
if (Found)
|
|
{
|
|
AtmLaneDereferenceMacEntry(pMacEntry, "table");
|
|
}
|
|
|
|
RELEASE_MAC_ENTRY_LOCK_DPC(pMacEntry);
|
|
RELEASE_ELAN_MAC_TABLE_LOCK(pElan);
|
|
ACQUIRE_MAC_ENTRY_LOCK(pMacEntry);
|
|
|
|
//
|
|
// Unlink MAC Entry from the Atm Entry
|
|
//
|
|
if (pMacEntry->pAtmEntry != NULL_PATMLANE_ATM_ENTRY)
|
|
{
|
|
SET_FLAG(
|
|
pMacEntry->Flags,
|
|
MAC_ENTRY_STATE_MASK,
|
|
MAC_ENTRY_NEW);
|
|
|
|
Found = AtmLaneUnlinkMacEntryFromAtmEntry(pMacEntry);
|
|
pMacEntry->pAtmEntry = NULL_PATMLANE_ATM_ENTRY;
|
|
|
|
if (Found)
|
|
{
|
|
AtmLaneDereferenceMacEntry(pMacEntry, "atm");
|
|
}
|
|
}
|
|
|
|
//
|
|
// Stop Arp or Aging timer running on the MAC Entry.
|
|
//
|
|
TimerWasRunning = AtmLaneStopTimer(&(pMacEntry->Timer), pElan);
|
|
if (TimerWasRunning)
|
|
{
|
|
AtmLaneDereferenceMacEntry(pMacEntry, "timer");
|
|
}
|
|
|
|
//
|
|
// Stop Bus Timer
|
|
//
|
|
NdisCancelTimer(&pMacEntry->BusTimer, &TimerWasRunning);
|
|
if (TimerWasRunning)
|
|
{
|
|
AtmLaneDereferenceMacEntry(pMacEntry, "bus timer");
|
|
}
|
|
|
|
//
|
|
// Stop Flush Timer
|
|
//
|
|
TimerWasRunning = AtmLaneStopTimer(&pMacEntry->FlushTimer, pElan);
|
|
if (TimerWasRunning)
|
|
{
|
|
AtmLaneDereferenceMacEntry(pMacEntry, "flush timer");
|
|
}
|
|
|
|
//
|
|
// Now complete all packets hanging on the MacEntry
|
|
//
|
|
DBGP((1, "%d: Aborting MAC %x, Before: PktList %x, PktListCount %d\n",
|
|
pElan->ElanNumber, pMacEntry, pMacEntry->PacketList, pMacEntry->PacketListCount));
|
|
AtmLaneFreePacketQueue(pMacEntry, NDIS_STATUS_SUCCESS);
|
|
|
|
DBGP((1, "%d: Aborting MAC %x, After: PktList %x, PktListCount %d\n",
|
|
pElan->ElanNumber, pMacEntry, pMacEntry->PacketList, pMacEntry->PacketListCount));
|
|
|
|
//
|
|
// Remove temp reference and unlock if still around
|
|
//
|
|
rc = AtmLaneDereferenceMacEntry(pMacEntry, "temp");
|
|
if (rc > 0)
|
|
{
|
|
RELEASE_MAC_ENTRY_LOCK(pMacEntry);
|
|
}
|
|
}
|
|
while (FALSE);
|
|
|
|
TRACEOUT(AbortMacEntry);
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
AtmLaneMacEntryAgingTimeout(
|
|
IN PATMLANE_TIMER pTimer,
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called if some time has passed since an
|
|
MAC entry was last validated.
|
|
|
|
If there is no VC associated with this MAC entry, delete it.
|
|
If there has been no sends on the entry since last validated, delete it.
|
|
Otherwise revalidate the entry by starting the ARP protocol.
|
|
|
|
Arguments:
|
|
|
|
pTimer - Pointer to timer that went off
|
|
Context - Actually a pointer to our ATMLANE Mac Entry structure
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PATMLANE_MAC_ENTRY pMacEntry; // Mac Entry that has aged out
|
|
ULONG rc; // Ref count on Mac Entry
|
|
PATMLANE_VC pVc; // VC going to this Mac Entry
|
|
ULONG Flags; // Flags on above VC
|
|
PATMLANE_ELAN pElan;
|
|
PATMLANE_ATM_ENTRY pAtmEntry;
|
|
|
|
TRACEOUT(MacEntryAgingTimeout);
|
|
|
|
pMacEntry = (PATMLANE_MAC_ENTRY)Context;
|
|
STRUCT_ASSERT(pMacEntry, atmlane_mac);
|
|
|
|
do
|
|
{
|
|
ACQUIRE_MAC_ENTRY_LOCK(pMacEntry);
|
|
|
|
DBGP((2, "MacEntryAgingTimeout: pMacEntry %x MacAddr %s\n",
|
|
pMacEntry, MacAddrToString(&pMacEntry->MacAddress)));
|
|
|
|
rc = AtmLaneDereferenceMacEntry(pMacEntry, "aging timer");
|
|
if (rc == 0)
|
|
{
|
|
break; // It's gone!
|
|
}
|
|
|
|
//
|
|
// Continue only if the Elan is not going down
|
|
//
|
|
pElan = pMacEntry->pElan;
|
|
if (ELAN_STATE_OPERATIONAL != pElan->AdminState)
|
|
{
|
|
RELEASE_MAC_ENTRY_LOCK(pMacEntry);
|
|
break;
|
|
}
|
|
|
|
pVc = NULL_PATMLANE_VC;
|
|
pAtmEntry = pMacEntry->pAtmEntry;
|
|
if (pAtmEntry != NULL_PATMLANE_ATM_ENTRY)
|
|
{
|
|
pVc = pAtmEntry->pVcList;
|
|
}
|
|
|
|
|
|
if (pVc != NULL_PATMLANE_VC &&
|
|
(pMacEntry->Flags & MAC_ENTRY_USED_FOR_SEND) != 0)
|
|
{
|
|
//
|
|
// There is a VC for this Mac Address and it's been
|
|
// used for a send in the last aging period.
|
|
// So we try to revalidate this Mac entry.
|
|
//
|
|
//
|
|
// Set state to AGED
|
|
//
|
|
SET_FLAG(
|
|
pMacEntry->Flags,
|
|
MAC_ENTRY_STATE_MASK,
|
|
MAC_ENTRY_AGED);
|
|
|
|
pMacEntry->Flags &= ~ MAC_ENTRY_USED_FOR_SEND;
|
|
|
|
//
|
|
// Send ARP Request
|
|
//
|
|
pMacEntry->RetriesLeft = pElan->MaxRetryCount;
|
|
AtmLaneReferenceMacEntry(pMacEntry, "timer");
|
|
AtmLaneStartTimer(
|
|
pElan,
|
|
&pMacEntry->Timer,
|
|
AtmLaneArpTimeout,
|
|
pElan->ArpResponseTime,
|
|
(PVOID)pMacEntry
|
|
);
|
|
|
|
AtmLaneSendArpRequest(pElan, pMacEntry);
|
|
//
|
|
// MAC Entry lock released in above
|
|
//
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// No VC associated with this Mac Entry or
|
|
// it hasn't been used in last aging period.
|
|
// Delete it.
|
|
//
|
|
AtmLaneAbortMacEntry(pMacEntry);
|
|
//
|
|
// The Mac Entry lock is released in the above routine.
|
|
//
|
|
}
|
|
|
|
}
|
|
while (FALSE);
|
|
|
|
TRACEOUT(MacEntryAgingTimeout);
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
AtmLaneArpTimeout(
|
|
IN PATMLANE_TIMER pTimer,
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is called when we time out waiting for a response to an ARP Request
|
|
we had sent ages ago in order to resolve/refresh an MAC entry.
|
|
|
|
Check if we have tried enough times. If we have retries left, send another
|
|
ARP Request.
|
|
|
|
If we have run out of retries, delete the MAC entry, and any VCs going to it.
|
|
|
|
Arguments:
|
|
|
|
pTimer - Pointer to timer that went off
|
|
Context - Actually a pointer to our ATMLANE MAC Entry structure
|
|
ContextValue - ignored
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PATMLANE_MAC_ENTRY pMacEntry; // MAC Entry being ARP'ed for.
|
|
PATMLANE_VC pVc; // VC to this MAC destination
|
|
PATMLANE_ELAN pElan;
|
|
ULONG rc; // Ref Count on MAC Entry
|
|
ULONG IsBroadcast;
|
|
|
|
TRACEIN(ArpTimeout);
|
|
|
|
do
|
|
{
|
|
pMacEntry = (PATMLANE_MAC_ENTRY)Context;
|
|
STRUCT_ASSERT(pMacEntry, atmlane_mac);
|
|
|
|
ACQUIRE_MAC_ENTRY_LOCK(pMacEntry);
|
|
|
|
pElan = pMacEntry->pElan;
|
|
|
|
DBGP((2, "ArpTimeout: Mac Entry %x\n", pMacEntry));
|
|
|
|
rc = AtmLaneDereferenceMacEntry(pMacEntry, "timer"); // Timer reference
|
|
if (rc == 0)
|
|
{
|
|
break; // It's gone!
|
|
}
|
|
|
|
//
|
|
// Retry if any retries left
|
|
//
|
|
if (pMacEntry->RetriesLeft != 0)
|
|
{
|
|
pMacEntry->RetriesLeft--;
|
|
|
|
AtmLaneReferenceMacEntry(pMacEntry, "timer");
|
|
|
|
AtmLaneStartTimer(
|
|
pElan,
|
|
&pMacEntry->Timer,
|
|
AtmLaneArpTimeout,
|
|
pElan->ArpResponseTime,
|
|
(PVOID)pMacEntry
|
|
);
|
|
|
|
AtmLaneSendArpRequest(pElan, pMacEntry);
|
|
//
|
|
// MAC Entry lock released in above
|
|
//
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// Is this the broadcast/BUS entry?
|
|
//
|
|
IsBroadcast = (pMacEntry->Flags & MAC_ENTRY_BROADCAST);
|
|
|
|
//
|
|
// We are out of retries. Abort the Mac Entry
|
|
//
|
|
AtmLaneAbortMacEntry(pMacEntry);
|
|
//
|
|
// lock is released in the above routine
|
|
//
|
|
|
|
if (IsBroadcast)
|
|
{
|
|
//
|
|
// Signal the event to the state machine
|
|
//
|
|
ACQUIRE_ELAN_LOCK(pElan);
|
|
AtmLaneQueueElanEvent(pElan, ELAN_EVENT_ARP_RESPONSE, NDIS_STATUS_TIMEOUT);
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
}
|
|
}
|
|
while (FALSE);
|
|
|
|
TRACEOUT(ArpTimeout);
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
AtmLaneConfigureResponseTimeout(
|
|
IN PATMLANE_TIMER pTimer,
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is called when we time out waiting for a response
|
|
to an LE_CONFIGURE_REQUEST we sent to the LECS.
|
|
|
|
Arguments:
|
|
|
|
pTimer - Pointer to timer that went off
|
|
Context - Actually a pointer to our ATMLANE Elan structure
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PATMLANE_ELAN pElan;
|
|
ULONG rc;
|
|
|
|
TRACEIN(ConfigureResponseTimeout);
|
|
|
|
pElan = (PATMLANE_ELAN)Context;
|
|
STRUCT_ASSERT(pElan, atmlane_elan);
|
|
|
|
do
|
|
{
|
|
ACQUIRE_ELAN_LOCK_DPC(pElan);
|
|
rc = AtmLaneDereferenceElan(pElan, "timer"); // Timer deref
|
|
|
|
if (rc == 0)
|
|
{
|
|
//
|
|
// The ELAN is gone.
|
|
//
|
|
break;
|
|
}
|
|
|
|
AtmLaneQueueElanEvent(pElan, ELAN_EVENT_CONFIGURE_RESPONSE, NDIS_STATUS_TIMEOUT);
|
|
|
|
RELEASE_ELAN_LOCK_DPC(pElan);
|
|
|
|
}
|
|
while (FALSE);
|
|
|
|
TRACEOUT(ConfigureResponseTimeout);
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
AtmLaneJoinResponseTimeout(
|
|
IN PATMLANE_TIMER pTimer,
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is called when we time out waiting for a response
|
|
to an LE_JOIN_REQUEST we sent to the LES.
|
|
|
|
Arguments:
|
|
|
|
pTimer - Pointer to timer that went off
|
|
Context - Actually a pointer to our ATMLANE Elan structure
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PATMLANE_ELAN pElan;
|
|
ULONG rc;
|
|
|
|
TRACEIN(JoinResponseTimeout);
|
|
|
|
pElan = (PATMLANE_ELAN)Context;
|
|
STRUCT_ASSERT(pElan, atmlane_elan);
|
|
|
|
do
|
|
{
|
|
ACQUIRE_ELAN_LOCK_DPC(pElan);
|
|
|
|
rc = AtmLaneDereferenceElan(pElan, "timer"); // Timer deref
|
|
|
|
if (rc == 0)
|
|
{
|
|
//
|
|
// The ELAN is gone.
|
|
//
|
|
break;
|
|
}
|
|
|
|
AtmLaneQueueElanEvent(pElan, ELAN_EVENT_JOIN_RESPONSE, NDIS_STATUS_TIMEOUT);
|
|
|
|
RELEASE_ELAN_LOCK_DPC(pElan);
|
|
}
|
|
while (FALSE);
|
|
|
|
TRACEOUT(JoinResponseTimeout);
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
AtmLaneInitializeMiniportDevice(
|
|
IN PNDIS_WORK_ITEM NdisWorkItem,
|
|
IN PVOID Context
|
|
)
|
|
{
|
|
PATMLANE_ELAN pElan;
|
|
ULONG rc;
|
|
BOOLEAN bDontBotherToInit;
|
|
NDIS_STATUS Status;
|
|
|
|
TRACEIN(InitializeMiniportDevice);
|
|
|
|
pElan = (PATMLANE_ELAN)Context;
|
|
STRUCT_ASSERT(pElan, atmlane_elan);
|
|
|
|
//
|
|
// If we are shutting down this ELAN (e.g. because we are
|
|
// unbinding from the ATM adapter), then don't bother to
|
|
// initiate MiniportInit.
|
|
//
|
|
ACQUIRE_ELAN_LOCK(pElan);
|
|
if (pElan->AdminState == ELAN_STATE_SHUTDOWN)
|
|
{
|
|
bDontBotherToInit = TRUE;
|
|
}
|
|
else
|
|
{
|
|
bDontBotherToInit = FALSE;
|
|
pElan->Flags |= ELAN_MINIPORT_INIT_PENDING;
|
|
INIT_BLOCK_STRUCT(&pElan->InitBlock);
|
|
}
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
|
|
if (!bDontBotherToInit)
|
|
{
|
|
DBGP((1, "%d Miniport INITIALIZING Device %s\n",
|
|
pElan->ElanNumber,
|
|
UnicodeToString(&pElan->CfgDeviceName)));
|
|
|
|
Status = NdisIMInitializeDeviceInstanceEx(
|
|
pAtmLaneGlobalInfo->MiniportDriverHandle,
|
|
&pElan->CfgDeviceName,
|
|
(NDIS_HANDLE)pElan);
|
|
|
|
if (Status != NDIS_STATUS_SUCCESS)
|
|
{
|
|
DBGP((0, "%d IMInitializeDeviceInstanceEx failed on ELAN %p (%x)\n",
|
|
pElan->ElanNumber, pElan, Status));
|
|
|
|
ACQUIRE_ELAN_LOCK(pElan);
|
|
pElan->Flags &= ~ELAN_MINIPORT_INIT_PENDING;
|
|
SIGNAL_BLOCK_STRUCT(&pElan->InitBlock, NDIS_STATUS_SUCCESS);
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
}
|
|
}
|
|
|
|
ACQUIRE_ELAN_LOCK(pElan);
|
|
rc = AtmLaneDereferenceElan(pElan, "workitem");
|
|
if (rc > 0)
|
|
{
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
}
|
|
|
|
TRACEOUT(InitializeMiniportDevice);
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
AtmLaneDeinitializeMiniportDevice(
|
|
IN PNDIS_WORK_ITEM NdisWorkItem,
|
|
IN PVOID Context
|
|
)
|
|
|
|
{
|
|
PATMLANE_ELAN pElan;
|
|
NDIS_STATUS Status;
|
|
ULONG rc;
|
|
NDIS_HANDLE AdapterHandle;
|
|
|
|
TRACEIN(DeinitializeMiniportDevice);
|
|
|
|
pElan = (PATMLANE_ELAN)Context;
|
|
STRUCT_ASSERT(pElan, atmlane_elan);
|
|
|
|
DBGP((1, "%d Miniport DEINITIALIZING, AdapterHandle %x, RefCount %d\n",
|
|
pElan->ElanNumber, pElan->MiniportAdapterHandle, pElan->RefCount));
|
|
|
|
ACQUIRE_ELAN_LOCK(pElan);
|
|
|
|
AdapterHandle = pElan->MiniportAdapterHandle;
|
|
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
|
|
if (NULL != AdapterHandle)
|
|
{
|
|
DBGP((1, "Will call NdisIMDeInit %x\n", AdapterHandle));
|
|
Status = NdisIMDeInitializeDeviceInstance(AdapterHandle);
|
|
ASSERT(Status == NDIS_STATUS_SUCCESS);
|
|
//
|
|
// Our MHalt routine will be called at some point.
|
|
//
|
|
}
|
|
//
|
|
// else our MHalt routine was called already.
|
|
//
|
|
|
|
DBGP((0, "DeInit completing, pElan %x, RefCount %d, State %d\n",
|
|
pElan, pElan->RefCount, pElan->State));
|
|
|
|
ACQUIRE_ELAN_LOCK(pElan);
|
|
rc = AtmLaneDereferenceElan(pElan, "workitem");
|
|
if (rc > 0)
|
|
{
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
}
|
|
|
|
TRACEOUT(DeinitializeMiniportDevice);
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
AtmLaneReadyTimeout(
|
|
IN PATMLANE_TIMER pTimer,
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is called when we time out waiting for a ready indication
|
|
on a incoming data direct VC.
|
|
|
|
Arguments:
|
|
|
|
pTimer - Pointer to timer that went off
|
|
Context - Actually a pointer to an ATMLANE Vc structure
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PATMLANE_VC pVc;
|
|
PATMLANE_ELAN pElan;
|
|
ULONG rc;
|
|
|
|
TRACEIN(AtmLaneReadyTimeout);
|
|
|
|
pVc = (PATMLANE_VC)Context;
|
|
STRUCT_ASSERT(pVc, atmlane_vc);
|
|
pElan = pVc->pElan;
|
|
STRUCT_ASSERT(pElan, atmlane_elan);
|
|
|
|
do
|
|
{
|
|
|
|
ACQUIRE_VC_LOCK(pVc);
|
|
|
|
//
|
|
// Remove ready timer reference
|
|
//
|
|
rc = AtmLaneDereferenceVc(pVc, "ready timer");
|
|
if (rc == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Vc is still around, check state
|
|
//
|
|
if (!IS_FLAG_SET(
|
|
pVc->Flags,
|
|
VC_CALL_STATE_MASK,
|
|
VC_CALL_STATE_ACTIVE
|
|
))
|
|
{
|
|
RELEASE_VC_LOCK(pVc);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Check if any retries left
|
|
//
|
|
if (pVc->RetriesLeft--)
|
|
{
|
|
//
|
|
// Start timer again
|
|
//
|
|
SET_FLAG(
|
|
pVc->Flags,
|
|
VC_READY_STATE_MASK,
|
|
VC_READY_WAIT
|
|
);
|
|
AtmLaneReferenceVc(pVc, "ready timer");
|
|
AtmLaneStartTimer(
|
|
pElan,
|
|
&pVc->ReadyTimer,
|
|
AtmLaneReadyTimeout,
|
|
pElan->ConnComplTimer,
|
|
pVc);
|
|
//
|
|
// Send Ready Query
|
|
//
|
|
AtmLaneSendReadyQuery(pElan, pVc);
|
|
//
|
|
// VC lock is released in above.
|
|
//
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Give up and mark as having received indication anyway
|
|
//
|
|
SET_FLAG(
|
|
pVc->Flags,
|
|
VC_READY_STATE_MASK,
|
|
VC_READY_INDICATED
|
|
);
|
|
RELEASE_VC_LOCK(pVc);
|
|
}
|
|
}
|
|
while (FALSE);
|
|
|
|
TRACEOUT(AtmLaneReadyTimeout);
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
AtmLaneFlushTimeout(
|
|
IN PATMLANE_TIMER pTimer,
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is called when we time out waiting for a response to a FLUSH Request.
|
|
|
|
Arguments:
|
|
|
|
pTimer - Pointer to timer that went off
|
|
Context - Actually a pointer to a ATMLANE MAC Entry structure
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PATMLANE_MAC_ENTRY pMacEntry;
|
|
PATMLANE_ATM_ENTRY pAtmEntry;
|
|
PATMLANE_VC pVc;
|
|
PNDIS_PACKET pNdisPacket;
|
|
ULONG rc;
|
|
|
|
TRACEIN(FlushTimeout);
|
|
|
|
pMacEntry = (PATMLANE_MAC_ENTRY)Context;
|
|
STRUCT_ASSERT(pMacEntry, atmlane_mac);
|
|
|
|
ACQUIRE_MAC_ENTRY_LOCK(pMacEntry);
|
|
|
|
do
|
|
{
|
|
if (!IS_FLAG_SET(
|
|
pMacEntry->Flags,
|
|
MAC_ENTRY_STATE_MASK,
|
|
MAC_ENTRY_FLUSHING))
|
|
{
|
|
DBGP((0, "%d FlushTimeout: MacEntry %p, bad state, Flags %x\n",
|
|
pMacEntry->pElan->ElanNumber,
|
|
pMacEntry,
|
|
pMacEntry->Flags));
|
|
break;
|
|
}
|
|
|
|
if (pMacEntry->pAtmEntry == NULL_PATMLANE_ATM_ENTRY)
|
|
{
|
|
DBGP((0, "%d FlushTimeout: Mac Entry %p, Flags %x, NULL AtmEntry\n",
|
|
pMacEntry->pElan->ElanNumber,
|
|
pMacEntry,
|
|
pMacEntry->Flags));
|
|
|
|
SET_FLAG(
|
|
pMacEntry->Flags,
|
|
MAC_ENTRY_STATE_MASK,
|
|
MAC_ENTRY_NEW);
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Mark MAC Entry active
|
|
//
|
|
SET_FLAG(
|
|
pMacEntry->Flags,
|
|
MAC_ENTRY_STATE_MASK,
|
|
MAC_ENTRY_ACTIVE);
|
|
|
|
//
|
|
// Send any queued packets
|
|
//
|
|
if (pMacEntry->PacketList == (PNDIS_PACKET)NULL)
|
|
{
|
|
break;
|
|
}
|
|
|
|
pVc = pMacEntry->pAtmEntry->pVcList;
|
|
|
|
if (pVc == NULL_PATMLANE_VC)
|
|
{
|
|
break;
|
|
}
|
|
|
|
ACQUIRE_VC_LOCK(pVc);
|
|
|
|
//
|
|
// Make sure this VC doesn't go away.
|
|
//
|
|
AtmLaneReferenceVc(pVc, "flushtemp");
|
|
|
|
RELEASE_VC_LOCK(pVc);
|
|
|
|
while ((pNdisPacket = AtmLaneDequeuePacketFromHead(pMacEntry)) !=
|
|
(PNDIS_PACKET)NULL)
|
|
{
|
|
//
|
|
// Send it
|
|
//
|
|
RELEASE_MAC_ENTRY_LOCK(pMacEntry);
|
|
|
|
ACQUIRE_VC_LOCK(pVc);
|
|
AtmLaneSendPacketOnVc(pVc, pNdisPacket, TRUE);
|
|
|
|
ACQUIRE_MAC_ENTRY_LOCK(pMacEntry);
|
|
}
|
|
|
|
ACQUIRE_VC_LOCK(pVc);
|
|
|
|
rc = AtmLaneDereferenceVc(pVc, "flushtemp");
|
|
|
|
if (rc != 0)
|
|
{
|
|
RELEASE_VC_LOCK(pVc);
|
|
}
|
|
break;
|
|
}
|
|
while (FALSE);
|
|
|
|
rc = AtmLaneDereferenceMacEntry(pMacEntry, "flush timer");
|
|
if (rc != 0)
|
|
{
|
|
RELEASE_MAC_ENTRY_LOCK(pMacEntry);
|
|
}
|
|
|
|
TRACEOUT(FlushTimeout);
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
AtmLaneVcAgingTimeout(
|
|
IN PATMLANE_TIMER pTimer,
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is called when the VC aging timeout fires.
|
|
It will fire if this VC hasn't been used to transmit
|
|
a packet for the timeout period. The VC will be
|
|
closed unless it has had receive activity since the last
|
|
timeout. The data receive path sets a flag if a packet
|
|
has been received.
|
|
|
|
|
|
Arguments:
|
|
|
|
pTimer - Pointer to timer that went off
|
|
Context - Actually a pointer to a ATMLANE VC structure
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PATMLANE_VC pVc;
|
|
ULONG rc;
|
|
PATMLANE_ELAN pElan;
|
|
|
|
TRACEIN(VcAgingTimeout);
|
|
|
|
do
|
|
{
|
|
pVc = (PATMLANE_VC)Context;
|
|
STRUCT_ASSERT(pVc, atmlane_vc);
|
|
ASSERT(IS_FLAG_SET(pVc->Flags, VC_TYPE_MASK, VC_TYPE_SVC));
|
|
|
|
ACQUIRE_VC_LOCK(pVc);
|
|
pElan = pVc->pElan;
|
|
|
|
//
|
|
// Continue only if VC still active
|
|
// Otherwise dereference, unlock it, and return
|
|
//
|
|
if (!(IS_FLAG_SET(pVc->Flags,
|
|
VC_CALL_STATE_MASK,
|
|
VC_CALL_STATE_ACTIVE)))
|
|
{
|
|
rc = AtmLaneDereferenceVc(pVc, "aging timer");
|
|
if (rc > 0)
|
|
{
|
|
RELEASE_VC_LOCK(pVc);
|
|
}
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Continue only if the ELAN isn't going down
|
|
// Otherwise dereference, unlock it, and return
|
|
//
|
|
if (ELAN_STATE_OPERATIONAL != pElan->AdminState)
|
|
{
|
|
rc = AtmLaneDereferenceVc(pVc, "aging timer");
|
|
if (rc > 0)
|
|
{
|
|
RELEASE_VC_LOCK(pVc);
|
|
}
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If received activity is non-zero,
|
|
// clear flag, restart aging timer, release lock
|
|
// and return
|
|
//
|
|
if (pVc->ReceiveActivity != 0)
|
|
{
|
|
pVc->ReceiveActivity = 0;
|
|
|
|
// timer reference still on VC no need to re-reference
|
|
|
|
AtmLaneStartTimer(
|
|
pElan,
|
|
&pVc->AgingTimer,
|
|
AtmLaneVcAgingTimeout,
|
|
pVc->AgingTime,
|
|
(PVOID)pVc
|
|
);
|
|
|
|
DBGP((1, "%d Vc %x aging timer refreshed due to receive activity\n",
|
|
pVc->pElan->ElanNumber,
|
|
pVc));
|
|
|
|
|
|
RELEASE_VC_LOCK(pVc);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// VC is to be closed
|
|
//
|
|
DBGP((1, "%d Vc %x aged out\n",
|
|
pVc->pElan->ElanNumber,
|
|
pVc));
|
|
|
|
|
|
DBGP((3, "VcAgingTimeout: Vc %x RefCount %d Flags %x pAtmEntry %x\n",
|
|
pVc, pVc->RefCount, pVc->Flags, pVc->pAtmEntry));
|
|
|
|
//
|
|
// Remove timer reference and return if refcount goes to zero
|
|
//
|
|
rc = AtmLaneDereferenceVc(pVc, "aging timer");
|
|
if (rc == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Take this VC out of the VC list for this ATM destination
|
|
// and return if refcount goes to zero
|
|
//
|
|
if (pVc->pAtmEntry != NULL_PATMLANE_ATM_ENTRY)
|
|
{
|
|
if (AtmLaneUnlinkVcFromAtmEntry(pVc))
|
|
{
|
|
rc = AtmLaneDereferenceVc(pVc, "atm");
|
|
if (rc == 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Close this VC
|
|
//
|
|
AtmLaneCloseCall(pVc);
|
|
//
|
|
// The VC lock is released in CloseCall
|
|
//
|
|
} while (FALSE);
|
|
|
|
|
|
TRACEOUT(VcAgingTimeout);
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
AtmLaneShutdownElan(
|
|
IN PATMLANE_ELAN pElan LOCKIN NOLOCKOUT,
|
|
IN BOOLEAN Restart
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will "shutdown" an ELAN prior to it going back
|
|
to the Initial state or driver shutdown. The caller is
|
|
expected to hold the ELAN lock and it will be released here.
|
|
|
|
Arguments:
|
|
|
|
pElan - Pointer to an ATMLANE Elan structure.
|
|
Restart - If TRUE ELAN should restart at Initial state.
|
|
If FALSE ELAN should not restart.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
PATMLANE_ATM_ENTRY pAtmEntry;
|
|
PATMLANE_ATM_ENTRY pNextAtmEntry;
|
|
PATMLANE_MAC_ENTRY pMacEntry;
|
|
ULONG rc;
|
|
ULONG i;
|
|
BOOLEAN WasCancelled;
|
|
NDIS_STATUS Status;
|
|
NDIS_HANDLE NdisAfHandle;
|
|
BOOLEAN bTempRef;
|
|
|
|
TRACEIN(ShutdownElan);
|
|
STRUCT_ASSERT(pElan, atmlane_elan);
|
|
|
|
//
|
|
// Add a temp ref
|
|
//
|
|
bTempRef = TRUE;
|
|
AtmLaneReferenceElan(pElan, "tempshutdown");
|
|
|
|
do
|
|
{
|
|
DBGP((0, "%d ShutDownElan pElan %p/%x, Ref %d State %d, Restart %d\n",
|
|
pElan->ElanNumber, pElan, pElan->Flags, pElan->RefCount, pElan->State,
|
|
Restart));
|
|
|
|
//
|
|
// If state already == SHUTDOWN nothing to do
|
|
//
|
|
if (ELAN_STATE_SHUTDOWN == pElan->State)
|
|
{
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If we are transitioning from operational to shutdown,
|
|
// and our miniport is active, then indicate a media disconnect
|
|
// event.
|
|
//
|
|
if (pElan->State == ELAN_STATE_OPERATIONAL &&
|
|
pElan->MiniportAdapterHandle != NULL)
|
|
{
|
|
NdisMIndicateStatus(
|
|
pElan->MiniportAdapterHandle,
|
|
NDIS_STATUS_MEDIA_DISCONNECT,
|
|
NULL,
|
|
0);
|
|
|
|
NdisMIndicateStatusComplete(pElan->MiniportAdapterHandle);
|
|
}
|
|
|
|
//
|
|
// Change state to shutdown. If restarting state will
|
|
// be changed back to init after cleanup below.
|
|
//
|
|
pElan->AdminState = ELAN_STATE_SHUTDOWN;
|
|
pElan->State = ELAN_STATE_SHUTDOWN;
|
|
|
|
//
|
|
// Wait for any pending OpenAF operation to finish.
|
|
//
|
|
while (pElan->Flags & ELAN_OPENING_AF)
|
|
{
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
|
|
DBGP((0, "%d: Shutdown Elan %p/%x is opening AF\n",
|
|
pElan->ElanNumber, pElan, pElan->Flags));
|
|
|
|
(VOID)WAIT_ON_BLOCK_STRUCT(&pElan->AfBlock);
|
|
|
|
ACQUIRE_ELAN_LOCK(pElan);
|
|
}
|
|
|
|
NdisAfHandle = pElan->NdisAfHandle;
|
|
pElan->NdisAfHandle = NULL;
|
|
|
|
if (Restart)
|
|
{
|
|
pElan->Flags |= ELAN_NEEDS_RESTART;
|
|
}
|
|
|
|
//
|
|
// Are we waiting for MiniportInitialize to run and finish?
|
|
// If so, try to cancel IMInit.
|
|
//
|
|
if (pElan->Flags & ELAN_MINIPORT_INIT_PENDING)
|
|
{
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
|
|
Status = NdisIMCancelInitializeDeviceInstance(
|
|
pAtmLaneGlobalInfo->MiniportDriverHandle,
|
|
&pElan->CfgDeviceName);
|
|
|
|
DBGP((0, "%d ShutdownElan Elan %p/%x, Ref %d, CancelInit returned %x\n",
|
|
pElan->ElanNumber, pElan, pElan->Flags, pElan->RefCount, Status));
|
|
|
|
if (Status == NDIS_STATUS_SUCCESS)
|
|
{
|
|
//
|
|
// Canceled the IMInit process.
|
|
//
|
|
ACQUIRE_ELAN_LOCK(pElan);
|
|
pElan->Flags &= ~ELAN_MINIPORT_INIT_PENDING;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Our MiniportInit function -will- be called.
|
|
// Wait for it to finish.
|
|
//
|
|
(VOID)WAIT_ON_BLOCK_STRUCT(&pElan->InitBlock);
|
|
DBGP((2, "%d: Shutdown ELAN %p, Flags %x, woke up from InitBlock\n",
|
|
pElan->ElanNumber, pElan, pElan->Flags));
|
|
ACQUIRE_ELAN_LOCK(pElan);
|
|
ASSERT((pElan->Flags & ELAN_MINIPORT_INIT_PENDING) == 0);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Stop any timers running on the elan.
|
|
//
|
|
|
|
if (AtmLaneStopTimer(&pElan->Timer, pElan))
|
|
{
|
|
rc = AtmLaneDereferenceElan(pElan, "timer"); // Timer ref
|
|
ASSERT(rc > 0);
|
|
}
|
|
|
|
if (NULL != pElan->pDelayedEvent)
|
|
{
|
|
BOOLEAN TimerCancelled;
|
|
|
|
NdisCancelTimer(
|
|
&pElan->pDelayedEvent->DelayTimer,
|
|
&TimerCancelled);
|
|
|
|
DBGP((0, "ATMLANE: %d ShutdownElan %p, DelayedEvent %p, Cancelled %d\n",
|
|
pElan->ElanNumber,
|
|
pElan,
|
|
pElan->pDelayedEvent,
|
|
TimerCancelled));
|
|
|
|
if (TimerCancelled)
|
|
{
|
|
FREE_MEM(pElan->pDelayedEvent);
|
|
pElan->pDelayedEvent = NULL;
|
|
|
|
rc = AtmLaneDereferenceElan(pElan, "delayeventcancel");
|
|
ASSERT(rc > 0);
|
|
}
|
|
}
|
|
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
|
|
//
|
|
// Deregister all SAPs.
|
|
//
|
|
AtmLaneDeregisterSaps(pElan);
|
|
|
|
//
|
|
// Abort all MAC table entries.
|
|
//
|
|
for (i = 0; i < ATMLANE_MAC_TABLE_SIZE; i++)
|
|
{
|
|
ACQUIRE_ELAN_MAC_TABLE_LOCK(pElan);
|
|
while (pElan->pMacTable[i] != NULL_PATMLANE_MAC_ENTRY)
|
|
{
|
|
pMacEntry = pElan->pMacTable[i];
|
|
|
|
//
|
|
// Place a temp ref so that this won't go away
|
|
// when we release the MAC table lock.
|
|
//
|
|
ACQUIRE_MAC_ENTRY_LOCK_DPC(pMacEntry);
|
|
AtmLaneReferenceMacEntry(pMacEntry, "ShutDownTemp");
|
|
RELEASE_MAC_ENTRY_LOCK_DPC(pMacEntry);
|
|
|
|
RELEASE_ELAN_MAC_TABLE_LOCK(pElan);
|
|
|
|
ACQUIRE_MAC_ENTRY_LOCK(pMacEntry);
|
|
|
|
//
|
|
// Remove the temp ref
|
|
//
|
|
rc = AtmLaneDereferenceMacEntry(pMacEntry, "ShutDownTemp");
|
|
if (rc != 0)
|
|
{
|
|
AtmLaneAbortMacEntry(pMacEntry);
|
|
//
|
|
// MAC Entry Lock is released within the above.
|
|
//
|
|
}
|
|
//
|
|
// else the MAC entry is gone.
|
|
//
|
|
|
|
ACQUIRE_ELAN_MAC_TABLE_LOCK(pElan);
|
|
}
|
|
RELEASE_ELAN_MAC_TABLE_LOCK(pElan);
|
|
}
|
|
|
|
//
|
|
// Abort all ATM Entries.
|
|
//
|
|
|
|
// First, run through the list and reference
|
|
// all of them first, so that we don't
|
|
// skip to an invalid pointer when aborting the entries.
|
|
//
|
|
ACQUIRE_ELAN_ATM_LIST_LOCK(pElan);
|
|
|
|
for (pAtmEntry = pElan->pAtmEntryList;
|
|
pAtmEntry != NULL_PATMLANE_ATM_ENTRY;
|
|
pAtmEntry = pNextAtmEntry)
|
|
{
|
|
ACQUIRE_ATM_ENTRY_LOCK_DPC(pAtmEntry);
|
|
|
|
pAtmEntry->Flags |= ATM_ENTRY_WILL_ABORT;
|
|
AtmLaneReferenceAtmEntry(pAtmEntry, "tempS");
|
|
pNextAtmEntry = pAtmEntry->pNext;
|
|
|
|
RELEASE_ATM_ENTRY_LOCK_DPC(pAtmEntry);
|
|
}
|
|
|
|
//
|
|
// Now, do the actual abort.
|
|
//
|
|
pAtmEntry = pElan->pAtmEntryList;
|
|
while (pAtmEntry != NULL_PATMLANE_ATM_ENTRY)
|
|
{
|
|
RELEASE_ELAN_ATM_LIST_LOCK(pElan);
|
|
|
|
ACQUIRE_ATM_ENTRY_LOCK(pAtmEntry);
|
|
pNextAtmEntry = pAtmEntry->pNext;
|
|
AtmLaneInvalidateAtmEntry(pAtmEntry);
|
|
//
|
|
// The ATM Entry lock is released within the above.
|
|
//
|
|
|
|
ACQUIRE_ELAN_ATM_LIST_LOCK(pElan);
|
|
pAtmEntry = pNextAtmEntry;
|
|
}
|
|
|
|
//
|
|
// Remove all temp references.
|
|
//
|
|
for (pAtmEntry = pElan->pAtmEntryList;
|
|
pAtmEntry != NULL_PATMLANE_ATM_ENTRY;
|
|
pAtmEntry = pNextAtmEntry)
|
|
{
|
|
RELEASE_ELAN_ATM_LIST_LOCK(pElan);
|
|
|
|
ACQUIRE_ATM_ENTRY_LOCK(pAtmEntry);
|
|
|
|
ASSERT(pAtmEntry->Flags & ATM_ENTRY_WILL_ABORT);
|
|
pAtmEntry->Flags &= ~ATM_ENTRY_WILL_ABORT;
|
|
|
|
pNextAtmEntry = pAtmEntry->pNext;
|
|
|
|
rc = AtmLaneDereferenceAtmEntry(pAtmEntry,"tempS");
|
|
if (rc != 0)
|
|
{
|
|
RELEASE_ATM_ENTRY_LOCK(pAtmEntry);
|
|
}
|
|
|
|
ACQUIRE_ELAN_ATM_LIST_LOCK(pElan);
|
|
}
|
|
|
|
RELEASE_ELAN_ATM_LIST_LOCK(pElan);
|
|
|
|
//
|
|
// Remove the temp ref
|
|
//
|
|
STRUCT_ASSERT(pElan, atmlane_elan);
|
|
bTempRef = FALSE;
|
|
ACQUIRE_ELAN_LOCK(pElan);
|
|
rc = AtmLaneDereferenceElan(pElan, "tempshutdown");
|
|
if (rc == 0)
|
|
{
|
|
break;
|
|
}
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
|
|
if (NULL != NdisAfHandle)
|
|
{
|
|
Status = NdisClCloseAddressFamily(NdisAfHandle);
|
|
if (NDIS_STATUS_PENDING != Status)
|
|
{
|
|
AtmLaneCloseAfCompleteHandler(Status, (NDIS_HANDLE)pElan);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AtmLaneContinueShutdownElan(pElan);
|
|
}
|
|
|
|
} while (FALSE);
|
|
|
|
|
|
if (bTempRef)
|
|
{
|
|
ACQUIRE_ELAN_LOCK(pElan);
|
|
rc = AtmLaneDereferenceElan(pElan, "tempshutdown");
|
|
if (rc != 0)
|
|
{
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
}
|
|
}
|
|
|
|
TRACEOUT(ShutdownElan);
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
AtmLaneContinueShutdownElan(
|
|
IN PATMLANE_ELAN pElan
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine continues the shutting down process for an ELAN,
|
|
after the Af handle with the Call Manager has been closed.
|
|
|
|
Arguments:
|
|
|
|
pElan - the ELAN being shutdown.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
--*/
|
|
{
|
|
ULONG rc;
|
|
|
|
TRACEIN(ContinueShutdownElan);
|
|
|
|
DBGP((0, "%d ContinueShutdown ELAN %x Flags 0x%x State %d\n",
|
|
pElan->ElanNumber,
|
|
pElan,
|
|
pElan->Flags,
|
|
pElan->State));
|
|
|
|
do
|
|
{
|
|
if (pElan->Flags & ELAN_NEEDS_RESTART)
|
|
{
|
|
ACQUIRE_ELAN_LOCK(pElan);
|
|
//
|
|
// Change state to INIT
|
|
//
|
|
pElan->AdminState = ELAN_STATE_OPERATIONAL;
|
|
pElan->State = ELAN_STATE_INIT;
|
|
pElan->RetriesLeft = 4;
|
|
|
|
pElan->Flags &= ~ELAN_NEEDS_RESTART;
|
|
|
|
//
|
|
// Clear out the local ATM address so that we start off the
|
|
// ELAN properly when we obtain it from the Call manager.
|
|
//
|
|
NdisZeroMemory(&pElan->AtmAddress, sizeof(ATM_ADDRESS));
|
|
|
|
//
|
|
// Empty the event queue and schedule a restart in a little while
|
|
//
|
|
AtmLaneDrainElanEventQueue(pElan);
|
|
AtmLaneQueueElanEventAfterDelay(pElan, ELAN_EVENT_START, 0, 2*1000);
|
|
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Completely remove the ELAN
|
|
//
|
|
AtmLaneUnlinkElanFromAdapter(pElan);
|
|
|
|
ACQUIRE_ELAN_LOCK(pElan);
|
|
|
|
//
|
|
// Add workitem reference and remove adapter reference
|
|
//
|
|
AtmLaneReferenceElan(pElan, "workitem");
|
|
rc = AtmLaneDereferenceElan(pElan, "adapter");
|
|
|
|
//
|
|
// We are here for one of the following reasons:
|
|
//
|
|
// 1. Unbinding from an ATM adapter
|
|
// 2. The CM asked us to shut down the AF open on an ATM adapter
|
|
// 3. The virtual miniport was halted.
|
|
//
|
|
// If it isn't case (3), then we should make sure that the miniport
|
|
// gets halted, by calling NdisIMDeInitializeDeviceInstance.
|
|
//
|
|
|
|
if (pElan->MiniportAdapterHandle != NULL)
|
|
{
|
|
|
|
DBGP((1, "%d ContinueShutdown: pElan x%x, scheduling NdisIMDeInit, Handle %x\n",
|
|
pElan->ElanNumber, pElan, pElan->MiniportAdapterHandle));
|
|
|
|
//
|
|
// Schedule a PASSIVE_LEVEL thread to call
|
|
// NdisIMInitializeDeviceInstance
|
|
//
|
|
NdisInitializeWorkItem(
|
|
&pElan->NdisWorkItem,
|
|
AtmLaneDeinitializeMiniportDevice,
|
|
pElan);
|
|
|
|
// workitem reference already on Elan from above
|
|
|
|
NdisScheduleWorkItem(&pElan->NdisWorkItem);
|
|
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Just remove workitem reference and unlock if Elan still around
|
|
//
|
|
rc = AtmLaneDereferenceElan(pElan, "workitem");
|
|
if (rc > 0)
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
while (FALSE);
|
|
|
|
TRACEOUT(ContinueShutdownElan);
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
AtmLaneGetProtocolConfiguration(
|
|
IN NDIS_HANDLE AdapterConfigHandle,
|
|
IN PATMLANE_ADAPTER pAdapter
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will attempt to get any ATMLANE protocol specific
|
|
configuration information optionally stored under an adapter's
|
|
registry parameters.
|
|
|
|
Arguments:
|
|
|
|
AdapterConfigHandle - the handle that was returned by
|
|
AtmLaneOpenAdapterConfiguration.
|
|
pAdapter - Pointer to ATMLANE adapter structure.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
NDIS_STATUS Status;
|
|
PNDIS_CONFIGURATION_PARAMETER ParameterValue;
|
|
ATM_ADDRESS LecsAddress;
|
|
NDIS_STRING ValueName;
|
|
PATMLANE_NAME * ppNext;
|
|
PATMLANE_NAME pName;
|
|
PWSTR pTempStr;
|
|
USHORT StrLength;
|
|
|
|
TRACEIN(GetProtocolConfiguration);
|
|
|
|
//
|
|
// Get the UpperBindings parameter (it will only exist on Memphis)
|
|
//
|
|
NdisInitUnicodeString(&ValueName, ATMLANE_UPPERBINDINGS_STRING);
|
|
NdisReadConfiguration(
|
|
&Status,
|
|
&ParameterValue,
|
|
AdapterConfigHandle,
|
|
&ValueName,
|
|
NdisParameterString);
|
|
if (NDIS_STATUS_SUCCESS == Status)
|
|
{
|
|
// Copy the string into adapter struct
|
|
|
|
(VOID)AtmLaneCopyUnicodeString(
|
|
&(pAdapter->CfgUpperBindings),
|
|
&(ParameterValue->ParameterData.StringData),
|
|
TRUE,
|
|
FALSE);
|
|
DBGP((1, "GetProtocolConfiguration: UpperBindings %s\n",
|
|
UnicodeToString(&pAdapter->CfgUpperBindings)));
|
|
|
|
//
|
|
// Existence of this parameter is a definite clue we're running
|
|
// on Memphis/Win98
|
|
//
|
|
pAdapter->RunningOnMemphis = TRUE;
|
|
}
|
|
|
|
//
|
|
// Get the ElanName parameter (it will only exist on Memphis)
|
|
//
|
|
NdisInitUnicodeString(&ValueName, ATMLANE_ELANNAME_STRING);
|
|
NdisReadConfiguration(
|
|
&Status,
|
|
&ParameterValue,
|
|
AdapterConfigHandle,
|
|
&ValueName,
|
|
NdisParameterString);
|
|
if (NDIS_STATUS_SUCCESS == Status)
|
|
{
|
|
|
|
// Copy the string into adapter struct
|
|
|
|
(VOID)AtmLaneCopyUnicodeString(
|
|
&(pAdapter->CfgElanName),
|
|
&(ParameterValue->ParameterData.StringData),
|
|
TRUE,
|
|
FALSE);
|
|
DBGP((1, "GetProtocolConfiguration: ElanName %s\n",
|
|
UnicodeToString(&pAdapter->CfgElanName)));
|
|
|
|
//
|
|
// Existence of this parameter is definite clue we're running
|
|
// on Memphis/Win98
|
|
//
|
|
pAdapter->RunningOnMemphis = TRUE;
|
|
}
|
|
|
|
//
|
|
// If on Win98 we have to parse the upper bindings and elan name strings
|
|
//
|
|
|
|
if (pAdapter->RunningOnMemphis)
|
|
{
|
|
// cut up the upper bindings string
|
|
|
|
ppNext = &(pAdapter->UpperBindingsList);
|
|
pTempStr = AtmLaneStrTok(pAdapter->CfgUpperBindings.Buffer, L',', &StrLength);
|
|
|
|
do
|
|
{
|
|
*ppNext = NULL;
|
|
|
|
if (pTempStr == NULL)
|
|
{
|
|
break;
|
|
}
|
|
|
|
ALLOC_MEM(&pName, sizeof(ATMLANE_NAME));
|
|
|
|
if (pName == NULL)
|
|
{
|
|
break;
|
|
}
|
|
|
|
pName->Name.Buffer = pTempStr;
|
|
pName->Name.MaximumLength = StrLength+1;
|
|
pName->Name.Length = StrLength;
|
|
|
|
*ppNext = pName;
|
|
ppNext = &(pName->pNext);
|
|
|
|
pTempStr = AtmLaneStrTok(NULL, L',', &StrLength);
|
|
|
|
} while (TRUE);
|
|
|
|
|
|
// cut up the elan name string
|
|
|
|
ppNext = &(pAdapter->ElanNameList);
|
|
pTempStr = AtmLaneStrTok(pAdapter->CfgElanName.Buffer, L',', &StrLength);
|
|
|
|
do
|
|
{
|
|
*ppNext = NULL;
|
|
|
|
if (pTempStr == NULL)
|
|
{
|
|
break;
|
|
}
|
|
|
|
ALLOC_MEM(&pName, sizeof(ATMLANE_NAME));
|
|
|
|
if (pName == NULL)
|
|
{
|
|
break;
|
|
}
|
|
|
|
pName->Name.Buffer = pTempStr;
|
|
pName->Name.MaximumLength = StrLength+1;
|
|
pName->Name.Length = StrLength;
|
|
|
|
*ppNext = pName;
|
|
ppNext = &(pName->pNext);
|
|
|
|
pTempStr = AtmLaneStrTok(NULL, L',', &StrLength);
|
|
|
|
} while (TRUE);
|
|
|
|
}
|
|
|
|
TRACEOUT(GetProtocolConfiguration);
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
AtmLaneGetElanConfiguration(
|
|
IN PNDIS_STRING pElanKey,
|
|
IN PATMLANE_ELAN pElan
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will first initialize the configuration parameters
|
|
for the specified ELAN. Then it will attempt to get any ELAN
|
|
configuration information optionally stored under the ELAN's
|
|
registry key.
|
|
|
|
Arguments:
|
|
|
|
pElanKey - UNICODE string containing ELAN's
|
|
registry key
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
NDIS_STATUS Status;
|
|
PNDIS_CONFIGURATION_PARAMETER ParameterValue;
|
|
NDIS_STRING ValueName;
|
|
ANSI_STRING TempAnsiString;
|
|
ATM_ADDRESS LecsAddress;
|
|
NDIS_HANDLE AdapterConfigHandle;
|
|
NDIS_HANDLE ElanListConfigHandle;
|
|
NDIS_HANDLE ElanConfigHandle;
|
|
NDIS_HANDLE CommonConfigHandle;
|
|
NDIS_STRING ElanListKeyName;
|
|
PATMLANE_NAME pName;
|
|
ULONG Index;
|
|
|
|
TRACEIN(GetElanConfiguration);
|
|
|
|
//
|
|
// Init handles to null for proper cleanup later
|
|
//
|
|
AdapterConfigHandle = NULL_NDIS_HANDLE;
|
|
ElanListConfigHandle = NULL_NDIS_HANDLE;
|
|
ElanConfigHandle = NULL_NDIS_HANDLE;
|
|
|
|
//
|
|
// Init some defaults
|
|
//
|
|
pElan->CfgUseLecs = TRUE;
|
|
pElan->CfgDiscoverLecs = TRUE;
|
|
pElan->CfgLecsAddress = gWellKnownLecsAddress;
|
|
|
|
do
|
|
{
|
|
//
|
|
// Open the AtmLane protocol configuration section for this adapter.
|
|
// This must succeed on NT and Win98.
|
|
//
|
|
NdisOpenProtocolConfiguration(
|
|
&Status,
|
|
&AdapterConfigHandle,
|
|
&pElan->pAdapter->ConfigString
|
|
);
|
|
|
|
if (NDIS_STATUS_SUCCESS != Status)
|
|
{
|
|
AdapterConfigHandle = NULL_NDIS_HANDLE;
|
|
DBGP((0, "GetElanConfiguration: OpenProtocolConfiguration failed\n"));
|
|
Status = NDIS_STATUS_OPEN_FAILED;
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// If running on Win98 we will get ELAN config info from the
|
|
// adapter's parameters. For NT we will get ELAN config info
|
|
// from the ELAN's own parameters.
|
|
//
|
|
if (pElan->pAdapter->RunningOnMemphis)
|
|
{
|
|
//
|
|
// Use the adapter's config handle
|
|
//
|
|
CommonConfigHandle = AdapterConfigHandle;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Open the Elan List configuration key.
|
|
//
|
|
NdisInitUnicodeString(&ElanListKeyName, ATMLANE_ELANLIST_STRING);
|
|
|
|
NdisOpenConfigurationKeyByName(
|
|
&Status,
|
|
AdapterConfigHandle,
|
|
&ElanListKeyName,
|
|
&ElanListConfigHandle);
|
|
|
|
if (NDIS_STATUS_SUCCESS != Status)
|
|
{
|
|
ElanListConfigHandle = NULL_NDIS_HANDLE;
|
|
DBGP((0, "GetElanConfiguration: Failed open of ElanList key\n"));
|
|
Status = NDIS_STATUS_FAILURE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Open ELAN key
|
|
//
|
|
NdisOpenConfigurationKeyByName(
|
|
&Status,
|
|
ElanListConfigHandle,
|
|
pElanKey,
|
|
&ElanConfigHandle);
|
|
|
|
if (NDIS_STATUS_SUCCESS != Status)
|
|
{
|
|
ElanConfigHandle = NULL_NDIS_HANDLE;
|
|
DBGP((0, "GetElanConfiguration: Failed open of ELAN key\n"));
|
|
Status = NDIS_STATUS_FAILURE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Use the ELAN's config handle
|
|
//
|
|
CommonConfigHandle = ElanConfigHandle;
|
|
|
|
}
|
|
|
|
//
|
|
// Get the UseLECS parameter
|
|
//
|
|
NdisInitUnicodeString(&ValueName, ATMLANE_USELECS_STRING);
|
|
NdisReadConfiguration(
|
|
&Status,
|
|
&ParameterValue,
|
|
CommonConfigHandle,
|
|
&ValueName,
|
|
NdisParameterInteger);
|
|
if (NDIS_STATUS_SUCCESS == Status)
|
|
{
|
|
pElan->CfgUseLecs =
|
|
(ParameterValue->ParameterData.IntegerData == 0) ? FALSE : TRUE;
|
|
DBGP((1, "%d UseLECS = %s\n",
|
|
pElan->ElanNumber,
|
|
pElan->CfgUseLecs?"TRUE":"FALSE"));
|
|
}
|
|
|
|
//
|
|
// Get the DiscoverLECS parameter
|
|
//
|
|
NdisInitUnicodeString(&ValueName, ATMLANE_DISCOVERLECS_STRING);
|
|
NdisReadConfiguration(
|
|
&Status,
|
|
&ParameterValue,
|
|
CommonConfigHandle,
|
|
&ValueName,
|
|
NdisParameterInteger);
|
|
if (NDIS_STATUS_SUCCESS == Status)
|
|
{
|
|
pElan->CfgDiscoverLecs =
|
|
(ParameterValue->ParameterData.IntegerData == 0) ? FALSE : TRUE;
|
|
DBGP((1, "%d DiscoverLECS = %s\n",
|
|
pElan->ElanNumber,
|
|
pElan->CfgDiscoverLecs?"TRUE":"FALSE"));
|
|
}
|
|
|
|
//
|
|
// Get the LECS Address (only if Discover LECS is FALSE)
|
|
//
|
|
if (FALSE == pElan->CfgDiscoverLecs)
|
|
{
|
|
NdisInitUnicodeString(&ValueName, ATMLANE_LECSADDR_STRING);
|
|
NdisReadConfiguration(
|
|
&Status,
|
|
&ParameterValue,
|
|
CommonConfigHandle,
|
|
&ValueName,
|
|
NdisParameterString);
|
|
if (NDIS_STATUS_SUCCESS == Status)
|
|
{
|
|
NdisConvertStringToAtmAddress(
|
|
&Status,
|
|
&ParameterValue->ParameterData.StringData,
|
|
&LecsAddress);
|
|
if (NDIS_STATUS_SUCCESS == Status)
|
|
{
|
|
pElan->CfgLecsAddress = LecsAddress;
|
|
DBGP((1, "%d LECSAddr = %s\n",
|
|
pElan->ElanNumber,
|
|
AtmAddrToString(pElan->CfgLecsAddress.Address)));
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get the DeviceName parameter (different on NT5 and Memphis/Win98)
|
|
//
|
|
if (!pElan->pAdapter->RunningOnMemphis)
|
|
{
|
|
//
|
|
// NT5
|
|
//
|
|
NdisInitUnicodeString(&ValueName, ATMLANE_DEVICE_STRING);
|
|
NdisReadConfiguration(
|
|
&Status,
|
|
&ParameterValue,
|
|
ElanConfigHandle,
|
|
&ValueName,
|
|
NdisParameterString);
|
|
if (NDIS_STATUS_SUCCESS == Status)
|
|
{
|
|
//
|
|
// Copy into Elan struct.
|
|
//
|
|
(VOID)AtmLaneCopyUnicodeString(
|
|
&(pElan->CfgDeviceName),
|
|
&(ParameterValue->ParameterData.StringData),
|
|
TRUE,
|
|
FALSE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Memphis/Win98
|
|
//
|
|
//
|
|
// Index to this elan's device name string
|
|
//
|
|
pName = pElan->pAdapter->UpperBindingsList;
|
|
Index = pElan->ElanNumber;
|
|
while (Index > 0)
|
|
{
|
|
ASSERT(pName != NULL);
|
|
pName = pName->pNext;
|
|
Index--;
|
|
}
|
|
|
|
//
|
|
// Copy it to the Elan CfgDeviceName string
|
|
//
|
|
(VOID)AtmLaneCopyUnicodeString(
|
|
&(pElan->CfgDeviceName),
|
|
&(pName->Name),
|
|
TRUE,
|
|
FALSE);
|
|
}
|
|
DBGP((1, "%d Device Name = %s\n",
|
|
pElan->ElanNumber,
|
|
UnicodeToString(&pElan->CfgDeviceName)));
|
|
|
|
|
|
//
|
|
// Get the ELANName Parameter (different on NT5 and Memphis/Win98
|
|
//
|
|
if (!pElan->pAdapter->RunningOnMemphis)
|
|
{
|
|
//
|
|
// NT5
|
|
//
|
|
NdisInitUnicodeString(&ValueName, ATMLANE_ELANNAME_STRING);
|
|
NdisReadConfiguration(
|
|
&Status,
|
|
&ParameterValue,
|
|
ElanConfigHandle,
|
|
&ValueName,
|
|
NdisParameterString);
|
|
|
|
if (NDIS_STATUS_SUCCESS != Status)
|
|
{
|
|
NDIS_STRING DefaultNameString = NDIS_STRING_CONST("");
|
|
|
|
//
|
|
// Copy into the Elan data structure.
|
|
//
|
|
if (!AtmLaneCopyUnicodeString(
|
|
&(pElan->CfgElanName),
|
|
&DefaultNameString,
|
|
TRUE,
|
|
FALSE))
|
|
{
|
|
Status = NDIS_STATUS_RESOURCES;
|
|
break;
|
|
}
|
|
|
|
Status = NDIS_STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Copy into the Elan data structure.
|
|
//
|
|
if (!AtmLaneCopyUnicodeString(
|
|
&(pElan->CfgElanName),
|
|
&(ParameterValue->ParameterData.StringData),
|
|
TRUE,
|
|
FALSE))
|
|
{
|
|
Status = NDIS_STATUS_RESOURCES;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Convert it to ANSI and copy into run-time Elan variable
|
|
//
|
|
TempAnsiString.Length = 0;
|
|
TempAnsiString.MaximumLength = 32;
|
|
TempAnsiString.Buffer = pElan->ElanName;
|
|
|
|
NdisUnicodeStringToAnsiString(
|
|
&TempAnsiString,
|
|
&pElan->CfgElanName);
|
|
|
|
pElan->ElanNameSize = (UCHAR) TempAnsiString.Length;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Memphis/Win98
|
|
//
|
|
DBGP((2, "GetElanConfiguration: Getting Elan Name for Win98\n"));
|
|
|
|
//
|
|
// Index to this elan's name string
|
|
//
|
|
pName = pElan->pAdapter->ElanNameList;
|
|
Index = pElan->ElanNumber;
|
|
while (Index > 0 && pName != NULL)
|
|
{
|
|
pName = pName->pNext;
|
|
Index--;
|
|
}
|
|
|
|
//
|
|
// Copy it to the Elan CfgElanName string
|
|
//
|
|
if (pName != NULL)
|
|
{
|
|
DBGP((2, "GetElanConfiguration: Using Elan Name at 0x%x\n", pName->Name.Buffer));
|
|
|
|
(VOID)AtmLaneCopyUnicodeString(
|
|
&(pElan->CfgElanName),
|
|
&(pName->Name),
|
|
TRUE,
|
|
FALSE);
|
|
|
|
//
|
|
// Convert it to ANSI and copy into run-time Elan variable
|
|
//
|
|
TempAnsiString.Length = 0;
|
|
TempAnsiString.MaximumLength = 32;
|
|
TempAnsiString.Buffer = pElan->ElanName;
|
|
|
|
NdisUnicodeStringToAnsiString(
|
|
&TempAnsiString,
|
|
&pName->Name);
|
|
|
|
pElan->ElanNameSize = (UCHAR) TempAnsiString.Length;
|
|
}
|
|
}
|
|
DBGP((1, "%d ELAN Name = %s\n",
|
|
pElan->ElanNumber,
|
|
UnicodeToString(&pElan->CfgElanName)));
|
|
|
|
|
|
//
|
|
// Get the LAN type.
|
|
//
|
|
pElan->CfgLanType = LANE_LANTYPE_UNSPEC;
|
|
NdisInitUnicodeString(&ValueName, ATMLANE_LANTYPE_STRING);
|
|
NdisReadConfiguration(
|
|
&Status,
|
|
&ParameterValue,
|
|
CommonConfigHandle,
|
|
&ValueName,
|
|
NdisParameterInteger);
|
|
if (NDIS_STATUS_SUCCESS == Status)
|
|
{
|
|
pElan->CfgLanType = (UCHAR) ParameterValue->ParameterData.IntegerData;
|
|
DBGP((1, "%d LAN Type = %u\n",
|
|
ParameterValue->ParameterData.IntegerData));
|
|
}
|
|
|
|
if (pElan->CfgLanType > LANE_LANTYPE_TR)
|
|
{
|
|
pElan->CfgLanType = LANE_LANTYPE_UNSPEC;
|
|
}
|
|
DBGP((1, "%d LAN Type = %u\n", pElan->ElanNumber, pElan->CfgLanType));
|
|
|
|
//
|
|
// Get the Max Frame Size.
|
|
//
|
|
pElan->CfgMaxFrameSizeCode = LANE_MAXFRAMESIZE_CODE_UNSPEC;
|
|
NdisInitUnicodeString(&ValueName, ATMLANE_MAXFRAMESIZE_STRING);
|
|
NdisReadConfiguration(
|
|
&Status,
|
|
&ParameterValue,
|
|
CommonConfigHandle,
|
|
&ValueName,
|
|
NdisParameterInteger);
|
|
if (NDIS_STATUS_SUCCESS == Status)
|
|
{
|
|
pElan->CfgMaxFrameSizeCode = (UCHAR) ParameterValue->ParameterData.IntegerData;
|
|
}
|
|
|
|
if (pElan->CfgMaxFrameSizeCode > LANE_MAXFRAMESIZE_CODE_18190)
|
|
{
|
|
pElan->CfgMaxFrameSizeCode = LANE_MAXFRAMESIZE_CODE_UNSPEC;
|
|
}
|
|
DBGP((1, "%d MaxFrameSize Code = %u\n",
|
|
pElan->ElanNumber,
|
|
pElan->CfgMaxFrameSizeCode));
|
|
|
|
//
|
|
// Get the LES Address
|
|
//
|
|
NdisZeroMemory(&pElan->CfgLesAddress, sizeof(ATM_ADDRESS));
|
|
NdisInitUnicodeString(&ValueName, ATMLANE_LESADDR_STRING);
|
|
NdisReadConfiguration(
|
|
&Status,
|
|
&ParameterValue,
|
|
CommonConfigHandle,
|
|
&ValueName,
|
|
NdisParameterString);
|
|
if (NDIS_STATUS_SUCCESS == Status)
|
|
{
|
|
NdisConvertStringToAtmAddress(
|
|
&Status,
|
|
&ParameterValue->ParameterData.StringData,
|
|
&pElan->CfgLesAddress);
|
|
DBGP((1, "%d LESAddr = %s\n",
|
|
pElan->ElanNumber,
|
|
AtmAddrToString(pElan->CfgLesAddress.Address)));
|
|
}
|
|
|
|
//
|
|
// Get the HeaderBufSize
|
|
//
|
|
pElan->HeaderBufSize = DEF_HEADER_BUF_SIZE;
|
|
|
|
//
|
|
// Round the "real" HeaderBufSize up to mult of 4.
|
|
//
|
|
pElan->RealHeaderBufSize = (((pElan->HeaderBufSize + 3) / 4) * 4);
|
|
|
|
|
|
//
|
|
// Get the MaxHeaderBufs
|
|
//
|
|
pElan->MaxHeaderBufs = DEF_MAX_HEADER_BUFS;
|
|
|
|
//
|
|
// Make max pad buffers same as header buffers
|
|
//
|
|
pElan->MaxPadBufs = pElan->MaxHeaderBufs;
|
|
//
|
|
// PadBufSize not configurable - but make it multiple of 4
|
|
//
|
|
pElan->PadBufSize = MAX(LANE_MIN_ETHPACKET, LANE_MIN_TRPACKET);
|
|
pElan->PadBufSize = (((pElan->PadBufSize + 3) / 4) * 4);
|
|
|
|
|
|
//
|
|
// ProtocolBufSize not configurable.
|
|
//
|
|
pElan->ProtocolBufSize = ROUND_OFF(DEF_PROTOCOL_BUF_SIZE);
|
|
|
|
//
|
|
// Get the MaxProtocolBufs
|
|
//
|
|
pElan->MaxProtocolBufs = DEF_MAX_PROTOCOL_BUFS;
|
|
|
|
} while (FALSE);
|
|
|
|
//
|
|
// Close config handles
|
|
//
|
|
if (NULL_NDIS_HANDLE != ElanConfigHandle)
|
|
{
|
|
NdisCloseConfiguration(ElanConfigHandle);
|
|
ElanConfigHandle = NULL_NDIS_HANDLE;
|
|
}
|
|
if (NULL_NDIS_HANDLE != ElanListConfigHandle)
|
|
{
|
|
NdisCloseConfiguration(ElanListConfigHandle);
|
|
ElanListConfigHandle = NULL_NDIS_HANDLE;
|
|
}
|
|
if (NULL_NDIS_HANDLE != AdapterConfigHandle)
|
|
{
|
|
NdisCloseConfiguration(AdapterConfigHandle);
|
|
AdapterConfigHandle = NULL_NDIS_HANDLE;
|
|
}
|
|
|
|
TRACEOUT(GetElanConfiguration);
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
AtmLaneQueueElanEventAfterDelay(
|
|
IN PATMLANE_ELAN pElan,
|
|
IN ULONG Event,
|
|
IN NDIS_STATUS EventStatus,
|
|
IN ULONG DelayMs
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Queue an ELAN event on the ELAN's event queue after
|
|
a specified delay. Caller is assumed to hold the ELAN
|
|
lock.
|
|
|
|
Arguments:
|
|
|
|
pElan - Pointer to ELAN structure.
|
|
Event - Event code.
|
|
EventStatus - Status related to event.
|
|
DelayMs - Time to wait before queueing this event.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PATMLANE_DELAYED_EVENT pDelayedEvent;
|
|
PATMLANE_EVENT pEvent;
|
|
|
|
TRACEIN(QueueElanEventAfterDelay);
|
|
|
|
do
|
|
{
|
|
//
|
|
// If the ELAN is being shut down, don't queue any events.
|
|
//
|
|
if (ELAN_STATE_SHUTDOWN == pElan->AdminState)
|
|
{
|
|
DBGP((0, "QueueElanEventAfterDelay: Not queuing event (ELAN shutdown)\n"));
|
|
break;
|
|
}
|
|
|
|
if (NULL != pElan->pDelayedEvent)
|
|
{
|
|
DBGP((0, "QueueElanEventAfterDelay: Not queueing event %d (ELAN %x/%x already has one)\n",
|
|
Event, pElan, pElan->Flags));
|
|
DBGP((0, "QueueElanEventAfterDelay: ELAN %x: existing event %d\n",
|
|
pElan, pElan->pDelayedEvent->DelayedEvent.Event));
|
|
|
|
//
|
|
// Make sure we don't drop an ELAN_EVENT_STOP on the floor!
|
|
//
|
|
if (Event == ELAN_EVENT_STOP)
|
|
{
|
|
pElan->pDelayedEvent->DelayedEvent.Event = ELAN_EVENT_STOP;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Alloc an event struct and a timer struct.
|
|
//
|
|
ALLOC_MEM(&pDelayedEvent, sizeof(ATMLANE_DELAYED_EVENT));
|
|
if ((PATMLANE_DELAYED_EVENT)NULL == pDelayedEvent)
|
|
{
|
|
DBGP((0, "QueueElanEventAfterDelay: Event object alloc failed\n"));
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Stash event data in event struct
|
|
//
|
|
pEvent = &pDelayedEvent->DelayedEvent;
|
|
pEvent->Event = Event;
|
|
pEvent->EventStatus = EventStatus;
|
|
|
|
//
|
|
// Remember the ELAN.
|
|
//
|
|
pDelayedEvent->pElan = pElan;
|
|
|
|
//
|
|
// Stash a pointer to this delayed event in the ELAN
|
|
//
|
|
pElan->pDelayedEvent = pDelayedEvent;
|
|
|
|
//
|
|
// Reference the ELAN so that it doesn't go away for the
|
|
// duration this delayed event is alive.
|
|
//
|
|
AtmLaneReferenceElan(pElan, "delayevent");
|
|
|
|
//
|
|
// Set up the timer to fire after the specified delay.
|
|
//
|
|
NdisInitializeTimer(&pDelayedEvent->DelayTimer,
|
|
AtmLaneQueueDelayedElanEvent,
|
|
(PVOID)pDelayedEvent);
|
|
|
|
NdisSetTimer(&pDelayedEvent->DelayTimer, DelayMs);
|
|
|
|
} while (FALSE);
|
|
|
|
TRACEOUT(QueueElanEventAfterDelay);
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
AtmLaneQueueDelayedElanEvent(
|
|
IN PVOID SystemSpecific1,
|
|
IN PVOID TimerContext,
|
|
IN PVOID SystemSpecific2,
|
|
IN PVOID SystemSpecific3
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the routine fired off after a delay in order to
|
|
queue an event on an ELAN. The event is queued now.
|
|
|
|
Arguments:
|
|
|
|
SystemSpecific[1-3] - Ignored
|
|
TimerContext - Actually a pointer to the delayed event structure
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PATMLANE_DELAYED_EVENT pDelayedEvent;
|
|
PATMLANE_EVENT pEvent;
|
|
PATMLANE_ELAN pElan;
|
|
ULONG rc;
|
|
|
|
TRACEIN(QueueDelayedElanEvent);
|
|
|
|
pDelayedEvent = (PATMLANE_DELAYED_EVENT)TimerContext;
|
|
|
|
do
|
|
{
|
|
pElan = pDelayedEvent->pElan;
|
|
|
|
ACQUIRE_ELAN_LOCK(pElan);
|
|
|
|
pElan->pDelayedEvent = NULL;
|
|
|
|
//
|
|
// Take out the delay event reference.
|
|
//
|
|
rc = AtmLaneDereferenceElan(pElan, "delayevent");
|
|
|
|
if (rc == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
pEvent = &pDelayedEvent->DelayedEvent;
|
|
AtmLaneQueueElanEvent(pElan, pEvent->Event, pEvent->EventStatus);
|
|
|
|
RELEASE_ELAN_LOCK(pElan);
|
|
}
|
|
while (FALSE);
|
|
|
|
FREE_MEM(pDelayedEvent);
|
|
|
|
TRACEOUT(QueueDelayedElanEvent);
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
AtmLaneQueueElanEvent(
|
|
IN PATMLANE_ELAN pElan,
|
|
IN ULONG Event,
|
|
IN NDIS_STATUS EventStatus
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Queue an ELAN event on the ELAN's event queue and if
|
|
not already scheduled, schedule the handler. Caller
|
|
is assumed to hold ELAN's lock.
|
|
|
|
Arguments:
|
|
|
|
pElan - Pointer to ELAN structure.
|
|
Event - Event code.
|
|
EventStatus - Status related to event.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PATMLANE_EVENT pEvent;
|
|
|
|
TRACEIN(AtmLaneQueueElanEvent);
|
|
|
|
do
|
|
{
|
|
//
|
|
// If the ELAN is being shut down, don't queue any events.
|
|
//
|
|
if (ELAN_STATE_SHUTDOWN == pElan->AdminState)
|
|
{
|
|
if ((Event != ELAN_EVENT_START) &&
|
|
(Event != ELAN_EVENT_RESTART))
|
|
{
|
|
DBGP((0, "%d: QueueElanEvent: Not queuing event %d (ELAN shutdown)\n", pElan->ElanNumber, Event));
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Alloc an event struct
|
|
//
|
|
ALLOC_MEM(&pEvent, sizeof(ATMLANE_EVENT));
|
|
if ((PATMLANE_EVENT)NULL == pEvent)
|
|
{
|
|
DBGP((0, "QueueElanEvent: Event object alloc failed\n"));
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Stash event data in event struct
|
|
//
|
|
pEvent->Event = Event;
|
|
pEvent->EventStatus = EventStatus;
|
|
|
|
//
|
|
// Queue it at tail, reference Elan, and if required, schedule
|
|
// work item to handle it.
|
|
//
|
|
InsertTailList(&pElan->EventQueue, &pEvent->Link);
|
|
AtmLaneReferenceElan(pElan, "event");
|
|
|
|
if ((pElan->Flags & ELAN_EVENT_WORK_ITEM_SET) == 0)
|
|
{
|
|
NDIS_STATUS Status;
|
|
|
|
AtmLaneReferenceElan(pElan, "workitemevent");
|
|
pElan->Flags |= ELAN_EVENT_WORK_ITEM_SET;
|
|
|
|
NdisInitializeWorkItem(&pElan->EventWorkItem, AtmLaneEventHandler, pElan);
|
|
Status = NdisScheduleWorkItem(&pElan->EventWorkItem);
|
|
ASSERT(Status == NDIS_STATUS_SUCCESS);
|
|
}
|
|
|
|
} while (FALSE);
|
|
|
|
TRACEOUT(QueueElanEvent);
|
|
return;
|
|
}
|
|
|
|
PATMLANE_EVENT
|
|
AtmLaneDequeueElanEvent(
|
|
IN PATMLANE_ELAN pElan
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Remove an ELAN event from the head of the ELAN's
|
|
event queue. Caller is assumed to hold ELAN's lock.
|
|
Caller is responsible for freeing event object.
|
|
Arguments:
|
|
|
|
pElan - Pointer to ELAN structure.
|
|
|
|
Return Value:
|
|
|
|
Pointer to removed ELAN event or NULL if queue empty.
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY p;
|
|
PATMLANE_EVENT pEvent;
|
|
|
|
TRACEIN(DequeueElanEvent);
|
|
|
|
if (!IsListEmpty(&pElan->EventQueue))
|
|
{
|
|
p = RemoveHeadList(&pElan->EventQueue);
|
|
pEvent = CONTAINING_RECORD(p, ATMLANE_EVENT, Link);
|
|
(VOID)AtmLaneDereferenceElan(pElan, "event");
|
|
}
|
|
else
|
|
{
|
|
pEvent = NULL;
|
|
}
|
|
|
|
TRACEIN(DequeueElanEvent);
|
|
|
|
return pEvent;
|
|
}
|
|
|
|
VOID
|
|
AtmLaneDrainElanEventQueue(
|
|
IN PATMLANE_ELAN pElan
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Remove all ELAN events from the ELAN's event queue.
|
|
Caller is assumed to hold ELAN's lock.
|
|
|
|
Arguments:
|
|
|
|
pElan - Pointer to ELAN structure.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN WasCancelled;
|
|
PATMLANE_EVENT pEvent;
|
|
|
|
TRACEIN(DrainElanEventQueue);
|
|
|
|
while ((pEvent = AtmLaneDequeueElanEvent(pElan)) != NULL)
|
|
{
|
|
DBGP((0, "%d Drained event %x, Status %x from Elan %x\n",
|
|
pElan->ElanNumber,
|
|
pEvent->Event,
|
|
pEvent->EventStatus,
|
|
pElan));
|
|
|
|
FREE_MEM(pEvent);
|
|
}
|
|
|
|
TRACEIN(DrainElanEventQueue);
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
|