Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1756 lines
49 KiB

/*++
Copyright (c) 1995-1996 Microsoft Corporation
Module Name:
pxdown.c
Abstract:
The module contains the calls to NDIS for the NDIS Proxy.
Author:
Richard Machin (RMachin)
Revision History:
Who When What
-------- -------- ----------------------------------------------
RMachin 10-3-96 created
TonyBe 02-21-99 re-work/re-write
Notes:
--*/
#include "precomp.h"
#include <atm.h>
#define MODULE_NUMBER MODULE_CO
#define _FILENUMBER 'OCXP'
VOID
PxCoBindAdapter(
OUT PNDIS_STATUS pStatus,
IN NDIS_HANDLE BindContext,
IN PNDIS_STRING DeviceName,
IN PVOID SystemSpecific1,
IN PVOID SystemSpecific2
)
/*++
Routine Description:
Entry point that gets called by NDIS when an adapter appears on the
system.
Arguments:
pStatus - place for our Return Value
BindContext - to be used if we call NdisCompleteBindAdapter; we don't
DeviceName - Name of the adapter to be bound to
SystemSpecific1 - Name of the protocol-specific entry in this adapter's
registry section
SystemSpecific2 - Not used
Return Value:
None. We set *pStatus to NDIS_STATUS_SUCCESS if everything goes off well,
otherwise an NDIS error status.
--*/
{
NDIS_STATUS OpenError;
UINT SelectedIndex;
PPX_ADAPTER pAdapter = NULL;
NDIS_MEDIUM Media[NdisMediumMax];
NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
ULONG InitStage = 0;
PXDEBUGP(PXD_LOUD, PXM_CO, ("PxCoBindAdapter: %Z\n", DeviceName));
//
// Wait for all calls to NdisRegisterProtocol to complete.
//
NdisWaitEvent(&DeviceExtension->NdisEvent, 0);
//
// Use a do..while..false loop and break on error.
// Cleanup is at the end of the loop.
//
do
{
//
// Check if this is a device we have already bound to.
//
if (PxIsAdapterAlreadyBound(DeviceName)) {
Status = NDIS_STATUS_NOT_ACCEPTED;
PXDEBUGP(PXD_WARNING, PXM_CO, ("PxCoBindAdapter: already bound to %Z\n", DeviceName));
break;
}
// pAdapter gets the devicname stuck on the end -- alloc space for it
//
pAdapter =
PxAllocateAdapter(DeviceName->MaximumLength);
if(pAdapter == (PPX_ADAPTER)NULL) {
Status = NDIS_STATUS_RESOURCES;
break;
}
//
// We have memory allocated we will need to free it!
//
InitStage++;
//
// Go to the end of the string and work back until we find
// the first "{". Now start parsing the string converting
// and copying from WCHAR to CHAR all digits until we hit
// the closing "}".
//
{
ULONG i;
for (i = DeviceName->Length/2; i > 0; i--) {
if (DeviceName->Buffer[i] == (WCHAR)L'{') {
break;
}
}
if (i != 0) {
NDIS_STRING Src;
RtlInitUnicodeString(&Src, &DeviceName->Buffer[i]);
RtlGUIDFromString(&Src, &pAdapter->Guid);
PXDEBUGP(PXD_INFO, PXM_CO, ("GUID %4.4x-%2.2x-%2.2x-%1.1x%1.1x-%1.1x%1.1x%1.1x%1.1x%1.1x%1.1x\n",
pAdapter->Guid.Data1, pAdapter->Guid.Data2, pAdapter->Guid.Data3,
pAdapter->Guid.Data4[0],pAdapter->Guid.Data4[1],pAdapter->Guid.Data4[2],
pAdapter->Guid.Data4[3],pAdapter->Guid.Data4[4],pAdapter->Guid.Data4[5],
pAdapter->Guid.Data4[6],pAdapter->Guid.Data4[7]));
}
}
PxInitBlockStruc(&pAdapter->BindEvent);
PxInitBlockStruc(&pAdapter->OpenEvent); //blocks thread
PxInitBlockStruc(&pAdapter->AfRegisterEvent);
pAdapter->AfRegisteringCount = 0;
//
// We can't get away with a single open for both
// client and call manager components without changing the wrapper.
// Bite the bullet and do two opens.
//
//
// Build the medium array
//
{
ULONG i;
for (i = 0; i < NdisMediumMax; i++) {
Media[i] = i;
}
}
//
// Open the adapter as a client!
//
NdisOpenAdapter(&Status,
&OpenError,
&pAdapter->ClBindingHandle,
&SelectedIndex,
Media,
NdisMediumMax,
DeviceExtension->PxProtocolHandle,
(NDIS_HANDLE)pAdapter,
DeviceName,
0,
NULL);
if(Status == NDIS_STATUS_PENDING) {
Status = PxBlock(&pAdapter->OpenEvent);
}
if(Status != NDIS_STATUS_SUCCESS) {
// We had some sort of error
PXDEBUGP(PXD_ERROR, PXM_CO, ("Cl OpenAdapter Failed %x\n", Status));
pAdapter->ClBindingHandle = NULL;
pAdapter->MediaType = -1;
break;
}
NdisAcquireSpinLock(&pAdapter->Lock);
REF_ADAPTER(pAdapter);
NdisReleaseSpinLock(&pAdapter->Lock);
//
// We have a ref on the adapter
//
InitStage++;
PxInitBlockStruc(&pAdapter->OpenEvent); //blocks thread
//
// Open the adapter as a call manager!
//
NdisOpenAdapter(&Status,
&OpenError,
&pAdapter->CmBindingHandle,
&SelectedIndex,
Media,
NdisMediumMax,
DeviceExtension->PxProtocolHandle,
(NDIS_HANDLE)&pAdapter->Sig,
DeviceName,
0,
NULL);
if(Status == NDIS_STATUS_PENDING) {
Status = PxBlock(&pAdapter->OpenEvent);
}
if(Status != NDIS_STATUS_SUCCESS) {
// We had some sort of error
pAdapter->CmBindingHandle = NULL;
pAdapter->MediaType = -1;
PXDEBUGP(PXD_ERROR, PXM_CO, ("CM OpenAdapter Failed %x\n", Status));
break;
}
NdisAcquireSpinLock(&pAdapter->Lock);
REF_ADAPTER(pAdapter);
//
// We have another ref on the adapter
//
InitStage++;
pAdapter->State = PX_ADAPTER_OPEN;
PXDEBUGP(PXD_INFO, PXM_CO, ("Bound to %Z, Adapter %p, NdisHandle %p\n",
DeviceName, pAdapter, pAdapter->ClBindingHandle));
//
// Set up media type in adapters
//
pAdapter->MediaType =
Media[SelectedIndex];
NdisReleaseSpinLock(&pAdapter->Lock);
//
// Set MediaTypeName/MediaSubtypeName for this adapter
//
{
NDIS_WAN_MEDIUM_SUBTYPE SubType = 0;
PX_REQUEST ProxyRequest;
PNDIS_REQUEST Request;
PWCHAR MediaTypes[] = {
L"GENERIC",
L"X25",
L"ISDN",
L"SERIAL",
L"FRAMERELAY",
L"ATM",
L"SONET",
L"SW56",
L"PPTP VPN",
L"L2TP VPN",
L"IRDA",
L"PARALLEL"};
switch (pAdapter->MediaType) {
case NdisMediumWan:
case NdisMediumCoWan:
PxInitBlockStruc (&ProxyRequest.Block);
Request = &ProxyRequest.NdisRequest;
Request->RequestType =
NdisRequestQueryInformation;
Request->DATA.QUERY_INFORMATION.Oid =
OID_WAN_MEDIUM_SUBTYPE;
Request->DATA.QUERY_INFORMATION.InformationBuffer =
&SubType;
Request->DATA.QUERY_INFORMATION.InformationBufferLength =
sizeof(NDIS_WAN_MEDIUM_SUBTYPE);
NdisRequest(&Status,
pAdapter->ClBindingHandle,
Request);
if (Status == NDIS_STATUS_PENDING) {
Status = PxBlock(&ProxyRequest.Block);
}
if (Status != NDIS_STATUS_SUCCESS) {
SubType = 0;
}
break;
case NdisMediumAtm:
SubType = NdisWanMediumAtm;
break;
default:
SubType = NdisWanMediumHub;
break;
}
if ((ULONG)SubType > 11) {
SubType = 0;
}
pAdapter->MediumSubType = SubType;
NdisMoveMemory((PUCHAR)(pAdapter->MediaName),
(PUCHAR)(MediaTypes[SubType]),
wcslen(MediaTypes[SubType]) * sizeof(WCHAR));
pAdapter->MediaNameLength =
wcslen(MediaTypes[SubType]) * sizeof(WCHAR);
Status = NDIS_STATUS_SUCCESS;
}
//
// Stick the binding name string in the adapter. We use in subsequent
// checks that we're not already bound.
//
pAdapter->DeviceName.MaximumLength = DeviceName->MaximumLength;
pAdapter->DeviceName.Length = DeviceName->Length;
pAdapter->DeviceName.Buffer =
(PWCHAR)((PUCHAR)pAdapter + sizeof(PX_ADAPTER));
NdisMoveMemory(pAdapter->DeviceName.Buffer,
DeviceName->Buffer,
DeviceName->Length);
} while(FALSE); //end of do loop
if (pAdapter != NULL) {
PxSignal(&pAdapter->BindEvent, NDIS_STATUS_SUCCESS);
}
if (Status != NDIS_STATUS_SUCCESS) {
//
// Set the state to closing since we're going to get rid
// of the adapter.
//
if ((InitStage >= 1) && (InitStage <= 3)) {
pAdapter->State = PX_ADAPTER_CLOSING;
}
switch (InitStage) {
case 3:
//
// We have applied 2 additional refs on the adapter
// we don't need to do the entire deref code for
// the first one! Fall through to the case below
// to run the full deref code for the second ref.
//
pAdapter->RefCount--;
case 2:
//
// We have added at least one ref that will need
// the entire deref package applied! Break out
// so the deref code can free the memory.
//
PxInitBlockStruc(&pAdapter->ClCloseEvent);
NdisCloseAdapter(&Status, pAdapter->ClBindingHandle);
if (Status == NDIS_STATUS_PENDING) {
Status = PxBlock(&pAdapter->ClCloseEvent);
}
DEREF_ADAPTER(pAdapter);
break;
case 1:
//
// We have added no addition refs so we can
// just free the memory here.
//
PxFreeAdapter(pAdapter);
break;
default:
break;
}
}
ASSERT(Status != NDIS_STATUS_PENDING);
*pStatus = Status;
}
VOID
PxCoOpenAdaperComplete(
NDIS_HANDLE BindingContext,
NDIS_STATUS Status,
NDIS_STATUS OpenErrorStatus
)
/*++
Routine Description
Our OpenAdapter completion . We signal whoever opened the
adapter.
Arguments
BindingContext - A pointer to a PX_ADAPTER structure.
Status - Status of open attempt.
OpenErrorStatus - Additional status information.
Return Value:
None
--*/
{
PPX_ADAPTER pAdapter;
BOOLEAN IsClient;
PXDEBUGP(PXD_LOUD, PXM_CO, ("Open Adapter Complete %p %x\n", BindingContext, Status));
AdapterFromBindContext(BindingContext, pAdapter, IsClient);
PxSignal(&pAdapter->OpenEvent, Status);
}
VOID
PxCoUnbindAdapter(
OUT PNDIS_STATUS pStatus,
IN NDIS_HANDLE ProtocolBindContext,
IN PNDIS_HANDLE UnbindContext
)
/*++
Routine Description:
entry point called by NDIS when we need to destroy an existing
adapter binding. This is called for the CM open.
By now, al clients will have been called. So any clients that opend this AF will have cleaned up their
connections with us, and we will have cleaned up with the Call Manager.
We should have CL and related CL adapter structures to get rid of. The Client Bind, SAPs, VCs and Party structures
should already have gone. If not, we throw them away anyway.
Close and clean up the adapters.
Arguments:
pStatus - where we return the status of this call
ProtocolBindContext - actually a pointer to the Adapter structure
UnbindContext - we should pass this value in NdisCompleteUnbindAdapter
Return Value:
None; *pStatus contains the result code.
--*/
{
PPX_ADAPTER pAdapter;
NDIS_STATUS Status;
BOOLEAN IsClient;
AdapterFromBindContext(ProtocolBindContext, pAdapter, IsClient);
PXDEBUGP(PXD_LOUD, PXM_CO, ("PxCoUnbindAdapter: pAdapter %p, UnbindContext %p\n",
pAdapter, UnbindContext));
PxBlock(&pAdapter->BindEvent);
NdisAcquireSpinLock(&pAdapter->Lock);
pAdapter->UnbindContext = UnbindContext;
pAdapter->State = PX_ADAPTER_CLOSING;
//
// Wait for any threads registering AFs to exit.
//
while (pAdapter->AfRegisteringCount != 0) {
NdisReleaseSpinLock(&pAdapter->Lock);
PxBlock(&pAdapter->AfRegisterEvent);
NdisAcquireSpinLock(&pAdapter->Lock);
}
ASSERT((pAdapter->Flags & PX_CMAF_REGISTERING) == 0);
//
// Do we have any af's opend on the underlying call manager
// and are there any saps registered on them?
//
while (!IsListEmpty(&pAdapter->ClAfList)) {
PPX_TAPI_PROVIDER TapiProvider;
PPX_CL_AF pClAf;
pClAf = (PPX_CL_AF)RemoveHeadList(&pAdapter->ClAfList);
InsertTailList(&pAdapter->ClAfClosingList, &pClAf->Linkage);
NdisReleaseSpinLock(&pAdapter->Lock);
NdisAcquireSpinLock(&pClAf->Lock);
pClAf->State = PX_AF_CLOSING;
TapiProvider = pClAf->TapiProvider;
NdisReleaseSpinLock(&pClAf->Lock);
//
// Take all tapi devices associated with
// this address family offline
//
if (TapiProvider != NULL) {
NdisAcquireSpinLock(&TapiProvider->Lock);
MarkProviderOffline(TapiProvider);
NdisReleaseSpinLock(&TapiProvider->Lock);
}
//
// Build list of Vc's that need attention
//
NdisAcquireSpinLock(&pClAf->Lock);
while (!IsListEmpty(&pClAf->VcList)) {
PLIST_ENTRY Entry;
PPX_VC pActiveVc;
Entry = RemoveHeadList(&pClAf->VcList);
InsertHeadList(&pClAf->VcClosingList, Entry);
pActiveVc = CONTAINING_RECORD(Entry, PX_VC, ClAfLinkage);
NdisReleaseSpinLock(&pClAf->Lock);
NdisAcquireSpinLock(&pActiveVc->Lock);
pActiveVc->CloseFlags |= PX_VC_UNBIND;
REF_VC(pActiveVc);
PxVcCleanup(pActiveVc, 0);
DEREF_VC_LOCKED(pActiveVc);
NdisAcquireSpinLock(&pClAf->Lock);
}
//
// get rid of any saps on this af
//
{
PLIST_ENTRY pe;
PPX_CL_SAP pClSap;
pe = pClAf->ClSapList.Flink;
pClSap =
CONTAINING_RECORD(pe, PX_CL_SAP, Linkage);
while ((PVOID)pClSap != (PVOID)&pClAf->ClSapList) {
if (InterlockedCompareExchange((PLONG)&pClSap->State,
PX_SAP_CLOSING,
PX_SAP_OPENED) == PX_SAP_OPENED) {
RemoveEntryList(&pClSap->Linkage);
InsertTailList(&pClAf->ClSapClosingList, &pClSap->Linkage);
NdisReleaseSpinLock(&pClAf->Lock);
ClearSapWithTapiLine(pClSap);
Status = NdisClDeregisterSap(pClSap->NdisSapHandle);
if (Status != NDIS_STATUS_PENDING) {
PxClDeregisterSapComplete(Status, pClSap);
}
NdisAcquireSpinLock(&pClAf->Lock);
pe = pClAf->ClSapList.Flink;
pClSap =
CONTAINING_RECORD(pe, PX_CL_SAP, Linkage);
} else {
pe = pClSap->Linkage.Flink;
pClSap =
CONTAINING_RECORD(pe, PX_CL_SAP, Linkage);
}
}
}
//
// deref for the ref applied when we opened this af
// and put it on the adapter's list
//
DEREF_CL_AF_LOCKED(pClAf);
NdisAcquireSpinLock(&pAdapter->Lock);
}
//
// Do any clients have our AF open?
//
while (!IsListEmpty(&pAdapter->CmAfList)) {
PPX_CM_AF pCmAf;
PX_REQUEST ProxyRequest;
PPX_REQUEST pProxyRequest = &ProxyRequest;
ULONG Info = 0;
PNDIS_REQUEST Request;
pCmAf = (PPX_CM_AF)RemoveHeadList(&pAdapter->CmAfList);
InsertTailList(&pAdapter->CmAfClosingList, &pCmAf->Linkage);
NdisReleaseSpinLock(&pAdapter->Lock);
NdisAcquireSpinLock(&pCmAf->Lock);
pCmAf->State = PX_AF_CLOSING;
REF_CM_AF(pCmAf);
NdisReleaseSpinLock(&pCmAf->Lock);
NdisZeroMemory(pProxyRequest, sizeof(PX_REQUEST));
Request = &pProxyRequest->NdisRequest;
Request->RequestType =
NdisRequestSetInformation;
Request->DATA.QUERY_INFORMATION.Oid =
OID_CO_AF_CLOSE;
Request->DATA.QUERY_INFORMATION.InformationBuffer =
&Info;
Request->DATA.QUERY_INFORMATION.InformationBufferLength =
sizeof(ULONG);
PxInitBlockStruc(&pProxyRequest->Block);
Status = NdisCoRequest(pAdapter->CmBindingHandle,
pCmAf->NdisAfHandle,
NULL,
NULL,
Request);
if (Status == NDIS_STATUS_PENDING) {
Status = PxBlock(&pProxyRequest->Block);
}
DEREF_CM_AF(pCmAf);
NdisAcquireSpinLock(&pAdapter->Lock);
}
NdisReleaseSpinLock(&pAdapter->Lock);
if (IsClient) {
PxInitBlockStruc(&pAdapter->ClCloseEvent);
NdisCloseAdapter(&Status, pAdapter->ClBindingHandle);
if (Status == NDIS_STATUS_PENDING) {
PxBlock(&pAdapter->ClCloseEvent);
}
PXDEBUGP(PXD_LOUD, PXM_CO, ("PxCoUnbindAdapter: CloseAdapter-Cl(%p)\n",
pAdapter));
} else {
PxInitBlockStruc(&pAdapter->CmCloseEvent);
NdisCloseAdapter(&Status, pAdapter->CmBindingHandle);
if (Status == NDIS_STATUS_PENDING) {
PxBlock(&pAdapter->CmCloseEvent);
}
PXDEBUGP(PXD_LOUD, PXM_CO, ("PxCoUnbindAdapter: CloseAdapter-Cm(%p)\n",
pAdapter));
}
DEREF_ADAPTER(pAdapter);
*pStatus = NDIS_STATUS_SUCCESS;
}
VOID
PxCoCloseAdaperComplete(
NDIS_HANDLE BindingContext,
NDIS_STATUS Status
)
/*++
Routine Description
Our CloseAdapter completion handler. We signal whoever closed the
adapter.
Arguments
BindingContext - A pointer to a PX_ADAPTER structure.
Status - Status of close attempt.
Return Value:
None
--*/
{
PPX_ADAPTER pAdapter;
BOOLEAN IsClient;
AdapterFromBindContext(BindingContext, pAdapter, IsClient);
PXDEBUGP(PXD_LOUD, PXM_CO, ("PxCoCloseAdapterComp: Adapter %p\n", pAdapter));
if (IsClient) {
PxSignal(&pAdapter->ClCloseEvent, Status);
} else {
PxSignal(&pAdapter->CmCloseEvent, Status);
}
}
VOID
PxCoRequestComplete(
IN NDIS_HANDLE BindingContext,
IN PNDIS_REQUEST NdisRequest,
IN NDIS_STATUS Status
)
{
PPX_ADAPTER pAdapter;
PPX_REQUEST pProxyRequest;
BOOLEAN IsClient;
AdapterFromBindContext(BindingContext, pAdapter, IsClient);
PXDEBUGP(PXD_INFO, PXM_CO, ("PxCoRequestComplete: Adapter %p\n", pAdapter));
pProxyRequest = CONTAINING_RECORD(NdisRequest, PX_REQUEST, NdisRequest);
PxSignal(&pProxyRequest->Block, Status);
}
VOID
PxCoNotifyAfRegistration(
IN NDIS_HANDLE BindingContext,
IN PCO_ADDRESS_FAMILY pFamily
)
/*++
Routine Description:
We get called here each time a call manager registers an address family.
This is where we open the address family, and register a proxy version if we
fancy it.
Arguments:
PxBindingContext - our pointer to an adapter
pFamily - The AF that's been registered
Return Value:
None
--*/
{
PPX_ADAPTER pAdapter;
NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
PPX_CL_AF pClAf;
BOOLEAN IsClient;
BOOLEAN CmAfRegistered;
BOOLEAN Found;
BOOLEAN RegisterInProgress = FALSE;
PXDEBUGP(PXD_LOUD, PXM_CO, ("PxNotifyAfRegistration\n"));
//
// First, check we're not being called because we registered ourselves...
//
if(pFamily->AddressFamily == CO_ADDRESS_FAMILY_TAPI) {
PXDEBUGP(PXD_LOUD, PXM_CO, ("PxNotifyAfRegistration: AF_TAPI registration -- do nothing\n"));
return;
}
//
// Get the adapter
//
AdapterFromBindContext(BindingContext, pAdapter, IsClient);
if (!IsClient) {
PXDEBUGP(PXD_LOUD, PXM_CO, ("PxNotifyAfRegistration: Called for CM adapter -- do nothing\n"));
return;
}
//
// We need to keep the adapter around so place a ref on it!
//
NdisAcquireSpinLock(&pAdapter->Lock);
REF_ADAPTER(pAdapter);
NdisReleaseSpinLock(&pAdapter->Lock);
//
// Wait until we are finished with the binding!
//
PxBlock(&pAdapter->BindEvent);
NdisAcquireSpinLock(&pAdapter->Lock);
do {
if ((pAdapter->State == PX_ADAPTER_CLOSING ||
pAdapter->State == PX_ADAPTER_CLOSED)) {
PXDEBUGP(PXD_FATAL, PXM_CO, ("PxNotifyAfRegistration: Adapter: %p state: %x is invalid\n",
pAdapter, pAdapter->State));
break;
}
//
// See if this adapter already has this type of af registered
//
Found = FALSE;
pClAf = (PPX_CL_AF)pAdapter->ClAfList.Flink;
while ((PVOID)pClAf != (PVOID)&pAdapter->ClAfList) {
if (pClAf->Af.AddressFamily == pFamily->AddressFamily) {
Found = TRUE;
break;
}
pClAf = (PPX_CL_AF)pClAf->Linkage.Flink;
}
if (Found) {
PXDEBUGP(PXD_FATAL, PXM_CO, ("PxNotifyAfRegistration: Af already registered Adapter: %p, Af: %x\n",
pAdapter, pClAf->Af));
break;
}
pClAf =
PxAllocateClAf(pFamily, pAdapter);
if(pClAf == NULL) {
Status = NDIS_STATUS_RESOURCES;
PXDEBUGP(PXD_WARNING, PXM_CO, ("NotifyAfRegistration: failed to allocate a PX_CL_AF\n"));
break;
}
//
// Make sure that we don't let an Unbind thread invalidate our Binding
// handle.
//
if (pAdapter->AfRegisteringCount == 0) {
PxInitBlockStruc(&pAdapter->AfRegisterEvent);
}
pAdapter->AfRegisteringCount++;
RegisterInProgress = TRUE;
NdisReleaseSpinLock(&pAdapter->Lock);
//
// Open the address family
//
{
NDIS_CLIENT_CHARACTERISTICS ClChars;
PNDIS_CLIENT_CHARACTERISTICS pClChars = &ClChars;
//
// Do the client open on the address family
//
NdisZeroMemory (pClChars, sizeof(NDIS_CLIENT_CHARACTERISTICS));
pClChars->MajorVersion = NDIS_MAJOR_VERSION;
pClChars->MinorVersion = NDIS_MINOR_VERSION;
pClChars->Reserved = 0;
pClChars->ClCreateVcHandler = PxClCreateVc;
pClChars->ClDeleteVcHandler = PxClDeleteVc;
pClChars->ClOpenAfCompleteHandler = PxClOpenAfComplete;
pClChars->ClCloseAfCompleteHandler = PxClCloseAfComplete;
pClChars->ClRegisterSapCompleteHandler = PxClRegisterSapComplete;
pClChars->ClDeregisterSapCompleteHandler = PxClDeregisterSapComplete;
pClChars->ClMakeCallCompleteHandler = PxClMakeCallComplete;
pClChars->ClModifyCallQoSCompleteHandler = PxClModifyCallQosComplete;
pClChars->ClCloseCallCompleteHandler = PxClCloseCallComplete;
pClChars->ClAddPartyCompleteHandler = PxClAddPartyComplete;
pClChars->ClDropPartyCompleteHandler = PxClDropPartyComplete;
pClChars->ClIncomingCallHandler = PxClIncomingCall;
pClChars->ClIncomingCallQoSChangeHandler = PxClIncomingCallQosChange;
pClChars->ClIncomingCloseCallHandler = PxClIncomingCloseCall;
pClChars->ClIncomingDropPartyHandler = PxClIncomingDropParty;
pClChars->ClCallConnectedHandler = PxClCallConnected;
pClChars->ClRequestHandler = PxClRequest;
pClChars->ClRequestCompleteHandler = PxClRequestComplete;
PxInitBlockStruc(&pClAf->Block);
Status = NdisClOpenAddressFamily(pAdapter->ClBindingHandle,
pFamily,
(NDIS_HANDLE)pClAf,
pClChars,
sizeof(NDIS_CLIENT_CHARACTERISTICS),
&pClAf->NdisAfHandle);
if(Status == NDIS_STATUS_PENDING) {
Status = PxBlock(&pClAf->Block);
}
}
NdisAcquireSpinLock(&pAdapter->Lock);
if (Status != NDIS_STATUS_SUCCESS) {
PXDEBUGP(PXD_WARNING, PXM_CO, ("NotifyAfRegistration: Error opening Af %x, Adapter %p, Error %x!!!\n",
pFamily->AddressFamily, pAdapter, Status));
PxFreeClAf(pClAf);
break;
}
NdisAcquireSpinLock(&pClAf->Lock);
pClAf->State = PX_AF_OPENED;
NdisReleaseSpinLock(&pClAf->Lock);
//
// Have only need to register one instance of CO_ADDRESS_FAMILY_TAPI
// for each adapter.
//
InsertTailList(&pAdapter->ClAfList, &pClAf->Linkage);
if (pAdapter->Flags & PX_CMAF_REGISTERED) {
CmAfRegistered = TRUE;
} else {
CmAfRegistered = FALSE;
pAdapter->Flags |= PX_CMAF_REGISTERING;
}
REF_ADAPTER(pAdapter);
NdisReleaseSpinLock(&pAdapter->Lock);
if (!CmAfRegistered) {
CO_ADDRESS_FAMILY PxFamily;
NDIS_CALL_MANAGER_CHARACTERISTICS CmChars;
PNDIS_CALL_MANAGER_CHARACTERISTICS pCmChars = &CmChars;
//
// Now register the Proxied address family. First, get the CM adapter handle.
//
NdisZeroMemory(pCmChars, sizeof(CmChars));
pCmChars->MajorVersion = NDIS_MAJOR_VERSION;
pCmChars->MinorVersion = NDIS_MINOR_VERSION;
pCmChars->Reserved = 0;
pCmChars->CmCreateVcHandler = PxCmCreateVc;
pCmChars->CmDeleteVcHandler = PxCmDeleteVc;
pCmChars->CmOpenAfHandler = PxCmOpenAf;
pCmChars->CmCloseAfHandler = PxCmCloseAf;
pCmChars->CmRegisterSapHandler = PxCmRegisterSap;
pCmChars->CmDeregisterSapHandler = PxCmDeRegisterSap;
pCmChars->CmMakeCallHandler = PxCmMakeCall;
pCmChars->CmCloseCallHandler = PxCmCloseCall;
pCmChars->CmIncomingCallCompleteHandler = PxCmIncomingCallComplete;
pCmChars->CmAddPartyHandler = PxCmAddParty;
pCmChars->CmDropPartyHandler = PxCmDropParty;
pCmChars->CmActivateVcCompleteHandler = PxCmActivateVcComplete;
pCmChars->CmDeactivateVcCompleteHandler = PxCmDeActivateVcComplete;
pCmChars->CmModifyCallQoSHandler = PxCmModifyCallQos;
pCmChars->CmRequestHandler = PxCmRequest;
pCmChars->CmRequestCompleteHandler = PxCmRequestComplete;
NdisMoveMemory(&PxFamily, pFamily, sizeof(PxFamily));
PxFamily.AddressFamily = CO_ADDRESS_FAMILY_TAPI;
PXDEBUGP(PXD_LOUD, PXM_CO, ("NotifyAfRegistration: NdisCmRegisterAddressFamily\n"));
Status =
NdisCmRegisterAddressFamily(pAdapter->CmBindingHandle,
&PxFamily,
pCmChars,
sizeof(CmChars));
NdisAcquireSpinLock(&pAdapter->Lock);
pAdapter->Flags &= ~PX_CMAF_REGISTERING;
if(Status != NDIS_STATUS_SUCCESS) {
//
// Close the CM af again
//
PXDEBUGP(PXD_FATAL, PXM_CO, ("NotifyAfRegistration: NdisCmRegisterAddressFamily on Bind %p bad sts = %x\n", pAdapter->CmBindingHandle, Status));
RemoveEntryList(&pClAf->Linkage);
InsertTailList(&pAdapter->ClAfClosingList, &pClAf->Linkage);
NdisReleaseSpinLock(&pAdapter->Lock);
NdisAcquireSpinLock(&pClAf->Lock);
pClAf->State = PX_AF_CLOSING;
NdisReleaseSpinLock(&pClAf->Lock);
Status = NdisClCloseAddressFamily (pClAf->NdisAfHandle);
if (Status != NDIS_STATUS_PENDING) {
PxClCloseAfComplete(Status, pClAf);
}
NdisAcquireSpinLock(&pAdapter->Lock);
break;
} else {
pAdapter->Flags |= PX_CMAF_REGISTERED;
NdisReleaseSpinLock(&pAdapter->Lock);
}
}
NdisAcquireSpinLock(&pClAf->Lock);
REF_CL_AF(pClAf);
NdisReleaseSpinLock(&pClAf->Lock);
Status =
AllocateTapiResources(pAdapter, pClAf);
DEREF_CL_AF(pClAf);
NdisAcquireSpinLock(&pAdapter->Lock);
} while (FALSE);
if (RegisterInProgress) {
pAdapter->AfRegisteringCount--;
if (pAdapter->AfRegisteringCount == 0) {
PxSignal(&pAdapter->AfRegisterEvent, NDIS_STATUS_SUCCESS);
}
}
DEREF_ADAPTER_LOCKED(pAdapter);
}
VOID
PxCoUnloadProtocol(
VOID
)
/*++
Routine Description:
Unload the entire protocol (CM and CL).
Arguments:
None
Return Value:
None
--*/
{
NDIS_STATUS Status;
NdisDeregisterProtocol(&Status, DeviceExtension->PxProtocolHandle);
#if DBG
NdisAcquireSpinLock(&(DeviceExtension->Lock));
ASSERT(IsListEmpty(&DeviceExtension->AdapterList));
NdisReleaseSpinLock(&DeviceExtension->Lock);
#endif
}
NDIS_STATUS
PxCoPnPEvent(
IN NDIS_HANDLE BindingContext,
IN PNET_PNP_EVENT pNetPnPEvent
)
{
NDIS_STATUS Status;
PPX_ADAPTER pAdapter=NULL;
BOOLEAN IsClient;
if (BindingContext != NULL) {
AdapterFromBindContext(BindingContext, pAdapter, IsClient);
}
switch (pNetPnPEvent->NetEvent){
case NetEventSetPower:
Status = PxPnPSetPower(pAdapter, pNetPnPEvent);
break;
case NetEventQueryPower:
Status = PxPnPQueryPower(pAdapter, pNetPnPEvent);
break;
case NetEventQueryRemoveDevice:
Status = PxPnPQueryRemove(pAdapter, pNetPnPEvent);
break;
case NetEventCancelRemoveDevice:
Status = PxPnPCancelRemove(pAdapter, pNetPnPEvent);
break;
case NetEventReconfigure:
Status = PxPnPReconfigure(pAdapter, pNetPnPEvent);
break;
case NetEventBindList:
Status = NDIS_STATUS_NOT_SUPPORTED;
break;
default:
Status = NDIS_STATUS_NOT_SUPPORTED;
break;
}
PXDEBUGP(PXD_INFO, PXM_CO, ("PnPEvent(CM): Event %d, returning %x\n",
pNetPnPEvent->NetEvent, Status));
return (Status);
}
NDIS_STATUS
PxPnPSetPower(
IN PPX_ADAPTER pAdapter,
IN PNET_PNP_EVENT pNetPnPEvent
)
{
PNET_DEVICE_POWER_STATE pPowerState;
NDIS_STATUS Status;
pPowerState = (PNET_DEVICE_POWER_STATE)pNetPnPEvent->Buffer;
switch (*pPowerState) {
case NetDeviceStateD0:
Status = NDIS_STATUS_SUCCESS;
break;
default:
//
// We can't suspend, so we ask NDIS to unbind us
// by returning this status:
//
Status = NDIS_STATUS_NOT_SUPPORTED;
break;
}
return (Status);
}
NDIS_STATUS
PxPnPQueryPower(
IN PPX_ADAPTER pAdapter,
IN PNET_PNP_EVENT pNetPnPEvent
)
{
return (NDIS_STATUS_SUCCESS);
}
NDIS_STATUS
PxPnPQueryRemove(
IN PPX_ADAPTER pAdapter,
IN PNET_PNP_EVENT pNetPnPEvent
)
{
return (NDIS_STATUS_SUCCESS);
}
NDIS_STATUS
PxPnPCancelRemove(
IN PPX_ADAPTER pAdapter,
IN PNET_PNP_EVENT pNetPnPEvent
)
{
return (NDIS_STATUS_SUCCESS);
}
NDIS_STATUS
PxPnPReconfigure(
IN PPX_ADAPTER pAdapter OPTIONAL,
IN PNET_PNP_EVENT pNetPnPEvent
)
{
return (NDIS_STATUS_NOT_SUPPORTED);
}
VOID
PxCoSendComplete(
IN NDIS_HANDLE ProtocolBindingContext,
IN PNDIS_PACKET Packet,
IN NDIS_STATUS Status
)
{
PXDEBUGP(PXD_INFO, PXM_CO, ("PxCoSendComplete\n"));
}
VOID
PxCoTransferDataComplete(
IN NDIS_HANDLE ProtocolBindingContext,
IN PNDIS_PACKET Packet,
IN NDIS_STATUS Status,
IN UINT BytesTransferred
)
{
PXDEBUGP(PXD_INFO, PXM_CO, ("PxCoTransferDataComplete\n"));
}
VOID
PxCoResetComplete(
IN NDIS_HANDLE ProtocolBindingContext,
IN NDIS_STATUS Status
)
{
PXDEBUGP(PXD_INFO, PXM_CO, ("PxCoResetComplete\n"));
}
VOID
PxCoStatusComplete(
IN NDIS_HANDLE ProtocolBindingContext
)
{
PXDEBUGP(PXD_INFO, PXM_CO, ("PxCoStatusComplete\n"));
}
VOID
PxCoReceiveComplete(
IN NDIS_HANDLE ProtocolBindingContext
)
{
PXDEBUGP(PXD_INFO, PXM_CO, ("PxCoReceiveComplete\n"));
}
VOID
PxCoStatus(
IN NDIS_HANDLE ProtocolBindingContext,
IN NDIS_HANDLE ProtocolVcContext OPTIONAL,
IN NDIS_STATUS GeneralStatus,
IN PVOID StatusBuffer,
IN UINT StatusBufferSize
)
{
PPX_VC pVc = NULL;
PXDEBUGP(PXD_INFO, PXM_CO,
("PxCoStatus : %p, Status %x\n",
ProtocolBindingContext,GeneralStatus));
GetVcFromCtx(ProtocolVcContext, &pVc);
if (pVc == NULL) {
return;
}
switch (GeneralStatus) {
case NDIS_STATUS_TAPI_RECV_DIGIT:
PxHandleReceivedDigit(pVc,
StatusBuffer,
StatusBufferSize);
break;
case NDIS_STATUS_WAN_CO_LINKPARAMS:
PxHandleWanLinkParams(pVc,
StatusBuffer,
StatusBufferSize);
break;
default:
break;
}
DEREF_VC(pVc);
}
UINT
PxCoReceivePacket(
IN NDIS_HANDLE ProtocolBindingContext,
IN NDIS_HANDLE ProtocolVcContext,
IN PNDIS_PACKET pNdisPacket
)
{
PXDEBUGP(PXD_INFO, PXM_CO, ("CoReceivePacket\n"));
NDIS_SET_PACKET_STATUS(pNdisPacket, NDIS_STATUS_SUCCESS);
return 0;
}
ULONG
PxGetMillisecondTickCount()
{
LARGE_INTEGER TickCount, Milliseconds;
ULONG TimeIncrement;
//
// Return the current "tick count" (number of milliseconds since Windows started).
// TAPI wants this in its DTMF notification messages. We have to do a little math here because
// the kernel query tick count function returns the number of timer ticks, which is some
// multiple of 100ns.
//
KeQueryTickCount(&TickCount);
TimeIncrement = KeQueryTimeIncrement();
Milliseconds.QuadPart = (TickCount.QuadPart / 10000) * TimeIncrement;
//
// This might seem a bit sketchy but TAPI only gives us a 32-bit wide place to store the tick
// count. According to the SDK, TAPI apps are supposed to be aware that this will roll over every
// 49.7 days. (...it's amusing to think of TAPI staying up for 49.7 days, but I digress...)
//
return (Milliseconds.LowPart);
}
// PxTerminateDigitDetection
//
// Must be called with the VC lock held.
VOID
PxTerminateDigitDetection(
IN PPX_VC pVc,
IN PNDISTAPI_REQUEST pNdisTapiRequest,
IN ULONG ulReason
)
{
PNDIS_TAPI_GATHER_DIGITS pNdisTapiGatherDigits;
PIRP Irp;
PWCHAR pDigitsBuffer;
NDIS_STATUS Status;
PXDEBUGP(PXD_LOUD, PXM_CO, ("PxTerminateDigitDetection: Enter\n"));
pNdisTapiGatherDigits =
(PNDIS_TAPI_GATHER_DIGITS)pNdisTapiRequest->Data;
Irp = pNdisTapiRequest->Irp;
pNdisTapiGatherDigits->ulTickCount = PxGetMillisecondTickCount();
pNdisTapiGatherDigits->ulTerminationReason = ulReason;
//
// Put the null character at the end of the buffer, and send it on it's way.
//
pDigitsBuffer =
(PWCHAR) (((PUCHAR)pNdisTapiGatherDigits) + pNdisTapiGatherDigits->ulDigitsBufferOffset);
pDigitsBuffer[pNdisTapiGatherDigits->ulNumDigitsRead] =
UNICODE_NULL;
Irp->IoStatus.Status = NDIS_STATUS_SUCCESS;
Irp->IoStatus.Information =
sizeof(NDISTAPI_REQUEST) + (pNdisTapiRequest->ulDataSize - 1);
IoCompleteRequest(Irp, IO_NO_INCREMENT);
//
// Note: we call release here even though we didn't acquire in this function.
// This is OK since this function MUST be called with the lock held.
//
NdisReleaseSpinLock(&pVc->Lock);
Status = PxStopDigitReporting(pVc);
NdisAcquireSpinLock(&pVc->Lock); // See comment above.
// FIXME: If this failed (i.e. Status is some error value,
// there's not much we can do about it here).
if (Status != NDIS_STATUS_SUCCESS) {
PXDEBUGP(PXD_ERROR, PXM_CO,
("PxTerminateDigitDetection: PxStopDigitReporting returned Status 0x%x\n",
Status));
ASSERT(FALSE);
}
PXDEBUGP(PXD_LOUD, PXM_CO, ("PxTerminateDigitDetection: Exit\n"));
}
VOID
PxDigitTimerRoutine(
IN PVOID SystemSpecific1,
IN PVOID FunctionContext,
IN PVOID SystemSpecific2,
IN PVOID SystemSpecific3
)
{
PPX_VC pVc = (PPX_VC) FunctionContext;
PNDISTAPI_REQUEST pNdisTapiRequest;
PIRP Irp;
PNDIS_TAPI_GATHER_DIGITS pNdisTapiGatherDigits;
ULONG ulReason;
NdisAcquireSpinLock(&pVc->Lock);
do {
if (pVc->PendingGatherDigits == NULL) {
//
// The request either completed, or a digit is being processed right now, so this timeout is
// meaningless.
//
break;
}
pNdisTapiRequest = pVc->PendingGatherDigits;
Irp = pNdisTapiRequest->Irp;
if (!IoSetCancelRoutine(Irp, NULL))
{
//
// The cancel routine is running. Let it handle the IRP.
//
break;
}
pVc->PendingGatherDigits = NULL;
ASSERT(pNdisTapiRequest == Irp->AssociatedIrp.SystemBuffer);
pNdisTapiGatherDigits =
(PNDIS_TAPI_GATHER_DIGITS)pNdisTapiRequest->Data;
if (pNdisTapiGatherDigits->ulNumDigitsRead == 0) {
//
// We timed out before detecting the first digit.
//
ulReason = LINEGATHERTERM_FIRSTTIMEOUT;
} else {
ulReason = LINEGATHERTERM_INTERTIMEOUT;
}
PxTerminateDigitDetection(pVc, pNdisTapiRequest, ulReason);
} while (FALSE);
NdisReleaseSpinLock(&pVc->Lock);
DEREF_VC(pVc);
}
// ++ DTMFDigitToOrdinal
//
// Turn a DTMF digit into a number between 0 and 15. The digits are assigned
// numbers in the following order: '0' - '9', 'A' - 'D', '*', '#'.
//
// This is horribly ugly now, but we'll optimize later.
//
// Arguments:
// wcDigit - The digit, expressed as a UNICODE character.
//
// Return value:
// A number between 0 and 15, or 16 if the digit passed in was not a valid
// DTMF digit.
//
ULONG
DTMFDigitToOrdinal(
WCHAR wcDigit
)
{
ULONG ulOrdinal;
switch (wcDigit) {
case L'0':
ulOrdinal = 0;
break;
case L'1':
ulOrdinal = 1;
break;
case L'2':
ulOrdinal = 2;
break;
case L'3':
ulOrdinal = 3;
break;
case L'4':
ulOrdinal = 4;
break;
case L'5':
ulOrdinal = 5;
break;
case L'6':
ulOrdinal = 6;
break;
case L'7':
ulOrdinal = 7;
break;
case L'8':
ulOrdinal = 8;
break;
case L'9':
ulOrdinal = 9;
break;
case L'A':
ulOrdinal = 10;
break;
case L'B':
ulOrdinal = 11;
break;
case L'C':
ulOrdinal = 12;
break;
case L'D':
ulOrdinal = 13;
break;
case L'*':
ulOrdinal = 14;
break;
case L'#':
ulOrdinal = 15;
break;
default:
ulOrdinal = 16;
break;
};
return ulOrdinal;
}
NDIS_STATUS
PxStopDigitReporting(
PPX_VC pVc
)
{
PX_REQUEST ProxyRequest;
PNDIS_REQUEST NdisRequest;
ULONG Unused = 0;
NDIS_STATUS Status;
//
// Fill out our request structure to tell the miniport to stop reporting
// digits.
//
NdisZeroMemory(&ProxyRequest, sizeof(ProxyRequest));
PxInitBlockStruc(&ProxyRequest.Block);
NdisRequest = &ProxyRequest.NdisRequest;
NdisRequest->RequestType = NdisRequestSetInformation;
NdisRequest->DATA.SET_INFORMATION.Oid = OID_CO_TAPI_DONT_REPORT_DIGITS;
NdisRequest->DATA.SET_INFORMATION.InformationBuffer = (PVOID)&Unused;
NdisRequest->DATA.SET_INFORMATION.InformationBufferLength = sizeof(Unused);
Status = NdisCoRequest(pVc->Adapter->ClBindingHandle,
pVc->ClAf->NdisAfHandle,
pVc->ClVcHandle,
NULL,
NdisRequest);
if (Status == NDIS_STATUS_PENDING) {
Status = PxBlock(&ProxyRequest.Block);
}
return Status;
}
VOID
PxHandleReceivedDigit(
IN PPX_VC pVc,
IN PVOID Buffer,
IN UINT BufferSize
)
{
PNDIS_TAPI_GATHER_DIGITS pNdisTapiGatherDigits;
PWCHAR pDigitsBuffer;
PXDEBUGP(PXD_LOUD, PXM_CO, ("PxHandleReceiveDigit: Enter\n"));
do {
PLIST_ENTRY Entry;
PIRP Irp;
PNDISTAPI_REQUEST pNdisTapiRequest;
ULONG ulDigitOrdinal;
BOOLEAN bTimerCancelled = FALSE;
//
// We need at least one WCHAR in the buffer.
//
if (BufferSize < sizeof(WCHAR)) {
//
// No useful data, get out
//
break;
}
NdisAcquireSpinLock(&pVc->Lock);
if (pVc->ulMonitorDigitsModes != 0) {
NDIS_TAPI_EVENT Event;
PPX_TAPI_LINE pTapiLine;
//
// We're monitoring (not gathering) digits, so send up a message right away.
//
pTapiLine = pVc->TapiLine;
Event.htLine = pTapiLine->htLine;
Event.htCall = pVc->htCall;
Event.ulMsg = LINE_MONITORDIGITS;
Event.ulParam1 = (ULONG_PTR) (* ((PWCHAR)Buffer));
Event.ulParam2 = (ULONG_PTR) (pVc->ulMonitorDigitsModes); // ToDo - There could be > 1 mode here - have to get this from the driver.
Event.ulParam3 = (ULONG_PTR) (PxGetMillisecondTickCount());
NdisReleaseSpinLock(&pVc->Lock);
PxIndicateStatus((PVOID) &Event, sizeof(NDIS_TAPI_EVENT));
break;
}
if (pVc->PendingGatherDigits == NULL) {
//
// No Irp to complete, get out
//
NdisReleaseSpinLock(&pVc->Lock);
break;
}
NdisCancelTimer(&pVc->DigitTimer, &bTimerCancelled); // deref of VC is at the end - makes locking code a bit cleaner.
pNdisTapiRequest = pVc->PendingGatherDigits;
Irp = pNdisTapiRequest->Irp;
if (!IoSetCancelRoutine(Irp, NULL))
{
//
// The cancel routine is running. Let it handle the IRP.
//
NdisReleaseSpinLock(&pVc->Lock);
break;
}
pVc->PendingGatherDigits = NULL;
ASSERT(pNdisTapiRequest == Irp->AssociatedIrp.SystemBuffer);
pNdisTapiGatherDigits =
(PNDIS_TAPI_GATHER_DIGITS)pNdisTapiRequest->Data;
//
// Store the current digit, and increment the count.
//
pDigitsBuffer =
(PWCHAR) (((PUCHAR)pNdisTapiGatherDigits) + pNdisTapiGatherDigits->ulDigitsBufferOffset);
pDigitsBuffer[pNdisTapiGatherDigits->ulNumDigitsRead] =
*((PWCHAR)Buffer);
pNdisTapiGatherDigits->ulNumDigitsRead++;
//
// Check if we read a termination digit.
//
ulDigitOrdinal = DTMFDigitToOrdinal(*((PWCHAR)Buffer));
if (Irp->Cancel) {
PxTerminateDigitDetection(pVc, pNdisTapiRequest, LINEGATHERTERM_CANCEL);
} else if (pNdisTapiGatherDigits->ulTerminationDigitsMask & (1 << ulDigitOrdinal)) {
PxTerminateDigitDetection(pVc, pNdisTapiRequest, LINEGATHERTERM_TERMDIGIT);
} else if (pNdisTapiGatherDigits->ulNumDigitsRead == pNdisTapiGatherDigits->ulNumDigitsNeeded) {
PxTerminateDigitDetection(pVc, pNdisTapiRequest, LINEGATHERTERM_BUFFERFULL);
} else {
pVc->PendingGatherDigits = pNdisTapiRequest;
if (pNdisTapiGatherDigits->ulInterDigitTimeout) {
REF_VC(pVc);
NdisSetTimer(&pVc->DigitTimer, pNdisTapiGatherDigits->ulInterDigitTimeout);
}
IoSetCancelRoutine(Irp, PxCancelSetQuery);
}
NdisReleaseSpinLock(&pVc->Lock);
if (bTimerCancelled) {
//
// Do this only if the timer was actually cancelled. If it wasn't, then
// either it wasn't set and the VC wouldn't have been ref'd in the first
// place, or it fired, in which case the timer routine would have deref'd
// it already.
//
DEREF_VC(pVc);
}
} while (FALSE);
PXDEBUGP(PXD_LOUD, PXM_CO, ("PxHandleReceiveDigit: Exit\n"));
}
VOID
PxHandleWanLinkParams(
IN PPX_VC pVc,
IN PVOID Buffer,
IN UINT BufferSize
)
{
}