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.
 
 
 
 
 
 

3102 lines
66 KiB

/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
D:\nt\private\ntos\tdi\rawwan\core\tdiconn.c
Abstract:
TDI Entry points and support routines for Connection Objects.
Revision History:
Who When What
-------- -------- ----------------------------------------------
arvindm 04-30-97 Created
Notes:
--*/
#include <precomp.h>
#define _FILENUMBER 'NCDT'
//
// Private macros and definitions for the Connection Table. Copied from TCP.
//
#define RWAN_GET_SLOT_FROM_CONN_ID(_Id) ((_Id) & 0xffffff)
#define RWAN_GET_INSTANCE_FROM_CONN_ID(_Id) ((UCHAR)((_Id) >> 24))
#define RWAN_MAKE_CONN_ID(_Inst, _Slot) ((((RWAN_CONN_ID)(_Inst)) << 24) | ((RWAN_CONN_ID)(_Slot)))
#define RWAN_INVALID_CONN_ID RWAN_MAKE_CONN_ID(0xff, 0xffffff)
#define CONN_TABLE_GROW_DELTA 16
TDI_STATUS
RWanTdiOpenConnection(
IN OUT PTDI_REQUEST pTdiRequest,
IN PVOID ConnectionHandle
)
/*++
Routine Description:
This is the TDI entry point for opening (creating) a Connection Object.
We allocate a new Connection object and return an index to it in the
request itself.
Arguments:
pTdiRequest - Pointer to the TDI Request
ConnectionHandle- This is how we refer to this connection in up-calls
Return Value:
TDI_SUCCESS if a connection object was successfully created, TDI_XXX
failure code otherwise.
--*/
{
TDI_STATUS Status;
PRWAN_TDI_CONNECTION pConnObject;
RWAN_CONN_ID ConnId;
//
// Initialize.
//
pConnObject = NULL_PRWAN_TDI_CONNECTION;
do
{
pConnObject = RWanAllocateConnObject();
if (pConnObject == NULL_PRWAN_TDI_CONNECTION)
{
Status = TDI_NO_RESOURCES;
break;
}
RWAN_ACQUIRE_CONN_TABLE_LOCK();
//
// Prepare a context to be returned. We don't return a pointer
// to our Connection object as our context, because, seemingly,
// there is a chance that we might get invalid connection
// contexts in other TDI requests. So we need a way to validate
// a received connection context. This indirection (of using a
// Connection Index) helps us do so.
//
ConnId = RWanGetConnId(pConnObject);
RWAN_RELEASE_CONN_TABLE_LOCK();
if (ConnId == RWAN_INVALID_CONN_ID)
{
Status = TDI_NO_RESOURCES;
break;
}
RWanReferenceConnObject(pConnObject); // TdiOpenConnection ref
pConnObject->ConnectionHandle = ConnectionHandle;
//
// Return our context for this connection object.
//
pTdiRequest->Handle.ConnectionContext = (CONNECTION_CONTEXT)UlongToPtr(ConnId);
Status = TDI_SUCCESS;
break;
}
while (FALSE);
RWANDEBUGP(DL_EXTRA_LOUD, DC_CONNECT,
("RWanTdiOpenConnection: pConnObj x%p, Handle x%p, Status x%x\n",
pConnObject,
ConnectionHandle,
Status));
if (Status != TDI_SUCCESS)
{
//
// Clean up before returning.
//
if (pConnObject != NULL_PRWAN_TDI_CONNECTION)
{
RWAN_FREE_MEM(pConnObject);
}
}
return (Status);
}
#if DBG
PVOID
RWanTdiDbgGetConnObject(
IN HANDLE ConnectionContext
)
/*++
Routine Description:
DEBUGGING ONLY: Return our internal context for a connection
Arguments:
ConnectionContext - TDI context
Return Value:
Pointer to our Connection structure if found, else NULL.
--*/
{
PRWAN_TDI_CONNECTION pConnObject;
RWAN_ACQUIRE_CONN_TABLE_LOCK();
pConnObject = RWanGetConnFromId((RWAN_CONN_ID)PtrToUlong(ConnectionContext));
RWAN_RELEASE_CONN_TABLE_LOCK();
return ((PVOID)pConnObject);
}
#endif
TDI_STATUS
RWanTdiCloseConnection(
IN PTDI_REQUEST pTdiRequest
)
/*++
Routine Description:
This is the TDI entry point to close a Connection Object.
If the connection object is participating in a connection, we
initiate teardown. If it is associated with an address object, we
handle disassociation.
Arguments:
pTdiRequest - Pointer to the TDI Request
Return Value:
TDI_STATUS - this is TDI_PENDING if we started off CloseConnection
successfully, TDI_SUCCESS if we are done with CloseConnection in here,
TDI_INVALID_CONNECTION if the connection context is invalid.
--*/
{
PRWAN_TDI_CONNECTION pConnObject;
PRWAN_TDI_ADDRESS pAddrObject;
PRWAN_NDIS_VC pVc;
TDI_STATUS Status;
RWAN_CONN_ID ConnId;
PRWAN_CONN_REQUEST pConnReq;
NDIS_HANDLE NdisVcHandle;
INT rc;
BOOLEAN bIsLockAcquired;
#if DBG
RWAN_IRQL EntryIrq, ExitIrq;
#endif // DBG
RWAN_GET_ENTRY_IRQL(EntryIrq);
ConnId = (RWAN_CONN_ID) PtrToUlong(pTdiRequest->Handle.ConnectionContext);
Status = TDI_PENDING;
bIsLockAcquired = FALSE; // Do we hold the Conn Object locked?
do
{
RWAN_ACQUIRE_CONN_TABLE_LOCK();
pConnObject = RWanGetConnFromId(ConnId);
if (pConnObject == NULL_PRWAN_TDI_CONNECTION)
{
RWAN_RELEASE_CONN_TABLE_LOCK();
Status = TDI_INVALID_CONNECTION;
break;
}
//
// Remove this Connection Object from the Conn Table.
// This effectively invalidates this ConnId.
//
RWanFreeConnId(ConnId);
RWAN_RELEASE_CONN_TABLE_LOCK();
RWANDEBUGP(DL_LOUD, DC_DISCON,
("TdiCloseConnection: pConnObj x%p, State/Flags/Ref x%x/x%x/%d, pAddrObj x%p\n",
pConnObject,
pConnObject->State,
pConnObject->Flags,
pConnObject->RefCount,
pConnObject->pAddrObject));
RWAN_ACQUIRE_CONN_LOCK(pConnObject);
#if DBG
pConnObject->OldState = pConnObject->State;
pConnObject->OldFlags = pConnObject->Flags;
#endif
//
// Mark this Connection Object as closing, and set Delete
// Notification info: this will be called when the Connection
// is dereferenced to death.
//
RWAN_SET_BIT(pConnObject->Flags, RWANF_CO_CLOSING);
pConnObject->DeleteNotify.pDeleteRtn = pTdiRequest->RequestNotifyObject;
pConnObject->DeleteNotify.DeleteContext = pTdiRequest->RequestContext;
//
// Discard any pending operation.
//
pConnReq = pConnObject->pConnReq;
if (pConnReq != NULL)
{
RWanFreeConnReq(pConnReq);
pConnObject->pConnReq = NULL;
}
//
// Remove the TdiOpenConnection reference.
//
rc = RWanDereferenceConnObject(pConnObject); // deref: TdiCloseConn
if (rc == 0)
{
//
// The Connection object is gone. CloseConnection completion
// would have been called.
//
break;
}
pAddrObject = pConnObject->pAddrObject;
//
// Force Disassociate Address if associated.
//
if (pAddrObject != NULL_PRWAN_TDI_ADDRESS)
{
//
// Add a temp reference to keep this Conn Object alive while we
// reacquire locks in the right order.
//
RWanReferenceConnObject(pConnObject); // temp ref: CloseConn
RWAN_RELEASE_CONN_LOCK(pConnObject);
RWAN_ACQUIRE_ADDRESS_LOCK(pAddrObject);
RWAN_ACQUIRE_CONN_LOCK_DPC(pConnObject);
//
// Remove from list on Address Object.
//
RWAN_DELETE_FROM_LIST(&(pConnObject->ConnLink));
pConnObject->pAddrObject = NULL_PRWAN_TDI_ADDRESS;
rc = RWanDereferenceConnObject(pConnObject); // Force disassoc deref: CloseConn
RWAN_ASSERT(rc != 0);
RWAN_RELEASE_CONN_LOCK_DPC(pConnObject);
rc = RWanDereferenceAddressObject(pAddrObject); // Force Disassoc: CloseConn
if (rc != 0)
{
RWAN_RELEASE_ADDRESS_LOCK(pAddrObject);
}
//
// Reacquire ConnObject lock: we still have the temp reference on it.
//
RWAN_ACQUIRE_CONN_LOCK(pConnObject);
rc = RWanDereferenceConnObject(pConnObject); // remove temp ref: CloseConn
if (rc == 0)
{
RWAN_ASSERT(Status == TDI_PENDING);
break;
}
}
bIsLockAcquired = TRUE;
//
// If this is a root connection object, abort the connection.
//
if (RWAN_IS_BIT_SET(pConnObject->Flags, RWANF_CO_ROOT))
{
RWANDEBUGP(DL_FATAL, DC_DISCON,
("TdiCloseConn: found root Conn Obj x%p\n", pConnObject));
RWanReferenceConnObject(pConnObject); // temp ref: CloseConn (root)
RWAN_RELEASE_CONN_LOCK(pConnObject);
RWanDoAbortConnection(pConnObject);
RWAN_ACQUIRE_CONN_LOCK(pConnObject);
rc = RWanDereferenceConnObject(pConnObject); // temp ref: CloseConn (root)
if (rc == 0)
{
bIsLockAcquired = FALSE;
}
break;
}
//
// If the connection is active, tear it down.
//
switch (pConnObject->State)
{
case RWANS_CO_OUT_CALL_INITIATED:
case RWANS_CO_DISCON_REQUESTED:
case RWANS_CO_IN_CALL_ACCEPTING:
//
// An NDIS operation is in progress. When it completes,
// the flag (CLOSING) we set earlier will cause the
// CloseConnection to continue.
//
break;
case RWANS_CO_CONNECTED:
RWanDoTdiDisconnect(
pConnObject,
NULL, // pTdiRequest
NULL, // pTimeout
0, // Flags
NULL, // pDisconnInfo
NULL // pReturnInfo
);
//
// ConnObject Lock is released within the above.
//
bIsLockAcquired = FALSE;
break;
case RWANS_CO_DISCON_INDICATED:
case RWANS_CO_DISCON_HELD:
case RWANS_CO_ABORTING:
//
// We would have started off an NDIS CloseCall/DropParty
// operation.
//
break;
case RWANS_CO_IN_CALL_INDICATED:
//
// Reject the incoming call.
//
RWanNdisRejectIncomingCall(pConnObject, NDIS_STATUS_FAILURE);
//
// ConnObject Lock is released within the above.
//
bIsLockAcquired = FALSE;
break;
case RWANS_CO_CREATED:
case RWANS_CO_ASSOCIATED:
case RWANS_CO_LISTENING:
default:
//
// We should have broken out of the outer do..while
// earlier.
//
RWANDEBUGP(DL_FATAL, DC_WILDCARD,
("TdiCloseConn: pConnObj x%p/x%x, bad state %d\n",
pConnObject, pConnObject->Flags, pConnObject->State));
RWAN_ASSERT(FALSE);
break;
}
break;
}
while (FALSE);
if (bIsLockAcquired)
{
RWAN_RELEASE_CONN_LOCK(pConnObject);
}
RWAN_CHECK_EXIT_IRQL(EntryIrq, ExitIrq);
RWANDEBUGP(DL_VERY_LOUD, DC_DISCON,
("TdiCloseConn: pConnObject x%p, returning x%x\n", pConnObject, Status));
return (Status);
}
TDI_STATUS
RWanTdiAssociateAddress(
IN PTDI_REQUEST pTdiRequest,
IN PVOID AddressContext
)
/*++
Routine Description:
This is the TDI entry point to associate a connection object
with an address object. The connection object is identified
by its context buried in the TDI Request, and AddressContext
is our context for an address object.
Arguments:
pTdiRequest - Pointer to the TDI Request
AddressContext - Actually a pointer to our TDI Address object.
Return Value:
TDI_SUCCESS if the association was successful, TDI_ALREADY_ASSOCIATED
if the connection object is already associated with an address object,
TDI_INVALID_CONNECTION if the specified connection context is invalid.
--*/
{
PRWAN_TDI_ADDRESS pAddrObject;
PRWAN_TDI_CONNECTION pConnObject;
RWAN_CONN_ID ConnId;
TDI_STATUS Status;
RWAN_STATUS RWanStatus;
pAddrObject = (PRWAN_TDI_ADDRESS)AddressContext;
RWAN_STRUCT_ASSERT(pAddrObject, nta);
ConnId = (RWAN_CONN_ID) PtrToUlong(pTdiRequest->Handle.ConnectionContext);
do
{
RWAN_ACQUIRE_CONN_TABLE_LOCK();
pConnObject = RWanGetConnFromId(ConnId);
RWAN_RELEASE_CONN_TABLE_LOCK();
if (pConnObject == NULL_PRWAN_TDI_CONNECTION)
{
Status = TDI_INVALID_CONNECTION;
break;
}
if (pConnObject->pAddrObject != NULL_PRWAN_TDI_ADDRESS)
{
Status = TDI_ALREADY_ASSOCIATED;
break;
}
//
// Get a context for this associated connection object
// from the media-specific module.
//
if (pAddrObject->pProtocol->pAfInfo->AfChars.pAfSpAssociateConnection)
{
RWanStatus = (*pAddrObject->pProtocol->pAfInfo->AfChars.pAfSpAssociateConnection)(
pAddrObject->AfSpAddrContext,
(RWAN_HANDLE)pConnObject,
&(pConnObject->AfSpConnContext));
if (RWanStatus != RWAN_STATUS_SUCCESS)
{
Status = RWanToTdiStatus(RWanStatus);
break;
}
RWAN_SET_BIT(pConnObject->Flags, RWANF_CO_AFSP_CONTEXT_VALID);
RWANDEBUGP(DL_LOUD, DC_WILDCARD,
("Associate: AddrObj %p, ConnObj %p, AfSpAddrCont %x, AfSpConnCont %x\n",
pAddrObject,
pConnObject,
pAddrObject->AfSpAddrContext,
pConnObject->AfSpConnContext));
}
//
// Acquire locks in the right order.
//
RWAN_ACQUIRE_ADDRESS_LOCK(pAddrObject);
RWAN_ACQUIRE_CONN_LOCK_DPC(pConnObject);
RWAN_ASSERT(pConnObject->State == RWANS_CO_CREATED);
pConnObject->State = RWANS_CO_ASSOCIATED;
//
// Attach this Connection Object to this Address Object.
//
pConnObject->pAddrObject = pAddrObject;
RWAN_INSERT_TAIL_LIST(&(pAddrObject->IdleConnList),
&(pConnObject->ConnLink));
RWanReferenceConnObject(pConnObject); // Associate ref
//
// Check if this is a Leaf connection object.
//
if (RWAN_IS_BIT_SET(pAddrObject->Flags, RWANF_AO_PMP_ROOT))
{
RWAN_ASSERT(pAddrObject->pRootConnObject != NULL);
RWAN_SET_BIT(pConnObject->Flags, RWANF_CO_LEAF);
pConnObject->pRootConnObject = pAddrObject->pRootConnObject;
}
RWAN_RELEASE_CONN_LOCK_DPC(pConnObject);
RWanReferenceAddressObject(pAddrObject); // New Connection object associated
RWAN_RELEASE_ADDRESS_LOCK(pAddrObject);
Status = TDI_SUCCESS;
break;
}
while (FALSE);
RWANDEBUGP(DL_EXTRA_LOUD, DC_CONNECT,
("RWanTdiAssociate: pAddrObject x%p, ConnId x%x, pConnObj x%p, Status x%x\n",
pAddrObject, ConnId, pConnObject, Status));
return (Status);
}
TDI_STATUS
RWanTdiDisassociateAddress(
IN PTDI_REQUEST pTdiRequest
)
/*++
Routine Description:
This is the TDI entry point for disassociating a connection object
from the address object it is currently associated with. The connection
object is identified by its handle buried within the TDI request.
Arguments:
pTdiRequest - Pointer to the TDI Request
Return Value:
TDI_SUCCESS if successful, TDI_NOT_ASSOCIATED if the connection object
isn't associated with an address object, TDI_INVALID_CONNECTION if the
given connection context is invalid, TDI_CONNECTION_ACTIVE if the
connection is active.
--*/
{
PRWAN_TDI_CONNECTION pConnObject;
RWAN_CONN_ID ConnId;
PRWAN_TDI_ADDRESS pAddrObject;
TDI_STATUS Status;
INT rc;
ConnId = (RWAN_CONN_ID) PtrToUlong(pTdiRequest->Handle.ConnectionContext);
pAddrObject = NULL_PRWAN_TDI_ADDRESS;
do
{
RWAN_ACQUIRE_CONN_TABLE_LOCK();
pConnObject = RWanGetConnFromId(ConnId);
RWAN_RELEASE_CONN_TABLE_LOCK();
if (pConnObject == NULL_PRWAN_TDI_CONNECTION)
{
Status = TDI_INVALID_CONNECTION;
break;
}
//
// See if the connection is associated.
//
pAddrObject = pConnObject->pAddrObject;
if (pAddrObject == NULL_PRWAN_TDI_ADDRESS)
{
Status = TDI_NOT_ASSOCIATED;
break;
}
//
// Tell the media-specific module about this disassociation.
// This invalidates the module's context for this connection
// object.
//
if (RWAN_IS_BIT_SET(pConnObject->Flags, RWANF_CO_AFSP_CONTEXT_VALID) &&
pAddrObject->pProtocol->pAfInfo->AfChars.pAfSpDisassociateConnection)
{
(*pAddrObject->pProtocol->pAfInfo->AfChars.pAfSpDisassociateConnection)(
pConnObject->AfSpConnContext);
RWAN_RESET_BIT(pConnObject->Flags, RWANF_CO_AFSP_CONTEXT_VALID);
}
//
// Unlink this from the address object.
//
RWAN_ACQUIRE_ADDRESS_LOCK(pAddrObject);
RWAN_DELETE_FROM_LIST(&(pConnObject->ConnLink));
rc = RWanDereferenceAddressObject(pAddrObject); // Disassoc conn
if (rc != 0)
{
RWAN_RELEASE_ADDRESS_LOCK(pAddrObject);
}
RWAN_ACQUIRE_CONN_LOCK(pConnObject);
pConnObject->pAddrObject = NULL_PRWAN_TDI_ADDRESS;
rc = RWanDereferenceConnObject(pConnObject); // Disassoc deref
if (rc != 0)
{
RWAN_RELEASE_CONN_LOCK(pConnObject);
}
Status = TDI_SUCCESS;
break;
}
while (FALSE);
RWANDEBUGP(DL_LOUD, DC_DISCON,
("RWanTdiDisassociate: pAddrObject x%p, pConnObj x%p, Status x%x\n",
pAddrObject, pConnObject, Status));
return (Status);
}
TDI_STATUS
RWanTdiConnect(
IN PTDI_REQUEST pTdiRequest,
IN PVOID pTimeout OPTIONAL,
IN PTDI_CONNECTION_INFORMATION pRequestInfo,
IN PTDI_CONNECTION_INFORMATION pReturnInfo
)
/*++
Routine Description:
This is the TDI Entry point for setting up a connection.
The connection object is identified by its handle buried within
the TDI request.
Arguments:
pTdiRequest - Pointer to the TDI Request
pTimeout - Optional connect timeout
pRequestInfo - Points to information for making the connection
pReturnInfo - Place where we return final connection information
Return Value:
TDI_STATUS - this is TDI_PENDING if we successfully fired off
a Connect Request, TDI_NO_RESOURCES if we failed because of some
allocation problem, TDI_BAD_ADDR if the destination address isn't
valid, TDI_INVALID_CONNECTION if the specified Connection Object
isn't valid, TDI_NOT_ASSOCIATED if the connection object isn't
associated with an Address Object.
--*/
{
PRWAN_TDI_CONNECTION pConnObject;
RWAN_CONN_ID ConnId;
PRWAN_TDI_ADDRESS pAddrObject;
PRWAN_NDIS_AF_CHARS pAfChars;
PRWAN_NDIS_AF_INFO pAfInfo;
PRWAN_NDIS_AF pAf;
PRWAN_NDIS_VC pVc;
PRWAN_CONN_REQUEST pConnReq;
TDI_STATUS Status;
RWAN_STATUS RWanStatus;
ULONG CallFlags;
PCO_CALL_PARAMETERS pCallParameters;
NDIS_HANDLE NdisVcHandle;
NDIS_STATUS NdisStatus;
BOOLEAN bIsLockAcquired;
#if DBG
RWAN_IRQL EntryIrq, ExitIrq;
#endif // DBG
RWAN_GET_ENTRY_IRQL(EntryIrq);
//
// Initialize
//
pConnReq = NULL;
pCallParameters = NULL;
bIsLockAcquired = FALSE;
pVc = NULL;
#if DBG
pConnObject = NULL;
pAddrObject = NULL;
#endif
do
{
//
// See if the destination address is present.
//
if ((pRequestInfo == NULL) ||
(pRequestInfo->RemoteAddress == NULL))
{
Status = TDI_BAD_ADDR;
break;
}
//
// Allocate a Connection Request structure to keep track
// of this request.
//
pConnReq = RWanAllocateConnReq();
if (pConnReq == NULL)
{
Status = TDI_NO_RESOURCES;
break;
}
pConnReq->Request.pReqComplete = pTdiRequest->RequestNotifyObject;
pConnReq->Request.ReqContext = pTdiRequest->RequestContext;
pConnReq->pConnInfo = pReturnInfo;
//
// Get the Connection Object.
//
ConnId = (RWAN_CONN_ID) PtrToUlong(pTdiRequest->Handle.ConnectionContext);
RWAN_ACQUIRE_CONN_TABLE_LOCK();
pConnObject = RWanGetConnFromId(ConnId);
RWAN_RELEASE_CONN_TABLE_LOCK();
if (pConnObject == NULL_PRWAN_TDI_CONNECTION)
{
Status = TDI_INVALID_CONNECTION;
break;
}
bIsLockAcquired = TRUE;
RWAN_ACQUIRE_CONN_LOCK(pConnObject);
//
// See if it is associated.
//
pAddrObject = pConnObject->pAddrObject;
if (pAddrObject == NULL_PRWAN_TDI_ADDRESS)
{
Status = TDI_NOT_ASSOCIATED;
break;
}
//
// Check its state.
//
if (pConnObject->State != RWANS_CO_ASSOCIATED)
{
Status = TDI_INVALID_STATE;
break;
}
//
// Do we have atleast one NDIS AF for this protocol?
//
pAfInfo = pAddrObject->pProtocol->pAfInfo;
if (RWAN_IS_LIST_EMPTY(&(pAfInfo->NdisAfList)))
{
Status = TDI_BAD_ADDR;
break;
}
pAfChars = &(pAfInfo->AfChars);
CallFlags = RWAN_CALLF_OUTGOING_CALL;
if (RWAN_IS_BIT_SET(pAddrObject->Flags, RWANF_AO_PMP_ROOT))
{
CallFlags |= RWAN_CALLF_POINT_TO_MULTIPOINT;
pConnObject->pRootConnObject = pAddrObject->pRootConnObject;
if (pAddrObject->pRootConnObject->NdisConnection.pNdisVc == NULL)
{
CallFlags |= RWAN_CALLF_PMP_FIRST_LEAF;
RWANDEBUGP(DL_INFO, DC_CONNECT,
("TdiConnect PMP: First Leaf: ConnObj %p, RootConn %p, AddrObj %p\n",
pConnObject,
pConnObject->pRootConnObject,
pAddrObject));
}
else
{
CallFlags |= RWAN_CALLF_PMP_ADDNL_LEAF;
RWANDEBUGP(DL_INFO, DC_CONNECT,
("TdiConnect PMP: Subseq Leaf: ConnObj %p, RootConn %p, AddrObj %p, Vc %p\n",
pConnObject,
pConnObject->pRootConnObject,
pAddrObject,
pConnObject->pRootConnObject->NdisConnection.pNdisVc
));
}
}
else
{
CallFlags |= RWAN_CALLF_POINT_TO_POINT;
}
//
// We get the AF from the media specific module.
//
pAf = NULL;
//
// Validate and convert call parameters. Also get the AF (aka port) on
// which the call should be made.
//
RWanStatus = (*pAfChars->pAfSpTdi2NdisOptions)(
pConnObject->AfSpConnContext,
CallFlags,
pRequestInfo,
pRequestInfo->Options,
pRequestInfo->OptionsLength,
&pAf,
&pCallParameters
);
if (RWanStatus != RWAN_STATUS_SUCCESS)
{
RWANDEBUGP(DL_WARN, DC_CONNECT,
("TdiConnect: pConnObj x%p, Tdi2NdisOptions ret x%x\n", pConnObject, RWanStatus));
Status = RWanToTdiStatus(RWanStatus);
break;
}
if (pAf == NULL)
{
//
// Get at the first NDIS AF block for this TDI protocol.
//
pAf = CONTAINING_RECORD(pAfInfo->NdisAfList.Flink, RWAN_NDIS_AF, AfInfoLink);
}
RWAN_ASSERT(pAf != NULL);
RWAN_STRUCT_ASSERT(pAf, naf);
RWAN_ASSERT(pCallParameters != NULL);
if (CallFlags & RWAN_CALLF_POINT_TO_MULTIPOINT)
{
RWAN_RELEASE_CONN_LOCK(pConnObject);
bIsLockAcquired = FALSE;
Status = RWanTdiPMPConnect(
pAfInfo,
pAddrObject,
pConnObject,
pCallParameters,
CallFlags,
pConnReq
);
break;
}
//
// Allocate an NDIS VC. To avoid deadlocks, we must relinquish
// the Conn Object lock temporarily.
//
RWAN_RELEASE_CONN_LOCK(pConnObject);
pVc = RWanAllocateVc(pAf, TRUE);
if (pVc == NULL)
{
Status = TDI_NO_RESOURCES;
bIsLockAcquired = FALSE;
break;
}
RWAN_SET_VC_CALL_PARAMS(pVc, pCallParameters);
RWAN_ACQUIRE_CONN_LOCK(pConnObject);
RWAN_SET_BIT(pVc->Flags, RWANF_VC_OUTGOING);
//
// We have completed all "immediate failure" checks.
//
//
// Link the VC to this Connection Object.
//
RWAN_LINK_CONNECTION_TO_VC(pConnObject, pVc);
RWanReferenceConnObject(pConnObject); // VC ref
//
// Save the Connection Request
//
pConnObject->pConnReq = pConnReq;
//
// Save the NDIS Call Parameters
//
pVc->pCallParameters = pCallParameters;
pConnObject->State = RWANS_CO_OUT_CALL_INITIATED;
RWAN_RELEASE_CONN_LOCK(pConnObject);
bIsLockAcquired = FALSE;
//
// Move this connection object from the Idle list to the
// Active list on the address object.
//
RWAN_ACQUIRE_ADDRESS_LOCK(pAddrObject);
RWAN_DELETE_FROM_LIST(&(pConnObject->ConnLink));
RWAN_INSERT_TAIL_LIST(&(pAddrObject->ActiveConnList),
&(pConnObject->ConnLink));
pAddrObject->pRootConnObject = pConnObject;
RWAN_RELEASE_ADDRESS_LOCK(pAddrObject);
NdisVcHandle = pVc->NdisVcHandle;
//
// Place the call.
//
NdisStatus = NdisClMakeCall(
NdisVcHandle,
pCallParameters,
NULL, // ProtocolPartyContext
NULL // pNdisPartyHandle
);
RWAN_CHECK_EXIT_IRQL(EntryIrq, ExitIrq);
if (NdisStatus != NDIS_STATUS_PENDING)
{
RWanNdisMakeCallComplete(
NdisStatus,
(NDIS_HANDLE)pVc,
NULL, // NdisPartyHandle
pCallParameters
);
}
RWAN_CHECK_EXIT_IRQL(EntryIrq, ExitIrq);
Status = TDI_PENDING;
break;
}
while (FALSE);
if (Status != TDI_PENDING)
{
//
// Clean up.
//
if (bIsLockAcquired)
{
RWAN_RELEASE_CONN_LOCK(pConnObject);
}
if (pConnReq != NULL)
{
RWanFreeConnReq(pConnReq);
}
if (pCallParameters != NULL)
{
(*pAfChars->pAfSpReturnNdisOptions)(
pAf->AfSpAFContext,
pCallParameters
);
}
if (pVc != NULL)
{
RWanFreeVc(pVc);
}
}
RWAN_CHECK_EXIT_IRQL(EntryIrq, ExitIrq);
RWANDEBUGP(DL_LOUD, DC_CONNECT,
("TdiConnect: pTdiReq x%p, pConnObj x%p, pAddrObj x%p, Status x%x\n",
pTdiRequest, pConnObject, pAddrObject, Status));
return (Status);
}
TDI_STATUS
RWanTdiPMPConnect(
IN PRWAN_NDIS_AF_INFO pAfInfo,
IN PRWAN_TDI_ADDRESS pAddrObject,
IN PRWAN_TDI_CONNECTION pConnObject,
IN PCO_CALL_PARAMETERS pCallParameters,
IN ULONG CallFlags,
IN PRWAN_CONN_REQUEST pConnReq
)
/*++
Routine Description:
Handle a TDI Connect for a point-to-multipoint call.
Arguments:
pAfInfo - Pointer to AF Info structure
pAddrObject - Address Object on which this PMP call is made
pConnObject - Connection object representing a node of the PMP call
pCallParameters - NDIS call parameters
CallFlags - Flags indicating type of call
pConnReq - Information about the TDI request
Return Value:
TDI_PENDING if a PMP call was launched successfully, TDI_XXX error code
otherwise.
--*/
{
PRWAN_TDI_CONNECTION pRootConnObject;
PRWAN_NDIS_AF pAf;
PRWAN_NDIS_VC pVc;
PRWAN_NDIS_PARTY pParty;
NDIS_HANDLE NdisVcHandle;
NDIS_STATUS NdisStatus;
TDI_STATUS Status;
BOOLEAN bIsFirstLeaf;
#if DBG
RWAN_IRQL EntryIrq, ExitIrq;
#endif // DBG
RWAN_GET_ENTRY_IRQL(EntryIrq);
bIsFirstLeaf = ((CallFlags & RWAN_CALLF_PMP_LEAF_TYPE_MASK) == RWAN_CALLF_PMP_FIRST_LEAF);
Status = TDI_PENDING;
pParty = NULL;
pVc = NULL;
pRootConnObject = NULL;
RWANDEBUGP(DL_LOUD, DC_CONNECT,
("TdiPMPConnect: pAddrObj x%p/x%x, pConnObj x%p/x%x, CallFlags x%x\n",
pAddrObject, pAddrObject->Flags,
pConnObject, pConnObject->Flags,
CallFlags));
do
{
//
// Allocate party object.
//
RWAN_ALLOC_MEM(pParty, RWAN_NDIS_PARTY, sizeof(RWAN_NDIS_PARTY));
if (pParty == NULL)
{
Status = TDI_NO_RESOURCES;
break;
}
RWAN_ZERO_MEM(pParty, sizeof(RWAN_NDIS_PARTY));
RWAN_SET_SIGNATURE(pParty, npy);
//
// Get at the root Connection object.
//
pRootConnObject = pAddrObject->pRootConnObject;
RWAN_ASSERT(pRootConnObject != NULL);
if (bIsFirstLeaf)
{
//
// Get at the first NDIS AF block for this TDI protocol.
//
pAf = CONTAINING_RECORD(pAfInfo->NdisAfList.Flink, RWAN_NDIS_AF, AfInfoLink);
pVc = RWanAllocateVc(pAf, TRUE);
if (pVc == NULL)
{
Status = TDI_NO_RESOURCES;
break;
}
RWAN_SET_BIT(pVc->Flags, RWANF_VC_OUTGOING);
RWAN_SET_BIT(pVc->Flags, RWANF_VC_PMP);
RWAN_SET_VC_CALL_PARAMS(pVc, pCallParameters);
//
// Link the VC to the Root Connection Object.
//
RWAN_ACQUIRE_CONN_LOCK(pRootConnObject);
RWAN_LINK_CONNECTION_TO_VC(pRootConnObject, pVc);
//
// Save pointer to this first party, for use in MakeCallComplete.
//
pVc->pPartyMakeCall = pParty;
RWanReferenceConnObject(pRootConnObject); // VC ref: TDI Conn PMP
RWAN_RELEASE_CONN_LOCK(pRootConnObject);
}
else
{
pVc = pRootConnObject->NdisConnection.pNdisVc;
}
//
// We have finished all local checks. Fill in more of the Party structure.
//
pParty->pVc = pVc;
pParty->pConnObject = pConnObject;
pParty->pCallParameters = pCallParameters;
RWAN_ACQUIRE_CONN_LOCK(pConnObject);
RWanReferenceConnObject(pConnObject); // Party ref
pConnObject->State = RWANS_CO_OUT_CALL_INITIATED;
//
// Save the Connection Request
//
pConnObject->pConnReq = pConnReq;
//
// Link the Party to this Connection Object.
//
RWAN_ASSERT(pConnObject->NdisConnection.pNdisParty == NULL);
pConnObject->NdisConnection.pNdisParty = pParty;
RWAN_RELEASE_CONN_LOCK(pConnObject);
//
// Link the Party and VC structures.
//
RWAN_ACQUIRE_CONN_LOCK(pRootConnObject);
RWAN_INSERT_TAIL_LIST(&(pVc->NdisPartyList), &(pParty->PartyLink));
pVc->AddingPartyCount ++;
NdisVcHandle = pVc->NdisVcHandle;
RWAN_RELEASE_CONN_LOCK(pRootConnObject);
//
// Move this connection object from the Idle list to the
// Active list on the address object.
//
RWAN_ACQUIRE_ADDRESS_LOCK(pAddrObject);
RWAN_DELETE_FROM_LIST(&(pConnObject->ConnLink));
RWAN_INSERT_TAIL_LIST(&(pAddrObject->ActiveConnList),
&(pConnObject->ConnLink));
RWAN_RELEASE_ADDRESS_LOCK(pAddrObject);
RWANDEBUGP(DL_LOUD, DC_CONNECT,
("RWanTdiPMPConnect: AddrObj x%p, ConnObj x%p, RootConn x%p, VC %p, Pty %p, FirstLeaf %d\n",
pAddrObject, pConnObject, pRootConnObject, pVc, pParty, bIsFirstLeaf));
if (bIsFirstLeaf)
{
//
// Place the call.
//
NdisStatus = NdisClMakeCall(
NdisVcHandle,
pCallParameters,
(NDIS_HANDLE)pParty, // ProtocolPartyContext
&pParty->NdisPartyHandle // pNdisPartyHandle
);
RWAN_CHECK_EXIT_IRQL(EntryIrq, ExitIrq);
if (NdisStatus != NDIS_STATUS_PENDING)
{
RWanNdisMakeCallComplete(
NdisStatus,
(NDIS_HANDLE)pVc,
pParty->NdisPartyHandle, // NdisPartyHandle
pCallParameters
);
}
}
else
{
//
// Add the new party.
//
NdisStatus = NdisClAddParty(
NdisVcHandle,
(NDIS_HANDLE)pParty,
pCallParameters,
&pParty->NdisPartyHandle
);
if (NdisStatus != NDIS_STATUS_PENDING)
{
RWanNdisAddPartyComplete(
NdisStatus,
(NDIS_HANDLE)pParty,
pParty->NdisPartyHandle,
pCallParameters
);
}
}
RWAN_ASSERT(Status == TDI_PENDING);
break;
}
while (FALSE);
if (Status != TDI_PENDING)
{
//
// Failure - clean up.
//
RWAN_ASSERT(Status == TDI_NO_RESOURCES);
if (pParty != NULL)
{
RWAN_FREE_MEM(pParty);
}
if (pVc != NULL)
{
RWanFreeVc(pVc);
}
}
RWAN_CHECK_EXIT_IRQL(EntryIrq, ExitIrq);
return (Status);
}
TDI_STATUS
RWanTdiListen(
IN PTDI_REQUEST pTdiRequest,
IN USHORT Flags,
IN PTDI_CONNECTION_INFORMATION pAcceptableAddr,
IN PTDI_CONNECTION_INFORMATION pConnectedAddr
)
/*++
Routine Description:
This is the TDI Entry point for posting a Listen. The Connection
Object is identified by its context buried within the TDI request.
We save off information about this request, and move the connection
from the idle list to the listen list.
For now, we ignore any given remote address information.
TBD: Support remote address information in TdiListen().
Arguments:
pTdiRequest - Pointer to the TDI Request
Flags - Listen flags
pAcceptableAddr - List of acceptable remote addresses
pConnectedAddr - Place to return connected remote address
Return Value:
TDI_STATUS - this is TDI_PENDING if we successfully queued a Listen,
TDI_NO_RESOURCES if we ran into a resource failure, TDI_NOT_ASSOCIATED
if the given Connection object isn't associated with an address,
TDI_INVALID_CONNECTION if the specified connection object is invalid.
--*/
{
PRWAN_TDI_CONNECTION pConnObject;
RWAN_CONN_ID ConnId;
PRWAN_TDI_ADDRESS pAddrObject;
PRWAN_CONN_REQUEST pConnReq;
TDI_STATUS Status;
#if DBG
RWAN_IRQL EntryIrq, ExitIrq;
#endif // DBG
RWAN_GET_ENTRY_IRQL(EntryIrq);
//
// Initialize
//
pConnReq = NULL;
do
{
//
// XXX: Ignore Acceptable address(es) for now.
//
//
// Allocate a Connection Request structure to keep track
// of this request.
//
pConnReq = RWanAllocateConnReq();
if (pConnReq == NULL)
{
Status = TDI_NO_RESOURCES;
break;
}
pConnReq->Request.pReqComplete = pTdiRequest->RequestNotifyObject;
pConnReq->Request.ReqContext = pTdiRequest->RequestContext;
pConnReq->pConnInfo = pConnectedAddr;
pConnReq->Flags = Flags;
//
// Get the Connection Object.
//
ConnId = (RWAN_CONN_ID) PtrToUlong(pTdiRequest->Handle.ConnectionContext);
RWAN_ACQUIRE_CONN_TABLE_LOCK();
pConnObject = RWanGetConnFromId(ConnId);
RWAN_RELEASE_CONN_TABLE_LOCK();
if (pConnObject == NULL_PRWAN_TDI_CONNECTION)
{
Status = TDI_INVALID_CONNECTION;
break;
}
RWAN_ACQUIRE_CONN_LOCK(pConnObject);
//
// See if it is associated.
//
pAddrObject = pConnObject->pAddrObject;
if (pAddrObject == NULL)
{
RWAN_RELEASE_CONN_LOCK(pConnObject);
Status = TDI_NOT_ASSOCIATED;
break;
}
//
// We can move this Connection Object to the listen list
// only if there isn't any active connection on this.
//
if (pConnObject->State != RWANS_CO_ASSOCIATED)
{
RWAN_RELEASE_CONN_LOCK(pConnObject);
Status = TDI_INVALID_STATE;
break;
}
pConnObject->State = RWANS_CO_LISTENING;
//
// Save the Connection Request
//
pConnObject->pConnReq = pConnReq;
RWAN_RELEASE_CONN_LOCK(pConnObject);
RWANDEBUGP(DL_VERY_LOUD, DC_BIND,
("Listen: pConnObject x%p, pAddrObject x%p\n", pConnObject, pAddrObject));
//
// Move this connection object from the Idle list to the
// Listen list.
//
RWAN_ACQUIRE_ADDRESS_LOCK(pAddrObject);
RWAN_DELETE_FROM_LIST(&(pConnObject->ConnLink));
RWAN_INSERT_TAIL_LIST(&(pAddrObject->ListenConnList),
&(pConnObject->ConnLink));
RWAN_RELEASE_ADDRESS_LOCK(pAddrObject);
Status = TDI_PENDING;
break;
}
while (FALSE);
RWAN_CHECK_EXIT_IRQL(EntryIrq, ExitIrq);
if (Status != TDI_PENDING)
{
//
// Cleanup
//
if (pConnReq != NULL)
{
RWanFreeConnReq(pConnReq);
}
}
return (Status);
}
TDI_STATUS
RWanTdiUnListen(
IN PTDI_REQUEST pTdiRequest
)
/*++
Routine Description:
This is the TDI Entry point for terminating a Listen. The Connection
Object is identified by its context buried within the TDI request.
We move the connection from the listen list to the idle list.
Arguments:
pTdiRequest - Pointer to the TDI Request
Return Value:
TDI_SUCCESS if successful.
--*/
{
PRWAN_TDI_CONNECTION pConnObject;
PRWAN_CONN_REQUEST pConnReq;
RWAN_CONN_ID ConnId;
PRWAN_TDI_ADDRESS pAddrObject;
TDI_STATUS Status;
#if DBG
RWAN_IRQL EntryIrq, ExitIrq;
#endif // DBG
RWAN_GET_ENTRY_IRQL(EntryIrq);
do
{
//
// Get the Connection Object.
//
ConnId = (RWAN_CONN_ID) PtrToUlong(pTdiRequest->Handle.ConnectionContext);
RWAN_ACQUIRE_CONN_TABLE_LOCK();
pConnObject = RWanGetConnFromId(ConnId);
RWAN_RELEASE_CONN_TABLE_LOCK();
if (pConnObject == NULL_PRWAN_TDI_CONNECTION)
{
Status = TDI_INVALID_CONNECTION;
break;
}
RWAN_ACQUIRE_CONN_LOCK(pConnObject);
//
// See if it is associated.
//
pAddrObject = pConnObject->pAddrObject;
if (pAddrObject == NULL)
{
RWAN_RELEASE_CONN_LOCK(pConnObject);
Status = TDI_NOT_ASSOCIATED;
break;
}
//
// We can move this Connection Object to the idle list
// only if there isn't any active connection on this.
//
if (pConnObject->State != RWANS_CO_LISTENING)
{
RWAN_RELEASE_CONN_LOCK(pConnObject);
Status = TDI_INVALID_STATE;
break;
}
pConnObject->State = RWANS_CO_ASSOCIATED;
pConnReq = pConnObject->pConnReq;
pConnObject->pConnReq = NULL;
RWAN_RELEASE_CONN_LOCK(pConnObject);
RWANDEBUGP(DL_VERY_LOUD, DC_BIND,
("UnListen: pConnObject x%p, pAddrObject x%p\n", pConnObject, pAddrObject));
//
// Move this connection object from the Listen list to the
// Idle list.
//
RWAN_ACQUIRE_ADDRESS_LOCK(pAddrObject);
RWAN_DELETE_FROM_LIST(&(pConnObject->ConnLink));
RWAN_INSERT_TAIL_LIST(&(pAddrObject->IdleConnList),
&(pConnObject->ConnLink));
RWAN_RELEASE_ADDRESS_LOCK(pAddrObject);
RWanCompleteConnReq( // InCall: Listen OK
NULL,
pConnReq,
FALSE,
NULL,
NULL,
TDI_CANCELLED
);
Status = TDI_SUCCESS;
break;
}
while (FALSE);
return (Status);
}
TDI_STATUS
RWanTdiAccept(
IN PTDI_REQUEST pTdiRequest,
IN PTDI_CONNECTION_INFORMATION pAcceptInfo,
IN PTDI_CONNECTION_INFORMATION pConnectInfo
)
/*++
Routine Description:
This is the TDI entry point for accepting an incoming connection.
The Connection Object is identified by its context buried within
the TDI request.
We translate this to a call to NdisClIncomingCallComplete, and
pend this request. If all goes well, this request is completed
when we receive a CallConnected primitive from NDIS.
Arguments:
pTdiRequest - Pointer to the TDI Request
pAcceptInfo - Contains options for the connection accept
pConnectInfo - Place to return final connection information
Return Value:
TDI_STATUS - this is TDI_PENDING if we successfully processed
the Accept, TDI_INVALID_CONNECTION if the given Connection Object
isn't valid, TDI_NOT_ASSOCIATED if the connection object isn't
associated with an address object.
--*/
{
PRWAN_TDI_CONNECTION pConnObject;
PRWAN_NDIS_VC pVc;
RWAN_CONN_ID ConnId;
PRWAN_TDI_ADDRESS pAddrObject;
PRWAN_CONN_REQUEST pConnReq;
PRWAN_NDIS_AF_CHARS pAfChars;
TDI_STATUS Status;
NDIS_HANDLE NdisVcHandle;
PCO_CALL_PARAMETERS pCallParameters;
BOOLEAN bIsLockAcquired; // Have we locked the Conn Object?
//
// Initialize
//
pConnReq = NULL;
bIsLockAcquired = FALSE;
do
{
//
// XXX: Ignore Acceptable address(es) for now.
//
//
// Allocate a Connection Request structure to keep track
// of this request.
//
pConnReq = RWanAllocateConnReq();
if (pConnReq == NULL)
{
Status = TDI_NO_RESOURCES;
break;
}
pConnReq->Request.pReqComplete = pTdiRequest->RequestNotifyObject;
pConnReq->Request.ReqContext = pTdiRequest->RequestContext;
pConnReq->pConnInfo = pConnectInfo;
//
// Copy from Accept Info to Connect Info.
//
if ((pAcceptInfo != NULL) &&
(pAcceptInfo->Options != NULL) &&
(pConnectInfo != NULL) &&
(pConnectInfo->Options != NULL) &&
(pConnectInfo->OptionsLength >= pAcceptInfo->OptionsLength))
{
RWAN_COPY_MEM(pConnectInfo->Options,
pAcceptInfo->Options,
pAcceptInfo->OptionsLength);
}
//
// Get the Connection Object.
//
ConnId = (RWAN_CONN_ID) PtrToUlong(pTdiRequest->Handle.ConnectionContext);
RWAN_ACQUIRE_CONN_TABLE_LOCK();
pConnObject = RWanGetConnFromId(ConnId);
RWAN_RELEASE_CONN_TABLE_LOCK();
if (pConnObject == NULL_PRWAN_TDI_CONNECTION)
{
Status = TDI_INVALID_CONNECTION;
break;
}
bIsLockAcquired = TRUE;
RWAN_ACQUIRE_CONN_LOCK(pConnObject);
//
// Make sure that the Connection is in the right state.
//
if (pConnObject->State != RWANS_CO_IN_CALL_INDICATED)
{
Status = TDI_INVALID_STATE;
break;
}
pVc = pConnObject->NdisConnection.pNdisVc;
if (pVc == NULL)
{
Status = TDI_INVALID_CONNECTION;
break;
}
pCallParameters = pVc->pCallParameters;
pVc->pCallParameters = NULL;
//
// Update NDIS Call Parameters if Accept Options are present.
//
pAfChars = &(pVc->pNdisAf->pAfInfo->AfChars);
if (pAfChars->pAfSpUpdateNdisOptions)
{
RWAN_STATUS RWanStatus;
ULONG CallFlags = RWAN_CALLF_INCOMING_CALL|RWAN_CALLF_POINT_TO_POINT;
PVOID pTdiQoS;
ULONG TdiQoSLength;
if (pAcceptInfo)
{
pTdiQoS = pAcceptInfo->Options;
TdiQoSLength = pAcceptInfo->OptionsLength;
}
else
{
pTdiQoS = NULL;
TdiQoSLength = 0;
}
RWanStatus = (*pAfChars->pAfSpUpdateNdisOptions)(
pVc->pNdisAf->AfSpAFContext,
pConnObject->AfSpConnContext,
CallFlags,
pAcceptInfo,
pTdiQoS,
TdiQoSLength,
&pCallParameters
);
if (RWanStatus != RWAN_STATUS_SUCCESS)
{
Status = RWanToTdiStatus(RWanStatus);
break;
}
}
NdisVcHandle = pVc->NdisVcHandle;
//
// Update Connection Object state.
//
pConnObject->State = RWANS_CO_IN_CALL_ACCEPTING;
//
// Save the Connection Request
//
pConnObject->pConnReq = pConnReq;
RWAN_RELEASE_CONN_LOCK(pConnObject);
//
// Accept the call now.
//
NdisClIncomingCallComplete(
NDIS_STATUS_SUCCESS,
NdisVcHandle,
pCallParameters
);
Status = TDI_PENDING;
break;
}
while (FALSE);
if (Status != TDI_PENDING)
{
//
// Cleanup
//
if (bIsLockAcquired)
{
RWAN_RELEASE_CONN_LOCK(pConnObject);
}
if (pConnReq != NULL)
{
RWanFreeConnReq(pConnReq);
}
}
return (Status);
}
TDI_STATUS
RWanTdiDisconnect(
IN PTDI_REQUEST pTdiRequest,
IN PVOID pTimeout,
IN USHORT Flags,
IN PTDI_CONNECTION_INFORMATION pDisconnInfo,
OUT PTDI_CONNECTION_INFORMATION pReturnInfo
)
/*++
Routine Description:
This is the TDI Disconnect entry point. If this is an incoming
call waiting to be accepted, we call NdisClIncomingCallComplete
with a rejection status. Otherwise, we call NdisClCloseCall.
The Connection Object is identified by its context buried within
the TDI request.
Note that this is never called for point-to-multipoint calls.
Those are disconnected within TdiCloseConnection.
Arguments:
pTdiRequest - Pointer to the TDI Request
pTimeout - Points to timeout. Ignored.
Flags - Type of disconnect. Only Abortive is supported for now.
pDisconnInfo - Information for the disconnect. Ignored for now.
pReturnInfo - Return information about the disconnect. Ignored for now.
Return Value:
TDI_STATUS - this is TDI_SUCCESS if we just rejected an incoming
call, TDI_PENDING if we initiated NDIS CloseCall, TDI_INVALID_CONNECTION
if the Connection Object context is invalid,
--*/
{
PRWAN_TDI_CONNECTION pConnObject;
RWAN_CONN_ID ConnId;
TDI_STATUS Status;
do
{
//
// Get the Connection Object.
//
RWAN_ACQUIRE_CONN_TABLE_LOCK();
ConnId = (RWAN_CONN_ID) PtrToUlong(pTdiRequest->Handle.ConnectionContext);
pConnObject = RWanGetConnFromId(ConnId);
RWAN_RELEASE_CONN_TABLE_LOCK();
if (pConnObject == NULL_PRWAN_TDI_CONNECTION)
{
Status = TDI_INVALID_CONNECTION;
break;
}
RWANDEBUGP(DL_LOUD, DC_DISCON,
("RWanTdiDiscon: pConnObj x%p, State/Flags x%x/x%x, pAddrObj x%p\n",
pConnObject,
pConnObject->State,
pConnObject->Flags,
pConnObject->pAddrObject));
RWAN_ACQUIRE_CONN_LOCK(pConnObject);
//
// Make sure that the Connection is in the right state for TdiDisconnect.
//
if ((pConnObject->State != RWANS_CO_CONNECTED) &&
(pConnObject->State != RWANS_CO_DISCON_INDICATED) &&
(pConnObject->State != RWANS_CO_IN_CALL_INDICATED) &&
(pConnObject->State != RWANS_CO_IN_CALL_ACCEPTING) &&
(pConnObject->State != RWANS_CO_OUT_CALL_INITIATED) &&
(pConnObject->State != RWANS_CO_DISCON_HELD))
{
RWAN_RELEASE_CONN_LOCK(pConnObject);
RWANDEBUGP(DL_INFO, DC_DISCON,
("RWanTdiDiscon: pConnObj x%p/x%x, bad state x%x for TdiDiscon\n",
pConnObject,
pConnObject->Flags,
pConnObject->State));
Status = TDI_INVALID_STATE;
break;
}
if ((pConnObject->State == RWANS_CO_DISCON_INDICATED) ||
(pConnObject->State == RWANS_CO_DISCON_HELD))
{
//
// We would have initiated an NDIS CloseCall/DropParty already.
// Simply succeed this TDI Disconnect.
//
RWAN_RELEASE_CONN_LOCK(pConnObject);
RWANDEBUGP(DL_INFO, DC_DISCON,
("RWanTdiDiscon: pConnObj x%p/x%x, Discon recvd state %d\n",
pConnObject,
pConnObject->Flags,
pConnObject->State));
Status = TDI_SUCCESS;
break;
}
Status = RWanDoTdiDisconnect(
pConnObject,
pTdiRequest,
pTimeout,
Flags,
pDisconnInfo,
pReturnInfo);
//
// Conn Object lock is released within the above.
//
break;
}
while (FALSE);
return (Status);
}
TDI_STATUS
RWanDoTdiDisconnect(
IN PRWAN_TDI_CONNECTION pConnObject,
IN PTDI_REQUEST pTdiRequest OPTIONAL,
IN PVOID pTimeout OPTIONAL,
IN USHORT Flags,
IN PTDI_CONNECTION_INFORMATION pDisconnInfo OPTIONAL,
OUT PTDI_CONNECTION_INFORMATION pReturnInfo OPTIONAL
)
/*++
Routine Description:
Perform a TDI Disconnect on the connection endpoint.
Separated out from the main TdiDisconnect routine
so that it can be reused by TdiCloseConnection.
NOTE: This is called with the connection object lock held. This
lock is released here.
Arguments:
pConnObject - Represents the TDI Connection being disconnected.
pTdiRequest - Pointer to the TDI Request.
pTimeout - Points to timeout. Ignored.
Flags - Type of disconnect. Only Abortive is supported for now.
pDisconnInfo - Information for the disconnect. Ignored for now.
pReturnInfo - Return information about the disconnect. Ignored for now.
Return Value:
TDI_STATUS - this is TDI_SUCCESS if we just rejected an incoming
call, TDI_PENDING if we initiated NDIS CloseCall or DropParty.
--*/
{
TDI_STATUS Status;
INT rc;
PRWAN_NDIS_VC pVc;
PRWAN_NDIS_PARTY pParty;
PRWAN_CONN_REQUEST pConnReq;
PRWAN_NDIS_AF pAf;
PCO_CALL_PARAMETERS pCallParameters;
NDIS_STATUS NdisStatus;
NDIS_HANDLE NdisPartyHandle;
BOOLEAN bIncomingCall;
BOOLEAN bIsPMPRoot;
BOOLEAN bIsLastLeaf;
RWAN_HANDLE AfSpConnContext;
UNREFERENCED_PARAMETER(pTimeout);
UNREFERENCED_PARAMETER(Flags);
UNREFERENCED_PARAMETER(pDisconnInfo);
UNREFERENCED_PARAMETER(pReturnInfo);
//
// Initialize
//
pConnReq = NULL;
Status = TDI_SUCCESS;
do
{
bIsPMPRoot = (pConnObject->pRootConnObject != NULL);
if (bIsPMPRoot)
{
pVc = pConnObject->pRootConnObject->NdisConnection.pNdisVc;
if (pVc == NULL)
{
//
// Can happen if DoAbort has run on this connection.
// Bail out.
//
RWANDEBUGP(DL_INFO, DC_WILDCARD,
("DoTdiDiscon(Root): pConnObj %p/%x: VC is null, bailing out\n",
pConnObject, pConnObject->Flags));
RWAN_RELEASE_CONN_LOCK(pConnObject);
break;
}
RWAN_STRUCT_ASSERT(pVc, nvc);
RWAN_ASSERT(pVc->AddingPartyCount + pVc->ActivePartyCount > 0);
bIsLastLeaf = ((pVc->AddingPartyCount + pVc->ActivePartyCount) == 1);
pParty = pConnObject->NdisConnection.pNdisParty;
RWAN_ASSERT(pParty != NULL);
RWAN_STRUCT_ASSERT(pParty, npy);
if (RWAN_IS_BIT_SET(pParty->Flags, RWANF_PARTY_DROPPING))
{
RWANDEBUGP(DL_FATAL, DC_DISCON,
("DoTdiDiscon (Root): pConnObj x%x, Party x%x already dropping\n",
pConnObject, pParty));
RWAN_RELEASE_CONN_LOCK(pConnObject);
break;
}
NdisPartyHandle = pParty->NdisPartyHandle;
RWANDEBUGP(DL_VERY_LOUD, DC_DISCON,
("DoTdiDiscon (Root): pConnObj x%x, pVc x%x, pParty x%x, Adding %d, Active %d\n",
pConnObject,
pVc,
pParty,
pVc->AddingPartyCount,
pVc->ActivePartyCount));
}
else
{
pVc = pConnObject->NdisConnection.pNdisVc;
if (pVc == NULL)
{
//
// Can happen if DoAbort has run on this connection.
// Bail out.
//
RWANDEBUGP(DL_INFO, DC_WILDCARD,
("DoTdiDiscon: pConnObj %p/%x: VC is null, bailing out\n",
pConnObject, pConnObject->Flags));
RWAN_RELEASE_CONN_LOCK(pConnObject);
break;
}
RWAN_STRUCT_ASSERT(pVc, nvc);
//
// Set last-leaf to TRUE to simplify processing later.
//
bIsLastLeaf = TRUE;
}
RWAN_ASSERT(pVc != NULL);
pAf = pVc->pNdisAf;
//
// If an outgoing call is in progress, we complete the
// pended TDI_CONNECT with a cancel status, mark this
// Connection Object as Disconnecting and exit. When the
// outgoing call completes, we will clear it if it was
// successful.
//
if (pConnObject->State == RWANS_CO_OUT_CALL_INITIATED)
{
pConnObject->State = RWANS_CO_DISCON_REQUESTED;
//
// Take out the pending TDI_CONNECT.
//
pConnReq = pConnObject->pConnReq;
RWAN_ASSERT(pConnReq != NULL);
pConnObject->pConnReq = NULL;
AfSpConnContext = pConnObject->AfSpConnContext;
RWAN_RELEASE_CONN_LOCK(pConnObject);
//
// Complete the TDI_CONNECT with a cancelled status.
//
RWanCompleteConnReq(
pAf,
pConnReq,
TRUE, // Is an outgoing call
NULL, // No call params
AfSpConnContext,
TDI_CANCELLED
);
//
// We will succeed this TDI_DISCONNECT.
//
pConnReq = NULL;
Status = TDI_SUCCESS;
break;
}
//
// If this connection is in the process of being accepted,
// then complete the pending TDI_ACCEPT with a cancel
// status and reject the incoming call.
//
if (pConnObject->State == RWANS_CO_IN_CALL_ACCEPTING)
{
RWANDEBUGP(DL_FATAL, DC_DISCON,
("DoTdiDiscon: ConnObj %x/%x, in call accepting, VC %x\n",
pConnObject,
pConnObject->Flags,
pVc));
//
// Take out the pending TDI_CONNECT.
//
pConnReq = pConnObject->pConnReq;
RWAN_ASSERT(pConnReq != NULL);
pConnObject->pConnReq = NULL;
AfSpConnContext = pConnObject->AfSpConnContext;
RWAN_RELEASE_CONN_LOCK(pConnObject);
//
// Complete the TDI_ACCEPT with a cancelled status.
//
RWanCompleteConnReq(
pAf,
pConnReq,
FALSE, // Is an incoming call
NULL, // No call params
AfSpConnContext,
TDI_CANCELLED
);
//
// We will succeed this TDI_DISCONNECT.
//
pConnReq = NULL;
Status = TDI_SUCCESS;
break;
}
#if DBG
if (pConnObject->pConnReq != NULL)
{
RWANDEBUGP(DL_FATAL, DC_WILDCARD,
("DoTdiDiscon: pConnObj %x/%x, State %x, non-NULL ConnReq %x\n",
pConnObject, pConnObject->Flags, pConnObject->State,
pConnObject->pConnReq));
}
#endif // DBG
RWAN_ASSERT(pConnObject->pConnReq == NULL);
if (pTdiRequest != NULL)
{
//
// Allocate a Connection Request structure to keep track
// of this Disconnect request.
//
pConnReq = RWanAllocateConnReq();
if (pConnReq == NULL)
{
RWAN_RELEASE_CONN_LOCK(pConnObject);
Status = TDI_NO_RESOURCES;
break;
}
pConnReq->Request.pReqComplete = pTdiRequest->RequestNotifyObject;
pConnReq->Request.ReqContext = pTdiRequest->RequestContext;
pConnReq->pConnInfo = NULL;
pConnReq->Flags = 0;
//
// Save info about the TDI Disconnect request.
//
pConnObject->pConnReq = pConnReq;
}
else
{
pConnReq = NULL;
}
bIncomingCall = (pConnObject->State == RWANS_CO_IN_CALL_INDICATED);
if (bIncomingCall)
{
pCallParameters = pVc->pCallParameters;
pVc->pCallParameters = NULL;
}
pConnObject->State = RWANS_CO_DISCON_REQUESTED;
if (bIncomingCall)
{
//
// Reject the incoming call.
//
RWanNdisRejectIncomingCall(pConnObject, NDIS_STATUS_FAILURE);
}
else
{
//
// Closing an existing call.
// TBD: we don't support Close data yet.
//
if (bIsLastLeaf)
{
RWanStartCloseCall(pConnObject, pVc);
//
// ConnObject lock is released within the above.
//
}
else
{
pVc->DroppingPartyCount ++; // DoTdiDiscon: not last leaf (DropParty)
pVc->ActivePartyCount --; // DoTdiDiscon: will DropParty
RWAN_ASSERT(pParty != NULL);
RWAN_STRUCT_ASSERT(pParty, npy);
RWAN_SET_BIT(pParty->Flags, RWANF_PARTY_DROPPING);
RWAN_RELEASE_CONN_LOCK(pConnObject);
//
// Dropping a leaf of a PMP call.
//
NdisStatus = NdisClDropParty(
NdisPartyHandle,
NULL, // No Drop data
0 // Length of drop data
);
if (NdisStatus != NDIS_STATUS_PENDING)
{
RWanNdisDropPartyComplete(
NdisStatus,
(NDIS_HANDLE)pParty
);
}
}
}
Status = TDI_PENDING;
break;
}
while (FALSE);
if (Status != TDI_PENDING)
{
//
// Cleanup.
//
if (pConnReq)
{
RWanFreeConnReq(pConnReq);
}
}
return (Status);
}
RWAN_CONN_ID
RWanGetConnId(
IN PRWAN_TDI_CONNECTION pConnObject
)
/*++
Routine Description:
Get a free Connection ID to assign to the Connection Object.
This Connection ID is used as our context for the Connection
object.
It is assumed that the caller holds a lock to the Connection Table.
Validation scheme courtesy TCP source.
Arguments:
pConnObject - Pointer to the TDI Connection Object
Return Value:
RWAN_CONN_ID: this is RWAN_INVALID_CONN_ID iff we cannot allocate
a Connection Id.
--*/
{
ULONG Slot;
ULONG i;
BOOLEAN bFound;
RWAN_CONN_ID ConnId;
for (;;)
{
//
// Look for a free slot in the Connection Index table.
// Start from where we left off the last time we were called.
//
Slot = pRWanGlobal->NextConnIndex;
for (i = 0; i < pRWanGlobal->ConnTableSize; i++)
{
if (Slot == pRWanGlobal->ConnTableSize)
{
Slot = 0; // wrap around
}
if (pRWanGlobal->pConnTable[Slot] == NULL)
{
// Found free slot
break;
}
++Slot;
}
if (i < pRWanGlobal->ConnTableSize)
{
bFound = TRUE;
break;
}
//
// Grow the Connection Index table, if we can.
//
if (pRWanGlobal->ConnTableSize != pRWanGlobal->MaxConnections)
{
ULONG NewTableSize;
PRWAN_TDI_CONNECTION * pNewConnTable;
PRWAN_TDI_CONNECTION * pOldConnTable;
NewTableSize = MIN(pRWanGlobal->ConnTableSize + CONN_TABLE_GROW_DELTA,
pRWanGlobal->MaxConnections);
RWAN_ALLOC_MEM(pNewConnTable,
PRWAN_TDI_CONNECTION,
NewTableSize * sizeof(PRWAN_TDI_CONNECTION));
if (pNewConnTable != NULL)
{
RWAN_ZERO_MEM(pNewConnTable, NewTableSize * sizeof(PRWAN_TDI_CONNECTION));
pOldConnTable = pRWanGlobal->pConnTable;
pRWanGlobal->pConnTable = pNewConnTable;
if (pOldConnTable != NULL)
{
//
// Copy in the contents of the old table.
//
RWAN_COPY_MEM(pNewConnTable,
pOldConnTable,
pRWanGlobal->ConnTableSize * sizeof(PRWAN_TDI_CONNECTION));
RWAN_FREE_MEM(pOldConnTable);
}
pRWanGlobal->ConnTableSize = NewTableSize;
//
// Continue search.
//
}
else
{
//
// Resource failure.
//
bFound = FALSE;
break;
}
}
else
{
//
// ConnTable is full, and we aren't permitted to grow it any further.
//
bFound = FALSE;
break;
}
}
if (bFound)
{
//
// Use the slot that we found.
//
pRWanGlobal->pConnTable[Slot] = pConnObject;
pRWanGlobal->NextConnIndex = Slot + 1;
//
// Assign an instance value for this. This is used to validate
// a given ConnId.
//
pRWanGlobal->ConnInstance++;
pConnObject->ConnInstance = pRWanGlobal->ConnInstance;
ConnId = RWAN_MAKE_CONN_ID(pConnObject->ConnInstance, Slot);
}
else
{
ConnId = RWAN_INVALID_CONN_ID;
}
return (ConnId);
}
PRWAN_TDI_CONNECTION
RWanGetConnFromId(
IN RWAN_CONN_ID ConnId
)
/*++
Routine Description:
Given a Connection ID, validate it. If found OK, return a pointer
to the TDI Connection that it represents.
It is assumed that the caller holds a lock to the Connection Table.
Validation scheme courtesy TCP source.
Arguments:
ConnId - Connection Id.
Return Value:
PRWAN_TDI_CONNECTION - pointer to a TDI Connection structure that
matches the given ConnId, if valid. Otherwise, NULL.
--*/
{
ULONG Slot;
RWAN_CONN_INSTANCE ConnInstance;
PRWAN_TDI_CONNECTION pConnObject;
Slot = RWAN_GET_SLOT_FROM_CONN_ID(ConnId);
if (Slot < pRWanGlobal->ConnTableSize)
{
pConnObject = pRWanGlobal->pConnTable[Slot];
if (pConnObject != NULL_PRWAN_TDI_CONNECTION)
{
RWAN_STRUCT_ASSERT(pConnObject, ntc);
ConnInstance = RWAN_GET_INSTANCE_FROM_CONN_ID(ConnId);
if (pConnObject->ConnInstance != ConnInstance)
{
pConnObject = NULL_PRWAN_TDI_CONNECTION;
}
}
}
else
{
pConnObject = NULL_PRWAN_TDI_CONNECTION;
}
return (pConnObject);
}
VOID
RWanFreeConnId(
IN RWAN_CONN_ID ConnId
)
/*++
Routine Description:
Free a Connection ID.
Arguments:
ConnId - ID to be freed.
Return Value:
None
--*/
{
ULONG Slot;
Slot = RWAN_GET_SLOT_FROM_CONN_ID(ConnId);
RWAN_ASSERT(Slot < pRWanGlobal->ConnTableSize);
pRWanGlobal->pConnTable[Slot] = NULL;
return;
}
TDI_STATUS
RWanToTdiStatus(
IN RWAN_STATUS RWanStatus
)
/*++
Routine Description:
Map the given local status code to an equivalent TDI status code.
Arguments:
RWanStatus - Local status code
Return Value:
TDI Status code.
--*/
{
TDI_STATUS TdiStatus;
switch (RWanStatus)
{
case RWAN_STATUS_SUCCESS:
TdiStatus = TDI_SUCCESS;
break;
case RWAN_STATUS_BAD_ADDRESS:
TdiStatus = TDI_BAD_ADDR;
break;
case RWAN_STATUS_BAD_PARAMETER:
TdiStatus = TDI_INVALID_PARAMETER;
break;
case RWAN_STATUS_MISSING_PARAMETER:
TdiStatus = TDI_INVALID_PARAMETER;
break;
case RWAN_STATUS_FAILURE:
default:
TdiStatus = TDI_INVALID_STATE; // XXX: find a better one?
break;
}
return (TdiStatus);
}
PRWAN_CONN_REQUEST
RWanAllocateConnReq(
VOID
)
/*++
Routine Description:
Allocate a structure to hold context about a TDI Connection Request.
This includes TDI_CONNECT, TDI_DISCONNECT, TDI_LISTEN and TDI_ACCEPT.
Arguments:
None
Return Value:
Pointer to allocate structure if successful, else NULL.
--*/
{
PRWAN_CONN_REQUEST pConnReq;
RWAN_ALLOC_MEM(pConnReq, RWAN_CONN_REQUEST, sizeof(RWAN_CONN_REQUEST));
if (pConnReq != NULL)
{
RWAN_SET_SIGNATURE(pConnReq, nrc);
}
return (pConnReq);
}
VOID
RWanFreeConnReq(
IN PRWAN_CONN_REQUEST pConnReq
)
/*++
Routine Description:
Free a connect request context structure.
Arguments:
pConnReq - Points to structure to be freed.
Return Value:
None
--*/
{
RWAN_STRUCT_ASSERT(pConnReq, nrc);
RWAN_FREE_MEM(pConnReq);
}
VOID
RWanAbortConnection(
IN CONNECTION_CONTEXT ConnectionContext
)
/*++
Routine Description:
Abortively closes a connection and issues a Disconnect Indication
to the user. This is called when a send or receive is cancelled,
implying that an NDIS connection is in place.
Arguments:
ConnectionContext- Our context for a TDI Connection object.
Return Value:
None
--*/
{
RWAN_CONN_ID ConnId;
PRWAN_TDI_CONNECTION pConnObject;
ConnId = (RWAN_CONN_ID)PtrToUlong(ConnectionContext);
RWAN_ACQUIRE_CONN_TABLE_LOCK();
pConnObject = RWanGetConnFromId(ConnId);
RWAN_RELEASE_CONN_TABLE_LOCK();
RWanDoAbortConnection(pConnObject);
}
VOID
RWanDoAbortConnection(
IN PRWAN_TDI_CONNECTION pConnObject
)
/*++
Routine Description:
Does the actual connection abort. Split out from RWanAbortConnection
just so that this can be called from elsewhere.
See comments under RWanAbortConnection.
Arguments:
pConnObject - Points to TDI Connection to be aborted.
Return Value:
None
--*/
{
PRWAN_NDIS_VC pVc;
PRWAN_NDIS_PARTY pParty;
PRWAN_TDI_CONNECTION pLeafConnObject;
INT rc;
BOOLEAN bIsLockReleased = TRUE;
ULONG OldState;
ULONG OldLeafState;
PLIST_ENTRY pPartyEntry;
PLIST_ENTRY pNextPartyEntry;
RWANDEBUGP(DL_INFO, DC_DISCON,
("DoAbortConnection: pConnObject x%x/%x, pAddrObject x%x\n",
pConnObject, (pConnObject? pConnObject->Flags: 0), (pConnObject? pConnObject->pAddrObject: 0)));
do
{
if (pConnObject == NULL_PRWAN_TDI_CONNECTION)
{
break;
}
RWAN_ACQUIRE_CONN_LOCK(pConnObject);
//
// Make sure we don't do this more than once on a Connection.
//
if (pConnObject->State == RWANS_CO_ABORTING)
{
RWAN_RELEASE_CONN_LOCK(pConnObject);
break;
}
//
// Make sure the Conn Object doesn't go away during the time we
// need it.
//
RWanReferenceConnObject(pConnObject); // temp ref: RWanAbortConnection
OldState = pConnObject->State;
pConnObject->State = RWANS_CO_ABORTING;
if (RWAN_IS_BIT_SET(pConnObject->Flags, RWANF_CO_ROOT))
{
bIsLockReleased = FALSE;
//
// This is a Root Connection object.
// Indicate disconnect and schedule Closing each leaf.
//
pVc = pConnObject->NdisConnection.pNdisVc;
if (pVc != NULL)
{
for (pPartyEntry = pVc->NdisPartyList.Flink;
pPartyEntry != &(pVc->NdisPartyList);
pPartyEntry = pNextPartyEntry)
{
pParty = CONTAINING_RECORD(pPartyEntry, RWAN_NDIS_PARTY, PartyLink);
pNextPartyEntry = pParty->PartyLink.Flink;
pLeafConnObject = pParty->pConnObject;
RWAN_ASSERT(pLeafConnObject);
RWAN_STRUCT_ASSERT(pLeafConnObject, ntc);
RWAN_ACQUIRE_CONN_LOCK(pLeafConnObject);
if (pLeafConnObject->State == RWANS_CO_ABORTING)
{
RWAN_RELEASE_CONN_LOCK(pLeafConnObject);
continue;
}
#if DBG
pLeafConnObject->OldState = pLeafConnObject->State;
pLeafConnObject->OldFlags = pLeafConnObject->Flags;
#endif // DBG
OldLeafState = pLeafConnObject->State;
pLeafConnObject->State = RWANS_CO_ABORTING;
if ((OldLeafState == RWANS_CO_CONNECTED) &&
(pLeafConnObject->pAddrObject != NULL_PRWAN_TDI_ADDRESS))
{
PDisconnectEvent pDisconInd;
PVOID IndContext;
PVOID ConnectionHandle;
pDisconInd = pLeafConnObject->pAddrObject->pDisconInd;
IndContext = pLeafConnObject->pAddrObject->DisconIndContext;
if (pDisconInd != NULL)
{
pLeafConnObject->State = RWANS_CO_DISCON_INDICATED;
ConnectionHandle = pLeafConnObject->ConnectionHandle;
RWAN_RELEASE_CONN_LOCK(pLeafConnObject);
RWAN_RELEASE_CONN_LOCK(pConnObject);
RWANDEBUGP(DL_FATAL, DC_DISCON,
("DoAbort[Leaf]: will indicate Discon, pConnObj x%x, pAddrObj x%x\n",
pLeafConnObject, pLeafConnObject->pAddrObject));
(*pDisconInd)(
IndContext,
ConnectionHandle,
0, // Disconnect Data Length
NULL, // Disconnect Data
0, // Disconnect Info Length
NULL, // Disconnect Info
TDI_DISCONNECT_ABORT
);
RWAN_ACQUIRE_CONN_LOCK(pConnObject);
RWAN_ACQUIRE_CONN_LOCK(pLeafConnObject);
}
}
RWanScheduleDisconnect(pLeafConnObject);
//
// Leaf Conn Object lock is freed within the above.
//
}
//
// end For all parties
//
}
//
// else Root Conn object has no associated VC.
//
rc = RWanDereferenceConnObject(pConnObject); // temp ref: RWanAbortConnection
if (rc == 0)
{
bIsLockReleased = TRUE;
break; // The Conn Object has been deref'ed away.
}
}
else
{
//
// Not PMP connection.
//
pVc = pConnObject->NdisConnection.pNdisVc;
// 157217: this prevents CoSendComplete for pended packets from
// continuing on to do StartCloseCall.
// RWAN_UNLINK_CONNECTION_AND_VC(pConnObject, pVc);
//
// First, initiate a network call close.
//
RWanStartCloseCall(pConnObject, pVc);
//
// The lock is freed within the above. Reacquire it.
//
bIsLockReleased = FALSE;
RWAN_ACQUIRE_CONN_LOCK(pConnObject);
rc = RWanDereferenceConnObject(pConnObject); // temp ref: RWanAbortConnection
if (rc == 0)
{
bIsLockReleased = TRUE;
break; // The Conn Object has been deref'ed away.
}
//
// Now, indicate a disconnect to the user, if required & possible.
//
if ((OldState == RWANS_CO_CONNECTED) &&
(pConnObject->pAddrObject != NULL_PRWAN_TDI_ADDRESS))
{
PDisconnectEvent pDisconInd;
PVOID IndContext;
PVOID ConnectionHandle;
pDisconInd = pConnObject->pAddrObject->pDisconInd;
IndContext = pConnObject->pAddrObject->DisconIndContext;
if (pDisconInd != NULL)
{
ConnectionHandle = pConnObject->ConnectionHandle;
RWAN_RELEASE_CONN_LOCK(pConnObject);
(*pDisconInd)(
IndContext,
ConnectionHandle,
0, // Disconnect Data Length
NULL, // Disconnect Data
0, // Disconnect Info Length
NULL, // Disconnect Info
TDI_DISCONNECT_ABORT
);
bIsLockReleased = TRUE;
}
}
}
if (!bIsLockReleased)
{
RWAN_RELEASE_CONN_LOCK(pConnObject);
}
break;
}
while (FALSE);
return;
}
VOID
RWanScheduleDisconnect(
IN PRWAN_TDI_CONNECTION pConnObject
)
/*++
Routine Description:
Schedule a call to RWanDoTdiDisconnect on the specified connection
object, as a work item.
NOTE: The Connection object is locked by the caller.
Arguments:
pConnObject - Points to TDI Connection to be aborted.
Return Value:
None
--*/
{
NDIS_STATUS Status;
RWANDEBUGP(DL_LOUD, DC_DISCON,
("ScheduleDiscon: pConnObj x%x/x%x, state %d\n",
pConnObject, pConnObject->Flags, pConnObject->State));
do
{
//
// Check if we've already done this.
//
if (RWAN_IS_BIT_SET(pConnObject->Flags, RWANF_CO_CLOSE_SCHEDULED))
{
break;
}
RWAN_SET_BIT(pConnObject->Flags, RWANF_CO_CLOSE_SCHEDULED);
//
// Make sure the connection doesn't go away till the
// work item is handled.
//
RWanReferenceConnObject(pConnObject); // Schedule Discon ref
NdisInitializeWorkItem(
&pConnObject->CloseWorkItem,
RWanDelayedDisconnectHandler,
(PVOID)pConnObject);
Status = NdisScheduleWorkItem(&pConnObject->CloseWorkItem);
RWAN_ASSERT(Status == NDIS_STATUS_SUCCESS);
}
while (FALSE);
RWAN_RELEASE_CONN_LOCK(pConnObject);
return;
}
VOID
RWanDelayedDisconnectHandler(
IN PNDIS_WORK_ITEM pCloseWorkItem,
IN PVOID Context
)
/*++
Routine Description:
Work item routine to initiate a connection teardown.
Arguments:
pCloseWorkItem - Points to work item structure embedded in the
Connection object.
Context - Actually a pointer to the Connection object.
Return Value:
None
--*/
{
PRWAN_TDI_CONNECTION pConnObject;
ULONG rc;
pConnObject = (PRWAN_TDI_CONNECTION)Context;
RWAN_STRUCT_ASSERT(pConnObject, ntc);
RWANDEBUGP(DL_LOUD, DC_DISCON,
("DelayedDiscon handler: pConnObj x%x/x%x, state %d\n",
pConnObject, pConnObject->Flags, pConnObject->State));
do
{
RWAN_ACQUIRE_CONN_LOCK(pConnObject);
rc = RWanDereferenceConnObject(pConnObject); // Delayed (scheduled) Discon deref
if (rc == 0)
{
//
// The Conn Object is gone.
//
break;
}
//
// Do the Disconnect now.
//
RWanDoTdiDisconnect(
pConnObject,
NULL, // pTdiRequest
NULL, // pTimeout
0, // Flags
NULL, // pDisconnInfo
NULL // pReturnInfo
);
//
// Conn object lock is released within the above.
//
break;
}
while (FALSE);
return;
}