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

5261 lines
151 KiB
C

/*++
Copyright (c) 1990-1995 Microsoft Corporation
Module Name:
ndis_co.c
Abstract:
CO-NDIS miniport wrapper functions
Author:
Jameel Hyder (JameelH) 01-Feb-96
Environment:
Kernel mode, FSD
Revision History:
--*/
#include <precomp.h>
#include <atm.h>
#pragma hdrstop
//
// Define the module number for debug code.
//
#define MODULE_NUMBER MODULE_NDIS_CO
/*
Connection-oriented section of NDIS exposes the following objects and apis to
manipulate these objects.
AF Address Family
SAP Service Access Point
VC Virtual Circuit
Party A node in a point-multipoint VC
There is a notion of a call-manager and a client on a per-binding basis. The
call manager acts as a helper dll for NDIS wrapper to manage the aforementioned
objects.
The concept of AF makes possible the existence of multiple call-managers. An
example of this is the UNI call-manager and a SPANS call-manager for the ATM
media.
SAPs provides a way for incoming calls to be routed to the right entity. A
protocol can register for more than one SAPs. Its upto the call-manager to
allow/dis-allow multiple protocol modules to register the same SAP.
VCs are created either by a protocol module requesting to make an outbound call
or by the call-manager dispatching an incoming call. VCs can either be point-point
or point-multi-point. Leaf nodes can be added to VCs at any time provided the first
leaf was created appropriately.
References:
An AF association results in the reference of file-object for the call-manager.
A SAP registration results in the reference of the AF.
A send or receive does not reference a VC. This is because miniports are required to
pend DeactivateVc calls till all I/O completes. So when it calls NdisMCoDeactivateVcComplete
no other packets will be indicated up and there are no sends outstanding.
*/
NDIS_STATUS
NdisCmRegisterAddressFamily(
IN NDIS_HANDLE NdisBindingHandle,
IN PCO_ADDRESS_FAMILY AddressFamily,
IN PNDIS_CALL_MANAGER_CHARACTERISTICS CmCharacteristics,
IN UINT SizeOfCmCharacteristics
)
/*++
Routine Description:
This is a call from the call-manager to register the address family
supported by this call-manager.
Arguments:
NdisBindingHandle - Pointer to the call-managers NDIS_OPEN_BLOCK.
AddressFamily - The address family being registered.
CmCharacteristics - Call-Manager characteristics
SizeOfCmCharacteristics - Size of Call-Manager characteristics
Return Value:
NDIS_STATUS_SUCCESS if the address family registration is successfully.
NDIS_STATUS_FAILURE if the caller is not a call-manager or this address
family is already registered for this miniport.
--*/
{
NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
KIRQL OldIrql;
PNDIS_AF_LIST AfList;
PNDIS_PROTOCOL_BLOCK Protocol;
PNDIS_OPEN_BLOCK CallMgrOpen;
PNDIS_MINIPORT_BLOCK Miniport;
PNDIS_AF_NOTIFY AfNotify = NULL;
CallMgrOpen = (PNDIS_OPEN_BLOCK)NdisBindingHandle;
Miniport = CallMgrOpen->MiniportHandle;
Protocol = CallMgrOpen->ProtocolHandle;
PnPReferencePackage();
//
// Make sure that the miniport is a CoNdis miniport and
// there is no other module registering the same address family.
//
NDIS_ACQUIRE_MINIPORT_SPIN_LOCK(Miniport, &OldIrql);
do
{
CallMgrOpen->Flags |= fMINIPORT_OPEN_CALL_MANAGER;
//
// Make sure the binding is not closing down
//
if (CallMgrOpen->Flags & fMINIPORT_OPEN_CLOSING)
{
Status = NDIS_STATUS_FAILURE;
break;
}
//
// Make sure that the miniport is a CoNdis miniport and
// protocol is also a NDIS 5.0 or later protocol.
//
if (!MINIPORT_TEST_FLAG(Miniport, fMINIPORT_IS_CO))
{
//
// Not a NDIS 5.0 or later miniport
//
Status = NDIS_STATUS_FAILURE;
break;
}
if (Protocol->ProtocolCharacteristics.MajorNdisVersion < 5)
{
//
// Not a NDIS 5.0 or later protocol
//
Status = NDIS_STATUS_FAILURE;
break;
}
//
// Make sure that the call-manager characteristics are 5.0 or later
//
if ((CmCharacteristics->MajorVersion < 5) ||
(SizeOfCmCharacteristics < sizeof(NDIS_CALL_MANAGER_CHARACTERISTICS)))
{
//
// Not a NDIS 5.0 or later protocol
//
Status = NDIS_STATUS_FAILURE;
break;
}
//
// Search registered call-managers for this miniport and make sure there is no
// clash. A call-manager can only register one address family per-open. This
// is due to the way we cache handlers. Can be over-come if the handlers are
// identical for each address-family - but decided not to since it is un-interesting.
//
for (AfList = Miniport->CallMgrAfList;
AfList != NULL;
AfList = AfList->NextAf)
{
if (NdisEqualMemory(&AfList->AddressFamily, AddressFamily, sizeof(CO_ADDRESS_FAMILY)))
{
Status = NDIS_STATUS_FAILURE;
break;
}
}
if (AfList == NULL)
{
//
// No other entity has claimed this address family.
// Allocate an AfList and a notify structure
//
AfList = (PNDIS_AF_LIST)ALLOC_FROM_POOL(sizeof(NDIS_AF_LIST), NDIS_TAG_CO);
if (AfList == NULL)
{
Status = NDIS_STATUS_RESOURCES;
break;
}
Status = ndisCreateNotifyQueue(Miniport, NULL, AddressFamily, &AfNotify);
if (Status != NDIS_STATUS_SUCCESS)
{
FREE_POOL(AfList);
break;
}
AfList->AddressFamily = *AddressFamily;
CopyMemory(&AfList->CmChars,
CmCharacteristics,
sizeof(NDIS_CALL_MANAGER_CHARACTERISTICS));
//
// link it in the miniport list
//
AfList->Open = CallMgrOpen;
AfList->NextAf = Miniport->CallMgrAfList;
Miniport->CallMgrAfList = AfList;
//
// Finally cache some handlers in the open-block
//
CallMgrOpen->CoCreateVcHandler = CmCharacteristics->CmCreateVcHandler;
CallMgrOpen->CoDeleteVcHandler = CmCharacteristics->CmDeleteVcHandler;
CallMgrOpen->CmActivateVcCompleteHandler = CmCharacteristics->CmActivateVcCompleteHandler;
CallMgrOpen->CmDeactivateVcCompleteHandler = CmCharacteristics->CmDeactivateVcCompleteHandler;
CallMgrOpen->CoRequestCompleteHandler = CmCharacteristics->CmRequestCompleteHandler;
if (AfNotify != NULL)
{
//
// Notify existing clients of this registration
//
QUEUE_WORK_ITEM(&AfNotify->WorkItem, DelayedWorkQueue);
}
}
} while (FALSE);
NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql);
PnPDereferencePackage();
return(Status);
}
NDIS_STATUS
NdisMCmRegisterAddressFamily(
IN NDIS_HANDLE MiniportAdapterHandle,
IN PCO_ADDRESS_FAMILY AddressFamily,
IN PNDIS_CALL_MANAGER_CHARACTERISTICS CmCharacteristics,
IN UINT SizeOfCmCharacteristics
)
/*++
Routine Description:
This is a call from the miniport supported call-manager to register the address family
supported by this call-manager.
Arguments:
MiniportAdapterHandle - Pointer to the miniports NDIS_MINIPORT_BLOCK.
AddressFamily - The address family being registered.
CmCharacteristics - Call-Manager characteristics
SizeOfCmCharacteristics - Size of Call-Manager characteristics
Return Value:
NDIS_STATUS_SUCCESS if the address family registration is successfully.
NDIS_STATUS_FAILURE if the caller is not a call-manager or this address
family is already registered for this miniport.
--*/
{
PNDIS_MINIPORT_BLOCK Miniport;
NDIS_STATUS Status;
PNDIS_AF_LIST AfList;
KIRQL OldIrql;
PnPReferencePackage();
Miniport = (PNDIS_MINIPORT_BLOCK)MiniportAdapterHandle;
//
// Make sure that the miniport is a CoNdis miniport and
// there is no other module registering the same address family.
//
NDIS_ACQUIRE_MINIPORT_SPIN_LOCK(Miniport, &OldIrql);
do
{
//
// Make sure that the miniport is a CoNdis miniport
//
if (!MINIPORT_TEST_FLAG(Miniport, fMINIPORT_IS_CO))
{
//
// Not a NDIS 5.0 or later miniport
//
Status = NDIS_STATUS_FAILURE;
break;
}
//
// Make sure that the call-manager characteristics are 5.0 or later
//
if ((CmCharacteristics->MajorVersion < 5) ||
(SizeOfCmCharacteristics < sizeof(NDIS_CALL_MANAGER_CHARACTERISTICS)))
{
//
// Not a NDIS 5.0 or later protocol
//
Status = NDIS_STATUS_FAILURE;
break;
}
//
// Search registered call-managers for this miniport and make sure there is no
// clash. A call-manager can only register one address family per-open. This
// is due to the way we cache handlers. Can be over-come if the handlers are
// identical for each address-family - but decided not to since it is un-interesting.
//
for (AfList = Miniport->CallMgrAfList;
AfList != NULL;
AfList = AfList->NextAf)
{
if (NdisEqualMemory(&AfList->AddressFamily, AddressFamily, sizeof(CO_ADDRESS_FAMILY)))
{
Status = NDIS_STATUS_FAILURE;
break;
}
}
if ((AfList == NULL) && MINIPORT_INCREMENT_REF(Miniport))
{
//
// No other entity has claimed this address family.
// Allocate an AfList and a notify structure
//
AfList = (PNDIS_AF_LIST)ALLOC_FROM_POOL(sizeof(NDIS_AF_LIST), NDIS_TAG_CO);
if (AfList == NULL)
{
Status = NDIS_STATUS_RESOURCES;
break;
}
AfList->AddressFamily = *AddressFamily;
CopyMemory(&AfList->CmChars,
CmCharacteristics,
sizeof(NDIS_CALL_MANAGER_CHARACTERISTICS));
//
// link it in the miniport list
//
AfList->Open = NULL;
AfList->NextAf = Miniport->CallMgrAfList;
Miniport->CallMgrAfList = AfList;
MINIPORT_DECREMENT_REF(Miniport);
Status = NDIS_STATUS_SUCCESS;
}
} while (FALSE);
NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql);
PnPDereferencePackage();
return Status;
}
NDIS_STATUS
ndisCreateNotifyQueue(
IN PNDIS_MINIPORT_BLOCK Miniport,
IN PNDIS_OPEN_BLOCK Open OPTIONAL,
IN PCO_ADDRESS_FAMILY AddressFamily OPTIONAL,
IN PNDIS_AF_NOTIFY * AfNotify
)
/*++
Routine Description:
Collect a set of address-family notifications that can then be passed on to ndisNotifyAfRegistration.
If the Open is NULL, this implies that an AddressFamily is being registered and must be notified to
all protocols on this miniport. If the AddressFamily is NULL (and the Open is specified) then all
registered AddressFamilies on this miniport must be indicated to the Open.
Arguments:
Miniport - Miniport of interest
Open - Optional - see comments above
AddressFamily - Optional - see comments above
AfNotify - Place holder for the list of notifications
Return Value:
NDIS_STATUS_SUCCESS - No errors, AfNotify can be NULL
NDIS_STATUS_RESOURCES - Failed to allocate memory
Note: Called at DPC with Miniport lock held.
--*/
{
NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
PNDIS_AF_NOTIFY AfN;
PNDIS_OPEN_BLOCK tOpen;
PNDIS_AF_LIST AfList;
ASSERT(ARGUMENT_PRESENT(Open) ^ ARGUMENT_PRESENT(AddressFamily));
*AfNotify = NULL;
if (ARGUMENT_PRESENT(Open))
{
ACQUIRE_SPIN_LOCK_DPC(&Open->SpinLock);
ASSERT(Open->ProtocolHandle->ProtocolCharacteristics.CoAfRegisterNotifyHandler != NULL);
for (AfList = Miniport->CallMgrAfList;
AfList != NULL;
AfList = AfList->NextAf)
{
if (MINIPORT_TEST_FLAG(Open, fMINIPORT_OPEN_UNBINDING | fMINIPORT_OPEN_CLOSING))
{
//
// this open is going away. skip it.
//
break;
}
else
{
OPEN_INCREMENT_AF_NOTIFICATION(Open);
// DbgPrint("ndisCreateNotifyQueue: Open %p, AFNotifyRef %lx\n", Open, Open->PendingAfNotifications);
}
AfN = (PNDIS_AF_NOTIFY)ALLOC_FROM_POOL(sizeof(NDIS_AF_NOTIFY), NDIS_TAG_CO);
if (AfN == NULL)
{
Status = NDIS_STATUS_RESOURCES;
break;
}
AfN->Miniport = Miniport;
AfN->Open = Open;
M_OPEN_INCREMENT_REF_INTERLOCKED(Open);
AfN->AddressFamily = AfList->AddressFamily;
INITIALIZE_WORK_ITEM(&AfN->WorkItem, ndisNotifyAfRegistration, AfN);
AfN->Next = *AfNotify;
*AfNotify = AfN;
}
RELEASE_SPIN_LOCK_DPC(&Open->SpinLock);
}
else
{
for (tOpen = Miniport->OpenQueue;
tOpen != NULL;
tOpen = tOpen->MiniportNextOpen)
{
if (tOpen->ProtocolHandle->ProtocolCharacteristics.CoAfRegisterNotifyHandler != NULL)
{
ACQUIRE_SPIN_LOCK_DPC(&tOpen->SpinLock);
if (MINIPORT_TEST_FLAG(tOpen, fMINIPORT_OPEN_UNBINDING | fMINIPORT_OPEN_CLOSING))
{
//
// this open is going away. skip it.
//
RELEASE_SPIN_LOCK_DPC(&tOpen->SpinLock);
continue;
}
else
{
OPEN_INCREMENT_AF_NOTIFICATION(tOpen);
// DbgPrint("ndisCreateNotifyQueue: tOpen %p, AFNotifyRef %lx\n", tOpen, tOpen->PendingAfNotifications);
RELEASE_SPIN_LOCK_DPC(&tOpen->SpinLock);
}
AfN = (PNDIS_AF_NOTIFY)ALLOC_FROM_POOL(sizeof(NDIS_AF_NOTIFY), NDIS_TAG_CO);
if (AfN == NULL)
{
Status = NDIS_STATUS_RESOURCES;
ndisDereferenceAfNotification(tOpen);
break;
}
AfN->Miniport = Miniport;
AfN->Open = tOpen;
M_OPEN_INCREMENT_REF_INTERLOCKED(tOpen);
AfN->AddressFamily = *AddressFamily;
INITIALIZE_WORK_ITEM(&AfN->WorkItem, ndisNotifyAfRegistration, AfN);
AfN->Next = *AfNotify;
*AfNotify = AfN;
}
}
}
return Status;
}
VOID
ndisNotifyAfRegistration(
IN PNDIS_AF_NOTIFY AfNotify
)
/*++
Routine Description:
Notify protocols that care that a new address family has been registered.
Arguments:
AfNotify - Structure that holds context information.
Return Value:
None.
--*/
{
KIRQL OldIrql;
PNDIS_MINIPORT_BLOCK Miniport;
PNDIS_PROTOCOL_BLOCK Protocol;
PNDIS_OPEN_BLOCK Open;
PNDIS_AF_NOTIFY Af, AfNext;
Miniport = AfNotify->Miniport;
PnPReferencePackage();
for (Af = AfNotify; Af != NULL; Af = AfNext)
{
AfNext = Af->Next;
Open = Af->Open;
Protocol = Open->ProtocolHandle;
if (!MINIPORT_TEST_FLAG(Open, fMINIPORT_OPEN_UNBINDING | fMINIPORT_OPEN_CLOSING))
{
(*Protocol->ProtocolCharacteristics.CoAfRegisterNotifyHandler)(
Open->ProtocolBindingContext,
&Af->AddressFamily);
}
FREE_POOL(Af);
ndisDereferenceAfNotification(Open);
NDIS_ACQUIRE_MINIPORT_SPIN_LOCK(Miniport, &OldIrql);
ndisMDereferenceOpen(Open);
NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql);
}
PnPDereferencePackage();
}
NDIS_STATUS
NdisClOpenAddressFamily(
IN NDIS_HANDLE NdisBindingHandle,
IN PCO_ADDRESS_FAMILY AddressFamily,
IN NDIS_HANDLE ClientAfContext,
IN PNDIS_CLIENT_CHARACTERISTICS ClCharacteristics,
IN UINT SizeOfClCharacteristics,
OUT PNDIS_HANDLE NdisAfHandle
)
/*++
Routine Description:
This is a call from a NDIS 5.0 or later protocol to open a particular
address familty - in essence getting a handle to the call-manager.
Arguments:
NdisBindingHandle - Pointer to the protocol's NDIS_OPEN_BLOCK.
PCO_ADDRESS_FAMILY - The address family being registered.
ClientAfContext - Protocol context associated with this handle.
NdisAfHandle - Handle returned by NDIS for this address family.
Return Value:
NDIS_STATUS_SUCCESS if the address family open is successfully.
NDIS_STATUS_PENDING if the call-manager pends this call. The caller will get
called at the completion handler when done.
NDIS_STATUS_FAILURE if the caller is not a NDIS 5.0 prototcol or this address
family is not registered for this miniport.
--*/
{
PNDIS_CO_AF_BLOCK pAf;
PNDIS_AF_LIST AfList;
PNDIS_OPEN_BLOCK CallMgrOpen, ClientOpen;
PNDIS_MINIPORT_BLOCK Miniport;
PNDIS_PROTOCOL_BLOCK Protocol;
KIRQL OldIrql;
NTSTATUS Status;
*NdisAfHandle = NULL;
ClientOpen = (PNDIS_OPEN_BLOCK)NdisBindingHandle;
Miniport = ClientOpen->MiniportHandle;
Protocol = ClientOpen->ProtocolHandle;
PnPReferencePackage();
do
{
ClientOpen->Flags |= fMINIPORT_OPEN_CLIENT;
//
// Make sure the binding is not closing down
//
if (ClientOpen->Flags & fMINIPORT_OPEN_CLOSING)
{
Status = NDIS_STATUS_FAILURE;
break;
}
//
// Make sure that the miniport is a CoNdis miniport and
// protocol is also a NDIS 5.0 or later protocol.
//
if ((Miniport->DriverHandle->MiniportCharacteristics.MajorNdisVersion < 5) ||
(!MINIPORT_TEST_FLAG(Miniport, fMINIPORT_IS_CO)))
{
//
// Not a NDIS 5.0 or later miniport
//
Status = NDIS_STATUS_FAILURE;
break;
}
if (Protocol->ProtocolCharacteristics.MajorNdisVersion < 5)
{
//
// Not a NDIS 5.0 or later protocol
//
Status = NDIS_STATUS_FAILURE;
break;
}
//
// Make sure that the client characteristics are 5.0 or later
//
if ((ClCharacteristics->MajorVersion < 5) ||
(SizeOfClCharacteristics < sizeof(NDIS_CLIENT_CHARACTERISTICS)))
{
//
// Not a NDIS 5.0 or later protocol
//
Status = NDIS_STATUS_FAILURE;
break;
}
NDIS_ACQUIRE_MINIPORT_SPIN_LOCK(Miniport, &OldIrql);
//
// Search the miniport block for a registered call-manager for this address family
//
for (AfList = Miniport->CallMgrAfList;
AfList != NULL;
AfList = AfList->NextAf)
{
if (AfList->AddressFamily.AddressFamily == AddressFamily->AddressFamily)
{
CallMgrOpen = AfList->Open;
break;
}
}
//
// If we found a matching call manager, make sure that the callmgr
// is not currently closing.
//
if ((AfList == NULL) ||
((AfList != NULL) && (AfList->Open != NULL) && (AfList->Open->Flags & fMINIPORT_OPEN_CLOSING)) ||
(MINIPORT_PNP_TEST_FLAG(Miniport, fMINIPORT_PM_HALTED)))
{
NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql);
Status = NDIS_STATUS_FAILURE;
break;
}
//
// Allocate memory for the AF block.
//
pAf = ALLOC_FROM_POOL(sizeof(NDIS_CO_AF_BLOCK), NDIS_TAG_CO);
if (pAf == NULL)
{
NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql);
Status = NDIS_STATUS_RESOURCES;
break;
}
pAf->References = 1;
pAf->Flags = (AfList->Open == NULL) ? AF_COMBO : 0;
pAf->Miniport = Miniport;
pAf->ClientOpen = ClientOpen;
pAf->CallMgrOpen = CallMgrOpen = AfList->Open;
pAf->ClientContext = ClientAfContext;
//
// Reference the call-manager's file object - we do not want to let it
// duck from under the client.
//
//
// Reference the client and the call-manager opens
//
M_OPEN_INCREMENT_REF_INTERLOCKED(ClientOpen);
NdisInterlockedIncrement(&ClientOpen->AfReferences);
if (CallMgrOpen != NULL)
{
M_OPEN_INCREMENT_REF_INTERLOCKED(CallMgrOpen);
NdisInterlockedIncrement(&CallMgrOpen->AfReferences);
}
else
{
ObReferenceObject(Miniport->DeviceObject);
Miniport->Ref.ReferenceCount ++;
#ifdef TRACK_MINIPORT_REFCOUNTS
M_LOG_MINIPORT_INCREMENT_REF(Miniport, __LINE__, MODULE_NUMBER);
#endif
}
NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql);
INITIALIZE_SPIN_LOCK(&pAf->Lock);
//
// Cache in call-manager entry points
//
pAf->CallMgrEntries = &AfList->CmChars;
//
// And also Cache in client entry points
//
CopyMemory(&pAf->ClientEntries,
ClCharacteristics,
sizeof(NDIS_CLIENT_CHARACTERISTICS));
//
// Cache some handlers in the open-block
//
ClientOpen->CoCreateVcHandler = ClCharacteristics->ClCreateVcHandler;
ClientOpen->CoDeleteVcHandler = ClCharacteristics->ClDeleteVcHandler;
ClientOpen->CoRequestCompleteHandler = ClCharacteristics->ClRequestCompleteHandler;
//
// Now call the CallMgr's OpenAfHandler
//
Status = (*AfList->CmChars.CmOpenAfHandler)((CallMgrOpen != NULL) ?
CallMgrOpen->ProtocolBindingContext :
Miniport->MiniportAdapterContext,
AddressFamily,
pAf,
&pAf->CallMgrContext);
if (Status != NDIS_STATUS_PENDING)
{
NdisCmOpenAddressFamilyComplete(Status,
pAf,
pAf->CallMgrContext);
Status = NDIS_STATUS_PENDING;
}
} while (FALSE);
PnPDereferencePackage();
return Status;
}
VOID
NdisCmOpenAddressFamilyComplete(
IN NDIS_STATUS Status,
IN NDIS_HANDLE NdisAfHandle,
IN NDIS_HANDLE CallMgrAfContext
)
/*++
Routine Description:
Completion routine for the OpenAddressFamily call. The call manager had pended this
call earlier (or will pend). If the call succeeded there is a valid CallMgrContext
supplied here as well
Arguments:
Status - Completion status
NdisAfHandle - Pointer to the AfBlock
CallMgrAfContext - Call manager's context used in other calls into the call manager.
Return Value:
NONE. The client's completion handler is called.
--*/
{
PNDIS_CO_AF_BLOCK pAf;
PNDIS_OPEN_BLOCK ClientOpen;
PNDIS_MINIPORT_BLOCK Miniport;
KIRQL OldIrql;
ASSERT(Status != NDIS_STATUS_PENDING);
pAf = (PNDIS_CO_AF_BLOCK)NdisAfHandle;
ClientOpen = pAf->ClientOpen;
Miniport = pAf->Miniport;
if (Status != NDIS_STATUS_SUCCESS)
{
if (pAf->CallMgrOpen == NULL)
{
ObDereferenceObject(Miniport->DeviceObject);
}
}
NDIS_ACQUIRE_MINIPORT_SPIN_LOCK(Miniport, &OldIrql);
pAf->CallMgrContext = CallMgrAfContext;
if (Status != NDIS_STATUS_SUCCESS)
{
//
// OpenAfHandler failed
//
if (pAf->CallMgrOpen != NULL)
{
NdisInterlockedDecrement(&pAf->CallMgrOpen->AfReferences);
ndisMDereferenceOpen(pAf->CallMgrOpen);
}
else
{
MINIPORT_DECREMENT_REF(Miniport);
}
NdisInterlockedDecrement(&ClientOpen->AfReferences);
ndisMDereferenceOpen(ClientOpen);
}
else
{
//
// queue this CallMgr open onto the miniport open
//
pAf->NextAf = ClientOpen->NextAf;
ClientOpen->NextAf = pAf;
}
NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql);
//
// Finally call the client's completion handler
//
(*pAf->ClientEntries.ClOpenAfCompleteHandler)(Status,
pAf->ClientContext,
(Status == NDIS_STATUS_SUCCESS) ? pAf : NULL);
if (Status != NDIS_STATUS_SUCCESS)
{
FREE_POOL(pAf);
}
}
NDIS_STATUS
NdisClCloseAddressFamily(
IN NDIS_HANDLE NdisAfHandle
)
/*++
Routine Description:
This call closes the Af object which essentially tears down the client-callmanager
'binding'. Causes all open Vcs to be closed and saps to be de-registered "by the call
manager".
Arguments:
NdisAfHandle - Pointer to the Af.
Return Value:
Status from Call Manager.
--*/
{
PNDIS_CO_AF_BLOCK pAf = (PNDIS_CO_AF_BLOCK)NdisAfHandle;
NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
KIRQL OldIrql;
//
// Mark the address family as closing and call the call-manager to process.
//
ACQUIRE_SPIN_LOCK(&pAf->Lock, &OldIrql);
if (pAf->Flags & AF_CLOSING)
{
Status = NDIS_STATUS_FAILURE;
}
pAf->Flags |= AF_CLOSING;
RELEASE_SPIN_LOCK(&pAf->Lock, OldIrql);
if (Status == NDIS_STATUS_SUCCESS)
{
Status = (*pAf->CallMgrEntries->CmCloseAfHandler)(pAf->CallMgrContext);
if (Status != NDIS_STATUS_PENDING)
{
NdisCmCloseAddressFamilyComplete(Status, pAf);
Status = NDIS_STATUS_PENDING;
}
}
return Status;
}
VOID
NdisCmCloseAddressFamilyComplete(
IN NDIS_STATUS Status,
IN NDIS_HANDLE NdisAfHandle
)
/*++
Routine Description:
Completion routine for the CloseAddressFamily call. The call manager had pended this
call earlier (or will pend). If the call succeeded there is a valid CallMgrContext
supplied here as well
Arguments:
Status - Completion status
NdisAfHandle - Pointer to the AfBlock
Return Value:
NONE. The client's completion handler is called.
--*/
{
PNDIS_CO_AF_BLOCK pAf = (PNDIS_CO_AF_BLOCK)NdisAfHandle;
PNDIS_CO_AF_BLOCK * ppAf;
PNDIS_MINIPORT_BLOCK Miniport;
KIRQL OldIrql;
Miniport = pAf->Miniport;
//
// NOTE: Memphis closes the adapters synchronously. so we have to deref
// the open -before- calling ClCloseAfCompleteHandler because
// ClCloseAfCompleteHandler will try to close the adapter and CloseAdapter
// will pend forever since the ref count never goes to zero
//
//
// Complete the call to the client
//
(*pAf->ClientEntries.ClCloseAfCompleteHandler)(Status,
pAf->ClientContext);
if (Status == NDIS_STATUS_SUCCESS)
{
if (pAf->CallMgrOpen == NULL)
{
ObDereferenceObject(Miniport->DeviceObject);
}
Miniport = pAf->Miniport;
NDIS_ACQUIRE_MINIPORT_SPIN_LOCK(Miniport, &OldIrql);
if (pAf->CallMgrOpen != NULL)
{
NdisInterlockedDecrement(&pAf->CallMgrOpen->AfReferences);
ndisMDereferenceOpen(pAf->CallMgrOpen);
}
else
{
MINIPORT_DECREMENT_REF(Miniport);
}
//
// Unlink from list of open AFs for this client.
//
for (ppAf = &pAf->ClientOpen->NextAf;
*ppAf != NULL;
ppAf = &((*ppAf)->NextAf))
{
if (*ppAf == pAf)
{
*ppAf = pAf->NextAf;
break;
}
}
NdisInterlockedDecrement(&pAf->ClientOpen->AfReferences);
ndisMDereferenceOpen(pAf->ClientOpen);
NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql);
}
//
// Finally dereference the AF Block, if the call-manager successfully closed it.
//
if (Status == NDIS_STATUS_SUCCESS)
{
ndisDereferenceAf(pAf);
}
}
BOOLEAN
FASTCALL
ndisReferenceAf(
IN PNDIS_CO_AF_BLOCK pAf
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
KIRQL OldIrql;
BOOLEAN rc = FALSE;
ACQUIRE_SPIN_LOCK(&pAf->Lock, &OldIrql);
if ((pAf->Flags & AF_CLOSING) == 0)
{
pAf->References ++;
rc = TRUE;
}
RELEASE_SPIN_LOCK(&pAf->Lock, OldIrql);
return rc;
}
VOID
FASTCALL
ndisDereferenceAf(
IN PNDIS_CO_AF_BLOCK pAf
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
KIRQL OldIrql;
BOOLEAN Done = FALSE;
ACQUIRE_SPIN_LOCK(&pAf->Lock, &OldIrql);
ASSERT(pAf->References > 0);
pAf->References --;
if (pAf->References == 0)
{
ASSERT(pAf->Flags & AF_CLOSING);
Done = TRUE;
}
RELEASE_SPIN_LOCK(&pAf->Lock, OldIrql);
if (Done)
FREE_POOL(pAf);
}
NDIS_STATUS
NdisClRegisterSap(
IN NDIS_HANDLE NdisAfHandle,
IN NDIS_HANDLE ProtocolSapContext,
IN PCO_SAP Sap,
OUT PNDIS_HANDLE NdisSapHandle
)
/*++
Routine Description:
This is a call from a NDIS 5.0 or later protocol to register its SAP
with the call manager.
Arguments:
NdisBindingHandle - Pointer to the protocol's NDIS_OPEN_BLOCK.
PCO_ADDRESS_FAMILY - The address family being registered.
ClientAfContext - Protocol context associated with this handle.
NdisAfHandle - Handle returned by NDIS for this address family.
Return Value:
NDIS_STATUS_SUCCESS if the address family open is successfully.
NDIS_STATUS_PENDING if the call-manager pends this call. The caller will get
NDIS_STATUS_FAILURE if the caller is not a NDIS 5.0 prototcol or this address
family is not registered for this miniport.
--*/
{
NDIS_STATUS Status;
PNDIS_CO_AF_BLOCK pAf = (PNDIS_CO_AF_BLOCK)NdisAfHandle;
PNDIS_CO_SAP_BLOCK pSap;
*NdisSapHandle = NULL;
do
{
//
// Reference the Af for this SAP
//
if (!ndisReferenceAf(pAf))
{
Status = NDIS_STATUS_FAILURE;
break;
}
pSap = (PNDIS_CO_SAP_BLOCK)ALLOC_FROM_POOL(sizeof(NDIS_CO_SAP_BLOCK), NDIS_TAG_CO);
if (pSap == NULL)
{
*NdisSapHandle = NULL;
Status = NDIS_STATUS_RESOURCES;
break;
}
pSap->Flags = 0;
pSap->References = 1;
INITIALIZE_SPIN_LOCK(&pSap->Lock);
pSap->AfBlock = pAf;
pSap->Sap = Sap;
pSap->ClientContext = ProtocolSapContext;
Status = (*pAf->CallMgrEntries->CmRegisterSapHandler)(pAf->CallMgrContext,
Sap,
pSap,
&pSap->CallMgrContext);
if (Status != NDIS_STATUS_PENDING)
{
NdisCmRegisterSapComplete(Status, pSap, pSap->CallMgrContext);
Status = NDIS_STATUS_PENDING;
}
} while (FALSE);
return Status;
}
VOID
NdisCmRegisterSapComplete(
IN NDIS_STATUS Status,
IN NDIS_HANDLE NdisSapHandle,
IN NDIS_HANDLE CallMgrSapContext
)
/*++
Routine Description:
Completion routine for the registerSap call. The call manager had pended this
call earlier (or will pend). If the call succeeded there is a valid CallMgrContext
supplied here as well
Arguments:
Status - Completion status
NdisAfHandle - Pointer to the AfBlock
CallMgrAfContext - Call manager's context used in other calls into the call manager.
Return Value:
NONE. The client's completion handler is called.
--*/
{
PNDIS_CO_SAP_BLOCK pSap = (PNDIS_CO_SAP_BLOCK)NdisSapHandle;
PNDIS_CO_AF_BLOCK pAf;
ASSERT(Status != NDIS_STATUS_PENDING);
pAf = pSap->AfBlock;
pSap->CallMgrContext = CallMgrSapContext;
//
// Call the clients completion handler
//
(*pAf->ClientEntries.ClRegisterSapCompleteHandler)(Status,
pSap->ClientContext,
pSap->Sap,
pSap);
if (Status != NDIS_STATUS_SUCCESS)
{
ndisDereferenceAf(pSap->AfBlock);
FREE_POOL(pSap);
}
}
NDIS_STATUS
NdisClDeregisterSap(
IN NDIS_HANDLE NdisSapHandle
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PNDIS_CO_SAP_BLOCK pSap = (PNDIS_CO_SAP_BLOCK)NdisSapHandle;
NDIS_STATUS Status;
KIRQL OldIrql;
BOOLEAN fAlreadyClosing;
ACQUIRE_SPIN_LOCK(&pSap->Lock, &OldIrql);
fAlreadyClosing = FALSE;
if (pSap->Flags & SAP_CLOSING)
{
fAlreadyClosing = TRUE;
}
pSap->Flags |= SAP_CLOSING;
RELEASE_SPIN_LOCK(&pSap->Lock, OldIrql);
if (fAlreadyClosing)
{
return NDIS_STATUS_FAILURE;
}
//
// Notify the call-manager that this sap is being de-registered
//
Status = (*pSap->AfBlock->CallMgrEntries->CmDeregisterSapHandler)(pSap->CallMgrContext);
if (Status != NDIS_STATUS_PENDING)
{
NdisCmDeregisterSapComplete(Status, pSap);
Status = NDIS_STATUS_PENDING;
}
return Status;
}
VOID
NdisCmDeregisterSapComplete(
IN NDIS_STATUS Status,
IN NDIS_HANDLE NdisSapHandle
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PNDIS_CO_SAP_BLOCK pSap = (PNDIS_CO_SAP_BLOCK)NdisSapHandle;
ASSERT(Status != NDIS_STATUS_PENDING);
//
// Complete the call to the client and deref the sap
//
(*pSap->AfBlock->ClientEntries.ClDeregisterSapCompleteHandler)(Status,
pSap->ClientContext);
if (Status == NDIS_STATUS_SUCCESS)
{
ndisDereferenceAf(pSap->AfBlock);
ndisDereferenceSap(pSap);
}
}
BOOLEAN
FASTCALL
ndisReferenceSap(
IN PNDIS_CO_SAP_BLOCK pSap
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
KIRQL OldIrql;
BOOLEAN rc = FALSE;
ACQUIRE_SPIN_LOCK(&pSap->Lock, &OldIrql);
if ((pSap->Flags & SAP_CLOSING) == 0)
{
pSap->References ++;
rc = TRUE;
}
RELEASE_SPIN_LOCK(&pSap->Lock, OldIrql);
return rc;
}
VOID
FASTCALL
ndisDereferenceSap(
IN PNDIS_CO_SAP_BLOCK pSap
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
KIRQL OldIrql;
BOOLEAN Done = FALSE;
ACQUIRE_SPIN_LOCK(&pSap->Lock, &OldIrql);
ASSERT(pSap->References > 0);
pSap->References --;
if (pSap->References == 0)
{
ASSERT(pSap->Flags & SAP_CLOSING);
Done = TRUE;
}
RELEASE_SPIN_LOCK(&pSap->Lock, OldIrql);
if (Done)
FREE_POOL(pSap);
}
NDIS_STATUS
NdisCoCreateVc(
IN NDIS_HANDLE NdisBindingHandle,
IN NDIS_HANDLE NdisAfHandle OPTIONAL,
IN NDIS_HANDLE ProtocolVcContext,
IN OUT PNDIS_HANDLE NdisVcHandle
)
/*++
Routine Description:
This is a call from either the call-manager or from the client to create a vc.
The vc would then be owned by call-manager (signalling vc) or the client.
This is a synchronous call to all parties and simply creates an end-point over
which either incoming calls can be dispatched or out-going calls can be made.
Combined Miniport/Call Manager drivers call NdisMCmCreateVc instead of NdisCoCreateVc.
Arguments:
NdisBindingHandle - Pointer to the caller's NDIS_OPEN_BLOCK.
NdisAfHandle - Pointer to the AF Block. Not specified for call-manager's private vcs.
NdisVcHandle - Where the handle to this Vc will be returned. If this is non-NULL,
then we assume a valid Vc and return a new handle to it (this would be
a call from the NDIS Proxy to create a second handle to an existing Vc).
Return Value:
NDIS_STATUS_SUCCESS if all the components succeed.
ErrorCode to signify why the call failed.
--*/
{
NDIS_STATUS Status;
PNDIS_OPEN_BLOCK Open;
PNDIS_MINIPORT_BLOCK Miniport;
PNDIS_CO_AF_BLOCK pAf;
PNDIS_CO_VC_PTR_BLOCK VcPtr; // VcPtr is returned in NdisVcHandle
PNDIS_CO_VC_PTR_BLOCK ExistingVcPtr; // Passed in optionally by caller
PNDIS_CO_VC_BLOCK VcBlock; // Created if ExistingVcPtr is NULL
KIRQL OldIrql;
BOOLEAN bCallerIsProxy;
BOOLEAN bCallerIsClient;
BOOLEAN bVcToComboMiniport; // VC being created to Integrated MCM
DBGPRINT(DBG_COMP_CO, DBG_LEVEL_INFO,
("=>NdisCoCreateVc\n"));
//
// Get the caller's open for the miniport.
//
Open = (PNDIS_OPEN_BLOCK)NdisBindingHandle;
Miniport = Open->MiniportHandle;
pAf = (PNDIS_CO_AF_BLOCK)NdisAfHandle;
//
// Is this VC being created to an integrated call manager + miniport driver?
//
bVcToComboMiniport = ((pAf) && ((pAf->Flags & AF_COMBO) != 0));
//
// The caller is either a Client or a Call manager.
//
bCallerIsClient = ((pAf != NULL) && (Open == pAf->ClientOpen));
//
// The caller could be a Proxy protocol.
//
bCallerIsProxy = ((Open->ProtocolHandle->ProtocolCharacteristics.Flags & NDIS_PROTOCOL_PROXY) != 0);
//
// A proxy protocol could pass in its handle to an existing VC, in order
// to "duplicate" it to another client.
//
ExistingVcPtr = *NdisVcHandle;
//
// Initialize.
//
VcPtr = NULL;
VcBlock = NULL;
Status = NDIS_STATUS_SUCCESS;
do
{
//
// Only a proxy protocol can pass in an existing VC pointer.
//
if (ExistingVcPtr && !bCallerIsProxy)
{
Status = NDIS_STATUS_FAILURE;
break;
}
//
// Allocate context for this VC creation: a VC Pointer Block.
// We return a pointer to this as the NdisVcHandle for the caller.
//
VcPtr = ALLOC_FROM_POOL(sizeof(NDIS_CO_VC_PTR_BLOCK), NDIS_TAG_CO);
if (VcPtr == NULL)
{
Status = NDIS_STATUS_RESOURCES;
break;
}
//
// Initialize the VC Ptr
//
NdisZeroMemory(VcPtr, sizeof(NDIS_CO_VC_PTR_BLOCK));
INITIALIZE_SPIN_LOCK(&VcPtr->Lock);
InitializeListHead(&VcPtr->CallMgrLink);
InitializeListHead(&VcPtr->ClientLink);
InitializeListHead(&VcPtr->VcLink);
if (ExistingVcPtr == NULL)
{
//
// New VC being created. Allocate and prepare the base VC block.
//
DBGPRINT(DBG_COMP_CO, DBG_LEVEL_INFO,
("NdisCoCreateVc: passed in ptr == NULL\n"));
VcBlock = ALLOC_FROM_POOL(sizeof(NDIS_CO_VC_BLOCK), NDIS_TAG_CO);
if (VcBlock == NULL)
{
Status = NDIS_STATUS_RESOURCES;
FREE_POOL(VcPtr);
VcPtr = NULL;
break;
}
//
// Initialize the VC block
//
NdisZeroMemory(VcBlock, sizeof(NDIS_CO_VC_BLOCK));
INITIALIZE_SPIN_LOCK(&VcBlock->Lock);
//
// Stick the Miniport in the VC for use in NdisM functions
//
VcBlock->Miniport = Miniport;
if (!bVcToComboMiniport)
{
//
// Call the miniport to get its context for this VC.
//
Status = (*Open->MiniportCoCreateVcHandler)(Miniport->MiniportAdapterContext,
VcPtr,
&VcPtr->MiniportContext);
if (Status != NDIS_STATUS_SUCCESS)
{
FREE_POOL(VcBlock);
break;
}
}
}
else
{
//
// A VC Pointer was passed in.
//
DBGPRINT(DBG_COMP_CO, DBG_LEVEL_INFO,
("NdisCoCreateVc: NdisVcHandle is not NULL!\n"));
//
// Get the Vc from the passed-in VcPtr.
//
VcBlock = ExistingVcPtr->VcBlock;
//
// Copy the Miniport Context into the new VC ptr.
//
VcPtr->MiniportContext = ExistingVcPtr->MiniportContext;
}
//
// Cache some miniport handlers in the new VC pointer Block
//
VcPtr->WCoSendPacketsHandler = Miniport->DriverHandle->MiniportCharacteristics.CoSendPacketsHandler;
if (!bVcToComboMiniport)
{
//
// For an MCM driver, CreateVc and DeleteVc go only to the Call Manager
// section.
//
VcPtr->WCoDeleteVcHandler = Miniport->DriverHandle->MiniportCharacteristics.CoDeleteVcHandler;
}
VcPtr->WCoActivateVcHandler = Miniport->DriverHandle->MiniportCharacteristics.CoActivateVcHandler;
VcPtr->WCoDeactivateVcHandler = Miniport->DriverHandle->MiniportCharacteristics.CoDeactivateVcHandler;
//
// Set up some reverse pointers in the new VC Pointer Block
//
VcPtr->Miniport = Miniport;
VcPtr->VcBlock = VcBlock;
VcPtr->AfBlock = pAf;
VcPtr->References = 1;
VcPtr->pVcFlags = &VcBlock->Flags;
if (ARGUMENT_PRESENT(NdisAfHandle))
{
//
// This VC is associated with an AF block, meaning that it is
// a normal Client-CM-Miniport VC.
//
VcPtr->ClientOpen = pAf->ClientOpen;
VcPtr->CallMgrOpen = pAf->CallMgrOpen;
//
// Cache non-data path client handlers in new VcPtr.
//
VcPtr->ClModifyCallQoSCompleteHandler = pAf->ClientEntries.ClModifyCallQoSCompleteHandler;
VcPtr->ClIncomingCallQoSChangeHandler = pAf->ClientEntries.ClIncomingCallQoSChangeHandler;
VcPtr->ClCallConnectedHandler = pAf->ClientEntries.ClCallConnectedHandler;
VcPtr->CmActivateVcCompleteHandler = pAf->CallMgrEntries->CmActivateVcCompleteHandler;
VcPtr->CmDeactivateVcCompleteHandler = pAf->CallMgrEntries->CmDeactivateVcCompleteHandler;
VcPtr->CmModifyCallQoSHandler = pAf->CallMgrEntries->CmModifyCallQoSHandler;
//
// Mark this VC if the proxy is handing it off to a proxied client.
//
if (ExistingVcPtr != NULL)
{
VcBlock->Flags |= VC_HANDOFF_IN_PROGRESS;
}
//
// Update data path handlers based on who is calling this, and for
// what purpose.
//
if (!bCallerIsProxy)
{
VcBlock->ClientOpen = pAf->ClientOpen;
VcBlock->CoReceivePacketHandler = pAf->ClientOpen->ProtocolHandle->ProtocolCharacteristics.CoReceivePacketHandler;
VcBlock->CoSendCompleteHandler = pAf->ClientOpen->ProtocolHandle->ProtocolCharacteristics.CoSendCompleteHandler;
VcPtr->OwnsVcBlock = TRUE;
if (bCallerIsClient)
{
//
// Client-created VC, for an outgoing call.
//
VcBlock->pClientVcPtr = VcPtr;
}
else
{
//
// Call Manager-created VC, for an incoming call.
//
VcBlock->pProxyVcPtr = VcPtr;
}
}
else
{
//
// The caller is a proxy.
//
if (bCallerIsClient)
{
//
// CreateVc from a proxy client to a real Call manager.
//
if (ExistingVcPtr == NULL)
{
//
// Proxy client creating a new VC, e.g. for a TAPI outgoing call.
//
VcBlock->ClientOpen = pAf->ClientOpen;
VcBlock->CoReceivePacketHandler = pAf->ClientOpen->ProtocolHandle->ProtocolCharacteristics.CoReceivePacketHandler;
VcBlock->CoSendCompleteHandler = pAf->ClientOpen->ProtocolHandle->ProtocolCharacteristics.CoSendCompleteHandler;
}
else
{
//
// Proxy client creating a VC on behalf of a CreateVc called
// by a proxied client. The data handlers belong to the
// proxied client, but deletion of this VC does not.
//
VcBlock->pClientVcPtr = ExistingVcPtr;
ExistingVcPtr->OwnsVcBlock = FALSE; // Real (Proxied) Client doesn't own it
}
VcBlock->pProxyVcPtr = VcPtr;
VcPtr->OwnsVcBlock = TRUE; // Proxy client owns it
}
else
{
//
// CreateVc from a proxy Call manager to a proxied client.
//
VcBlock->ClientOpen = pAf->ClientOpen;
VcBlock->CoReceivePacketHandler = pAf->ClientOpen->ProtocolHandle->ProtocolCharacteristics.CoReceivePacketHandler;
VcBlock->CoSendCompleteHandler = pAf->ClientOpen->ProtocolHandle->ProtocolCharacteristics.CoSendCompleteHandler;
VcBlock->pClientVcPtr = VcPtr;
if (ExistingVcPtr != NULL)
{
//
// Proxy CM forwarding a call to a proxied client.
//
VcBlock->pProxyVcPtr = ExistingVcPtr;
ExistingVcPtr->OwnsVcBlock = TRUE;
}
else
{
//
// Proxy CM creating a fresh VC to a proxied client.
// No well-known examples of this case, here for completeness.
//
VcPtr->OwnsVcBlock = TRUE;
}
}
}
//
// Determine who the caller is and initialize the other. NOTE: As soon as the Proxy Create handler
// is called, this function can get re-entered. Lock down the VcPtr.
//
ACQUIRE_SPIN_LOCK(&VcPtr->Lock, &OldIrql);
if (Open == pAf->ClientOpen)
{
VcPtr->ClientContext = ProtocolVcContext;
//
// Call-up to the call-manager now to get its context
//
Status = (*pAf->CallMgrEntries->CmCreateVcHandler)(pAf->CallMgrContext,
VcPtr,
&VcPtr->CallMgrContext);
if (bVcToComboMiniport)
{
//
// Need the MiniportContext field filled in for NdisCoSendPackets
//
VcPtr->MiniportContext = VcPtr->CallMgrContext;
}
}
else
{
ASSERT(pAf->CallMgrOpen == Open);
VcPtr->CallMgrContext = ProtocolVcContext;
//
// Call-up to the client now to get its context
//
Status = (*pAf->ClientOpen->CoCreateVcHandler)(pAf->ClientContext,
VcPtr,
&VcPtr->ClientContext);
}
//
// Set up Client Context in VC if non-proxy, so the miniport passes the right client
// context (client's handle to the VcPtr) when indicating packets. If the passd-in handle
// is NULL, it's simple -- move the context. If it's not NULL, AND this is a Proxy call mgr,
// move it so data goes to the new client and not to the Proxy.
//
if ((Status == NDIS_STATUS_SUCCESS) &&
((ExistingVcPtr == NULL) || (bCallerIsProxy && !bCallerIsClient)))
{
VcBlock->ClientContext = VcPtr->ClientContext;
}
if (ExistingVcPtr != NULL)
{
VcBlock->Flags &= ~VC_HANDOFF_IN_PROGRESS;
}
RELEASE_SPIN_LOCK(&VcPtr->Lock, OldIrql);
if (Status == NDIS_STATUS_SUCCESS)
{
//
// Link this VC Pointer in the client's and call manager's
// Open blocks. Also remember the DeleteVc handler of the
// non-creator of this VC pointer, to be called when this
// VC pointer is deleted.
//
if (bCallerIsClient)
{
//
// Link into Client's Open block.
//
ExInterlockedInsertHeadList(&Open->InactiveVcHead,
&VcPtr->ClientLink,
&Open->SpinLock);
VcPtr->DeleteVcContext = VcPtr->CallMgrContext;
VcPtr->CoDeleteVcHandler = pAf->CallMgrEntries->CmDeleteVcHandler;
if (!bVcToComboMiniport)
{
//
// Link into CM's Open block.
//
ExInterlockedInsertHeadList(&pAf->CallMgrOpen->InactiveVcHead,
&VcPtr->CallMgrLink,
&pAf->CallMgrOpen->SpinLock);
}
}
else
{
//
// Caller is a Call Manager.
//
VcPtr->DeleteVcContext = VcPtr->ClientContext;
VcPtr->CoDeleteVcHandler = pAf->ClientOpen->CoDeleteVcHandler;
ExInterlockedInsertHeadList(&Open->InactiveVcHead,
&VcPtr->CallMgrLink,
&Open->SpinLock);
ExInterlockedInsertHeadList(&pAf->ClientOpen->InactiveVcHead,
&VcPtr->ClientLink,
&pAf->ClientOpen->SpinLock);
}
}
else
{
//
// The target protocol (Client or CM) failed CreateVc.
// Tell the miniport about it.
//
NDIS_STATUS Sts;
if (!bVcToComboMiniport)
{
Sts = (*VcPtr->WCoDeleteVcHandler)(VcPtr->MiniportContext);
}
if (ExistingVcPtr == NULL)
{
FREE_POOL(VcBlock);
}
FREE_POOL(VcPtr);
VcPtr = NULL;
}
}
else
{
//
// No AF handle present. This is a call-manager only VC and so the call-manager
// is the client and there is no call-manager associated with it. This VC cannot
// be used with a ClMakeCall or CmDispatchIncomingCall. Set the client values to the
// call-manager
//
DBGPRINT(DBG_COMP_CO, DBG_LEVEL_INFO,
("NdisCoCreateVc: signaling vc\n"));
VcPtr->ClientOpen = Open;
VcPtr->ClientContext = ProtocolVcContext;
VcBlock->pClientVcPtr = VcPtr;
VcPtr->OwnsVcBlock = TRUE; // CM owns the VC block
VcBlock->ClientContext = VcPtr->ClientContext;
VcBlock->ClientOpen = Open;
VcBlock->CoSendCompleteHandler = Open->ProtocolHandle->ProtocolCharacteristics.CoSendCompleteHandler;
VcBlock->CoReceivePacketHandler = Open->ProtocolHandle->ProtocolCharacteristics.CoReceivePacketHandler;
//
// Do set the following call-manager entries since this VC will need to be
// activated. Also set the call-managers context for the same reasons.
//
VcPtr->CmActivateVcCompleteHandler = Open->CmActivateVcCompleteHandler;
VcPtr->CmDeactivateVcCompleteHandler = Open->CmDeactivateVcCompleteHandler;
VcPtr->CallMgrContext = ProtocolVcContext;
//
// Link this in the open_block
//
ExInterlockedInsertHeadList(&Open->InactiveVcHead,
&VcPtr->ClientLink,
&Open->SpinLock);
}
break;
}
while (FALSE);
if (NDIS_STATUS_SUCCESS == Status)
{
LARGE_INTEGER Increment = {0, 1};
//
// Assign this VC an ID and update the miniports count.
//
VcPtr->VcIndex = ExInterlockedAddLargeInteger(&Miniport->VcIndex, Increment, &ndisGlobalLock);
}
*NdisVcHandle = VcPtr;
DBGPRINT(DBG_COMP_CO, DBG_LEVEL_INFO,
("<=NdisCoCreateVc: VcPtr %x, Status %x\n", VcPtr, Status));
return Status;
}
NDIS_STATUS
NdisCoDeleteVc(
IN PNDIS_HANDLE NdisVcHandle
)
/*++
Routine Description:
Synchronous call from either the call-manager or the client to delete a VC. Only inactive
VCs can be deleted. Active Vcs or partially active Vcs cannot be.
Arguments:
NdisVcHandle The Vc to delete
Return Value:
NDIS_STATUS_SUCCESS If all goes well
NDIS_STATUS_NOT_ACCEPTED If Vc is active
NDIS_STATUS_CLOSING If Vc de-activation is pending
--*/
{
PNDIS_CO_VC_PTR_BLOCK VcPtr = (PNDIS_CO_VC_PTR_BLOCK)NdisVcHandle;
NDIS_STATUS Status;
KIRQL OldIrql;
DBGPRINT(DBG_COMP_CO, DBG_LEVEL_INFO,
("NdisCoDeleteVc VcPtr %x/%x, Ref %d VcBlock %x, Flags %x\n",
VcPtr, VcPtr->CallFlags, VcPtr->References, VcPtr->VcBlock, *VcPtr->pVcFlags));
ACQUIRE_SPIN_LOCK(&VcPtr->Lock, &OldIrql);
if (*VcPtr->pVcFlags & (VC_ACTIVE | VC_ACTIVATE_PENDING))
{
Status = NDIS_STATUS_NOT_ACCEPTED;
}
else if (*VcPtr->pVcFlags & (VC_DEACTIVATE_PENDING))
{
Status = NDIS_STATUS_CLOSING;
}
else
{
//
// Take this VcPtr out of the VC's list
//
// If the VC isn't already closing mark it as closing.
//
// We call the miniport's delete handler if the VC block's Proxy ptr points
// to this VC ptr. (This indicates that the VC block is owned/created by the
// CM/Proxy, not the CL).
//
// NOTE: We don't delete the VC until all these pointers
// have gone since the Proxy may wish to redirect the VC to another protocol.
// However, in general the Proxy would follow a call to DeleteVc for the Client ptr
// with one for the Proxy.
// (Note the MP context refers to the VC, not the VcPtr).
//
VcPtr->CallFlags |= VC_PTR_BLOCK_CLOSING;
if (VcPtr->OwnsVcBlock &&
(VcPtr->WCoDeleteVcHandler != NULL))
{
*VcPtr->pVcFlags |= VC_DELETE_PENDING;
}
//
// If this VC is responding to WMI then get rid of it.
//
if (NULL != VcPtr->VcInstanceName.Buffer)
{
//
// Notify the removal of this VC.
//
PWNODE_SINGLE_INSTANCE wnode;
PUCHAR ptmp;
NTSTATUS NtStatus;
ndisSetupWmiNode(VcPtr->Miniport,
&VcPtr->VcInstanceName,
0,
(PVOID)&GUID_NDIS_NOTIFY_VC_REMOVAL,
&wnode);
if (wnode != NULL)
{
//
// Indicate the event to WMI. WMI will take care of freeing
// the WMI struct back to pool.
//
NtStatus = IoWMIWriteEvent(wnode);
if (!NT_SUCCESS(NtStatus))
{
DBGPRINT(DBG_COMP_WMI, DBG_LEVEL_ERR,
("IoWMIWriteEvent failed %lx\n", NtStatus));
FREE_POOL(wnode);
}
}
ACQUIRE_SPIN_LOCK_DPC(&VcPtr->Miniport->VcCountLock);
//
// Remove the VC from the list of WMI enabled VCs
//
RemoveEntryList(&VcPtr->WmiLink);
//
// Decrement the number of VC's that have names assigned to them.
//
VcPtr->Miniport->VcCount--;
//
// Free the VC's name buffer.
//
FREE_POOL(VcPtr->VcInstanceName.Buffer);
VcPtr->VcInstanceName.Buffer = NULL;
VcPtr->VcInstanceName.Length = VcPtr->VcInstanceName.MaximumLength = 0;
RELEASE_SPIN_LOCK_DPC(&VcPtr->Miniport->VcCountLock);
}
//
// Next the non-creator's delete handler, if any
//
if (VcPtr->CoDeleteVcHandler != NULL)
{
Status = (*VcPtr->CoDeleteVcHandler)(VcPtr->DeleteVcContext);
ASSERT(Status == NDIS_STATUS_SUCCESS);
}
//
// Now de-link the VcPtr from the client and the call-manager
//
ACQUIRE_SPIN_LOCK_DPC(&VcPtr->ClientOpen->SpinLock);
RemoveEntryList(&VcPtr->ClientLink);
RELEASE_SPIN_LOCK_DPC(&VcPtr->ClientOpen->SpinLock);
if (VcPtr->CallMgrOpen != NULL)
{
ACQUIRE_SPIN_LOCK_DPC(&VcPtr->CallMgrOpen->SpinLock);
RemoveEntryList(&VcPtr->CallMgrLink);
RELEASE_SPIN_LOCK_DPC(&VcPtr->CallMgrOpen->SpinLock);
}
Status = NDIS_STATUS_SUCCESS;
}
RELEASE_SPIN_LOCK(&VcPtr->Lock, OldIrql);
if (Status == NDIS_STATUS_SUCCESS)
{
ndisDereferenceVcPtr(VcPtr);
}
return Status;
}
NDIS_STATUS
NdisMCmCreateVc(
IN NDIS_HANDLE MiniportAdapterHandle,
IN NDIS_HANDLE NdisAfHandle,
IN NDIS_HANDLE MiniportVcContext,
OUT PNDIS_HANDLE NdisVcHandle
)
/*++
Routine Description:
This is a call by the miniport (with a resident CM) to create a Vc for an incoming call.
Arguments:
MiniportAdapterHandle - Miniport's adapter context
NdisAfHandle - Pointer to the AF Block.
MiniportVcContext - Miniport's context to associate with this vc.
NdisVcHandle - Where the handle to this Vc will be returned.
Return Value:
NDIS_STATUS_SUCCESS if all the components succeed.
ErrorCode to signify why the call failed.
--*/
{
PNDIS_MINIPORT_BLOCK Miniport = (PNDIS_MINIPORT_BLOCK)MiniportAdapterHandle;
PNDIS_CO_VC_BLOCK VcBlock;
PNDIS_CO_AF_BLOCK pAf;
PNDIS_CO_VC_PTR_BLOCK VcPtr;
NDIS_STATUS Status;
*NdisVcHandle = NULL;
//
// Allocate the memory for NDIS_VC_BLOCK
//
VcBlock = ALLOC_FROM_POOL(sizeof(NDIS_CO_VC_BLOCK), NDIS_TAG_CO);
if (VcBlock == NULL)
return NDIS_STATUS_RESOURCES;
//
// Initialize the VC block
//
NdisZeroMemory(VcBlock, sizeof(NDIS_CO_VC_BLOCK));
INITIALIZE_SPIN_LOCK(&VcBlock->Lock);
//
// Allocate the memory for NDIS_VC_PTR_BLOCK
//
VcPtr = ALLOC_FROM_POOL(sizeof(NDIS_CO_VC_PTR_BLOCK), NDIS_TAG_CO);
if (VcPtr == NULL)
{
FREE_POOL(VcBlock);
return NDIS_STATUS_RESOURCES;
}
//
// Initialize the VC Pointer block
//
NdisZeroMemory(VcPtr, sizeof(NDIS_CO_VC_PTR_BLOCK));
INITIALIZE_SPIN_LOCK(&VcPtr->Lock);
//
// Cache some miniport handlers
//
VcPtr->Miniport = Miniport;
VcPtr->WCoSendPacketsHandler = Miniport->DriverHandle->MiniportCharacteristics.CoSendPacketsHandler;
VcPtr->WCoDeleteVcHandler = Miniport->DriverHandle->MiniportCharacteristics.CoDeleteVcHandler;
VcPtr->WCoActivateVcHandler = Miniport->DriverHandle->MiniportCharacteristics.CoActivateVcHandler;
VcPtr->WCoDeactivateVcHandler = Miniport->DriverHandle->MiniportCharacteristics.CoDeactivateVcHandler;
VcBlock->Miniport = Miniport;
VcBlock->MiniportContext = MiniportVcContext;
VcPtr->MiniportContext = MiniportVcContext;
//
// Set up the VcBlock in the new VcPtr
//
VcPtr->VcBlock = VcBlock;
// VcPtrs to preempt potential for unsynched state when Protocols, Miniports and
// Miniport-exported Call Managers refer to VCs/VcPtrs as appropriate...similar
// for References, which is accessed from Vc directly in IndicateReceivePacket.
//
VcPtr->pVcFlags = &VcBlock->Flags;
//
// We have only one reference for vc on creation.
//
pAf = (PNDIS_CO_AF_BLOCK)NdisAfHandle;
VcPtr->AfBlock = pAf;
VcPtr->References = 1;
ASSERT(ARGUMENT_PRESENT(NdisAfHandle));
VcPtr->ClientOpen = pAf->ClientOpen;
VcPtr->CallMgrOpen = NULL;
VcBlock->ClientOpen = pAf->ClientOpen;
VcBlock->CoSendCompleteHandler = pAf->ClientOpen->ProtocolHandle->ProtocolCharacteristics.CoSendCompleteHandler;
VcBlock->CoReceivePacketHandler = pAf->ClientOpen->ProtocolHandle->ProtocolCharacteristics.CoReceivePacketHandler;
VcPtr->ClModifyCallQoSCompleteHandler = pAf->ClientEntries.ClModifyCallQoSCompleteHandler;
VcPtr->ClIncomingCallQoSChangeHandler = pAf->ClientEntries.ClIncomingCallQoSChangeHandler;
VcPtr->ClCallConnectedHandler = pAf->ClientEntries.ClCallConnectedHandler;
VcPtr->CmActivateVcCompleteHandler = pAf->CallMgrEntries->CmActivateVcCompleteHandler;
VcPtr->CmDeactivateVcCompleteHandler = pAf->CallMgrEntries->CmDeactivateVcCompleteHandler;
VcPtr->CmModifyCallQoSHandler = pAf->CallMgrEntries->CmModifyCallQoSHandler;
VcPtr->CallMgrContext = MiniportVcContext;
VcBlock->CallMgrContext = MiniportVcContext;
//
// Call-up to the client now to get its context
//
Status = (*pAf->ClientOpen->CoCreateVcHandler)(pAf->ClientContext,
VcPtr,
&VcPtr->ClientContext);
if (Status == NDIS_STATUS_SUCCESS)
{
//
// Setup the client context in the VC block. This may be overwritten by the
// new client context in a subsequent call to CoCreateVc by the proxy.
// Link this in the open_block
//
VcBlock->ClientContext = VcPtr->ClientContext;
VcPtr->DeleteVcContext = VcPtr->ClientContext;
VcPtr->CoDeleteVcHandler = pAf->ClientOpen->CoDeleteVcHandler;
ExInterlockedInsertHeadList(&pAf->ClientOpen->InactiveVcHead,
&VcPtr->ClientLink,
&pAf->ClientOpen->SpinLock);
VcBlock->pClientVcPtr = VcPtr;
}
else
{
FREE_POOL(VcBlock);
FREE_POOL(VcPtr);
VcPtr = NULL;
}
*NdisVcHandle = VcPtr;
return Status;
}
NDIS_STATUS
NdisMCmDeleteVc(
IN PNDIS_HANDLE NdisVcHandle
)
/*++
Routine Description:
This is a called by the miniport (with a resident CM) to delete a Vc created by it. Identical to
NdisMCoDeleteVc but a seperate api for completeness.
Arguments:
NdisVcHandle The Vc to delete
Return Value:
NDIS_STATUS_SUCCESS If all goes well
NDIS_STATUS_NOT_ACCEPTED If Vc is active
NDIS_STATUS_CLOSING If Vc de-activation is pending
--*/
{
PNDIS_CO_VC_PTR_BLOCK VcPtr = (PNDIS_CO_VC_PTR_BLOCK)NdisVcHandle;
PNDIS_CO_VC_BLOCK VcBlock = VcPtr->VcBlock;
if (VcBlock->pProxyVcPtr != NULL)
{
return (NdisCoDeleteVc ((PNDIS_HANDLE)VcBlock->pProxyVcPtr));
}
else
{
ASSERT(VcBlock->pClientVcPtr != NULL);
return (NdisCoDeleteVc((PNDIS_HANDLE)VcBlock->pClientVcPtr));
}
}
NDIS_STATUS
NdisCmActivateVc(
IN PNDIS_HANDLE NdisVcHandle,
IN OUT PCO_CALL_PARAMETERS CallParameters
)
/*++
Routine Description:
Called by the call-manager to set the Vc parameters on the Vc. The wrapper
saved the media id (e.g. Vpi/Vci for atm) in the Vc so that a p-mode protocol can
get this info as well on receives.
Arguments:
NdisVcHandle The Vc to set parameters on.
MediaParameters The parameters to set.
Return Value:
NDIS_STATUS_PENDING If the miniport pends the call.
NDIS_STATUS_CLOSING If Vc de-activation is pending
--*/
{
PNDIS_CO_VC_PTR_BLOCK VcPtr = (PNDIS_CO_VC_PTR_BLOCK)NdisVcHandle;
PNDIS_CO_VC_BLOCK VcBlock = (PNDIS_CO_VC_BLOCK)VcPtr->VcBlock;
NDIS_STATUS Status;
KIRQL OldIrql;
DBGPRINT(DBG_COMP_CO, DBG_LEVEL_INFO,
("NdisCmActivateVC: VcPtr is 0x%x; VC is 0x%x; MiniportContext is 0x%x\n", VcPtr,
VcPtr->VcBlock, VcPtr->MiniportContext));
ACQUIRE_SPIN_LOCK(&VcPtr->Lock, &OldIrql);
//
// Make sure the Vc does not have an activation/de-activation pending
// Not that it is ok for the Vc to be already active - then it is a re-activation.
//
if (*VcPtr->pVcFlags & VC_ACTIVATE_PENDING)
{
Status = NDIS_STATUS_NOT_ACCEPTED;
}
else if (*VcPtr->pVcFlags & VC_DEACTIVATE_PENDING)
{
Status = NDIS_STATUS_CLOSING;
}
else
{
*VcPtr->pVcFlags |= VC_ACTIVATE_PENDING;
//
// Save the media id for the Vc
//
Status = NDIS_STATUS_SUCCESS;
ASSERT(CallParameters->MediaParameters->MediaSpecific.Length >= sizeof(ULONGLONG));
VcBlock->VcId = *(UNALIGNED ULONGLONG *)(&CallParameters->MediaParameters->MediaSpecific.Parameters);
}
//
// Set up CM Context and ActivateComplete handler in VC before
// calling miniports activate func
//
VcBlock->CmActivateVcCompleteHandler = VcPtr->CmActivateVcCompleteHandler;
VcBlock->CallMgrContext = VcPtr->CallMgrContext;
RELEASE_SPIN_LOCK(&VcPtr->Lock, OldIrql);
if (Status == NDIS_STATUS_SUCCESS)
{
//
// Now call down to the miniport to activate it. MiniportContext contains
// Miniport's handle for underlying VC (not VcPtr).
//
Status = (*VcPtr->WCoActivateVcHandler)(VcPtr->MiniportContext, CallParameters);
}
if (Status != NDIS_STATUS_PENDING)
{
NdisMCoActivateVcComplete(Status, VcPtr, CallParameters);
Status = NDIS_STATUS_PENDING;
}
return Status;
}
NDIS_STATUS
NdisMCmActivateVc(
IN PNDIS_HANDLE NdisVcHandle,
IN PCO_CALL_PARAMETERS CallParameters
)
/*++
Routine Description:
Called by the miniport resident call-manager to set the Vc parameters on the Vc.
Arguments:
NdisVcHandle The Vc to set parameters on.
MediaParameters The parameters to set.
Return Value:
NDIS_STATUS_CLOSING If Vc de-activation is pending
--*/
{
PNDIS_CO_VC_PTR_BLOCK VcPtr = (PNDIS_CO_VC_PTR_BLOCK)NdisVcHandle;
PNDIS_CO_VC_BLOCK VcBlock = VcPtr->VcBlock;
NDIS_STATUS Status;
KIRQL OldIrql;
ACQUIRE_SPIN_LOCK(&VcBlock->Lock, &OldIrql);
VcBlock->Flags |= VC_ACTIVE;
VcBlock->VcId = *(UNALIGNED ULONGLONG *)(&CallParameters->MediaParameters->MediaSpecific.Parameters);
RELEASE_SPIN_LOCK(&VcBlock->Lock, OldIrql);
Status = NDIS_STATUS_SUCCESS;
return Status;
}
VOID
NdisMCoActivateVcComplete(
IN NDIS_STATUS Status,
IN PNDIS_HANDLE NdisVcHandle,
IN PCO_CALL_PARAMETERS CallParameters
)
/*++
Routine Description:
Called by the mini-port to complete a pending activation call.
Also called by CmActivateVc when the miniport doesn't pend the CreateVc call.
Note that in the second case, we've copied the flags/context/CM function into the
VC from the VC Ptr.
Arguments:
Status Status of activation.
NdisVcHandle The Vc in question.
Return Value:
NONE
The call-manager's completion routine is called.
--*/
{
PNDIS_CO_VC_PTR_BLOCK VcPtr = (PNDIS_CO_VC_PTR_BLOCK)NdisVcHandle;
PNDIS_CO_VC_BLOCK VcBlock = VcPtr->VcBlock;
KIRQL OldIrql;
ACQUIRE_SPIN_LOCK(&VcBlock->Lock, &OldIrql);
ASSERT(VcBlock->Flags & VC_ACTIVATE_PENDING);
VcBlock->Flags &= ~VC_ACTIVATE_PENDING;
if (Status == NDIS_STATUS_SUCCESS)
{
VcBlock->Flags |= VC_ACTIVE;
}
RELEASE_SPIN_LOCK(&VcBlock->Lock, OldIrql);
//
// Complete the call to the call-manager
//
(*VcBlock->CmActivateVcCompleteHandler)(Status, VcBlock->CallMgrContext, CallParameters);
}
NDIS_STATUS
NdisCmDeactivateVc(
IN PNDIS_HANDLE NdisVcHandle
)
/*++
Routine Description:
Called by the call-manager to de-activate a Vc.
Arguments:
NdisVcHandle The Vc to de-activate the Vc.
Return Value:
NDIS_STATUS_PENDING If the miniport pends the call.
NDIS_STATUS_SUCCESS If all goes well
NDIS_STATUS_CLOSING If Vc de-activation is pending
--*/
{
PNDIS_CO_VC_PTR_BLOCK VcPtr = (PNDIS_CO_VC_PTR_BLOCK)NdisVcHandle;
PNDIS_CO_VC_BLOCK VcBlock = VcPtr->VcBlock;
NDIS_STATUS Status;
KIRQL OldIrql;
ACQUIRE_SPIN_LOCK(&VcPtr->Lock, &OldIrql);
if ((*VcPtr->pVcFlags & (VC_ACTIVE | VC_ACTIVATE_PENDING)) == 0)
{
Status = NDIS_STATUS_NOT_ACCEPTED;
}
else if (*VcPtr->pVcFlags & VC_DEACTIVATE_PENDING)
{
Status = NDIS_STATUS_CLOSING;
}
else
{
*VcPtr->pVcFlags |= VC_DEACTIVATE_PENDING;
}
RELEASE_SPIN_LOCK(&VcPtr->Lock, OldIrql);
//
// Set up flags, CM Context and DeactivateComplete handler in VC before
// calling mimiports deactivate func
//
VcBlock->CmDeactivateVcCompleteHandler = VcPtr->CmDeactivateVcCompleteHandler;
VcBlock->CallMgrContext = VcPtr->CallMgrContext;
//
// Now call down to the miniport to de-activate it
//
Status = (*VcPtr->WCoDeactivateVcHandler)(VcPtr->MiniportContext);
if (Status != NDIS_STATUS_PENDING)
{
NdisMCoDeactivateVcComplete(Status, VcPtr);
Status = NDIS_STATUS_PENDING;
}
return Status;
}
NDIS_STATUS
NdisMCmDeactivateVc(
IN PNDIS_HANDLE NdisVcHandle
)
/*++
Routine Description:
Called by the miniport resident call-manager to de-activate the Vc. This is a
synchronous call.
Arguments:
NdisVcHandle The Vc to set parameters on.
Return Value:
NDIS_STATUS_NOT_ACCEPTED If Vc is not activated
NDIS_STATUS_SUCCESS Otherwise
--*/
{
PNDIS_CO_VC_PTR_BLOCK VcPtr = (PNDIS_CO_VC_PTR_BLOCK)NdisVcHandle;
PNDIS_CO_VC_BLOCK VcBlock = VcPtr->VcBlock;
NDIS_STATUS Status;
KIRQL OldIrql;
ACQUIRE_SPIN_LOCK(&VcBlock->Lock, &OldIrql);
if ((VcBlock->Flags & VC_ACTIVE) == 0)
{
Status = NDIS_STATUS_NOT_ACCEPTED;
}
else
{
Status = NDIS_STATUS_SUCCESS;
VcBlock->Flags &= ~VC_ACTIVE;
}
RELEASE_SPIN_LOCK(&VcBlock->Lock, OldIrql);
return Status;
}
VOID
NdisMCoDeactivateVcComplete(
IN NDIS_STATUS Status,
IN PNDIS_HANDLE NdisVcHandle
)
/*++
Routine Description:
Called by the mini-port to complete a pending de-activation of a Vc.
Arguments:
NdisVcHandle The Vc in question.
Return Value:
NONE
The call-manager's completion routine is called.
--*/
{
PNDIS_CO_VC_PTR_BLOCK VcPtr = (PNDIS_CO_VC_PTR_BLOCK)NdisVcHandle;
PNDIS_CO_VC_BLOCK VcBlock = VcPtr->VcBlock;
KIRQL OldIrql;
ACQUIRE_SPIN_LOCK(&VcBlock->Lock, &OldIrql);
ASSERT(VcBlock->Flags & VC_DEACTIVATE_PENDING);
VcBlock->Flags &= ~VC_DEACTIVATE_PENDING;
if (Status == NDIS_STATUS_SUCCESS)
{
VcBlock->Flags &= ~VC_ACTIVE;
}
RELEASE_SPIN_LOCK(&VcBlock->Lock, OldIrql);
//
// Complete the call to the call-manager
//
(*VcBlock->CmDeactivateVcCompleteHandler)(Status, VcBlock->CallMgrContext);
}
NDIS_STATUS
NdisClMakeCall(
IN NDIS_HANDLE NdisVcHandle,
IN OUT PCO_CALL_PARAMETERS CallParameters,
IN NDIS_HANDLE ProtocolPartyContext OPTIONAL,
OUT PNDIS_HANDLE NdisPartyHandle OPTIONAL
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PNDIS_CO_VC_PTR_BLOCK VcPtr = (PNDIS_CO_VC_PTR_BLOCK)NdisVcHandle;
PNDIS_CO_AF_BLOCK pAf;
PNDIS_CO_PARTY_BLOCK pParty = NULL;
PVOID CallMgrPartyContext = NULL;
NDIS_STATUS Status;
KIRQL OldIrql;
do
{
pAf = VcPtr->AfBlock;
ASSERT(pAf != NULL);
if (!ndisReferenceAf(pAf))
{
Status = NDIS_STATUS_FAILURE;
break;
}
//
// Ref the VC for the life of the active vc.
// This is Deref'd is in MakeCallComplete If the call fails and CloseCallComplete
// when it succeeds
//
if (!ndisReferenceVcPtr(VcPtr))
{
ndisDereferenceAf(pAf);
Status = NDIS_STATUS_FAILURE;
break;
}
if (ARGUMENT_PRESENT(NdisPartyHandle))
{
*NdisPartyHandle = NULL;
pParty = (PNDIS_CO_PARTY_BLOCK)ALLOC_FROM_POOL(sizeof(NDIS_CO_PARTY_BLOCK),
NDIS_TAG_CO);
if (pParty == NULL)
{
ndisDereferenceAf(pAf);
ndisDereferenceVcPtr(VcPtr);
Status = NDIS_STATUS_RESOURCES;
break;
}
pParty->VcPtr = VcPtr;
pParty->ClientContext = ProtocolPartyContext;
pParty->ClIncomingDropPartyHandler = pAf->ClientEntries.ClIncomingDropPartyHandler;
pParty->ClDropPartyCompleteHandler = pAf->ClientEntries.ClDropPartyCompleteHandler;
}
ACQUIRE_SPIN_LOCK(&VcPtr->Lock, &OldIrql);
ASSERT((VcPtr->CallFlags & (VC_CALL_ACTIVE |
VC_CALL_PENDING |
VC_CALL_ABORTED |
VC_CALL_CLOSE_PENDING)) == 0);
VcPtr->CallFlags |= VC_CALL_PENDING;
RELEASE_SPIN_LOCK(&VcPtr->Lock, OldIrql);
//
// Pass the request off to the call manager
//
Status = (*pAf->CallMgrEntries->CmMakeCallHandler)(VcPtr->CallMgrContext,
CallParameters,
pParty,
&CallMgrPartyContext);
if (Status != NDIS_STATUS_PENDING)
{
NdisCmMakeCallComplete(Status,
VcPtr,
pParty,
CallMgrPartyContext,
CallParameters);
Status = NDIS_STATUS_PENDING;
}
} while (FALSE);
return Status;
}
VOID
NdisCmMakeCallComplete(
IN NDIS_STATUS Status,
IN NDIS_HANDLE NdisVcHandle,
IN NDIS_HANDLE NdisPartyHandle OPTIONAL,
IN NDIS_HANDLE CallMgrPartyContext OPTIONAL,
IN PCO_CALL_PARAMETERS CallParameters
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PNDIS_CO_AF_BLOCK pAf;
PNDIS_CO_VC_PTR_BLOCK VcPtr = (PNDIS_CO_VC_PTR_BLOCK)NdisVcHandle;
PNDIS_CO_PARTY_BLOCK pParty = (PNDIS_CO_PARTY_BLOCK)NdisPartyHandle;
KIRQL OldIrql;
BOOLEAN fAborted;
DBGPRINT(DBG_COMP_CO, DBG_LEVEL_INFO,
("NdisCmMakeCallComplete(%x): VcPtr %x/%x, Ref %d, VCBlock %x/%x\n",
Status, VcPtr, VcPtr->CallFlags, VcPtr->References,
VcPtr->VcBlock, VcPtr->VcBlock->Flags));
pAf = VcPtr->AfBlock;
ASSERT(Status != NDIS_STATUS_PENDING);
ACQUIRE_SPIN_LOCK(&VcPtr->Lock, &OldIrql);
VcPtr->CallFlags &= ~VC_CALL_PENDING;
if (Status == NDIS_STATUS_SUCCESS)
{
VcPtr->CallFlags |= VC_CALL_ACTIVE;
}
else
{
fAborted = ((VcPtr->CallFlags & VC_CALL_ABORTED) != 0);
}
RELEASE_SPIN_LOCK(&VcPtr->Lock, OldIrql);
if (Status == NDIS_STATUS_SUCCESS)
{
//
// Call completed successfully. Complete it to the client.
//
if (ARGUMENT_PRESENT(NdisPartyHandle))
{
pParty->CallMgrContext = CallMgrPartyContext;
ndisReferenceVcPtr(VcPtr);
}
ACQUIRE_SPIN_LOCK(&pAf->ClientOpen->SpinLock, &OldIrql);
RemoveEntryList(&VcPtr->ClientLink);
InsertHeadList(&pAf->ClientOpen->ActiveVcHead,
&VcPtr->ClientLink);
RELEASE_SPIN_LOCK(&pAf->ClientOpen->SpinLock, OldIrql);
}
else
{
//
// Deref the VC and Af (was ref'd in MakeCall) - but only if the call was
// not aborted. In this case CloseCall will do the right thing.
//
if (!fAborted)
{
ndisDereferenceVcPtr(VcPtr);
ndisDereferenceAf(pAf);
if (pParty)
{
FREE_POOL(pParty);
}
}
DBGPRINT(DBG_COMP_CO, DBG_LEVEL_INFO,
("NdisCmMakeCallComplete: Failed %lx\n", Status));
}
(*pAf->ClientEntries.ClMakeCallCompleteHandler)(Status,
VcPtr->ClientContext,
pParty,
CallParameters);
}
NDIS_STATUS
NdisCmDispatchIncomingCall(
IN NDIS_HANDLE NdisSapHandle,
IN NDIS_HANDLE NdisVcHandle,
IN OUT PCO_CALL_PARAMETERS CallParameters
)
/*++
Routine Description:
Call from the call-manager to dispatch an incoming vc to the client who registered the Sap.
The client is identified by the NdisSapHandle.
Arguments:
NdisBindingHandle - Identifies the miniport on which the Vc is created
NdisSapHandle - Identifies the client
CallParameters - Self explanatory
NdisVcHandle - Pointer to the NDIS_CO_VC_BLOCK created via NdisCmCreateVc
Return Value:
Return value from the client or an processing error.
--*/
{
PNDIS_CO_SAP_BLOCK Sap;
PNDIS_CO_VC_PTR_BLOCK VcPtr;
PNDIS_CO_AF_BLOCK pAf;
NDIS_STATUS Status;
Sap = (PNDIS_CO_SAP_BLOCK)NdisSapHandle;
VcPtr = (PNDIS_CO_VC_PTR_BLOCK)NdisVcHandle;
pAf = Sap->AfBlock;
ASSERT(pAf == VcPtr->AfBlock);
//
// Make sure the SAP's not closing
//
if (!ndisReferenceSap(Sap))
{
return(NDIS_STATUS_FAILURE);
}
//
// Make sure the AF is not closing
//
if (!ndisReferenceAf(pAf))
{
ndisDereferenceSap(Sap);
return(NDIS_STATUS_FAILURE);
}
//
// Notify the client of this call
//
Status = (*pAf->ClientEntries.ClIncomingCallHandler)(Sap->ClientContext,
VcPtr->ClientContext,
CallParameters);
if (Status != NDIS_STATUS_PENDING)
{
NdisClIncomingCallComplete(Status, VcPtr, CallParameters);
Status = NDIS_STATUS_PENDING;
}
ndisDereferenceSap(Sap);
return Status;
}
VOID
NdisClIncomingCallComplete(
IN NDIS_STATUS Status,
IN NDIS_HANDLE NdisVcHandle,
IN PCO_CALL_PARAMETERS CallParameters
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PNDIS_CO_VC_PTR_BLOCK VcPtr = (PNDIS_CO_VC_PTR_BLOCK)NdisVcHandle;
KIRQL OldIrql;
ASSERT(Status != NDIS_STATUS_PENDING);
if (Status == NDIS_STATUS_SUCCESS)
{
ACQUIRE_SPIN_LOCK(&VcPtr->ClientOpen->SpinLock, &OldIrql);
//
// Reference the VcPtr. This is dereferenced when NdisClCloseCall is called.
//
VcPtr->References ++;
RemoveEntryList(&VcPtr->ClientLink);
InsertHeadList(&VcPtr->ClientOpen->ActiveVcHead,
&VcPtr->ClientLink);
RELEASE_SPIN_LOCK(&VcPtr->ClientOpen->SpinLock, OldIrql);
ACQUIRE_SPIN_LOCK(&VcPtr->Lock, &OldIrql);
ASSERT((VcPtr->CallFlags & (VC_CALL_ABORTED | VC_CALL_PENDING)) == 0);
VcPtr->CallFlags |= VC_CALL_ACTIVE;
RELEASE_SPIN_LOCK(&VcPtr->Lock, OldIrql);
}
//
// Call the call-manager handler to notify that client is done with this.
//
(*VcPtr->AfBlock->CallMgrEntries->CmIncomingCallCompleteHandler)(
Status,
VcPtr->CallMgrContext,
CallParameters);
}
VOID
NdisCmDispatchCallConnected(
IN NDIS_HANDLE NdisVcHandle
)
/*++
Routine Description:
Called by the call-manager to complete the final hand-shake on an incoming call.
Arguments:
NdisVcHandle - Pointer to the vc block
Return Value:
None.
--*/
{
PNDIS_CO_VC_PTR_BLOCK VcPtr = (PNDIS_CO_VC_PTR_BLOCK)NdisVcHandle;
(*VcPtr->ClCallConnectedHandler)(VcPtr->ClientContext);
}
NDIS_STATUS
NdisClModifyCallQoS(
IN NDIS_HANDLE NdisVcHandle,
IN PCO_CALL_PARAMETERS CallParameters
)
/*++
Routine Description:
Initiated by the client to modify the QoS associated with the call.
Arguments:
NdisVcHandle - Pointer to the vc block
CallParameters - New call QoS
Return Value:
--*/
{
PNDIS_CO_VC_PTR_BLOCK VcPtr = (PNDIS_CO_VC_PTR_BLOCK)NdisVcHandle;
NDIS_STATUS Status;
//
// Ask the call-manager to take care of this
//
Status = (*VcPtr->CmModifyCallQoSHandler)(VcPtr->CallMgrContext,
CallParameters);
return Status;
}
VOID
NdisCmModifyCallQoSComplete(
IN NDIS_STATUS Status,
IN NDIS_HANDLE NdisVcHandle,
IN PCO_CALL_PARAMETERS CallParameters
)
{
PNDIS_CO_VC_PTR_BLOCK VcPtr = (PNDIS_CO_VC_PTR_BLOCK)NdisVcHandle;
//
// Simply notify the client
//
(*VcPtr->ClModifyCallQoSCompleteHandler)(Status,
VcPtr->ClientContext,
CallParameters);
}
VOID
NdisCmDispatchIncomingCallQoSChange(
IN NDIS_HANDLE NdisVcHandle,
IN PCO_CALL_PARAMETERS CallParameters
)
/*++
Routine Description:
Called by the call-manager to indicate a remote requested change in the call-qos. This is
simply an indication. A client must respond by either accepting it (do nothing) or reject
it (by either modifying the call qos or by tearing down the call).
Arguments:
NdisVcHandle - Pointer to the vc block
CallParameters - New call qos
Return Value:
None.
--*/
{
PNDIS_CO_VC_PTR_BLOCK VcPtr = (PNDIS_CO_VC_PTR_BLOCK)NdisVcHandle;
//
// Simply notify the client
//
(*VcPtr->ClIncomingCallQoSChangeHandler)(VcPtr->ClientContext,
CallParameters);
}
NDIS_STATUS
NdisClCloseCall(
IN NDIS_HANDLE NdisVcHandle,
IN NDIS_HANDLE NdisPartyHandle OPTIONAL,
IN PVOID Buffer OPTIONAL,
IN UINT Size OPTIONAL
)
/*++
Routine Description:
Called by the client to close down a connection established via either NdisClMakeCall
or accepting an incoming call via NdisClIncomingCallComplete. The optional buffer can
be specified by the client to send a disconnect message. Upto the call-manager to do
something reasonable with it.
Arguments:
NdisVcHandle - Pointer to the vc block
Buffer - Optional disconnect message
Size - Size of the disconnect message
Return Value:
--*/
{
PNDIS_CO_VC_PTR_BLOCK VcPtr = (PNDIS_CO_VC_PTR_BLOCK)NdisVcHandle;
PNDIS_CO_PARTY_BLOCK pParty = (PNDIS_CO_PARTY_BLOCK)NdisPartyHandle;
NDIS_STATUS Status;
KIRQL OldIrql;
DBGPRINT(DBG_COMP_CO, DBG_LEVEL_INFO,
("NdisClCloseCall: VcPtr %x/%x, Ref %d, VCBlock %x/%x\n",
VcPtr, VcPtr->CallFlags, VcPtr->References,
VcPtr->VcBlock, VcPtr->VcBlock->Flags));
//
// Ref the VC. (Gets DeRef'd in CloseCallComplete)
//
if (!ndisReferenceVcPtr(VcPtr))
{
return (NDIS_STATUS_FAILURE);
}
ACQUIRE_SPIN_LOCK(&VcPtr->Lock, &OldIrql);
VcPtr->CallFlags |= VC_CALL_CLOSE_PENDING;
if (VcPtr->CallFlags & VC_CALL_PENDING)
VcPtr->CallFlags |= VC_CALL_ABORTED;
RELEASE_SPIN_LOCK(&VcPtr->Lock, OldIrql);
//
// Simply notify the call-manager
//
Status = (*VcPtr->AfBlock->CallMgrEntries->CmCloseCallHandler)(VcPtr->CallMgrContext,
(pParty != NULL) ?
pParty->CallMgrContext :
NULL,
Buffer,
Size);
if (Status != NDIS_STATUS_PENDING)
{
NdisCmCloseCallComplete(Status, VcPtr, pParty);
Status = NDIS_STATUS_PENDING;
}
return Status;
}
VOID
NdisCmCloseCallComplete(
IN NDIS_STATUS Status,
IN NDIS_HANDLE NdisVcHandle,
IN NDIS_HANDLE NdisPartyHandle OPTIONAL
)
/*++
Routine Description:
Arguments:
NdisVcHandle - Pointer to the vc block
Return Value:
Nothing. Client handler called
--*/
{
PNDIS_CO_VC_PTR_BLOCK VcPtr = (PNDIS_CO_VC_PTR_BLOCK)NdisVcHandle;
PNDIS_CO_PARTY_BLOCK pParty = (PNDIS_CO_PARTY_BLOCK)NdisPartyHandle;
NDIS_HANDLE ClientVcContext;
NDIS_HANDLE ClientPartyContext;
CL_CLOSE_CALL_COMPLETE_HANDLER CloseCallCompleteHandler;
KIRQL OldIrql;
ULONG VcFlags;
DBGPRINT(DBG_COMP_CO, DBG_LEVEL_INFO,
("NdisCmCloseCallComplete(%x): VcPtr %x/%x, Ref %d, VCBlock %x/%x\n",
Status, VcPtr, VcPtr->CallFlags, VcPtr->References,
VcPtr->VcBlock, VcPtr->VcBlock->Flags));
ACQUIRE_SPIN_LOCK(&VcPtr->Lock, &OldIrql);
VcPtr->CallFlags &= ~(VC_CALL_CLOSE_PENDING | VC_CALL_ABORTED);
ClientVcContext = VcPtr->ClientContext;
ClientPartyContext = (pParty != NULL)? pParty->ClientContext: NULL;
CloseCallCompleteHandler = VcPtr->AfBlock->ClientEntries.ClCloseCallCompleteHandler;
if (Status == NDIS_STATUS_SUCCESS)
{
VcFlags = VcPtr->CallFlags;
VcPtr->CallFlags &= ~(VC_CALL_ACTIVE);
RELEASE_SPIN_LOCK(&VcPtr->Lock, OldIrql);
if (pParty != NULL)
{
ASSERT(VcPtr == pParty->VcPtr);
ndisDereferenceVcPtr(pParty->VcPtr);
FREE_POOL(pParty);
}
//
// Deref the Vc and Af for refs taken in MakeCall/IncomingCallComplete
//
ndisDereferenceAf(VcPtr->AfBlock);
if (VcFlags & VC_CALL_ACTIVE)
{
ndisDereferenceVcPtr(VcPtr);
}
}
else
{
//
// Leave the VC and VC Ptr in their original states (before this
// failed CloseCall happened)
//
RELEASE_SPIN_LOCK(&VcPtr->Lock, OldIrql);
}
//
// Deref the VC (Refs were taken in CloseCall)
//
ndisDereferenceVcPtr(VcPtr);
//
// Now inform the client of CloseCall completion.
//
(*CloseCallCompleteHandler)(Status,
ClientVcContext,
ClientPartyContext);
}
VOID
NdisCmDispatchIncomingCloseCall(
IN NDIS_STATUS CloseStatus,
IN NDIS_HANDLE NdisVcHandle,
IN PVOID Buffer,
IN UINT Size
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PNDIS_CO_VC_PTR_BLOCK VcPtr = (PNDIS_CO_VC_PTR_BLOCK)NdisVcHandle;
//
// Notify the client
//
(*VcPtr->AfBlock->ClientEntries.ClIncomingCloseCallHandler)(
CloseStatus,
VcPtr->ClientContext,
Buffer,
Size);
}
NDIS_STATUS
NdisClAddParty(
IN NDIS_HANDLE NdisVcHandle,
IN NDIS_HANDLE ProtocolPartyContext,
IN OUT PCO_CALL_PARAMETERS CallParameters,
OUT PNDIS_HANDLE NdisPartyHandle
)
/*++
Routine Description:
Call from the client to the call-manager to add a party to a point-to-multi-point call.
Arguments:
NdisVcHandle - The handle client obtained via NdisClMakeCall()
ProtocolPartyContext - Protocol's context for this leaf
Flags - Call flags
CallParameters - Call parameters
NdisPartyHandle - Place holder for the handle to identify the leaf
Return Value:
NDIS_STATUS_PENDING The call has pended and will complete via CoAddPartyCompleteHandler.
--*/
{
PNDIS_CO_VC_PTR_BLOCK VcPtr = (PNDIS_CO_VC_PTR_BLOCK)NdisVcHandle;
PNDIS_CO_PARTY_BLOCK pParty;
NDIS_STATUS Status;
do
{
*NdisPartyHandle = NULL;
if (!ndisReferenceVcPtr(VcPtr))
{
Status = NDIS_STATUS_FAILURE;
break;
}
pParty = ALLOC_FROM_POOL(sizeof(NDIS_CO_PARTY_BLOCK), NDIS_TAG_CO);
if (pParty == NULL)
{
Status = NDIS_STATUS_RESOURCES;
break;
}
pParty->ClientContext = ProtocolPartyContext;
pParty->VcPtr = VcPtr;
pParty->ClIncomingDropPartyHandler = VcPtr->AfBlock->ClientEntries.ClIncomingDropPartyHandler;
pParty->ClDropPartyCompleteHandler = VcPtr->AfBlock->ClientEntries.ClDropPartyCompleteHandler;
//
// Simply call the call-manager to do its stuff.
//
Status = (*VcPtr->AfBlock->CallMgrEntries->CmAddPartyHandler)(
VcPtr->CallMgrContext,
CallParameters,
pParty,
&pParty->CallMgrContext);
if (Status != NDIS_STATUS_PENDING)
{
NdisCmAddPartyComplete(Status,
pParty,
pParty->CallMgrContext,
CallParameters);
Status = NDIS_STATUS_PENDING;
}
} while (FALSE);
return Status;
}
VOID
NdisCmAddPartyComplete(
IN NDIS_STATUS Status,
IN NDIS_HANDLE NdisPartyHandle,
IN NDIS_HANDLE CallMgrPartyContext OPTIONAL,
IN PCO_CALL_PARAMETERS CallParameters
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PNDIS_CO_PARTY_BLOCK pParty = (PNDIS_CO_PARTY_BLOCK)NdisPartyHandle;
ASSERT(Status != NDIS_STATUS_PENDING);
if (Status == NDIS_STATUS_SUCCESS)
{
pParty->CallMgrContext = CallMgrPartyContext;
}
//
// Complete the call to the client
//
(*pParty->VcPtr->AfBlock->ClientEntries.ClAddPartyCompleteHandler)(
Status,
pParty->ClientContext,
pParty,
CallParameters);
if (Status != NDIS_STATUS_SUCCESS)
{
ndisDereferenceVcPtr(pParty->VcPtr);
FREE_POOL(pParty);
}
}
NDIS_STATUS
NdisClDropParty(
IN NDIS_HANDLE NdisPartyHandle,
IN PVOID Buffer OPTIONAL,
IN UINT Size OPTIONAL
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PNDIS_CO_PARTY_BLOCK pParty = (PNDIS_CO_PARTY_BLOCK)NdisPartyHandle;
NDIS_STATUS Status;
//
// Pass it along to the call-manager to handle this
//
Status = (*pParty->VcPtr->AfBlock->CallMgrEntries->CmDropPartyHandler)(
pParty->CallMgrContext,
Buffer,
Size);
if (Status != NDIS_STATUS_PENDING)
{
NdisCmDropPartyComplete(Status, pParty);
Status = NDIS_STATUS_PENDING;
}
return Status;
}
NTSTATUS
ndisUnicodeStringToPointer(
IN PUNICODE_STRING String,
IN ULONG Base OPTIONAL,
OUT PVOID * Value
)
/*++
Routine Description:
Converts an address represented as a unicode string into a pointer.
(stolen from RtlUnicodeStringToInteger() in ntos\rtl\cnvint.c)
Arguments:
String - The Unicode String holding the address
Base - Radix of the address represented in the string (2, 8, 10, or 16)
Value - Address of the pointer in which to store the address.
Return Value:
STATUS_SUCCESS - for successful conversion
STATUS_INVALID_ARG - if the base supplied is invalid
Other exception code - if another exception occurs
--*/
{
PCWSTR s;
WCHAR c, Sign;
ULONG nChars, Digit, Shift;
#if defined(_WIN64)
ULONGLONG Result;
#else
ULONG Result;
#endif
s = String->Buffer;
nChars = String->Length / sizeof( WCHAR );
while (nChars-- && (Sign = *s++) <= ' ')
{
if (!nChars)
{
Sign = UNICODE_NULL;
break;
}
}
c = Sign;
if ((c == L'-') || (c == L'+'))
{
if (nChars)
{
nChars--;
c = *s++;
}
else
{
c = UNICODE_NULL;
}
}
if (Base == 0)
{
Base = 10;
Shift = 0;
if (c == L'0')
{
if (nChars)
{
nChars--;
c = *s++;
if (c == L'x')
{
Base = 16;
Shift = 4;
}
else
if (c == L'o')
{
Base = 8;
Shift = 3;
}
else
if (c == L'b')
{
Base = 2;
Shift = 1;
}
else
{
nChars++;
s--;
}
}
if (nChars)
{
nChars--;
c = *s++;
}
else
{
c = UNICODE_NULL;
}
}
}
else
{
switch(Base)
{
case 16:
Shift = 4;
break;
case 8:
Shift = 3;
break;
case 2:
Shift = 1;
break;
case 10:
Shift = 0;
break;
default:
return(STATUS_INVALID_PARAMETER);
}
}
Result = 0;
while (c != UNICODE_NULL)
{
if (c >= L'0' && c <= L'9')
{
Digit = c - L'0';
}
else if (c >= L'A' && c <= L'F')
{
Digit = c - L'A' + 10;
}
else if (c >= L'a' && c <= L'f')
{
Digit = c - L'a' + 10;
}
else
{
break;
}
if (Digit >= Base)
{
break;
}
if (Shift == 0)
{
Result = (Base * Result) + Digit;
}
else
{
Result = (Result << Shift) | Digit;
}
if (!nChars)
{
break;
}
nChars--;
c = *s++;
}
if (Sign == L'-')
{
#if defined(_WIN64)
Result = (ULONGLONG)(-(LONGLONG)Result);
#else
Result = (ULONG)(-(LONG)Result);
#endif
}
try
{
*Value = (PVOID)Result;
}
except(EXCEPTION_EXECUTE_HANDLER)
{
return (GetExceptionCode());
}
return( STATUS_SUCCESS );
}
NDIS_STATUS
NdisClGetProtocolVcContextFromTapiCallId(
IN UNICODE_STRING TapiCallId,
OUT PNDIS_HANDLE ProtocolVcContext
)
/*++
Routine Description:
Retrieves the protocol VC context for a VC identified by a TAPI Call ID string
(this string is the UNICODE representation of the identifier returned by
NdisCoGetTapiCallId).
Arguments:
TapiCallId - A TAPI Call Id String
ProtocolVcContext - Pointer to a NDIS_HANDLE variable in which to store the
Protocol VC Context
Return Value:
NDIS_STATUS_FAILURE if the VC context could not be obtained, NDIS_STATUS_SUCCESS
otherwise.
--*/
{
NTSTATUS Status = ndisUnicodeStringToPointer(&TapiCallId,
16,
(PVOID *)ProtocolVcContext);
return (NT_SUCCESS(Status) ? NDIS_STATUS_SUCCESS : NDIS_STATUS_FAILURE);
}
VOID
NdisCmDropPartyComplete(
IN NDIS_STATUS Status,
IN NDIS_HANDLE NdisPartyHandle
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PNDIS_CO_PARTY_BLOCK pParty = (PNDIS_CO_PARTY_BLOCK)NdisPartyHandle;
ASSERT(Status != NDIS_STATUS_PENDING);
//
// Complete the call to the client
//
(*pParty->ClDropPartyCompleteHandler)(Status,
pParty->ClientContext);
if (Status == NDIS_STATUS_SUCCESS)
{
ndisDereferenceVcPtr(pParty->VcPtr);
FREE_POOL(pParty);
}
}
VOID
NdisCmDispatchIncomingDropParty(
IN NDIS_STATUS DropStatus,
IN NDIS_HANDLE NdisPartyHandle,
IN PVOID Buffer,
IN UINT Size
)
/*++
Routine Description:
Called by the call-manager to notify the client that this leaf of the multi-party
call is terminated. The client cannot use the NdisPartyHandle after completing this
call - synchronously or by calling NdisClIncomingDropPartyComplete.
Arguments:
Return Value:
--*/
{
PNDIS_CO_PARTY_BLOCK pParty = (PNDIS_CO_PARTY_BLOCK)NdisPartyHandle;
//
// Notify the client
//
(*pParty->ClIncomingDropPartyHandler)(DropStatus,
pParty->ClientContext,
Buffer,
Size);
}
BOOLEAN
FASTCALL
ndisReferenceVcPtr(
IN PNDIS_CO_VC_PTR_BLOCK VcPtr
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
KIRQL OldIrql;
BOOLEAN rc = FALSE;
DBGPRINT(DBG_COMP_CO, DBG_LEVEL_INFO,
("ndisReferenceVcPtr: VcPtr %x/%x, Flags %x, Ref %d, VcBlock %x\n",
VcPtr, VcPtr->CallFlags, *VcPtr->pVcFlags, VcPtr->References, VcPtr->VcBlock));
ACQUIRE_SPIN_LOCK(&VcPtr->Lock, &OldIrql);
if ((VcPtr->CallFlags & VC_PTR_BLOCK_CLOSING) == 0)
{
VcPtr->References ++;
rc = TRUE;
}
RELEASE_SPIN_LOCK(&VcPtr->Lock, OldIrql);
return rc;
}
VOID
FASTCALL
ndisDereferenceVcPtr(
IN PNDIS_CO_VC_PTR_BLOCK VcPtr
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
KIRQL OldIrql;
BOOLEAN Done = FALSE;
BOOLEAN IsProxyVc;
PNDIS_CO_VC_BLOCK VcBlock;
DBGPRINT(DBG_COMP_CO, DBG_LEVEL_INFO,
("ndisDereferenceVcPtr: VcPtr %x/%x, Flags %x, Ref %d, VcBlock %x\n",
VcPtr, VcPtr->CallFlags, *VcPtr->pVcFlags, VcPtr->References, VcPtr->VcBlock));
ACQUIRE_SPIN_LOCK(&VcPtr->Lock, &OldIrql);
//
// Take this VcPtr out of the VC's list
//
VcBlock = VcPtr->VcBlock;
ASSERT(VcBlock != NULL);
ASSERT(VcPtr->References > 0);
VcPtr->References --;
if (VcPtr->References == 0)
{
ASSERT(VcPtr->CallFlags & VC_PTR_BLOCK_CLOSING);
if (*VcPtr->pVcFlags & VC_DELETE_PENDING)
{
NDIS_STATUS Status;
DBGPRINT(DBG_COMP_CO, DBG_LEVEL_INFO,
("ndisDereferenceVcPtr: Calling minport\n"));
*VcPtr->pVcFlags &= ~VC_DELETE_PENDING; // don't call DeleteVc > once
RELEASE_SPIN_LOCK_DPC(&VcPtr->Lock);
Status = (*VcPtr->WCoDeleteVcHandler)(VcPtr->MiniportContext);
ACQUIRE_SPIN_LOCK_DPC(&VcPtr->Lock);
ASSERT(Status == NDIS_STATUS_SUCCESS);
}
if (VcPtr == VcBlock->pClientVcPtr)
{
IsProxyVc = FALSE;
}
else
{
DBGPRINT(DBG_COMP_CO, DBG_LEVEL_INFO,
("ndisDereferenceVcPtr: VC ptr is Proxy\n"));
ASSERT(VcPtr == VcBlock->pProxyVcPtr);
IsProxyVc = TRUE;
}
RELEASE_SPIN_LOCK(&VcPtr->Lock, OldIrql);
DBGPRINT(DBG_COMP_CO, DBG_LEVEL_INFO,
("ndisDereferenceVcPtr: freeing VcPtr %x (VcBlock %x)\n", VcPtr, VcPtr->VcBlock));
FREE_POOL(VcPtr);
Done = TRUE;
}
else
{
RELEASE_SPIN_LOCK(&VcPtr->Lock, OldIrql);
}
if (Done)
{
//
// Any more VC ptrs q'd off this VC? If not,
// free the VC too. Note both pointers need to be empty, since
// a VC with no proxy can only ever be a normal
// non- (or pre-) proxied VC (so we leave it alone).
//
// Note that you can have a VC with no Proxy pointer, and a VC
// with no non-Proxy pointer. [REVIEWERS: Maybe we should assert that a VC
// that's been proxied should never be left without a proxy pointer when the
// non-proxy ptr is not null! (This would be a 'dangling' VC with no owner). This
// would require a 'proxied' flag in the VC].
//
ACQUIRE_SPIN_LOCK(&VcBlock->Lock, &OldIrql);
if (IsProxyVc)
{
VcBlock->pProxyVcPtr = NULL;
}
else
{
VcBlock->pClientVcPtr = NULL;
}
if ((VcBlock->pProxyVcPtr == NULL) &&
(VcBlock->pClientVcPtr == NULL))
{
RELEASE_SPIN_LOCK(&VcBlock->Lock, OldIrql);
DBGPRINT(DBG_COMP_CO, DBG_LEVEL_INFO,
("ndisDereferenceVcPtr: refs are 0; VcPtrs are both NULL; freeing VCBlock %x\n", VcBlock));
FREE_POOL(VcBlock);
}
else
{
RELEASE_SPIN_LOCK(&VcBlock->Lock, OldIrql);
}
}
}
VOID
FASTCALL
ndisMCoFreeResources(
PNDIS_OPEN_BLOCK Open
)
/*++
Routine Description:
Cleans-up address family list for call-managers etc.
CALLED WITH MINIPORT LOCK HELD.
Arguments:
Open - Pointer to the Open block for miniports
Return Value:
None
--*/
{
PNDIS_MINIPORT_BLOCK Miniport;
PNDIS_AF_LIST *pAfList, pTmp;
Miniport = Open->MiniportHandle;
for (pAfList = &Miniport->CallMgrAfList;
(pTmp = *pAfList) != NULL;
NOTHING)
{
if (pTmp->Open == Open)
{
*pAfList = pTmp->NextAf;
FREE_POOL(pTmp);
}
else
{
pAfList = &pTmp->NextAf;
}
}
ASSERT(IsListEmpty(&Open->ActiveVcHead));
}
NDIS_STATUS
NdisCoAssignInstanceName(
IN NDIS_HANDLE NdisVcHandle,
IN PNDIS_STRING BaseInstanceName,
OUT PNDIS_STRING pVcInstanceName OPTIONAL
)
{
NDIS_STATUS Status;
PNDIS_CO_VC_PTR_BLOCK VcBlock = (PNDIS_CO_VC_PTR_BLOCK)NdisVcHandle;
PNDIS_MINIPORT_BLOCK Miniport = VcBlock->Miniport;
USHORT cbSize;
PWSTR pwBuffer;
INT c;
UINT Value;
UNICODE_STRING VcInstance;
ULONGLONG VcIndex;
KIRQL OldIrql;
do
{
//
// Is there already a name associated with this VC?
//
cbSize = VcBlock->VcInstanceName.Length;
if (NULL == VcBlock->VcInstanceName.Buffer)
{
//
// The VC instance name will be of the format:
// [XXXX:YYYYYYYYYYYYYYYY] Base Name
// Where XXXX is the adapter instance number and YY..YY is the zero extended VC index.
//
cbSize = VC_INSTANCE_ID_SIZE;
if (NULL != BaseInstanceName)
{
cbSize += BaseInstanceName->Length;
}
pwBuffer = ALLOC_FROM_POOL(cbSize, NDIS_TAG_NAME_BUF);
if (NULL == pwBuffer)
{
Status = NDIS_STATUS_RESOURCES;
break;
}
NdisZeroMemory(pwBuffer, cbSize);
//
// Setup the prolog and the seperator and fill in the adapter instance #
//
pwBuffer[0] = L'[';
pwBuffer[VC_ID_INDEX] = VC_IDENTIFIER;
//
// Add the adapter instance number.
//
Value = Miniport->InstanceNumber;
for (c = 4; c > 0; c--)
{
pwBuffer[c] = ndisHexLookUp[Value & NIBBLE_MASK];
Value >>= 4;
}
//
// Add the VC index.
//
VcIndex = VcBlock->VcIndex.QuadPart;
for (c = 15; c >= 0; c--)
{
//
// Get the nibble to convert.
//
Value = (UINT)(VcIndex & NIBBLE_MASK);
pwBuffer[5+c] = ndisHexLookUp[Value];
//
// Shift the VcIndex by a nibble.
//
VcIndex >>= 4;
}
//
// Add closing bracket and a space
//
pwBuffer[21] = L']';;
pwBuffer[22] = L' ';;
//
// Initialize a temporary UNICODE_STRING to build the name.
//
VcInstance.Buffer = pwBuffer;
VcInstance.Length = VC_INSTANCE_ID_SIZE;
VcInstance.MaximumLength = cbSize;
if (NULL != BaseInstanceName)
{
//
// Append the base instance name passed into us to the end.
//
RtlAppendUnicodeStringToString(&VcInstance, BaseInstanceName);
}
ACQUIRE_SPIN_LOCK(&Miniport->VcCountLock, &OldIrql);
Miniport->VcCount++;
VcBlock->VcInstanceName = VcInstance;
//
// Add the VC to the list of WMI enabled VCs
//
InsertTailList(&Miniport->WmiEnabledVcs, &VcBlock->WmiLink);
RELEASE_SPIN_LOCK(&Miniport->VcCountLock, OldIrql);
//
// Notify the arrival of this VC.
//
{
PWNODE_SINGLE_INSTANCE wnode;
PUCHAR ptmp;
NTSTATUS NtStatus;
ndisSetupWmiNode(Miniport,
&VcInstance,
0,
(PVOID)&GUID_NDIS_NOTIFY_VC_ARRIVAL,
&wnode);
if (wnode != NULL)
{
//
// Indicate the event to WMI. WMI will take care of freeing
// the WMI struct back to pool.
//
NtStatus = IoWMIWriteEvent(wnode);
if (!NT_SUCCESS(NtStatus))
{
DBGPRINT(DBG_COMP_WMI, DBG_LEVEL_ERR,
("IoWMIWriteEvent failed %lx\n", NtStatus));
FREE_POOL(wnode);
}
}
}
}
//
// Copy the instance name string into callers NDIS_STRING.
//
if (ARGUMENT_PRESENT(pVcInstanceName))
{
pVcInstanceName->Buffer = ALLOC_FROM_POOL(cbSize, NDIS_TAG_NAME_BUF);
if (NULL == pVcInstanceName->Buffer)
{
Status = NDIS_STATUS_RESOURCES;
break;
}
NdisMoveMemory(pVcInstanceName->Buffer, VcBlock->VcInstanceName.Buffer, cbSize);
pVcInstanceName->Length = VcBlock->VcInstanceName.Length;
pVcInstanceName->MaximumLength = cbSize;
}
Status = NDIS_STATUS_SUCCESS;
} while (FALSE);
return(Status);
}
NDIS_STATUS
NdisCoRequest(
IN NDIS_HANDLE NdisBindingHandle,
IN NDIS_HANDLE NdisAfHandle OPTIONAL,
IN NDIS_HANDLE NdisVcHandle OPTIONAL,
IN NDIS_HANDLE NdisPartyHandle OPTIONAL,
IN PNDIS_REQUEST NdisRequest
)
/*++
Routine Description:
This api is used for two separate paths.
1. A symmetric call between the client and the call-manager. This mechanism is a
two-way mechanism for the call-manager and client to communicate with each other in an
asynchronous manner.
2. A request down to the miniport.
Arguments:
NdisBindingHandle - Specifies the binding and identifies the caller as call-manager/client
NdisAfHandle - Pointer to the AF Block and identifies the target. If absent, the
request is targeted to the miniport.
NdisVcHandle - Pointer to optional VC PTR block. If present the request relates to the
VC
NdisPartyHandle - Pointer to the optional Party Block. If present the request relates
to the party.
NdisRequest - The request itself
Return Value:
NDIS_STATUS_PENDING if the target pends the call.
NDIS_STATUS_FAILURE if the binding or af is closing.
Anything else return code from the other end.
--*/
{
PNDIS_OPEN_BLOCK Open;
PNDIS_CO_AF_BLOCK pAf;
NDIS_HANDLE VcContext;
PNDIS_COREQ_RESERVED CoReqRsvd;
PNDIS_CO_VC_PTR_BLOCK VcPtr = (PNDIS_CO_VC_PTR_BLOCK)NdisVcHandle;
NDIS_STATUS Status;
KIRQL OldIrql;
CoReqRsvd = PNDIS_COREQ_RESERVED_FROM_REQUEST(NdisRequest);
Open = (PNDIS_OPEN_BLOCK)NdisBindingHandle;
do
{
if (ARGUMENT_PRESENT(NdisAfHandle))
{
CO_REQUEST_HANDLER CoRequestHandler;
NDIS_HANDLE AfContext, PartyContext;
pAf = (PNDIS_CO_AF_BLOCK)NdisAfHandle;
//
// Attempt to reference the AF
//
if (!ndisReferenceAf(pAf))
{
Status = NDIS_STATUS_FAILURE;
break;
}
VcContext = NULL;
PartyContext = NULL;
NdisZeroMemory(CoReqRsvd, sizeof(NDIS_COREQ_RESERVED));
INITIALIZE_EVENT(&CoReqRsvd->Event);
PNDIS_RESERVED_FROM_PNDIS_REQUEST(NdisRequest)->Flags = REQST_SIGNAL_EVENT;
//
// Figure out who we are and call the peer
//
if (pAf->ClientOpen == Open)
{
//
// This is the client, so call the call-manager's CoRequestHandler
//
CoRequestHandler = pAf->CallMgrEntries->CmRequestHandler;
AfContext = pAf->CallMgrContext;
CoReqRsvd->AfContext = pAf->ClientContext;
CoReqRsvd->CoRequestCompleteHandler = pAf->ClientEntries.ClRequestCompleteHandler;
if (ARGUMENT_PRESENT(NdisVcHandle))
{
CoReqRsvd->VcContext = VcPtr->ClientContext;
VcContext = VcPtr->CallMgrContext;
}
if (ARGUMENT_PRESENT(NdisPartyHandle))
{
CoReqRsvd->PartyContext = ((PNDIS_CO_PARTY_BLOCK)NdisPartyHandle)->ClientContext;
PartyContext = ((PNDIS_CO_PARTY_BLOCK)NdisPartyHandle)->CallMgrContext;
}
}
else
{
ASSERT(pAf->CallMgrOpen == Open);
//
// This is the call-manager, so call the client's CoRequestHandler
//
CoRequestHandler = pAf->ClientEntries.ClRequestHandler;
AfContext = pAf->ClientContext;
CoReqRsvd->AfContext = pAf->CallMgrContext;
CoReqRsvd->CoRequestCompleteHandler = pAf->CallMgrEntries->CmRequestCompleteHandler;
if (ARGUMENT_PRESENT(NdisVcHandle))
{
CoReqRsvd->VcContext = VcPtr->CallMgrContext;
VcContext = VcPtr->ClientContext;
}
if (ARGUMENT_PRESENT(NdisPartyHandle))
{
CoReqRsvd->PartyContext = ((PNDIS_CO_PARTY_BLOCK)NdisPartyHandle)->CallMgrContext;
PartyContext = ((PNDIS_CO_PARTY_BLOCK)NdisPartyHandle)->ClientContext;
}
}
if (CoRequestHandler == NULL)
{
Status = NDIS_STATUS_NOT_SUPPORTED;
break;
}
if (MINIPORT_PNP_TEST_FLAG(Open->MiniportHandle, fMINIPORT_DEVICE_FAILED))
{
Status = (NdisRequest->RequestType == NdisRequestSetInformation) ?
NDIS_STATUS_SUCCESS : NDIS_STATUS_FAILURE;
}
else
{
//
// Now call the handler
//
Status = (*CoRequestHandler)(AfContext, VcContext, PartyContext, NdisRequest);
}
if (Status != NDIS_STATUS_PENDING)
{
NdisCoRequestComplete(Status,
NdisAfHandle,
NdisVcHandle,
NdisPartyHandle,
NdisRequest);
Status = NDIS_STATUS_PENDING;
}
}
else
{
PNDIS_MINIPORT_BLOCK Miniport;
Miniport = Open->MiniportHandle;
//
// Start off by referencing the open.
//
NDIS_ACQUIRE_MINIPORT_SPIN_LOCK(Miniport, &OldIrql);
if (Open->Flags & fMINIPORT_OPEN_CLOSING)
{
Status = NDIS_STATUS_CLOSING;
}
else if (MINIPORT_TEST_FLAG(Miniport, (fMINIPORT_RESET_IN_PROGRESS | fMINIPORT_RESET_REQUESTED)))
{
Status = NDIS_STATUS_RESET_IN_PROGRESS;
}
else
{
Status = NDIS_STATUS_SUCCESS;
M_OPEN_INCREMENT_REF_INTERLOCKED(Open);
}
NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql);
if (Status == NDIS_STATUS_SUCCESS)
{
PNDIS_RESERVED_FROM_PNDIS_REQUEST(NdisRequest)->Open = Open;
PNDIS_RESERVED_FROM_PNDIS_REQUEST(NdisRequest)->Flags = 0;
CoReqRsvd->CoRequestCompleteHandler = Open->CoRequestCompleteHandler;
CoReqRsvd->VcContext = NULL;
if (ARGUMENT_PRESENT(NdisVcHandle))
{
if (VcPtr->ClientOpen == Open)
{
CoReqRsvd->VcContext = VcPtr->ClientContext;
}
else
{
CoReqRsvd->VcContext = VcPtr->CallMgrContext;
}
}
if (MINIPORT_PNP_TEST_FLAG(Open->MiniportHandle, fMINIPORT_DEVICE_FAILED))
{
Status = (NdisRequest->RequestType == NdisRequestSetInformation) ?
NDIS_STATUS_SUCCESS : NDIS_STATUS_FAILURE;
}
else
{
//
// Call the miniport's CoRequest Handler
//
Status = (*Open->MiniportCoRequestHandler)(Open->MiniportAdapterContext,
(NdisVcHandle != NULL) ?
VcPtr->MiniportContext : NULL,
NdisRequest);
}
if (Status != NDIS_STATUS_PENDING)
{
NdisMCoRequestComplete(Status,
Open->MiniportHandle,
NdisRequest);
Status = NDIS_STATUS_PENDING;
}
}
}
} while (FALSE);
return Status;
}
NDIS_STATUS
NdisMCmRequest(
IN NDIS_HANDLE NdisAfHandle,
IN NDIS_HANDLE NdisVcHandle OPTIONAL,
IN NDIS_HANDLE NdisPartyHandle OPTIONAL,
IN OUT PNDIS_REQUEST NdisRequest
)
/*++
Routine Description:
This api is a symmetric call between the client and an integrated call-manager.
This mechanism is a two-way mechanism for the call-manager and client to communicate
with each other in an asynchronous manner.
Arguments:
NdisAfHandle - Pointer to the AF Block and identifies the target. If absent, the
request is targeted to the miniport.
NdisVcHandle - Pointer to optional VC PTR block. If present the request relates to the
VC
NdisPartyHandle - Pointer to the optional Party Block. If present the request relates
to the party.
NdisRequest - The request itself
Return Value:
NDIS_STATUS_PENDING if the target pends the call.
NDIS_STATUS_FAILURE if the binding or af is closing.
Anything else return code from the other end.
--*/
{
PNDIS_CO_AF_BLOCK pAf;
NDIS_HANDLE VcContext, PartyContext;
PNDIS_COREQ_RESERVED CoReqRsvd;
NDIS_STATUS Status;
CoReqRsvd = PNDIS_COREQ_RESERVED_FROM_REQUEST(NdisRequest);
pAf = (PNDIS_CO_AF_BLOCK)NdisAfHandle;
do
{
//
// Attempt to reference the AF
//
if (!ndisReferenceAf(pAf))
{
Status = NDIS_STATUS_FAILURE;
break;
}
VcContext = NULL;
PartyContext = NULL;
NdisZeroMemory(CoReqRsvd, sizeof(NDIS_COREQ_RESERVED));
INITIALIZE_EVENT(&CoReqRsvd->Event);
PNDIS_RESERVED_FROM_PNDIS_REQUEST(NdisRequest)->Flags = REQST_SIGNAL_EVENT;
CoReqRsvd->AfContext = pAf->CallMgrContext;
CoReqRsvd->CoRequestCompleteHandler = pAf->CallMgrEntries->CmRequestCompleteHandler;
if (ARGUMENT_PRESENT(NdisVcHandle))
{
CoReqRsvd->VcContext = pAf->CallMgrContext;
VcContext = ((PNDIS_CO_VC_PTR_BLOCK)NdisVcHandle)->ClientContext;
}
if (ARGUMENT_PRESENT(NdisPartyHandle))
{
CoReqRsvd->PartyContext = ((PNDIS_CO_PARTY_BLOCK)NdisPartyHandle)->CallMgrContext;
PartyContext = ((PNDIS_CO_PARTY_BLOCK)NdisPartyHandle)->ClientContext;
}
//
// Now call the handler
//
Status = (*pAf->ClientEntries.ClRequestHandler)(pAf->ClientContext,
VcContext,
PartyContext,
NdisRequest);
if (Status != NDIS_STATUS_PENDING)
{
NdisCoRequestComplete(Status,
NdisAfHandle,
NdisVcHandle,
NdisPartyHandle,
NdisRequest);
Status = NDIS_STATUS_PENDING;
}
} while (FALSE);
return Status;
}
VOID
NdisCoRequestComplete(
IN NDIS_STATUS Status,
IN NDIS_HANDLE NdisAfHandle,
IN NDIS_HANDLE NdisVcHandle OPTIONAL,
IN NDIS_HANDLE NdisPartyHandle OPTIONAL,
IN PNDIS_REQUEST NdisRequest
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PNDIS_COREQ_RESERVED ReqRsvd = PNDIS_COREQ_RESERVED_FROM_REQUEST(NdisRequest);
//
// Simply call the request completion handler and deref the Af block
//
(*ReqRsvd->CoRequestCompleteHandler)(Status,
ReqRsvd->AfContext,
ReqRsvd->VcContext,
ReqRsvd->PartyContext,
NdisRequest);
ndisDereferenceAf((PNDIS_CO_AF_BLOCK)NdisAfHandle);
}
NDIS_STATUS
NdisCoGetTapiCallId(
IN NDIS_HANDLE NdisVcHandle,
IN OUT PVAR_STRING TapiCallId
)
/*++
Routine Description:
Returns a string that can be used by a TAPI application to identify a particular VC.
Arguments:
NdisVcHandle - The NDIS handle to the VC to identify
TapiCallId - Pointer to a VAR_STRING structure in which to return
the identifier
Return Value:
NDIS_STATUS_BUFFER_TOO_SHORT if the VAR_STRING structure's ulTotalSize field indicates
that it does not contain enough space to hold the VC's identifier. The ulNeededSize
field will be set to the size needed.
NDIS_STATUS_INVALID_DATA if the NdisVcHandle passed in is not valid.
NDIS_STATUS_SUCCESS otherwise.
--*/
{
NDIS_HANDLE ClientContext;
TapiCallId->ulUsedSize = 0;
if (NdisVcHandle)
ClientContext = ((PNDIS_CO_VC_PTR_BLOCK)NdisVcHandle)->ClientContext;
else
return NDIS_STATUS_INVALID_DATA;
//
// Determine the size we will need.
//
TapiCallId->ulNeededSize = sizeof(VAR_STRING) + sizeof(ClientContext);
//
// Check that there is enough space to copy the call ID. If not,
// we bail.
//
if (TapiCallId->ulTotalSize < TapiCallId->ulNeededSize)
return NDIS_STATUS_BUFFER_TOO_SHORT;
//
// Set fields, do the copy.
//
TapiCallId->ulStringFormat = STRINGFORMAT_BINARY;
TapiCallId->ulStringSize = sizeof(ClientContext);
TapiCallId->ulStringOffset = sizeof(VAR_STRING);
NdisMoveMemory(((PUCHAR)TapiCallId) + TapiCallId->ulStringOffset,
&ClientContext,
sizeof(ClientContext));
TapiCallId->ulUsedSize = sizeof(VAR_STRING) + sizeof(ClientContext);
return NDIS_STATUS_SUCCESS;
}
VOID
NdisMCoRequestComplete(
IN NDIS_STATUS Status,
IN NDIS_HANDLE NdisBindingHandle,
IN PNDIS_REQUEST NdisRequest
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PNDIS_REQUEST_RESERVED ReqRsvd;
PNDIS_COREQ_RESERVED CoReqRsvd;
PNDIS_MINIPORT_BLOCK Miniport;
PNDIS_OPEN_BLOCK Open;
ReqRsvd = PNDIS_RESERVED_FROM_PNDIS_REQUEST(NdisRequest);
CoReqRsvd = PNDIS_COREQ_RESERVED_FROM_REQUEST(NdisRequest);
Miniport = (PNDIS_MINIPORT_BLOCK)NdisBindingHandle;
Open = ReqRsvd->Open;
if ((NdisRequest->RequestType == NdisRequestQueryInformation) &&
(NdisRequest->DATA.QUERY_INFORMATION.Oid == OID_GEN_CURRENT_PACKET_FILTER) &&
(NdisRequest->DATA.QUERY_INFORMATION.InformationBufferLength != 0))
{
if ((Open != NULL) && (Open->Flags & fMINIPORT_OPEN_PMODE))
{
*(PULONG)(NdisRequest->DATA.QUERY_INFORMATION.InformationBuffer) |=
NDIS_PACKET_TYPE_PROMISCUOUS | NDIS_PACKET_TYPE_ALL_LOCAL;
}
}
if (Open != NULL)
{
KIRQL OldIrql;
if (ReqRsvd->Flags & REQST_DOWNLEVEL)
{
//
// Complete the request to the protocol and deref the open
//
if (NdisRequest->RequestType == NdisRequestSetInformation)
{
NdisMSetInformationComplete(Miniport, Status);
}
else
{
NdisMQueryInformationComplete(Miniport, Status);
}
}
else
{
//
// Complete the request to the protocol and deref the open
//
ReqRsvd->Flags |= REQST_COMPLETED;
(*CoReqRsvd->CoRequestCompleteHandler)(Status,
ReqRsvd->Open->ProtocolBindingContext,
CoReqRsvd->VcContext,
NULL,
NdisRequest);
NDIS_ACQUIRE_MINIPORT_SPIN_LOCK(Miniport, &OldIrql);
ndisMDereferenceOpen(Open);
NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql);
}
}
else
{
//
// Just set status and signal
//
CoReqRsvd->Status = Status;
SET_EVENT(&CoReqRsvd->Event);
}
}
VOID
NdisMCoIndicateReceivePacket(
IN NDIS_HANDLE NdisVcHandle,
IN PPNDIS_PACKET PacketArray,
IN UINT NumberOfPackets
)
/*++
Routine Description:
This routine is called by the Miniport to indicate a set of packets to
a particular VC.
Arguments:
NdisVcHandle - The handle suppplied by Ndis when the VC on which
data is received was first reserved.
PacketArray - Array of packets.
NumberOfPackets - Number of packets being indicated.
Return Value:
None.
--*/
{
PNULL_FILTER Filter;
UINT i, NumPmodeOpens;
PNDIS_STACK_RESERVED NSR;
PNDIS_PACKET_OOB_DATA pOob;
PPNDIS_PACKET pPktArray;
PNDIS_PACKET Packet;
PNDIS_CO_VC_PTR_BLOCK VcPtr = (PNDIS_CO_VC_PTR_BLOCK)NdisVcHandle;
PNDIS_CO_VC_BLOCK VcBlock = VcPtr->VcBlock;
PNDIS_MINIPORT_BLOCK Miniport;
LOCK_STATE LockState;
#ifdef TRACK_RECEIVED_PACKETS
ULONG OrgPacketStackLocation;
PETHREAD CurThread = PsGetCurrentThread();
// ULONG CurThread = KeGetCurrentProcessorNumber();
#endif
Miniport = VcBlock->Miniport;
Filter = Miniport->NullDB;
READ_LOCK_FILTER(Miniport, Filter, &LockState);
VcBlock->ClientOpen->Flags |= fMINIPORT_PACKET_RECEIVED;
//
// NOTE that checking Vc Flags for Closing should not be needed since the CallMgr
// holds onto the protocol's CloseCall request until the ref count goes to zero -
// which means the miniport has to have completed its RELEASE_VC, which will
// inturn mandate that we will NOT get any further indications from it.
// The miniport must not complete a RELEASE_VC until it is no longer indicating data.
//
for (i = 0, pPktArray = PacketArray;
i < NumberOfPackets;
i++, pPktArray++)
{
Packet = *pPktArray;
ASSERT(Packet != NULL);
#ifdef TRACK_RECEIVED_PACKETS
OrgPacketStackLocation = CURR_STACK_LOCATION(Packet);
#endif
pOob = NDIS_OOB_DATA_FROM_PACKET(Packet);
PUSH_PACKET_STACK(Packet);
NDIS_STACK_RESERVED_FROM_PACKET(Packet, &NSR)
DIRECTED_PACKETS_IN(Miniport);
DIRECTED_BYTES_IN_PACKET(Miniport, Packet);
//
// Set context in the packet so that NdisReturnPacket can do the right thing
//
NDIS_INITIALIZE_RCVD_PACKET(Packet, NSR, Miniport);
if (pOob->Status != NDIS_STATUS_RESOURCES)
{
pOob->Status = NDIS_STATUS_SUCCESS;
}
//
// Indicate the packet to the binding.
//
if ((VcBlock->Flags & VC_HANDOFF_IN_PROGRESS) == 0)
{
NSR->XRefCount = (SHORT)(*VcBlock->CoReceivePacketHandler)(VcBlock->ClientOpen->ProtocolBindingContext,
VcBlock->ClientContext,
Packet);
}
else
{
//
// This VC is being transitioned from the NDIS proxy to
// a proxied client. Since the proxy client may not be fully
// set up, don't indicate this packet.
//
NSR->XRefCount = 0;
}
//
// If there are promiscuous opens on this miniport, indicate it to them as well.
// The client context will identify the VC.
//
if ((NumPmodeOpens = Miniport->PmodeOpens) > 0)
{
PNULL_BINDING_INFO Open, NextOpen;
PNDIS_OPEN_BLOCK pPmodeOpen;
for (Open = Filter->OpenList;
Open && (NumPmodeOpens > 0);
Open = NextOpen)
{
NextOpen = Open->NextOpen;
pPmodeOpen = (PNDIS_OPEN_BLOCK)(Open->NdisBindingHandle);
if (pPmodeOpen->Flags & fMINIPORT_OPEN_PMODE)
{
NDIS_STATUS SavedStatus;
UINT Ref;
if (pPmodeOpen->ProtocolHandle->ProtocolCharacteristics.CoReceivePacketHandler != NULL)
{
pPmodeOpen->Flags |= fMINIPORT_PACKET_RECEIVED;
SavedStatus = NDIS_GET_PACKET_STATUS(Packet);
NDIS_SET_PACKET_STATUS(Packet, NDIS_STATUS_RESOURCES);
//
// For Pmode opens, we pass the VcId to the indication routine
// since the protocol does not really own the VC.
//
Ref = (*pPmodeOpen->ProtocolHandle->ProtocolCharacteristics.CoReceivePacketHandler)(
pPmodeOpen->ProtocolBindingContext,
&VcBlock->VcId,
Packet);
ASSERT(Ref == 0);
NDIS_SET_PACKET_STATUS(Packet, SavedStatus);
}
NumPmodeOpens --;
}
}
}
//
// Tackle refcounts now
//
TACKLE_REF_COUNT(Miniport, Packet, NSR, pOob);
}
READ_UNLOCK_FILTER(Miniport, Filter, &LockState);
}
VOID
NdisMCoReceiveComplete(
IN NDIS_HANDLE MiniportAdapterHandle
)
/*++
Routine Description:
This routine is called by the Miniport to indicate that the receive
process is complete to all bindings. Only those bindings which
have received packets will be notified. The Miniport lock is held
when this is called.
Arguments:
MiniportAdapterHandle - The handle supplied by Ndis at initialization
time through miniport initialize.
Return Value:
None.
--*/
{
PNDIS_MINIPORT_BLOCK Miniport = (PNDIS_MINIPORT_BLOCK)MiniportAdapterHandle;
PNULL_FILTER Filter;
PNDIS_OPEN_BLOCK Open;
LOCK_STATE LockState;
Filter = Miniport->NullDB;
READ_LOCK_FILTER(Miniport, Filter, &LockState);
//
// check all of the bindings on this adapter
//
for (Open = Miniport->OpenQueue;
Open != NULL;
Open = Open->MiniportNextOpen)
{
if (Open->Flags & fMINIPORT_PACKET_RECEIVED)
{
//
// Indicate the binding.
//
Open->Flags &= ~fMINIPORT_PACKET_RECEIVED;
(*Open->ReceiveCompleteHandler)(Open->ProtocolBindingContext);
}
}
READ_UNLOCK_FILTER(Miniport, Filter, &LockState);
}
VOID
NdisCoSendPackets(
IN NDIS_HANDLE NdisVcHandle,
IN PPNDIS_PACKET PacketArray,
IN UINT NumberOfPackets
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PNDIS_CO_VC_PTR_BLOCK VcPtr = (PNDIS_CO_VC_PTR_BLOCK)NdisVcHandle;
PNULL_FILTER Filter;
LOCK_STATE LockState;
PNDIS_CO_VC_BLOCK VcBlock = VcPtr->VcBlock;
PNDIS_STACK_RESERVED NSR;
PNDIS_MINIPORT_BLOCK Miniport = VcPtr->Miniport;
PNDIS_PACKET Packet;
UINT PacketCount, Index, NumToSend;
NDIS_STATUS Status;
ULONG NumPmodeOpens;
DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO,
("NdisCoSendPackets: VcPtr %x, FirstPkt %x, NumPkts %d\n",
VcPtr, *PacketArray, NumberOfPackets));
Filter = Miniport->NullDB;
READ_LOCK_FILTER(Miniport, Filter, &LockState);
//
// If there are promiscuous opens on this miniport, this must be indicated to them.
// Do this before it is send down to the miniport to preserve packet ordering.
//
if ((NumPmodeOpens = Miniport->PmodeOpens) > 0)
{
PNDIS_OPEN_BLOCK pPmodeOpen;
for (pPmodeOpen = Miniport->OpenQueue;
pPmodeOpen && (NumPmodeOpens > 0);
pPmodeOpen = pPmodeOpen->MiniportNextOpen)
{
if (pPmodeOpen->Flags & fMINIPORT_OPEN_PMODE)
{
ULONG Ref;
pPmodeOpen->Flags |= fMINIPORT_PACKET_RECEIVED;
for (PacketCount = 0; PacketCount < NumberOfPackets; PacketCount++)
{
Packet = PacketArray[PacketCount];
//
// For Pmode opens, we pass the VcId to the indication routine
// since the protocol does not really own the VC. On lookback
// the packet cannot be held.
//
Status = NDIS_GET_PACKET_STATUS(Packet);
NDIS_SET_PACKET_STATUS(Packet, NDIS_STATUS_RESOURCES);
Packet->Private.Flags |= NDIS_FLAGS_IS_LOOPBACK_PACKET;
Ref = (*pPmodeOpen->ProtocolHandle->ProtocolCharacteristics.CoReceivePacketHandler)(
pPmodeOpen->ProtocolBindingContext,
&VcBlock->VcId,
Packet);
ASSERT(Ref == 0);
NDIS_SET_PACKET_STATUS(Packet, Status);
Packet->Private.Flags &= ~NDIS_FLAGS_IS_LOOPBACK_PACKET;
}
NumPmodeOpens--;
}
}
}
Status = NDIS_STATUS_SUCCESS;
for (PacketCount = 0, Index = 0, NumToSend = 0;
PacketCount < NumberOfPackets;
PacketCount++)
{
Packet = PacketArray[PacketCount];
PUSH_PACKET_STACK(Packet);
NDIS_STACK_RESERVED_FROM_PACKET(Packet, &NSR)
if (MINIPORT_TEST_SEND_FLAG(Miniport, fMINIPORT_SEND_DO_NOT_MAP_MDLS))
{
ndisMCheckPacketAndGetStatsOutAlreadyMapped(Miniport, Packet);
}
else
{
ndisMCheckPacketAndGetStatsOut(Miniport, Packet, &Status);
}
if (Status == NDIS_STATUS_SUCCESS)
{
if (MINIPORT_TEST_FLAG(Miniport, fMINIPORT_SG_LIST))
{
NSR->Open = VcPtr->ClientOpen;
NSR->VcPtr = VcPtr;
ndisMAllocSGList(Miniport, Packet);
}
else
{
NumToSend ++;
}
}
else
{
NdisMCoSendComplete(NDIS_STATUS_RESOURCES, NdisVcHandle, Packet);
if (NumToSend != 0)
{
ASSERT (!MINIPORT_TEST_FLAG(Miniport, fMINIPORT_SG_LIST));
//
// Call down to the miniport to send this batch
// The miniport must complete the sends for all cases.
// The send either succeeds/pends or fails.
//
(*VcPtr->WCoSendPacketsHandler)(VcPtr->MiniportContext,
&PacketArray[Index],
NumToSend);
NumToSend = 0;
}
Index = PacketCount + 1;
}
}
if (NumToSend != 0)
{
//
// Send down the remaining packets
//
(*VcPtr->WCoSendPacketsHandler)(VcPtr->MiniportContext,
&PacketArray[Index],
NumToSend);
}
READ_UNLOCK_FILTER(Miniport, Filter, &LockState);
}
VOID
NdisMCoSendComplete(
IN NDIS_STATUS Status,
IN NDIS_HANDLE NdisVcHandle,
IN PNDIS_PACKET Packet
)
/*++
Routine Description:
This function is called by the miniport when a send has completed. This
routine simply calls the protocol to pass along the indication.
Arguments:
MiniportAdapterHandle - points to the adapter block.
NdisVcHandle - the handle supplied to the adapter on the OID_RESERVE_VC
PacketArray - a ptr to an array of NDIS_PACKETS
NumberOfPackets - the number of packets in PacketArray
Status - the send status that applies to all packets in the array
Return Value:
None.
--*/
{
PNDIS_MINIPORT_BLOCK Miniport;
PNDIS_OPEN_BLOCK Open;
PNDIS_CO_VC_PTR_BLOCK VcPtr = (PNDIS_CO_VC_PTR_BLOCK)NdisVcHandle;
PNDIS_CO_VC_BLOCK VcBlock = VcPtr->VcBlock;
PNDIS_STACK_RESERVED NSR;
DBGPRINT(DBG_COMP_SEND, DBG_LEVEL_INFO,
("NdisMCoSendComplete: Status %x, VcPtr %x, Pkt %x\n",
Status, VcPtr, Packet));
//
// There should not be any reason to grab the spin lock and increment the
// ref count on Open since the open cannot close until the Vc closes and
// the Vc cannot close in the middle of an indication because the miniport
// will not complete a RELEASE_VC until is it no longer indicating
//
//
// Indicate to Protocol;
//
Open = VcBlock->ClientOpen;
Miniport = VcBlock->Miniport;
if (MINIPORT_TEST_FLAG(Miniport, fMINIPORT_SG_LIST) &&
(NDIS_PER_PACKET_INFO_FROM_PACKET(Packet, ScatterGatherListPacketInfo) != NULL))
{
ndisMFreeSGList(Miniport, Packet);
}
NDIS_STACK_RESERVED_FROM_PACKET(Packet, &NSR)
MINIPORT_CLEAR_PACKET_FLAG(Packet, fPACKET_CLEAR_ITEMS);
CLEAR_WRAPPER_RESERVED(NSR);
POP_PACKET_STACK(Packet);
(VcBlock->CoSendCompleteHandler)(Status,
VcBlock->ClientContext,
Packet);
//
// Technically this Vc should not close since there is a send outstanding
// on it, and the client should not close a Vc with an outstanding send.
//
// Took out the VcBlock->References assertion since refs are now kept in VcPtr.
// NOTE: We could keep the Client ref count (i.e. the VcPtr of the protocol
// that receives the data indications) in the VC, and use a pointer in the VcPtr,
// in which case we would assert on *VcPtr->pReferences.
//
ASSERT(Open->References > 0);
}
VOID
NdisMCoIndicateStatus(
IN NDIS_HANDLE MiniportAdapterHandle,
IN NDIS_HANDLE NdisVcHandle,
IN NDIS_STATUS GeneralStatus,
IN PVOID StatusBuffer,
IN ULONG StatusBufferSize
)
/*++
Routine Description:
This routine handles passing CoStatus to the protocol. The miniport calls
this routine when it has status on a VC or a general status for all Vcs - in
this case the NdisVcHandle is null.
Arguments:
MiniportAdapterHandle - pointer to the mini-port block;
NdisVcHandle - a pointer to the Vc block
GeneralStatus - the completion status of the request.
StatusBuffer - a buffer containing medium and status specific info
StatusBufferSize - the size of the buffer.
Return Value:
none
--*/
{
PNDIS_MINIPORT_BLOCK Miniport = (PNDIS_MINIPORT_BLOCK)MiniportAdapterHandle;
PNDIS_CO_VC_PTR_BLOCK VcPtr = (PNDIS_CO_VC_PTR_BLOCK)NdisVcHandle;
PNDIS_CO_VC_BLOCK VcBlock;
PNDIS_OPEN_BLOCK Open;
BOOLEAN fMediaConnectStateIndication = FALSE;
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_INFO,
("==>NdisMCoIndicateStatus\n"));
if ((GeneralStatus == NDIS_STATUS_MEDIA_CONNECT) || (GeneralStatus == NDIS_STATUS_MEDIA_DISCONNECT))
{
fMediaConnectStateIndication = TRUE;
}
do
{
NTSTATUS NtStatus;
PUNICODE_STRING InstanceName;
PWNODE_SINGLE_INSTANCE wnode;
ULONG DataBlockSize = 0;
ULONG BufSize;
PUCHAR ptmp;
PNDIS_GUID pNdisGuid;
//
// Get nice pointers to the instance names.
//
if (NULL != NdisVcHandle)
{
InstanceName = &VcPtr->VcInstanceName;
}
else
{
InstanceName = Miniport->pAdapterInstanceName;
}
//
// If there is no instance name then we can't indicate an event.
//
if (NULL == InstanceName)
{
break;
}
//
// Check to see if the status is enabled for WMI event indication.
//
NtStatus = ndisWmiGetGuid(&pNdisGuid, Miniport, NULL, GeneralStatus);
if ((!NT_SUCCESS(NtStatus)) ||
!NDIS_GUID_TEST_FLAG(pNdisGuid, fNDIS_GUID_EVENT_ENABLED))
{
break;
}
//
// If the data item is an array then we need to add in the number of
// elements.
//
if (NDIS_GUID_TEST_FLAG(pNdisGuid, fNDIS_GUID_ARRAY))
{
DataBlockSize = StatusBufferSize + sizeof(ULONG);
}
else
{
DataBlockSize = pNdisGuid->Size;
}
//
// in case of media connect/disconnect status indication, include the
// NIC's name in the WMI event
//
if (fMediaConnectStateIndication && (NULL == NdisVcHandle))
{
DataBlockSize += Miniport->MiniportName.Length + sizeof(WCHAR);
}
ndisSetupWmiNode(Miniport,
InstanceName,
DataBlockSize,
(PVOID)&pNdisGuid->Guid,
&wnode);
if (wnode != NULL)
{
//
// Save the number of elements in the first ULONG.
//
ptmp = (PUCHAR)wnode + wnode->DataBlockOffset;
//
// Copy in the data.
//
if (NDIS_GUID_TEST_FLAG(pNdisGuid, fNDIS_GUID_ARRAY))
{
//
// If the status is an array but there is no data then complete it with no
// data and a 0 length
//
if ((NULL == StatusBuffer) || (0 == StatusBufferSize))
{
*((PULONG)ptmp) = 0;
ptmp += sizeof(ULONG);
}
else
{
//
// Save the number of elements in the first ULONG.
//
*((PULONG)ptmp) = StatusBufferSize / pNdisGuid->Size;
//
// Copy the data after the number of elements.
//
NdisMoveMemory(ptmp + sizeof(ULONG), StatusBuffer, StatusBufferSize);
ptmp += sizeof(ULONG) + StatusBufferSize;
}
}
else
{
//
// Do we indicate any data up?
//
if (0 != pNdisGuid->Size)
{
//
// Copy the data into the buffer.
//
NdisMoveMemory(ptmp, StatusBuffer, pNdisGuid->Size);
ptmp += pNdisGuid->Size;
}
}
if (fMediaConnectStateIndication && (NULL == NdisVcHandle))
{
//
// for media connect/disconnect status,
// add the name of the adapter
//
RtlCopyMemory(ptmp,
Miniport->MiniportName.Buffer,
Miniport->MiniportName.Length);
}
//
// Indicate the event to WMI. WMI will take care of freeing
// the WMI struct back to pool.
//
NtStatus = IoWMIWriteEvent(wnode);
if (!NT_SUCCESS(NtStatus))
{
DBGPRINT(DBG_COMP_WMI, DBG_LEVEL_ERR,
(" ndisMCoIndicateStatus: Unable to indicate the WMI event.\n"));
FREE_POOL(wnode);
}
}
} while (FALSE);
switch (GeneralStatus)
{
case NDIS_STATUS_MEDIA_DISCONNECT:
MINIPORT_CLEAR_FLAG(Miniport, fMINIPORT_MEDIA_CONNECTED);
//
// miniport can do media sense and indicate that status to Ndis
//
MINIPORT_CLEAR_FLAG(Miniport, fMINIPORT_REQUIRES_MEDIA_POLLING);
MINIPORT_SET_FLAG(Miniport, fMINIPORT_SUPPORTS_MEDIA_SENSE);
//
// Is this a PM enabled miniport? And is dynamic power policy
// enabled for the miniport?
//
if (MINIPORT_PNP_TEST_FLAG(Miniport, fMINIPORT_DEVICE_POWER_ENABLE) &&
(Miniport->WakeUpEnable & NDIS_PNP_WAKE_UP_LINK_CHANGE) &&
(Miniport->MediaDisconnectTimeOut != (USHORT)(-1)))
{
//
// Are we already waiting for the disconnect timer to fire?
//
if (!MINIPORT_PNP_TEST_FLAG(Miniport, fMINIPORT_MEDIA_DISCONNECT_WAIT))
{
//
// Mark the miniport as disconnecting and fire off the
// timer.
//
MINIPORT_PNP_CLEAR_FLAG(Miniport, fMINIPORT_MEDIA_DISCONNECT_CANCELLED);
MINIPORT_PNP_SET_FLAG(Miniport, fMINIPORT_MEDIA_DISCONNECT_WAIT);
NdisSetTimer(&Miniport->MediaDisconnectTimer, Miniport->MediaDisconnectTimeOut * 1000);
}
}
break;
case NDIS_STATUS_MEDIA_CONNECT:
MINIPORT_SET_FLAG(Miniport, fMINIPORT_MEDIA_CONNECTED);
//
// miniport can do media sense and can indicate that status to Ndis. Do not poll
//
MINIPORT_CLEAR_FLAG(Miniport, fMINIPORT_REQUIRES_MEDIA_POLLING);
MINIPORT_SET_FLAG(Miniport, fMINIPORT_SUPPORTS_MEDIA_SENSE);
//
// if media disconnect timer was set, cancel the timer
//
if (MINIPORT_PNP_TEST_FLAG(Miniport, fMINIPORT_MEDIA_DISCONNECT_WAIT))
{
BOOLEAN fTimerCancelled;
//
// Clear the disconnect wait bit and cancel the timer.
// IF the timer routine hasn't grabed the lock then we are ok.
//
MINIPORT_PNP_CLEAR_FLAG(Miniport, fMINIPORT_MEDIA_DISCONNECT_WAIT);
MINIPORT_PNP_SET_FLAG(Miniport, fMINIPORT_MEDIA_DISCONNECT_CANCELLED);
NdisCancelTimer(&Miniport->MediaDisconnectTimer, &fTimerCancelled);
}
break;
}
if (VcPtr != NULL)
{
VcBlock = VcPtr->VcBlock;
//
// If this is a proxied VC, indicate to the proxy.
//
if (VcBlock->pProxyVcPtr)
{
Open = VcBlock->pProxyVcPtr->ClientOpen;
(Open->ProtocolHandle->ProtocolCharacteristics.CoStatusHandler)(Open->ProtocolBindingContext,
VcPtr->ClientContext,
GeneralStatus,
StatusBuffer,
StatusBufferSize);
}
//
// Indicate to the client.
//
if (VcBlock->pClientVcPtr)
{
Open = VcBlock->pClientVcPtr->ClientOpen;
(Open->ProtocolHandle->ProtocolCharacteristics.CoStatusHandler)(Open->ProtocolBindingContext,
VcPtr->ClientContext,
GeneralStatus,
StatusBuffer,
StatusBufferSize);
}
}
else if (Miniport->NullDB != NULL)
{
LOCK_STATE LockState;
//
// this must be a general status for all clients of this miniport
// since the Vc handle is null, so indicate this to all protocols.
//
READ_LOCK_FILTER(Miniport, Miniport->NullDB, &LockState);
for (Open = Miniport->OpenQueue;
Open != NULL;
Open = Open->MiniportNextOpen)
{
if (((Open->Flags & fMINIPORT_OPEN_CLOSING) == 0) &&
(Open->ProtocolHandle->ProtocolCharacteristics.CoStatusHandler != NULL))
{
(Open->ProtocolHandle->ProtocolCharacteristics.CoStatusHandler)(
Open->ProtocolBindingContext,
NULL,
GeneralStatus,
StatusBuffer,
StatusBufferSize);
}
}
READ_UNLOCK_FILTER(Miniport, Miniport->NullDB, &LockState);
}
DBGPRINT(DBG_COMP_CO, DBG_LEVEL_INFO,
("<==NdisMCoIndicateStatus\n"));
}
VOID
ndisDereferenceAfNotification(
IN PNDIS_OPEN_BLOCK Open
)
{
ULONG Ref;
KIRQL OldIrql;
// DbgPrint("==>ndisDereferenceAfNotification Open: %p\n", Open);
ACQUIRE_SPIN_LOCK(&Open->SpinLock, &OldIrql);
OPEN_DECREMENT_AF_NOTIFICATION(Open, Ref);
if ((Ref == 0) &&
(Open->AfNotifyCompleteEvent != NULL))
{
SET_EVENT(Open->AfNotifyCompleteEvent);
}
RELEASE_SPIN_LOCK(&Open->SpinLock, OldIrql);
// DbgPrint("<==ndisDereferenceAfNotification Open: %p, Ref %lx\n", Open, Ref);
}