Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

2528 lines
52 KiB

/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
D:\nt\private\ntos\tdi\rawwan\core\ndisconn.c
Abstract:
NDIS Entry points and support routines for Connection setup and
release.
Revision History:
Who When What
-------- -------- ----------------------------------------------
arvindm 05-06-97 Created
Notes:
Code under ifndef NO_POST_DISCON: Nov 17, 98
Added code to send a TDI DisconInd to AFD before completing
a TDI Disconnect Request that AFD had sent to us. Without this,
if an app calls shutdown(SD_SEND) -> TDI Disconnect Request,
a subsequent call by the app to recv() blocks forever, because
AFD expects the TDI transport to send up a TDI DisconInd!
--*/
#include <precomp.h>
#define _FILENUMBER 'NCDN'
NDIS_STATUS
RWanNdisCreateVc(
IN NDIS_HANDLE ProtocolAfContext,
IN NDIS_HANDLE NdisVcHandle,
OUT PNDIS_HANDLE pProtocolVcContext
)
/*++
Routine Description:
This is the NDIS entry point for creating a new endpoint (VC).
We allocate a new NDIS_VC structure and return a pointer to it
as our context for the VC.
Arguments:
ProtocolAfContext - Pointer to our NDIS AF block
NdisVcHandle - Handle for the newly created VC
pProtocolVcContext - Place where we return our context for the VC
Return Value:
NDIS_STATUS_SUCCESS if we could allocate a VC,
NDIS_STATUS_RESOURCES otherwise.
--*/
{
PRWAN_NDIS_AF pAf;
PRWAN_NDIS_VC pVc;
NDIS_STATUS Status;
pAf = (PRWAN_NDIS_AF)ProtocolAfContext;
RWAN_STRUCT_ASSERT(pAf, naf);
do
{
pVc = RWanAllocateVc(pAf, FALSE);
if (pVc == NULL_PRWAN_NDIS_VC)
{
Status = NDIS_STATUS_RESOURCES;
break;
}
pVc->NdisVcHandle = NdisVcHandle;
RWANDEBUGP(DL_EXTRA_LOUD, DC_CONNECT,
("CreateVc: pVc x%x, pAf x%x\n", pVc, pAf));
Status = NDIS_STATUS_SUCCESS;
break;
}
while (FALSE);
*pProtocolVcContext = (NDIS_HANDLE)pVc;
return (Status);
}
NDIS_STATUS
RWanNdisDeleteVc(
IN NDIS_HANDLE ProtocolVcContext
)
/*++
Routine Description:
This entry point is called by NDIS to delete a VC context
used for an incoming call. At this time, there should be
no call on the VC. All we need to do is unlink the VC from
the list it belongs to, and free it.
Arguments:
ProtocolVcContext - Points to our VC context.
Return Value:
NDIS_STATUS_SUCCESS always.
--*/
{
PRWAN_NDIS_VC pVc;
pVc = (PRWAN_NDIS_VC)ProtocolVcContext;
RWAN_STRUCT_ASSERT(pVc, nvc);
RWAN_ASSERT(pVc->pConnObject == NULL_PRWAN_TDI_CONNECTION);
RWANDEBUGP(DL_EXTRA_LOUD, DC_DISCON,
("DeleteVc: pVc x%x, pAf x%x\n", pVc, pVc->pNdisAf));
//
// Unlink the VC from the list of VCs on the AF block
//
RWanUnlinkVcFromAf(pVc);
RWanFreeVc(pVc);
return (NDIS_STATUS_SUCCESS);
}
VOID
RWanNdisMakeCallComplete(
IN NDIS_STATUS Status,
IN NDIS_HANDLE ProtocolVcContext,
IN NDIS_HANDLE NdisPartyHandle OPTIONAL,
IN PCO_CALL_PARAMETERS pCallParameters
)
/*++
Routine Description:
This is the NDIS entry point that is called when a previous
call we made to NdisClMakeCall has completed.
We locate the TDI Connection Object for this call. If the
user hasn't aborted the Connect/JoinLeaf, we complete the
pended request. Otherwise, we initiate a CloseCall.
This primitive can happen only when the endpoint is in
the "Out call initiated" state.
Arguments:
Status - Final status of the MakeCall
ProtocolVcContext - Actually a pointer to our NDIS VC structure
NdisPartyHandle - If this is a PMP call, this is the handle to the
first party
pCallParameters - Final call parameters.
Return Value:
None
--*/
{
PRWAN_TDI_CONNECTION pRootConnObject;
PRWAN_TDI_CONNECTION pConnObject;
PRWAN_TDI_ADDRESS pAddrObject;
NDIS_HANDLE NdisVcHandle;
TDI_STATUS TdiStatus;
PRWAN_CONN_REQUEST pConnReq; // Saved context about the TdiConnect
BOOLEAN bIsConnClosing; // Have we seen a TdiCloseConnection?
PRWAN_NDIS_AF pAf;
PRWAN_NDIS_VC pVc;
PRWAN_NDIS_PARTY pParty;
PCO_CALL_PARAMETERS pOriginalParams;// What we used in the MakeCall
RWAN_HANDLE AfSpConnContext;
#if DBG
RWAN_IRQL EntryIrq, ExitIrq;
#endif // DBG
RWAN_GET_ENTRY_IRQL(EntryIrq);
pVc = (PRWAN_NDIS_VC)ProtocolVcContext;
RWAN_STRUCT_ASSERT(pVc, nvc);
//
// Check if this is a point-to-multipoint call.
//
if (!RWAN_IS_BIT_SET(pVc->Flags, RWANF_VC_PMP))
{
//
// Point-to-point call.
//
pConnObject = pVc->pConnObject;
pRootConnObject = pConnObject; // for consistency.
pParty = NULL;
pOriginalParams = pVc->pCallParameters;
}
else
{
//
// PMP Call. Get at the Party structure.
//
pParty = pVc->pPartyMakeCall;
RWAN_STRUCT_ASSERT(pParty, npy);
pConnObject = pParty->pConnObject;
pRootConnObject = pVc->pConnObject;
pOriginalParams = pParty->pCallParameters;
}
RWAN_ASSERT(pOriginalParams != NULL);
pAf = pVc->pNdisAf;
RWANDEBUGP(DL_LOUD, DC_CONNECT,
("MakeCallComplete: pConn x%x, State/Flags/Ref x%x/x%x/%d, pAddr %x, pVc x%x, Status x%x\n",
pConnObject, pConnObject->State, pConnObject->Flags, pConnObject->RefCount, pConnObject->pAddrObject, pVc, Status));
RWAN_ACQUIRE_CONN_LOCK(pConnObject);
RWAN_ASSERT(pConnObject->State == RWANS_CO_OUT_CALL_INITIATED ||
pConnObject->State == RWANS_CO_DISCON_REQUESTED);
//
// Has the user initiated a TdiCloseConnection() or a TdiDisconnect()
// while this outgoing call was in progress?
//
bIsConnClosing = RWAN_IS_BIT_SET(pConnObject->Flags, RWANF_CO_CLOSING) ||
(pConnObject->State == RWANS_CO_DISCON_REQUESTED);
//
// We would have saved context about the TdiConnect(). Get it.
//
pConnReq = pConnObject->pConnReq;
pConnObject->pConnReq = NULL;
if (pParty)
{
pVc->AddingPartyCount --;
pParty->pCallParameters = NULL;
pParty->NdisPartyHandle = NdisPartyHandle;
}
else
{
pVc->pCallParameters = NULL;
}
if (Status == NDIS_STATUS_SUCCESS)
{
RWAN_SET_VC_EVENT(pVc, RWANF_VC_EVT_MAKECALL_OK);
if (!bIsConnClosing)
{
//
// Outgoing connection successfully set up.
//
pConnObject->State = RWANS_CO_CONNECTED;
//
// Update PMP information.
//
if (pParty)
{
pVc->ActivePartyCount ++; // MakeCall PMP complete
pRootConnObject->State = RWANS_CO_CONNECTED;
}
AfSpConnContext = pConnObject->AfSpConnContext;
RWAN_RELEASE_CONN_LOCK(pConnObject);
RWanCompleteConnReq( // MakeCall OK
pAf,
pConnReq,
TRUE,
pCallParameters,
AfSpConnContext,
TDI_SUCCESS
);
}
else
{
//
// Abort this call.
//
pConnObject->State = RWANS_CO_ABORTING;
RWanStartCloseCall(pConnObject, pVc);
}
RWAN_CHECK_EXIT_IRQL(EntryIrq, ExitIrq);
}
else
{
//
// MakeCall failed.
//
INT rc;
//
// XXX TBD : how about trying this call on another NDIS AF?
//
RWAN_SET_VC_EVENT(pVc, RWANF_VC_EVT_MAKECALL_FAIL);
if (pParty == NULL)
{
//
// Point-to-Point call.
//
//
// Unlink the NDIS VC from this Conn Object.
//
RWAN_UNLINK_CONNECTION_AND_VC(pConnObject, pVc); // MakeCall fail
}
else
{
//
// PMP Call. The VC would be attached to the Root
// Connection Object. Unlink the VC and the Root Connection.
//
RWAN_ACQUIRE_CONN_LOCK(pRootConnObject);
RWAN_UNLINK_CONNECTION_AND_VC(pRootConnObject, pVc); // MakeCallPMP fail
rc = RWanDereferenceConnObject(pRootConnObject); // VC deref: MakeCallPMP fail
if (rc > 0)
{
RWAN_RELEASE_CONN_LOCK(pRootConnObject);
}
//
// Unlink the Party from the Connection and VC.
//
RWAN_DELETE_FROM_LIST(&(pParty->PartyLink));
pParty->pVc = NULL;
pParty->pConnObject = NULL;
pConnObject->NdisConnection.pNdisParty = NULL;
}
rc = RWanDereferenceConnObject(pConnObject); // VC/Pty deref: MakeCall fail
//
// Continue with the Connection Object for this MakeCall,
// if it is still alive.
//
if (rc > 0)
{
if (pConnObject->pAddrObject != NULL)
{
//
// Move the Connection Object to the Idle list.
//
pAddrObject = pConnObject->pAddrObject;
//
// Reacquire some locks in the right order.
//
RWAN_RELEASE_CONN_LOCK(pConnObject);
RWAN_ACQUIRE_ADDRESS_LOCK(pAddrObject);
RWAN_ACQUIRE_CONN_LOCK_DPC(pConnObject);
//
// Move this Connection to the Idle list.
//
RWAN_DELETE_FROM_LIST(&(pConnObject->ConnLink));
RWAN_INSERT_TAIL_LIST(&(pAddrObject->IdleConnList),
&(pConnObject->ConnLink));
//
// Send this back to the state it was in before the TdiConnect.
//
pConnObject->State = RWANS_CO_ASSOCIATED;
AfSpConnContext = pConnObject->AfSpConnContext;
RWAN_RELEASE_CONN_LOCK_DPC(pConnObject);
RWAN_RELEASE_ADDRESS_LOCK(pAddrObject);
//
// Complete the TdiConnect() with a failure status.
//
TdiStatus = RWanNdisToTdiStatus(Status);
if (TdiStatus == TDI_NOT_ACCEPTED)
{
TdiStatus = TDI_CONN_REFUSED;
}
RWanCompleteConnReq( // MakeCall Fail
pAf,
pConnReq,
TRUE,
NULL,
AfSpConnContext,
TdiStatus
);
}
else
{
RWAN_RELEASE_CONN_LOCK(pConnObject);
}
}
//
// else the Conn Object is gone. A TdiCloseConnection might be
// (must be?) in progress.
//
#if 1
// XXX: Remove this after debugging.
pVc = (PRWAN_NDIS_VC)ProtocolVcContext;
RWAN_STRUCT_ASSERT(pVc, nvc);
#endif // 1
NdisVcHandle = pVc->NdisVcHandle;
//
// Unlink the VC from the AF it is attached to.
//
RWanUnlinkVcFromAf(pVc);
//
// Get rid of the VC.
//
Status = NdisCoDeleteVc(NdisVcHandle);
RWAN_ASSERT(Status == NDIS_STATUS_SUCCESS);
RWanFreeVc(pVc); // MakeCall complete fail
if (pParty != NULL)
{
RWAN_FREE_MEM(pParty); // MakeCall complete fail
}
RWAN_CHECK_EXIT_IRQL(EntryIrq, ExitIrq);
}
//
// Return the Call Parameters structure to the AF-specific module.
//
(*pAf->pAfInfo->AfChars.pAfSpReturnNdisOptions)(
pAf->AfSpAFContext,
pOriginalParams
);
return;
}
VOID
RWanNdisAddPartyComplete(
IN NDIS_STATUS Status,
IN NDIS_HANDLE ProtocolPartyContext,
IN NDIS_HANDLE NdisPartyHandle,
IN PCO_CALL_PARAMETERS pCallParameters
)
/*++
Routine Description:
This is the NDIS entry indicating completion of a call to NdisClAddParty
that had pended.
Arguments:
Status - Final status of the AddParty
ProtocolPartyContext- Actually a pointer to our NDIS PARTY structure
NdisPartyHandle - If the AddParty was successful, an NDIS handle for it
pCallParameters - Final parameters after the AddParty
Return Value:
None
--*/
{
PRWAN_NDIS_PARTY pParty;
PRWAN_TDI_CONNECTION pConnObject;
PRWAN_TDI_ADDRESS pAddrObject;
PRWAN_CONN_REQUEST pConnReq;
PRWAN_NDIS_AF pAf;
PRWAN_NDIS_VC pVc;
PCO_CALL_PARAMETERS pOriginalParams; // what was used in the AddParty
TDI_STATUS TdiStatus;
BOOLEAN bIsConnClosing;
RWAN_HANDLE AfSpConnContext;
#if DBG
RWAN_IRQL EntryIrq, ExitIrq;
#endif // DBG
RWAN_GET_ENTRY_IRQL(EntryIrq);
pParty = (PRWAN_NDIS_PARTY)ProtocolPartyContext;
RWAN_STRUCT_ASSERT(pParty, npy);
pVc = pParty->pVc;
RWAN_STRUCT_ASSERT(pVc, nvc);
pAf = pVc->pNdisAf;
pConnObject = pParty->pConnObject;
RWAN_ACQUIRE_CONN_LOCK(pConnObject);
pOriginalParams = pParty->pCallParameters;
RWAN_ASSERT(pOriginalParams != NULL);
pParty->pCallParameters = NULL;
RWAN_ASSERT(pConnObject->State == RWANS_CO_OUT_CALL_INITIATED ||
pConnObject->State == RWANS_CO_DISCON_REQUESTED);
//
// Has the user initiated a TdiCloseConnection() or a TdiDisconnect()
// while this outgoing call was in progress?
//
bIsConnClosing = RWAN_IS_BIT_SET(pConnObject->Flags, RWANF_CO_CLOSING) ||
(pConnObject->State == RWANS_CO_DISCON_REQUESTED);
RWANDEBUGP(DL_LOUD, DC_CONNECT,
("AddPartyComplete: pConn x%x, State/Flags x%x/x%x, pVc x%x, Pty x%x, Status x%x\n",
pConnObject, pConnObject->State, pConnObject->Flags, pVc, pParty, Status));
//
// We would have saved context about the TdiConnect(). Get it.
//
pConnReq = pConnObject->pConnReq;
pConnObject->pConnReq = NULL;
pVc->AddingPartyCount --;
if (Status == NDIS_STATUS_SUCCESS)
{
pParty->NdisPartyHandle = NdisPartyHandle;
pConnObject->State = RWANS_CO_CONNECTED;
//
// Outgoing party successfully set up.
//
pVc->ActivePartyCount ++; // AddParty OK
if (!bIsConnClosing)
{
AfSpConnContext = pConnObject->AfSpConnContext;
RWAN_RELEASE_CONN_LOCK(pConnObject);
RWanCompleteConnReq( // AddParty OK
pAf,
pConnReq,
TRUE,
pCallParameters,
AfSpConnContext,
TDI_SUCCESS
);
}
else
{
//
// Abort this Party.
//
RWanDoTdiDisconnect(
pConnObject,
NULL, // pTdiRequest
NULL, // pTimeout
0, // Flags
NULL, // pDisconnInfo
NULL // pReturnInfo
);
//
// Conn Object lock is released above.
//
}
RWAN_CHECK_EXIT_IRQL(EntryIrq, ExitIrq);
}
else
{
//
// AddParty failed.
//
INT rc;
//
// Unlink the Party from the VC.
//
RWAN_DELETE_FROM_LIST(&(pParty->PartyLink));
pAddrObject = pConnObject->pAddrObject;
rc = RWanDereferenceConnObject(pConnObject); // Party deref: AddParty fail
if (rc > 0)
{
//
// Reacquire some locks in the right order.
//
RWAN_RELEASE_CONN_LOCK(pConnObject);
RWAN_ACQUIRE_ADDRESS_LOCK(pAddrObject);
RWAN_ACQUIRE_CONN_LOCK_DPC(pConnObject);
//
// Move this Connection to the Idle list.
//
RWAN_DELETE_FROM_LIST(&(pConnObject->ConnLink));
RWAN_INSERT_TAIL_LIST(&(pAddrObject->IdleConnList),
&(pConnObject->ConnLink));
//
// Send this back to the state it was in before the TdiConnect.
//
pConnObject->State = RWANS_CO_ASSOCIATED;
AfSpConnContext = pConnObject->AfSpConnContext;
RWAN_RELEASE_CONN_LOCK_DPC(pConnObject);
RWAN_RELEASE_ADDRESS_LOCK(pAddrObject);
//
// Complete the TdiConnect() with a failure status.
//
TdiStatus = RWanNdisToTdiStatus(Status);
if (TdiStatus == TDI_NOT_ACCEPTED)
{
TdiStatus = TDI_CONN_REFUSED;
}
RWanCompleteConnReq( // JoinLeaf Fail
pAf,
pConnReq,
TRUE,
NULL,
AfSpConnContext,
TdiStatus
);
}
//
// else the ConnObject is gone.
//
RWAN_FREE_MEM(pParty); // AddParty fail
RWAN_CHECK_EXIT_IRQL(EntryIrq, ExitIrq);
}
//
// Return the Call Parameters structure to the AF-specific module.
//
(*pAf->pAfInfo->AfChars.pAfSpReturnNdisOptions)(
pAf->AfSpAFContext,
pOriginalParams
);
return;
}
NDIS_STATUS
RWanNdisIncomingCall(
IN NDIS_HANDLE ProtocolSapContext,
IN NDIS_HANDLE ProtocolVcContext,
IN OUT PCO_CALL_PARAMETERS pCallParameters
)
/*++
Routine Description:
This is the NDIS entry point announcing a new Incoming call request,
on the specified SAP.
The SAP corresponds to an Address Object. If there are no Listens posted
on the Address object, we reject this call. Otherwise, we pick up an
arbitrary listening connection object and indicate this call on that.
TBD: support selection of listening conn object based on specified
remote criteria.
Arguments:
ProtocolSapContext - Our SAP context is a pointer to an NDIS SAP structure
ProtocolVcContext - Actually a pointer to our NDIS VC structure
pCallParameters - Points to Incoming call parameters.
Return Value:
None
--*/
{
PRWAN_NDIS_SAP pSap;
PRWAN_NDIS_VC pVc;
PRWAN_NDIS_AF pAf;
PRWAN_TDI_ADDRESS pAddrObject;
BOOLEAN IsAddrLockAcquired;
PRWAN_TDI_CONNECTION pConnObject;
PLIST_ENTRY pConnEntry;
NDIS_STATUS Status;
TDI_STATUS TdiStatus;
RWAN_STATUS RWanStatus;
PRWAN_CONN_REQUEST pConnReq;
PConnectEvent pConnInd;
PTDI_CONNECTION_INFORMATION pTdiInfo;
RWAN_HANDLE AfSpTdiOptionsContext;
PVOID pTdiQoS;
ULONG TdiQoSLength;
PVOID ConnIndContext;
PVOID AcceptConnContext;
RWAN_HANDLE AfSpConnContext;
#ifdef NT
PIO_STACK_LOCATION pIrpSp;
PTDI_REQUEST_KERNEL_ACCEPT pAcceptReq;
ConnectEventInfo *EventInfo;
#else
ConnectEventInfo EventInfo;
#endif // NT
pSap = (PRWAN_NDIS_SAP)ProtocolSapContext;
RWAN_STRUCT_ASSERT(pSap, nsp);
pAddrObject = pSap->pAddrObject;
RWAN_ASSERT(pAddrObject != NULL);
pVc = (PRWAN_NDIS_VC)ProtocolVcContext;
RWAN_STRUCT_ASSERT(pVc, nvc);
RWAN_SET_VC_EVENT(pVc, RWANF_VC_EVT_INCALL);
pAf = pVc->pNdisAf;
RWANDEBUGP(DL_INFO, DC_CONNECT,
("IncomingCall: pVc x%x, pAddrObj x%x/x%x, pConnInd x%x\n",
pVc, pAddrObject, pAddrObject->Flags, pAddrObject->pConnInd));
//
// Initialize.
//
pTdiInfo = NULL;
AfSpTdiOptionsContext = NULL;
pConnReq = NULL;
IsAddrLockAcquired = TRUE;
RWAN_ACQUIRE_ADDRESS_LOCK(pAddrObject);
do
{
if (RWAN_IS_BIT_SET(pAddrObject->Flags, RWANF_AO_CLOSING))
{
Status = NDIS_STATUS_FAILURE;
break;
}
if (pVc->pConnObject != NULL)
{
RWAN_ASSERT(FALSE);
Status = NDIS_STATUS_FAILURE;
break;
}
//
// Convert from NDIS Call Parameters to TDI Options.
//
RWanStatus = (*pAf->pAfInfo->AfChars.pAfSpNdis2TdiOptions)(
pAf->AfSpAFContext,
RWAN_CALLF_INCOMING_CALL|RWAN_CALLF_POINT_TO_POINT,
pCallParameters,
&pTdiInfo,
&pTdiQoS,
&TdiQoSLength,
&AfSpTdiOptionsContext
);
if (RWanStatus != RWAN_STATUS_SUCCESS)
{
RWANDEBUGP(DL_LOUD, DC_CONNECT,
("IncomingCall: conversion from NDIS to TDI failed, status x%x\n",
RWanStatus));
Status = NDIS_STATUS_FAILURE;
break;
}
RWAN_ASSERT(pTdiInfo != NULL);
RWAN_ASSERT(AfSpTdiOptionsContext != NULL);
//
// It has been decided to pass QOS and any provider-specific info
// as part of TDI options.
//
pTdiInfo->Options = pTdiQoS;
pTdiInfo->OptionsLength = TdiQoSLength;
pVc->pCallParameters = pCallParameters;
RWAN_SET_VC_CALL_PARAMS(pVc, pCallParameters);
//
// Find a listening connection.
//
for (pConnEntry = pAddrObject->ListenConnList.Flink;
pConnEntry != &pAddrObject->ListenConnList;
pConnEntry = pConnEntry->Flink)
{
pConnObject = CONTAINING_RECORD(pConnEntry, RWAN_TDI_CONNECTION, ConnLink);
RWAN_STRUCT_ASSERT(pConnObject, ntc);
RWANDEBUGP(DL_EXTRA_LOUD, DC_CONNECT,
("Incoming Call: looking at pConnObj x%x, state %d\n",
pConnObject, pConnObject->State));
if (pConnObject->State == RWANS_CO_LISTENING)
{
break;
}
}
if (pConnEntry != &pAddrObject->ListenConnList)
{
//
// Found a listening connection.
//
RWAN_ACQUIRE_CONN_LOCK_DPC(pConnObject);
//
// Move the Connection from the Idle list to the Active list.
//
RWAN_DELETE_FROM_LIST(&pConnObject->ConnLink);
RWAN_INSERT_TAIL_LIST(&pAddrObject->ActiveConnList,
&pConnObject->ConnLink);
RWAN_LINK_CONNECTION_TO_VC(pConnObject, pVc);
RWanReferenceConnObject(pConnObject); // VC ref - InCall, Listening conn
RWANDEBUGP(DL_LOUD, DC_CONNECT,
("IncomingCall: pVc x%x, pConnObj x%x is listening, ConnReqFlags x%x\n",
pVc, pConnObject, pConnObject->pConnReq->Flags));
if (pConnObject->pConnReq->pConnInfo)
{
*pConnObject->pConnReq->pConnInfo = *pTdiInfo;
}
//
// Check if it is pre-accepted. If so, tell NDIS that we have
// accepted the call.
//
if (!(pConnObject->pConnReq->Flags & TDI_QUERY_ACCEPT))
{
pConnObject->State = RWANS_CO_IN_CALL_ACCEPTING;
RWAN_RELEASE_CONN_LOCK_DPC(pConnObject);
//
// Request the media-specific module to update NDIS call parameters.
//
if (pAf->pAfInfo->AfChars.pAfSpUpdateNdisOptions)
{
(VOID)(*pAf->pAfInfo->AfChars.pAfSpUpdateNdisOptions)(
pAf->AfSpAFContext,
pConnObject->AfSpConnContext,
RWAN_CALLF_INCOMING_CALL|RWAN_CALLF_POINT_TO_POINT,
pTdiInfo,
pTdiQoS,
TdiQoSLength,
&pCallParameters
);
}
Status = NDIS_STATUS_SUCCESS;
break;
}
//
// It isn't pre-accepted. Complete the pended listen.
//
pConnReq = pConnObject->pConnReq;
pConnObject->pConnReq = NULL;
pConnObject->State = RWANS_CO_IN_CALL_INDICATED;
AfSpConnContext = pConnObject->AfSpConnContext;
RWAN_RELEASE_CONN_LOCK_DPC(pConnObject);
RWAN_RELEASE_ADDRESS_LOCK(pAddrObject);
IsAddrLockAcquired = FALSE;
RWanCompleteConnReq( // InCall: Listen OK
pSap->pNdisAf,
pConnReq,
FALSE,
NULL,
AfSpConnContext,
TDI_SUCCESS
);
pConnReq = NULL;
Status = NDIS_STATUS_PENDING;
break;
}
//
// There wasn't a listening connection available.
// See if there is a Connect Indication event handler on this
// Address Object.
//
if (pAddrObject->pConnInd == NULL)
{
//
// No event handler. Reject this call.
//
Status = NDIS_STATUS_FAILURE;
break;
}
//
// Get some resources.
//
pConnReq = RWanAllocateConnReq();
if (pConnReq == NULL)
{
Status = NDIS_STATUS_RESOURCES;
break;
}
pConnInd = pAddrObject->pConnInd;
ConnIndContext = pAddrObject->ConnIndContext;
RWAN_RELEASE_ADDRESS_LOCK(pAddrObject);
IsAddrLockAcquired = FALSE;
RWANDEBUGP(DL_VERY_LOUD, DC_CONNECT,
("IncomingCall: Will Indicate: pVc x%x, RemAddr x%x/%d, Options x%x/%d\n",
pVc,
pTdiInfo->RemoteAddress,
pTdiInfo->RemoteAddressLength,
pTdiInfo->Options,
pTdiInfo->OptionsLength));
//
// Indicate the call to the user.
//
TdiStatus = (*pConnInd)(
ConnIndContext,
pTdiInfo->RemoteAddressLength,
pTdiInfo->RemoteAddress,
pTdiInfo->UserDataLength,
pTdiInfo->UserData,
pTdiInfo->OptionsLength,
pTdiInfo->Options,
&AcceptConnContext,
&EventInfo
);
RWANDEBUGP(DL_LOUD, DC_CONNECT,
("IncomingCall: pVc x%x, pAddrObj x%x, Connect Ind returned x%x\n",
pVc, pAddrObject, TdiStatus));
if (TdiStatus != TDI_MORE_PROCESSING)
{
//
// Connection rejected.
//
Status = NDIS_STATUS_FAILURE;
break;
}
//
// This connection has been accepted. Collect all information
// about this implicit TdiAccept Request.
//
#ifdef NT
pIrpSp = IoGetCurrentIrpStackLocation(EventInfo);
Status = RWanPrepareIrpForCancel(
(PRWAN_ENDPOINT) pIrpSp->FileObject->FsContext,
EventInfo,
RWanCancelRequest
);
if (!NT_SUCCESS(Status))
{
//
// Reject this incoming call.
//
break;
}
pAcceptReq = (PTDI_REQUEST_KERNEL_ACCEPT) &(pIrpSp->Parameters);
pConnReq->Request.pReqComplete = (PVOID)RWanRequestComplete;
pConnReq->Request.ReqContext = EventInfo;
pConnReq->pConnInfo = pAcceptReq->ReturnConnectionInformation;
#else
pConnReq->Request.pReqComplete = EventInfo.cei_rtn;
pConnReq->Request.ReqContext = EventInfo.cei_context;
pConnReq->pConnInfo = EventInfo.cei_conninfo;
#endif // NT
//
// Find the connection object on which it has been accepted.
//
IsAddrLockAcquired = TRUE;
RWAN_ACQUIRE_ADDRESS_LOCK(pAddrObject);
for (pConnEntry = pAddrObject->IdleConnList.Flink;
pConnEntry != &pAddrObject->IdleConnList;
pConnEntry = pConnEntry->Flink)
{
pConnObject = CONTAINING_RECORD(pConnEntry, RWAN_TDI_CONNECTION, ConnLink);
RWAN_STRUCT_ASSERT(pConnObject, ntc);
if ((pConnObject->ConnectionHandle == AcceptConnContext) &&
!(RWAN_IS_BIT_SET(pConnObject->Flags, RWANF_CO_CLOSING)))
{
break;
}
}
if (pConnEntry == &pAddrObject->IdleConnList)
{
//
// Invalid connection context!
//
TdiStatus = TDI_INVALID_CONNECTION;
RWAN_RELEASE_ADDRESS_LOCK(pAddrObject);
IsAddrLockAcquired = FALSE;
//
// Fail the Accept req
//
RWanCompleteConnReq( // InCall: Accept is bad
pAf,
pConnReq,
FALSE,
NULL,
NULL,
TdiStatus
);
pConnReq = NULL;
//
// Reject the incoming call
//
Status = NDIS_STATUS_FAILURE;
break;
}
//
// Request the media-specific module to update NDIS call parameters.
//
if (pAf->pAfInfo->AfChars.pAfSpUpdateNdisOptions)
{
(VOID)(*pAf->pAfInfo->AfChars.pAfSpUpdateNdisOptions)(
pAf->AfSpAFContext,
pConnObject->AfSpConnContext,
RWAN_CALLF_INCOMING_CALL|RWAN_CALLF_POINT_TO_POINT,
pTdiInfo,
pTdiQoS,
TdiQoSLength,
&pCallParameters
);
}
//
// Set up the Connection Object for accepting this call.
//
RWAN_ACQUIRE_CONN_LOCK_DPC(pConnObject);
pConnObject->State = RWANS_CO_IN_CALL_ACCEPTING;
//
// Save info to help us complete the Accept Req when
// we get a CallConnected from NDIS.
//
RWAN_ASSERT(pConnObject->pConnReq == NULL);
pConnObject->pConnReq = pConnReq;
//
// Move the Connection from the Idle list to the Active list.
//
RWAN_DELETE_FROM_LIST(&pConnObject->ConnLink);
RWAN_INSERT_TAIL_LIST(&pAddrObject->ActiveConnList,
&pConnObject->ConnLink);
RWAN_LINK_CONNECTION_TO_VC(pConnObject, pVc);
RWanReferenceConnObject(pConnObject); // VC ref
RWAN_RELEASE_CONN_LOCK_DPC(pConnObject);
//
// Accept the call.
//
Status = NDIS_STATUS_SUCCESS;
break;
}
while (FALSE);
if (IsAddrLockAcquired)
{
RWAN_RELEASE_ADDRESS_LOCK(pAddrObject);
}
//
// If we are rejecting this call, clean up.
//
if ((Status != NDIS_STATUS_SUCCESS) &&
(Status != NDIS_STATUS_PENDING))
{
if (pConnReq != NULL)
{
RWanFreeConnReq(pConnReq);
}
}
//
// Return TDI options space to the media-specific module.
//
if (pTdiInfo != NULL)
{
RWAN_ASSERT(pAf);
RWAN_ASSERT(AfSpTdiOptionsContext);
(*pAf->pAfInfo->AfChars.pAfSpReturnTdiOptions)(
pAf->AfSpAFContext,
AfSpTdiOptionsContext
);
}
RWANDEBUGP(DL_LOUD, DC_CONNECT,
("IncomingCall: pVc x%x, returning status x%x\n", pVc, Status));
return (Status);
}
VOID
RWanNdisCallConnected(
IN NDIS_HANDLE ProtocolVcContext
)
/*++
Routine Description:
This is the NDIS entry point signifying successful setup of an
incoming call. If required, we complete the TDI user's Accept Request
here.
This primitive can happen only when the call is in the "Accepting" state.
Arguments:
ProtocolVcContext - Actually a pointer to our NDIS VC structure
Return Value:
None
--*/
{
PRWAN_NDIS_VC pVc;
PRWAN_TDI_CONNECTION pConnObject;
PRWAN_TDI_ADDRESS pAddrObject;
NDIS_HANDLE NdisVcHandle;
NDIS_STATUS Status;
PRWAN_CONN_REQUEST pConnReq;
RWAN_HANDLE AfSpConnContext;
ULONG rc;
BOOLEAN IsAborting = FALSE;
pVc = (PRWAN_NDIS_VC) ProtocolVcContext;
RWAN_STRUCT_ASSERT(pVc, nvc);
RWAN_ASSERT(pVc->pConnObject != NULL_PRWAN_TDI_CONNECTION);
pConnObject = pVc->pConnObject;
RWAN_STRUCT_ASSERT(pConnObject, ntc);
pAddrObject = pConnObject->pAddrObject;
RWAN_ACQUIRE_CONN_LOCK(pConnObject);
RWAN_SET_VC_EVENT(pVc, RWANF_VC_EVT_CALLCONN);
IsAborting = ((pConnObject->State != RWANS_CO_IN_CALL_ACCEPTING) ||
RWAN_IS_BIT_SET(pConnObject->Flags, RWANF_CO_CLOSING));
//
// Incoming Connection setup successfully.
//
if (!IsAborting)
{
pConnObject->State = RWANS_CO_CONNECTED;
}
//
// Add a temp ref to keep the conn object from going away.
//
RWanReferenceConnObject(pConnObject); // Temp ref, CallConn
//
// If we have an Accept Request to complete, complete it
// now. Note that we might not have one pending in case
// we had a pre-accepted listen.
//
pConnReq = pConnObject->pConnReq;
pConnObject->pConnReq = NULL;
AfSpConnContext = pConnObject->AfSpConnContext;
RWAN_RELEASE_CONN_LOCK(pConnObject);
if (pConnReq != NULL)
{
//
// Complete the Accept request.
//
RWanCompleteConnReq( // CallConnected: Accept OK
pVc->pNdisAf,
pConnReq,
FALSE,
NULL,
AfSpConnContext,
TDI_SUCCESS
);
}
//
// Trigger off data indications for any packets that were received and queued
// while we were in the process of accepting the call.
//
RWAN_ACQUIRE_CONN_LOCK(pConnObject);
rc = RWanDereferenceConnObject(pConnObject); // Temp ref - CallConn
//
// But first make sure that the connection still exists and is in a good
// state.
//
if (rc != 0)
{
if (!IsAborting)
{
RWanIndicateData(pConnObject);
}
else
{
RWAN_RELEASE_CONN_LOCK(pConnObject);
RWANDEBUGP(DL_FATAL, DC_WILDCARD,
("CallConn: ConnObj %x/%x, State %d, aborting\n",
pConnObject, pConnObject->Flags, pConnObject->State));
RWanDoAbortConnection(pConnObject);
}
}
//
// else the Connection is gone!
//
return;
}
VOID
RWanNdisIncomingCloseCall(
IN NDIS_STATUS CloseStatus,
IN NDIS_HANDLE ProtocolVcContext,
IN PVOID pCloseData,
IN UINT CloseDataLength
)
/*++
Routine Description:
This is the NDIS entry point called when a connection is torn
down by the remote peer or network. We mark the affected endpoint,
and if possible, indicate a Disconnect Event to the user. If we
do indicate to the user, call teardown is continued when the
user calls TdiDisconnect.
This primitive can happen while the endpoint is in one of these
states:
(1) Connected
(2) Accepting incoming call (TdiAccept pending)
Arguments:
CloseStatus - Status for the incoming close
ProtocolVcContext - Actually a pointer to our NDIS VC structure
pCloseData - Data/options associated with the close - NOT USED
CloseDataLength - Length of the above - NOT USED
Return Value:
None
--*/
{
PRWAN_NDIS_VC pVc;
PRWAN_NDIS_PARTY pParty;
PRWAN_NDIS_AF pAf;
PRWAN_TDI_CONNECTION pConnObject;
PRWAN_CONN_REQUEST pConnReq;
NDIS_HANDLE NdisVcHandle;
BOOLEAN bIsConnClosing; // TdiCloseConnection?
BOOLEAN bScheduleDisconnect;
RWAN_HANDLE AfSpConnContext;
pVc = (PRWAN_NDIS_VC)ProtocolVcContext;
RWAN_STRUCT_ASSERT(pVc, nvc);
if (!RWAN_IS_BIT_SET(pVc->Flags, RWANF_VC_PMP))
{
pConnObject = pVc->pConnObject;
pParty = NULL;
}
else
{
//
// Locate the connection object for the last leaf.
//
pParty = CONTAINING_RECORD(pVc->NdisPartyList.Flink, RWAN_NDIS_PARTY, PartyLink);
RWAN_STRUCT_ASSERT(pParty, npy);
pConnObject = pParty->pConnObject;
}
RWAN_ASSERT(pConnObject != NULL_PRWAN_TDI_CONNECTION);
RWAN_STRUCT_ASSERT(pConnObject, ntc);
RWANDEBUGP(DL_INFO, DC_DISCON,
("IncomingClose: pVc x%x, pConnObj x%x/x%x, pParty x%x\n",
pVc, pConnObject, pConnObject->Flags, pParty));
RWAN_ACQUIRE_CONN_LOCK(pConnObject);
RWAN_SET_VC_EVENT(pVc, RWANF_VC_EVT_INCLOSE);
NdisVcHandle = pVc->NdisVcHandle;
bIsConnClosing = RWAN_IS_BIT_SET(pConnObject->Flags, RWANF_CO_CLOSING);
if (bIsConnClosing)
{
//
// The user has initiated a TdiCloseConnection.
// Continue NDIS call teardown. When this completes,
// we'll complete the CloseConnection.
//
RWanStartCloseCall(pConnObject, pVc);
return;
}
pAf = pVc->pNdisAf;
switch (pConnObject->State)
{
case RWANS_CO_IN_CALL_ACCEPTING:
//
// If we have a pended Accept Request, fail it now.
// Otherwise, we must have had a pre-accepted listen,
// so we fall through and indicate a Disconnect.
//
pConnReq = pConnObject->pConnReq;
pConnObject->pConnReq = NULL;
if (pConnReq != NULL)
{
//
// Fix the state so that TdiDisconnect does the right thing
//
pConnObject->State = RWANS_CO_DISCON_INDICATED;
AfSpConnContext = pConnObject->AfSpConnContext;
RWanScheduleDisconnect(pConnObject);
//
// Conn Lock is released within the above.
//
RWanCompleteConnReq( // Incoming Close during IN_CALL_ACCEPT
pAf,
pConnReq,
FALSE,
NULL,
AfSpConnContext,
TDI_CONNECTION_ABORTED
);
break;
}
//
// else this must be a pre-accepted listen.
//
// FALLTHRU on "else" to RWANS_CO_CONNECTED
//
case RWANS_CO_CONNECTED:
//
// If there is a Disconnect Event handler, call it.
// Otherwise, simply mark this endpoint as having
// seen a Disconnect.
//
bScheduleDisconnect = TRUE;
if (pConnObject->pAddrObject != NULL_PRWAN_TDI_ADDRESS)
{
PDisconnectEvent pDisconInd;
PVOID IndContext;
PVOID ConnectionHandle;
pDisconInd = pConnObject->pAddrObject->pDisconInd;
IndContext = pConnObject->pAddrObject->DisconIndContext;
//
// Don't send up a Disconnect Indication if we are in the
// middle of indicating data.
//
if ((pDisconInd != NULL) &&
!(RWAN_IS_BIT_SET(pConnObject->Flags, RWANF_CO_INDICATING_DATA)))
{
RWANDEBUGP(DL_INFO, DC_WILDCARD,
("IncomingClose: pConnObj %x/%x, st %x, will discon ind\n",
pConnObject, pConnObject->Flags, pConnObject->State));
pConnObject->State = RWANS_CO_DISCON_INDICATED;
ConnectionHandle = pConnObject->ConnectionHandle;
//
// Schedule a work item to continue Disconnect
// first. This is because the call to DiscInd can
// lead to a call to CloseConnection and block there.
//
bScheduleDisconnect = FALSE;
RWanScheduleDisconnect(pConnObject);
(*pDisconInd)(
IndContext,
ConnectionHandle,
0, // Disconnect Data Length
NULL, // Disconnect Data
0, // Disconnect Info Length
NULL, // Disconnect Info
TDI_DISCONNECT_RELEASE
);
}
else
{
RWANDEBUGP(DL_FATAL, DC_DISCON,
("IncomingClose: pConnObj %x/%x, pending discon\n",
pConnObject, pConnObject->Flags));
pConnObject->State = RWANS_CO_DISCON_HELD;
RWAN_SET_BIT(pConnObject->Flags, RWANF_CO_PENDED_DISCON);
}
}
else
{
pConnObject->State = RWANS_CO_DISCON_HELD;
}
if (bScheduleDisconnect)
{
RWanScheduleDisconnect(pConnObject);
//
// Conn Object lock is released within the above.
//
}
break;
case RWANS_CO_ABORTING:
case RWANS_CO_DISCON_REQUESTED:
//
// Ignore this.
//
RWAN_RELEASE_CONN_LOCK(pConnObject);
break;
default:
RWAN_ASSERT(FALSE);
RWAN_RELEASE_CONN_LOCK(pConnObject);
break;
}
return;
}
VOID
RWanNdisCloseCallComplete(
IN NDIS_STATUS Status,
IN NDIS_HANDLE ProtocolVcContext,
IN NDIS_HANDLE ProtocolPartyContext
)
/*++
Routine Description:
The NDIS entry point that is called when a previous pended call
we made to NdisClCloseCall has completed.
Arguments:
Status - Final status of CloseCall
ProtocolVcContext - Actually a pointer to our NDIS VC structure
ProtocolPartyContext- Last party context, points to NDIS PARTY structure
if this is a point-to-multipoint call.
Return Value:
None
--*/
{
PRWAN_NDIS_VC pVc;
PRWAN_NDIS_PARTY pParty;
PRWAN_NDIS_AF pAf;
PRWAN_TDI_CONNECTION pConnObject;
PRWAN_TDI_CONNECTION pRootConnObject;
INT rc;
PRWAN_CONN_REQUEST pConnReq;
BOOLEAN IsOutgoingCall;
RWAN_HANDLE AfSpConnContext;
#ifndef NO_POST_DISCON
PDisconnectEvent pDisconInd;
PVOID IndContext;
PVOID ConnectionHandle;
#endif // !NO_POST_DISCON
RWAN_ASSERT(Status == NDIS_STATUS_SUCCESS);
pVc = (PRWAN_NDIS_VC)ProtocolVcContext;
RWAN_STRUCT_ASSERT(pVc, nvc);
RWAN_SET_VC_EVENT(pVc, RWANF_VC_EVT_CLOSECOMP);
//
// Check if this is a point-to-multipoint call.
//
pParty = (PRWAN_NDIS_PARTY)ProtocolPartyContext;
if (ProtocolPartyContext == NULL)
{
//
// Point to point call.
//
pConnObject = pVc->pConnObject;
pRootConnObject = NULL;
}
else
{
//
// PMP Call.
//
RWAN_STRUCT_ASSERT(pParty, npy);
pConnObject = pParty->pConnObject;
pRootConnObject = pConnObject->pRootConnObject;
}
RWANDEBUGP(DL_INFO, DC_DISCON,
("CloseCallComplete: pVc x%x, pPty x%x, pConnObj x%x, pRoot x%x\n",
pVc, pParty, pVc->pConnObject, pRootConnObject));
if (pConnObject != NULL)
{
RWAN_ACQUIRE_CONN_LOCK(pConnObject);
//
// A pended Disconnect Request may be around.
//
pConnReq = pConnObject->pConnReq;
pConnObject->pConnReq = NULL;
pAf = pVc->pNdisAf;
IsOutgoingCall = RWAN_IS_BIT_SET(pVc->Flags, RWANF_VC_OUTGOING);
//
// State change:
//
if (pConnObject->State != RWANS_CO_ABORTING)
{
pConnObject->State = ((pConnObject->pAddrObject != NULL) ?
RWANS_CO_ASSOCIATED:
RWANS_CO_CREATED);
}
if (pParty == NULL)
{
//
// Unlink the VC from the Connection Object.
//
RWAN_UNLINK_CONNECTION_AND_VC(pConnObject, pVc); // CloseCallComplete
}
else
{
//
// PMP Call. The VC is linked to the root Conn Object.
//
RWAN_STRUCT_ASSERT(pRootConnObject, ntc);
RWAN_ACQUIRE_CONN_LOCK(pRootConnObject);
pRootConnObject->State = ((pRootConnObject->pAddrObject != NULL) ?
RWANS_CO_ASSOCIATED:
RWANS_CO_CREATED);
pVc->DroppingPartyCount --; // CloseCallComplete (PMP)
RWAN_UNLINK_CONNECTION_AND_VC(pRootConnObject, pVc); // CloseCallCompletePMP
rc = RWanDereferenceConnObject(pRootConnObject); // VC deref in CloseCallCompletePMP
if (rc > 0)
{
RWAN_RELEASE_CONN_LOCK(pRootConnObject);
}
//
// Unlink the Party from the VC and Leaf Conn Object.
//
pParty->pVc = NULL;
RWAN_DELETE_FROM_LIST(&(pParty->PartyLink));
pParty->pConnObject = NULL;
pConnObject->NdisConnection.pNdisParty = NULL;
}
AfSpConnContext = pConnObject->AfSpConnContext;
#ifndef NO_POST_DISCON
if (pConnObject->pAddrObject != NULL_PRWAN_TDI_ADDRESS)
{
pDisconInd = pConnObject->pAddrObject->pDisconInd;
IndContext = pConnObject->pAddrObject->DisconIndContext;
ConnectionHandle = pConnObject->ConnectionHandle;
}
else
{
pDisconInd = NULL;
}
#endif // NO_POST_DISCON
rc = RWanDereferenceConnObject(pConnObject); // VC/Pty deref in CloseCallComplete
if (rc > 0)
{
RWAN_RELEASE_CONN_LOCK(pConnObject);
}
if (pConnReq != NULL)
{
#ifndef NO_POST_DISCON
if (pDisconInd != NULL)
{
(*pDisconInd)(
IndContext,
ConnectionHandle,
0, // Disconnect Data Length
NULL, // Disconnect Data
0, // Disconnect Info Length
NULL, // Disconnect Info
TDI_DISCONNECT_ABORT
);
}
#endif // !NO_POST_DISCON
RWanCompleteConnReq( // CloseCallComplete - completing discon req
pAf,
pConnReq,
IsOutgoingCall,
NULL,
AfSpConnContext,
TDI_SUCCESS
);
}
}
//
// See if the VC was created by us. If so, call NDIS to delete it,
// and free it.
//
if (RWAN_IS_BIT_SET(pVc->Flags, RWANF_VC_OUTGOING))
{
NDIS_HANDLE NdisVcHandle;
NdisVcHandle = pVc->NdisVcHandle;
//
// Unlink the VC from the list of VCs on the AF block
//
RWanUnlinkVcFromAf(pVc);
Status = NdisCoDeleteVc(NdisVcHandle);
RWAN_ASSERT(Status == NDIS_STATUS_SUCCESS);
RWanFreeVc(pVc);
}
//
// Otherwise this VC was created by the Call Manager.
// Leave it as it is.
//
if (pParty != NULL)
{
RWAN_FREE_MEM(pParty); // CloseCallComplete PMP
}
return;
}
VOID
RWanNdisDropPartyComplete(
IN NDIS_STATUS Status,
IN NDIS_HANDLE ProtocolPartyContext
)
/*++
Routine Description:
This is the NDIS entry point signifying completion of a previous
call to NdisClDropParty that had pended.
We locate and complete the TDI Disconnect, if any, that lead to this.
Arguments:
Status - Final status of the Drop party request
ProtocolPartyContext- Actually a pointer to our NDIS PARTY structure
Return Value:
None
--*/
{
PRWAN_NDIS_PARTY pParty;
PRWAN_NDIS_VC pVc;
PRWAN_NDIS_AF pAf;
PRWAN_TDI_CONNECTION pConnObject;
PRWAN_TDI_CONNECTION pRootConnObject;
PRWAN_CONN_REQUEST pConnReq;
ULONG rc;
BOOLEAN IsOutgoingCall = TRUE;
BOOLEAN bVcNeedsClose;
RWAN_HANDLE AfSpConnContext;
#ifndef NO_POST_DISCON
PDisconnectEvent pDisconInd;
PVOID IndContext;
PVOID ConnectionHandle;
#endif // !NO_POST_DISCON
RWAN_ASSERT(Status == NDIS_STATUS_SUCCESS);
pParty = (PRWAN_NDIS_PARTY)ProtocolPartyContext;
RWAN_STRUCT_ASSERT(pParty, npy);
pVc = pParty->pVc;
pConnObject = pParty->pConnObject;
if (pConnObject != NULL)
{
RWAN_ACQUIRE_CONN_LOCK(pConnObject);
//
// A pended Disconnect Request may be around.
//
pConnReq = pConnObject->pConnReq;
pConnObject->pConnReq = NULL;
pAf = pVc->pNdisAf;
//
// State change:
//
if (pConnObject->State != RWANS_CO_ABORTING)
{
pConnObject->State = ((pConnObject->pAddrObject != NULL) ?
RWANS_CO_ASSOCIATED:
RWANS_CO_CREATED);
}
AfSpConnContext = pConnObject->AfSpConnContext;
pRootConnObject = pVc->pConnObject;
RWAN_STRUCT_ASSERT(pRootConnObject, ntc);
#if DBG
if (pConnObject->pAddrObject != NULL)
{
RWAN_ASSERT(pRootConnObject == pConnObject->pAddrObject->pRootConnObject);
}
#endif // DBG
#ifndef NO_POST_DISCON
if (pConnObject->pAddrObject != NULL_PRWAN_TDI_ADDRESS)
{
pDisconInd = pConnObject->pAddrObject->pDisconInd;
IndContext = pConnObject->pAddrObject->DisconIndContext;
ConnectionHandle = pConnObject->ConnectionHandle;
}
else
{
pDisconInd = NULL;
}
#endif // NO_POST_DISCON
pConnObject->NdisConnection.pNdisParty = NULL;
rc = RWanDereferenceConnObject(pConnObject); // Pty deref in DropPartyComplete
if (rc > 0)
{
RWAN_RELEASE_CONN_LOCK(pConnObject);
}
if (pConnReq != NULL)
{
#ifndef NO_POST_DISCON
if (pDisconInd != NULL)
{
(*pDisconInd)(
IndContext,
ConnectionHandle,
0, // Disconnect Data Length
NULL, // Disconnect Data
0, // Disconnect Info Length
NULL, // Disconnect Info
TDI_DISCONNECT_ABORT
);
}
#endif // NO_POST_DISCON
RWanCompleteConnReq( // DropPartyComplete - completing discon req
pAf,
pConnReq,
IsOutgoingCall,
NULL,
AfSpConnContext,
TDI_SUCCESS
);
}
//
// The Root Connection object lock controls access to
// the VC structure.
//
RWAN_ACQUIRE_CONN_LOCK(pRootConnObject);
//
// Unlink the Party from the VC.
//
RWAN_DELETE_FROM_LIST(&(pParty->PartyLink));
pVc->DroppingPartyCount --; // DropPartyComplete
//
// We may be in the process of shutting down this connection.
// This may be the penultimate Party going away. If so,
// continue the call close.
//
if (RWAN_IS_BIT_SET(pVc->Flags, RWANF_VC_NEEDS_CLOSE))
{
RWanStartCloseCall(pRootConnObject, pVc);
//
// Root Conn lock is released within the above.
//
}
else
{
RWAN_RELEASE_CONN_LOCK(pRootConnObject);
}
}
else
{
//
// Not sure if we can be here.
//
RWAN_ASSERT(FALSE);
}
//
// End of the road for this Party structure.
//
RWAN_FREE_MEM(pParty); // DropParty Complete
}
VOID
RWanNdisIncomingDropParty(
IN NDIS_STATUS Status,
IN NDIS_HANDLE OurPartyContext,
IN PVOID pBuffer,
IN UINT BufferLength
)
/*++
Routine Description:
This is the NDIS entry point notifying us that a leaf of a PMP call is
being dropped, either because the remote station terminated its session
or because of network conditions.
We simply inform the TDI client of a Disconnect on the Connection Object
representing this leaf, similar to an incoming Close on a VC.
Arguments:
Status - Status code for the Drop party
OurPartyContext - Pointer to our Party structure
pBuffer - Optional accompanying info (ignored)
BufferLength - Length of above (ignored)
Return Value:
None
--*/
{
PRWAN_NDIS_PARTY pParty;
PRWAN_NDIS_VC pVc;
PRWAN_TDI_CONNECTION pConnObject;
PRWAN_CONN_REQUEST pConnReq;
NDIS_HANDLE NdisPartyHandle;
BOOLEAN bIsConnClosing; // TdiCloseConnection?
BOOLEAN bIsLastLeaf;
BOOLEAN bScheduleDisconnect;
pParty = (PRWAN_NDIS_PARTY)OurPartyContext;
RWAN_STRUCT_ASSERT(pParty, npy);
pVc = pParty->pVc;
RWAN_STRUCT_ASSERT(pVc, nvc);
RWANDEBUGP(DL_INFO, DC_DISCON,
("IncomingDrop: pPty x%x, pVc x%x, pConnObj x%x, AddingCnt %d, ActiveCnt %d\n",
pParty, pVc, pParty->pConnObject, pVc->AddingPartyCount, pVc->ActivePartyCount));
pConnObject = pParty->pConnObject;
RWAN_ASSERT(pConnObject != NULL_PRWAN_TDI_CONNECTION);
RWAN_STRUCT_ASSERT(pConnObject, ntc);
RWAN_ACQUIRE_CONN_LOCK(pConnObject);
bIsLastLeaf = (pVc->AddingPartyCount + pVc->ActivePartyCount == 0);
bIsConnClosing = RWAN_IS_BIT_SET(pConnObject->Flags, RWANF_CO_CLOSING);
if (bIsConnClosing)
{
//
// The user has initiated a TdiCloseConnection.
// Continue NDIS call teardown. When this completes,
// we'll complete the CloseConnection.
//
if (bIsLastLeaf)
{
RWanStartCloseCall(pConnObject, pVc);
//
// Conn Lock is released within the above.
//
}
else
{
NdisPartyHandle = pParty->NdisPartyHandle;
RWAN_RELEASE_CONN_LOCK(pConnObject);
Status = NdisClDropParty(
NdisPartyHandle,
NULL, // No Drop Data
0 // Length of above
);
if (Status != NDIS_STATUS_PENDING)
{
RWanNdisDropPartyComplete(
Status,
(NDIS_HANDLE)pParty
);
}
}
return;
}
switch (pConnObject->State)
{
case RWANS_CO_IN_CALL_ACCEPTING:
RWAN_ASSERT(FALSE);
RWAN_RELEASE_CONN_LOCK(pConnObject);
break;
case RWANS_CO_CONNECTED:
//
// If there is a Disconnect Event handler, call it.
// Otherwise, simply mark this endpoint as having
// seen a Disconnect.
//
bScheduleDisconnect = TRUE;
if (pConnObject->pAddrObject != NULL_PRWAN_TDI_ADDRESS)
{
PDisconnectEvent pDisconInd;
PVOID IndContext;
PVOID ConnectionHandle;
pDisconInd = pConnObject->pAddrObject->pDisconInd;
IndContext = pConnObject->pAddrObject->DisconIndContext;
if (pDisconInd != NULL)
{
pConnObject->State = RWANS_CO_DISCON_INDICATED;
ConnectionHandle = pConnObject->ConnectionHandle;
bScheduleDisconnect = FALSE;
RWanScheduleDisconnect(pConnObject);
//
// Conn Object lock is released within the above.
//
RWANDEBUGP(DL_EXTRA_LOUD, DC_DISCON,
("IncomingDrop: will indicate Discon, pConnObj x%x, pAddrObj x%x\n",
pConnObject, pConnObject->pAddrObject));
(*pDisconInd)(
IndContext,
ConnectionHandle,
0, // Disconnect Data Length
NULL, // Disconnect Data
0, // Disconnect Info Length
NULL, // Disconnect Info
TDI_DISCONNECT_ABORT
);
}
else
{
pConnObject->State = RWANS_CO_DISCON_HELD;
}
}
else
{
pConnObject->State = RWANS_CO_DISCON_HELD;
}
if (bScheduleDisconnect)
{
RWanScheduleDisconnect(pConnObject);
//
// Conn Object lock is released within the above.
//
}
break;
case RWANS_CO_ABORTING:
case RWANS_CO_DISCON_REQUESTED:
//
// Ignore this.
//
RWAN_RELEASE_CONN_LOCK(pConnObject);
break;
default:
RWAN_ASSERT(FALSE);
RWAN_RELEASE_CONN_LOCK(pConnObject);
break;
}
return;
}
VOID
RWanNdisModifyQoSComplete(
IN NDIS_STATUS Status,
IN NDIS_HANDLE OurVcContext,
IN PCO_CALL_PARAMETERS pCallParameters
)
/*++
Routine Description:
Arguments:
Return Value:
None
--*/
{
//
// Not expected, since we don't call NdisClModifyCallQoS
//
RWAN_ASSERT(FALSE);
}
VOID
RWanNdisRejectIncomingCall(
IN PRWAN_TDI_CONNECTION pConnObject,
IN NDIS_STATUS RejectStatus
)
/*++
Routine Description:
Reject the incoming call present on the specified Connection Object.
Arguments:
pConnObject - Points to the TDI Connection
RejectStatus - Reason for rejecting the call
Locks on Entry:
pConnObject
Locks on Exit:
None
Return Value:
None
--*/
{
PRWAN_NDIS_VC pVc;
NDIS_HANDLE NdisVcHandle;
PCO_CALL_PARAMETERS pCallParameters;
INT rc;
PRWAN_CONN_REQUEST pConnReq;
PRWAN_NDIS_AF pAf;
RWAN_HANDLE AfSpConnContext;
pVc = pConnObject->NdisConnection.pNdisVc;
NdisVcHandle = pVc->NdisVcHandle;
pCallParameters = pVc->pCallParameters;
pVc->pCallParameters = NULL;
pAf = pVc->pNdisAf;
//
// Unlink the VC from the Conn Object.
//
RWAN_UNLINK_CONNECTION_AND_VC(pConnObject, pVc); // Reject incoming call
RWAN_SET_BIT(pVc->Flags, RWANF_VC_CLOSING_CALL);
pConnReq = pConnObject->pConnReq;
pConnObject->pConnReq = NULL;
//
// State change.
//
if (pConnObject->State != RWANS_CO_ABORTING)
{
pConnObject->State = ((pConnObject->pAddrObject != NULL) ?
RWANS_CO_ASSOCIATED:
RWANS_CO_CREATED);
}
AfSpConnContext = pConnObject->AfSpConnContext;
rc = RWanDereferenceConnObject(pConnObject); // Unlinking VC in reject in-call
if (rc > 0)
{
RWAN_RELEASE_CONN_LOCK(pConnObject);
}
NdisClIncomingCallComplete(
RejectStatus,
NdisVcHandle,
pCallParameters
);
if (pConnReq != NULL)
{
RWanCompleteConnReq( // Discon Req for rejecting in call
pAf,
pConnReq,
FALSE,
NULL, // No Call Parameters
AfSpConnContext,
TDI_SUCCESS
);
}
return;
}
VOID
RWanStartCloseCall(
IN PRWAN_TDI_CONNECTION pConnObject,
IN PRWAN_NDIS_VC pVc
)
/*++
Routine Description:
Start NDIS call teardown on the VC associated with the given
connection object, if all pre-conditions are met:
0. An NDIS CloseCall isn't already going on
1. No outstanding sends
Arguments:
pConnObject - Points to TDI Connection object
pVc - Points to corresponding VC
Locks on Entry:
pConnObject
Locks on Exit:
None
Return Value:
None
--*/
{
PRWAN_NDIS_PARTY pParty;
NDIS_HANDLE NdisVcHandle;
NDIS_HANDLE NdisPartyHandle;
NDIS_STATUS Status;
PRWAN_RECEIVE_INDICATION pRcvIndHead;
PRWAN_RECEIVE_INDICATION pRcvInd;
RWANDEBUGP(DL_INFO, DC_DISCON,
("StartCloseCall: pVc x%x/x%x, PendingCount %d, pConnObj x%x\n",
pVc,
pVc->Flags,
pVc->PendingPacketCount,
pConnObject));
//
// Free up any pending receives.
//
pRcvIndHead = pVc->pRcvIndHead;
if (pRcvIndHead != NULL)
{
pVc->pRcvIndHead = NULL;
pVc->pRcvIndTail = NULL;
//
// Update the count of pending packets on this VC.
//
for (pRcvInd = pRcvIndHead; pRcvInd != NULL; pRcvInd = pRcvInd->pNextRcvInd)
{
pVc->PendingPacketCount--;
}
//
// We will free this list below.
//
}
if ((pVc != NULL) &&
(pVc->PendingPacketCount == 0) &&
(pVc->DroppingPartyCount == 0) &&
(!RWAN_IS_BIT_SET(pVc->Flags, RWANF_VC_CLOSING_CALL)))
{
NdisVcHandle = pVc->NdisVcHandle;
RWAN_SET_BIT(pVc->Flags, RWANF_VC_CLOSING_CALL);
RWAN_RESET_BIT(pVc->Flags, RWANF_VC_NEEDS_CLOSE);
if (RWAN_IS_LIST_EMPTY(&(pVc->NdisPartyList)))
{
pParty = NULL_PRWAN_NDIS_PARTY;
NdisPartyHandle = NULL;
RWAN_ASSERT(!RWAN_IS_BIT_SET(pVc->Flags, RWANF_VC_PMP));
}
else
{
pParty = CONTAINING_RECORD(pVc->NdisPartyList.Flink, RWAN_NDIS_PARTY, PartyLink);
NdisPartyHandle = pParty->NdisPartyHandle;
RWAN_SET_BIT(pParty->Flags, RWANF_PARTY_DROPPING);
pVc->DroppingPartyCount ++; // StartCloseCall PMP
pVc->ActivePartyCount --; // StartCloseCall PMP
}
RWAN_RELEASE_CONN_LOCK(pConnObject);
Status = NdisClCloseCall(
NdisVcHandle,
NdisPartyHandle,
NULL, // No CloseData
0
);
if (Status != NDIS_STATUS_PENDING)
{
RWanNdisCloseCallComplete(
Status,
(NDIS_HANDLE)pVc, // ProtocolVcContext
(NDIS_HANDLE)pParty // ProtocolPartyContext
);
}
}
else
{
if (pVc != NULL)
{
RWAN_SET_BIT(pVc->Flags, RWANF_VC_NEEDS_CLOSE);
}
RWAN_RELEASE_CONN_LOCK(pConnObject);
}
if (pRcvIndHead != NULL)
{
RWANDEBUGP(DL_INFO, DC_DISCON,
("RWanStartCloseCall: will free rcv ind list x%x on VC x%x\n",
pRcvIndHead, pVc));
RWanFreeReceiveIndList(pRcvIndHead);
}
}
VOID
RWanUnlinkVcFromAf(
IN PRWAN_NDIS_VC pVc
)
/*++
Routine Description:
Unlink a VC from the AF it belongs to.
Arguments:
pVc - Points to VC to be unlinked
Return Value:
None
--*/
{
PRWAN_NDIS_AF pAf;
INT rc;
pAf = pVc->pNdisAf;
RWAN_STRUCT_ASSERT(pAf, naf);
RWAN_ACQUIRE_AF_LOCK(pAf);
RWAN_DELETE_FROM_LIST(&(pVc->VcLink));
rc = RWanDereferenceAf(pAf); // VC unlink deref
if (rc != 0)
{
RWAN_RELEASE_AF_LOCK(pAf);
}
return;
}
VOID
RWanCompleteConnReq(
IN PRWAN_NDIS_AF pAf,
IN PRWAN_CONN_REQUEST pConnReq,
IN BOOLEAN IsOutgoingCall,
IN PCO_CALL_PARAMETERS pCallParameters OPTIONAL,
IN RWAN_HANDLE AfSpConnContext,
IN TDI_STATUS TdiStatus
)
/*++
Routine Description:
Call the completion routine for a pended TDI request on a connection.
Set up the options and completion status based on what's given to us.
Arguments:
pAf - The AF block on which the request was made
pConnReq - the pended request to be completed
IsOutgoingCall - Is this an outgoing call?
pCallParameters - if applicable, this should be mapped to connection info
AfSpConnContext - Connection context, if applicable, for the media-specific
module.
TdiStatus - completion status for the request
Return Value:
None
--*/
{
RWAN_STATUS RWanStatus;
ULONG TdiQoSLength = 0;
if (pConnReq == NULL)
{
return;
}
RWAN_STRUCT_ASSERT(pConnReq, nrc);
//
// Update Connection Information if we need to.
//
if ((pConnReq->pConnInfo != NULL) &&
(pCallParameters != NULL))
{
RWanStatus = (*pAf->pAfInfo->AfChars.pAfSpUpdateTdiOptions)(
pAf->AfSpAFContext,
AfSpConnContext,
IsOutgoingCall,
pCallParameters,
&pConnReq->pConnInfo,
pConnReq->pConnInfo->Options,
&pConnReq->pConnInfo->OptionsLength
);
}
//
// Call the completion routine.
//
(*pConnReq->Request.pReqComplete)(
pConnReq->Request.ReqContext,
TdiStatus,
0
);
RWanFreeConnReq(pConnReq);
}