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

4890 lines
117 KiB
C

/*++
Copyright (c) 1992 Microsoft Corporation
Module Name:
pap.c
Abstract:
This module implements the PAP protocol.
Author:
Jameel Hyder (jameelh@microsoft.com)
Nikhil Kamkolkar (nikhilk@microsoft.com)
Revision History:
30 Mar 1993 Initial Version
Notes: Tab stop: 4
--*/
#include <atalk.h>
#pragma hdrstop
#define FILENUM PAP
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, AtalkInitPapInitialize)
#pragma alloc_text(PAGE_PAP, AtalkPapCreateAddress)
#pragma alloc_text(PAGE_PAP, AtalkPapCreateConnection)
#pragma alloc_text(PAGE_PAP, AtalkPapCleanupAddress)
#pragma alloc_text(PAGE_PAP, AtalkPapCloseAddress)
#pragma alloc_text(PAGE_PAP, AtalkPapCleanupConnection)
#pragma alloc_text(PAGE_PAP, AtalkPapCloseConnection)
#pragma alloc_text(PAGE_PAP, AtalkPapAssociateAddress)
#pragma alloc_text(PAGE_PAP, AtalkPapDissociateAddress)
#pragma alloc_text(PAGE_PAP, AtalkPapPostListen)
#pragma alloc_text(PAGE_PAP, AtalkPapPrimeListener)
#pragma alloc_text(PAGE_PAP, AtalkPapCancelListen)
#pragma alloc_text(PAGE_PAP, AtalkPapPostConnect)
#pragma alloc_text(PAGE_PAP, AtalkPapDisconnect)
#pragma alloc_text(PAGE_PAP, AtalkPapRead)
#pragma alloc_text(PAGE_PAP, AtalkPapPrimeRead)
#pragma alloc_text(PAGE_PAP, AtalkPapWrite)
#pragma alloc_text(PAGE_PAP, AtalkPapSetStatus)
#pragma alloc_text(PAGE_PAP, AtalkPapGetStatus)
#pragma alloc_text(PAGE_PAP, AtalkPapQuery)
#pragma alloc_text(PAGE_PAP, atalkPapConnRefByPtrNonInterlock)
#pragma alloc_text(PAGE_PAP, atalkPapConnRefByCtxNonInterlock)
#pragma alloc_text(PAGE_PAP, atalkPapConnRefNextNc)
#pragma alloc_text(PAGE_PAP, atalkPapPostSendDataResp)
#pragma alloc_text(PAGE_PAP, atalkPapIncomingReadComplete)
#pragma alloc_text(PAGE_PAP, atalkPapPrimedReadComplete)
#pragma alloc_text(PAGE_PAP, atalkPapIncomingStatus)
#pragma alloc_text(PAGE_PAP, atalkPapSendDataRel)
#pragma alloc_text(PAGE_PAP, atalkPapSlsHandler)
#pragma alloc_text(PAGE_PAP, atalkPapIncomingReq)
#pragma alloc_text(PAGE_PAP, atalkPapIncomingOpenReply)
#pragma alloc_text(PAGE_PAP, atalkPapIncomingRel)
#pragma alloc_text(PAGE_PAP, atalkPapStatusRel)
#pragma alloc_text(PAGE_PAP, atalkPapConnAccept)
#pragma alloc_text(PAGE_PAP, atalkPapGetNextConnId)
#pragma alloc_text(PAGE_PAP, atalkPapQueueAddrGlobalList)
#pragma alloc_text(PAGE_PAP, atalkPapConnDeQueueAssocList)
#pragma alloc_text(PAGE_PAP, atalkPapConnDeQueueConnectList)
#pragma alloc_text(PAGE_PAP, atalkPapConnDeQueueListenList)
#pragma alloc_text(PAGE_PAP, atalkPapConnDeQueueActiveList)
#endif
//
// The model for PAP calls in this module is as follows:
// - For create calls (CreateAddress & CreateSession), a pointer to the created
// object is returned. This structure is referenced for creation.
// - For all other calls, it expects a referenced pointer to the object.
//
VOID
AtalkInitPapInitialize(
VOID
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
INITIALIZE_SPIN_LOCK(&atalkPapLock);
AtalkTimerInitialize(&atalkPapCMTTimer,
atalkPapConnMaintenanceTimer,
PAP_CONNECTION_INTERVAL);
AtalkTimerScheduleEvent(&atalkPapCMTTimer);
}
ATALK_ERROR
AtalkPapCreateAddress(
IN PATALK_DEV_CTX pDevCtx OPTIONAL,
OUT PPAP_ADDROBJ * ppPapAddr
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PPAP_ADDROBJ pPapAddr = NULL;
ATALK_ERROR error;
do
{
// Allocate memory for the Pap address object
if ((pPapAddr = AtalkAllocZeroedMemory(sizeof(PAP_ADDROBJ))) == NULL)
{
error = ATALK_RESR_MEM;
break;
}
// Create an Atp Socket on the port for the Sls/Connection Socket.
error = AtalkAtpOpenAddress(AtalkDefaultPort,
0,
NULL,
PAP_MAX_DATA_PACKET_SIZE,
TRUE, // SEND_USER_BYTES_ALL
NULL,
FALSE, // CACHE address
&pPapAddr->papao_pAtpAddr);
if (!ATALK_SUCCESS(error))
{
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_ERR,
("AtalkPapCreateAddress: AtalkAtpOpenAddress fail %ld\n",error));
break;
}
// Initialize the Pap address object
pPapAddr->papao_Signature = PAPAO_SIGNATURE;
INITIALIZE_SPIN_LOCK(&pPapAddr->papao_Lock);
// Creation reference
pPapAddr->papao_RefCount = 1;
} while (FALSE);
if (ATALK_SUCCESS(error))
{
// Insert into the global address list.
atalkPapQueueAddrGlobalList(pPapAddr);
*ppPapAddr = pPapAddr;
}
else if (pPapAddr != NULL)
{
AtalkFreeMemory(pPapAddr);
}
return error;
}
ATALK_ERROR
AtalkPapCleanupAddress(
IN PPAP_ADDROBJ pPapAddr
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PPAP_CONNOBJ pPapConn, pPapConnNext;
KIRQL OldIrql;
ATALK_ERROR error;
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_INFO,
("AtalkPapCleanupAddress: %lx\n", pPapAddr));
ACQUIRE_SPIN_LOCK(&pPapAddr->papao_Lock, &OldIrql);
#if DBG
pPapAddr->papao_Flags |= PAPAO_CLEANUP;
#endif
if ((pPapConn = pPapAddr->papao_pAssocConn) != NULL)
{
atalkPapConnRefNextNc(pPapConn, &pPapConnNext, &error);
if (ATALK_SUCCESS(error))
{
while (TRUE)
{
if ((pPapConn = pPapConnNext) == NULL)
{
break;
}
if ((pPapConnNext = pPapConn->papco_pNextAssoc) != NULL)
{
atalkPapConnRefNextNc(pPapConnNext, &pPapConnNext, &error);
if (!ATALK_SUCCESS(error))
{
pPapConnNext = NULL;
}
}
// Shutdown this connection
RELEASE_SPIN_LOCK(&pPapAddr->papao_Lock, OldIrql);
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_INFO,
("AtalkPapCloseAddress: Stopping conn %lx\n", pPapConn));
AtalkPapCleanupConnection(pPapConn);
AtalkPapConnDereference(pPapConn);
ACQUIRE_SPIN_LOCK(&pPapAddr->papao_Lock, &OldIrql);
}
}
}
RELEASE_SPIN_LOCK(&pPapAddr->papao_Lock, OldIrql);
return ATALK_NO_ERROR;
}
ATALK_ERROR
AtalkPapCloseAddress(
IN PPAP_ADDROBJ pPapAddr,
IN GENERIC_COMPLETION CompletionRoutine,
IN PVOID pCloseCtx
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
KIRQL OldIrql;
PPAP_CONNOBJ pPapConn, pPapConnNext;
DWORD dwAssocRefCounts=0;
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_INFO,
("AtalkPapCloseAddress: %lx\n", pPapAddr));
ACQUIRE_SPIN_LOCK(&pPapAddr->papao_Lock, &OldIrql);
if (pPapAddr->papao_Flags & PAPAO_CLOSING)
{
// We are already closing! This should never happen!
ASSERT(0);
}
pPapAddr->papao_Flags |= PAPAO_CLOSING;
// Set the completion info.
pPapAddr->papao_CloseComp = CompletionRoutine;
pPapAddr->papao_CloseCtx = pCloseCtx;
// Implicitly dissociate any connection objects
for (pPapConn = pPapAddr->papao_pAssocConn;
pPapConn != NULL;
pPapConn = pPapConnNext)
{
ACQUIRE_SPIN_LOCK_DPC(&pPapConn->papco_Lock);
pPapConnNext = pPapConn->papco_pNextAssoc;
// when the conn was associate, we put a refcount: remove it
if (pPapConn->papco_Flags & PAPCO_ADDR_ACTIVE)
{
pPapConn->papco_Flags &= ~PAPCO_ADDR_ACTIVE;
dwAssocRefCounts++;
}
RELEASE_SPIN_LOCK_DPC(&pPapConn->papco_Lock);
}
ASSERT(pPapAddr->papao_CloseComp != NULL);
ASSERT(pPapAddr->papao_CloseCtx != NULL);
// ok to subtract: at least Creation refcount is still around
pPapAddr->papao_RefCount -= dwAssocRefCounts;
RELEASE_SPIN_LOCK(&pPapAddr->papao_Lock, OldIrql);
// Close the ATP Address Object.
if (pPapAddr->papao_pAtpAddr != NULL)
AtalkAtpCloseAddress(pPapAddr->papao_pAtpAddr, NULL, NULL);
pPapAddr->papao_pAtpAddr = NULL;
// Remove the creation reference count
AtalkPapAddrDereference(pPapAddr);
return ATALK_PENDING;
}
ATALK_ERROR
AtalkPapCreateConnection(
IN PVOID pConnCtx, // Context to associate with the session
IN PATALK_DEV_CTX pDevCtx OPTIONAL,
OUT PPAP_CONNOBJ * ppPapConn
)
/*++
Routine Description:
Create an PAP session. The created session starts off being an orphan, i.e.
it has no parent address object. It gets one when it is associated.
Arguments:
Return Value:
--*/
{
PPAP_CONNOBJ pPapConn;
KIRQL OldIrql;
// Allocate memory for a connection object
if ((pPapConn = AtalkAllocZeroedMemory(sizeof(PAP_CONNOBJ))) == NULL)
{
return ATALK_RESR_MEM;
}
pPapConn->papco_Signature = PAPCO_SIGNATURE;
INITIALIZE_SPIN_LOCK(&pPapConn->papco_Lock);
pPapConn->papco_ConnCtx = pConnCtx;
pPapConn->papco_Flags = 0;
pPapConn->papco_RefCount = 1; // Creation reference
pPapConn->papco_NextOutgoingSeqNum = 1; // Set to 1, not 0.
pPapConn->papco_NextIncomingSeqNum = 1; // Next expected incoming.
AtalkInitializeRT(&pPapConn->papco_RT,
PAP_INIT_SENDDATA_REQ_INTERVAL,
PAP_MIN_SENDDATA_REQ_INTERVAL,
PAP_MAX_SENDDATA_REQ_INTERVAL);
*ppPapConn = pPapConn;
// Insert into the global connection list.
ACQUIRE_SPIN_LOCK(&atalkPapLock, &OldIrql);
AtalkLinkDoubleAtHead(atalkPapConnList, pPapConn, papco_Next, papco_Prev);
RELEASE_SPIN_LOCK(&atalkPapLock, OldIrql);
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_INFO,
("AtalkPapCreateConnection: %lx\n", pPapConn));
return ATALK_NO_ERROR;
}
ATALK_ERROR
AtalkPapCleanupConnection(
IN PPAP_CONNOBJ pPapConn
)
/*++
Routine Description:
Shutdown a session.
Arguments:
Return Value:
--*/
{
BOOLEAN stopping = FALSE;
BOOLEAN pendingRead = FALSE;
BOOLEAN fWaitingRead = FALSE;
KIRQL OldIrql;
ATALK_ERROR error = ATALK_NO_ERROR;
ASSERT(VALID_PAPCO(pPapConn));
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_INFO,
("AtalkPapCleanupConnection: %lx\n", pPapConn));
ACQUIRE_SPIN_LOCK(&pPapConn->papco_Lock, &OldIrql);
pPapConn->papco_Flags |= PAPCO_LOCAL_DISCONNECT;
#if DBG
pPapConn->papco_Flags |= PAPCO_CLEANUP;
#endif
if ((pPapConn->papco_Flags & PAPCO_STOPPING) == 0)
{
// Allows completion of cleanup irp in Deref.
pPapConn->papco_Flags |= PAPCO_STOPPING;
// If already effectively stopped, just return.
if (pPapConn->papco_Flags & PAPCO_ASSOCIATED)
{
pendingRead = (pPapConn->papco_Flags & PAPCO_READDATA_PENDING) ? TRUE : FALSE;
if (pPapConn->papco_Flags & PAPCO_READDATA_WAITING)
{
fWaitingRead = TRUE;
pPapConn->papco_Flags &= ~PAPCO_READDATA_WAITING;
}
ASSERTMSG("PapCleanup: Called with read data unread\n", !pendingRead);
stopping = TRUE;
}
else
{
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_INFO,
("AtalkPapCleanupConnection: Called for a stopped conn %lx.%lx\n",
pPapConn, pPapConn->papco_Flags));
}
}
RELEASE_SPIN_LOCK(&pPapConn->papco_Lock, OldIrql);
// Close the ATP Address Object if this was a server connection and
// opened its own socket.
if (stopping)
{
// If there is a pending read, then we need to cancel that atp request.
if ((pendingRead || fWaitingRead) && (pPapConn->papco_pAtpAddr != NULL))
{
AtalkAtpCancelReq(pPapConn->papco_pAtpAddr,
pPapConn->papco_ReadDataTid,
&pPapConn->papco_RemoteAddr);
}
// If we are already disconnecting this will return an error which
// we ignore. But if we were only in the ASSOCIATED state, then we
// need to call disassociate here.
error = AtalkPapDisconnect(pPapConn,
ATALK_LOCAL_DISCONNECT,
NULL,
NULL);
// We were already disconnected.
if (error == ATALK_INVALID_REQUEST)
{
AtalkPapDissociateAddress(pPapConn);
}
}
return ATALK_NO_ERROR;
}
ATALK_ERROR
AtalkPapCloseConnection(
IN PPAP_CONNOBJ pPapConn,
IN GENERIC_COMPLETION CompletionRoutine,
IN PVOID pCloseCtx
)
/*++
Routine Description:
Shutdown a session.
Arguments:
Return Value:
--*/
{
KIRQL OldIrql;
ASSERT(VALID_PAPCO(pPapConn));
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_INFO,
("AtalkPapCloseConnection: %lx\n", pPapConn));
ACQUIRE_SPIN_LOCK(&pPapConn->papco_Lock, &OldIrql);
if (pPapConn->papco_Flags & PAPCO_CLOSING)
{
// We are already closing! This should never happen!
KeBugCheck(0);
}
pPapConn->papco_Flags |= PAPCO_CLOSING;
// Set the completion info.
pPapConn->papco_CloseComp = CompletionRoutine;
pPapConn->papco_CloseCtx = pCloseCtx;
RELEASE_SPIN_LOCK(&pPapConn->papco_Lock, OldIrql);
// Remove the creation reference count
AtalkPapConnDereference(pPapConn);
return ATALK_PENDING;
}
ATALK_ERROR
AtalkPapAssociateAddress(
IN PPAP_ADDROBJ pPapAddr,
IN PPAP_CONNOBJ pPapConn
)
/*++
Routine Description:
Removed reference for the address for this connection. Causes deadlock in AFD where
AFD blocks on close of the address object and we wait for connections to be closed
first
Arguments:
Return Value:
--*/
{
ATALK_ERROR error;
KIRQL OldIrql;
ASSERT(VALID_PAPAO(pPapAddr));
ASSERT(VALID_PAPCO(pPapConn));
ACQUIRE_SPIN_LOCK(&pPapAddr->papao_Lock, &OldIrql);
ACQUIRE_SPIN_LOCK_DPC(&pPapConn->papco_Lock);
error = ATALK_ALREADY_ASSOCIATED;
if ((pPapConn->papco_Flags & PAPCO_ASSOCIATED) == 0)
{
error = ATALK_NO_ERROR;
// Link it in.
pPapConn->papco_pNextAssoc = pPapAddr->papao_pAssocConn;
pPapAddr->papao_pAssocConn = pPapConn;
// Remove not associated flag.
pPapConn->papco_Flags |= PAPCO_ASSOCIATED;
pPapConn->papco_pAssocAddr = pPapAddr;
// put Association refcount
pPapAddr->papao_RefCount++;
// mark that fact that we now have a refcount on addr obj for this conn
pPapConn->papco_Flags |= PAPCO_ADDR_ACTIVE;
}
RELEASE_SPIN_LOCK_DPC(&pPapConn->papco_Lock);
RELEASE_SPIN_LOCK(&pPapAddr->papao_Lock, OldIrql);
return error;
}
ATALK_ERROR
AtalkPapDissociateAddress(
IN PPAP_CONNOBJ pPapConn
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PPAP_ADDROBJ pPapAddr;
KIRQL OldIrql;
ATALK_ERROR error = ATALK_NO_ERROR;
BOOLEAN fDerefAddr = FALSE;
ASSERT(VALID_PAPCO(pPapConn));
ACQUIRE_SPIN_LOCK(&pPapConn->papco_Lock, &OldIrql);
if ((pPapConn->papco_Flags & (PAPCO_LISTENING |
PAPCO_CONNECTING |
PAPCO_ACTIVE |
PAPCO_ASSOCIATED)) != PAPCO_ASSOCIATED)
{
error = ATALK_INVALID_CONNECTION;
}
else
{
pPapAddr = pPapConn->papco_pAssocAddr ;
ASSERT(VALID_PAPAO(pPapAddr));
// Set not associated flag.
pPapConn->papco_Flags &= ~PAPCO_ASSOCIATED;
pPapConn->papco_pAssocAddr = NULL;
if (pPapConn->papco_Flags & PAPCO_ADDR_ACTIVE)
{
pPapConn->papco_Flags &= ~PAPCO_ADDR_ACTIVE;
fDerefAddr = TRUE;
}
}
RELEASE_SPIN_LOCK(&pPapConn->papco_Lock, OldIrql);
// Unlink it if ok.
if (ATALK_SUCCESS(error))
{
ACQUIRE_SPIN_LOCK(&pPapAddr->papao_Lock, &OldIrql);
ACQUIRE_SPIN_LOCK_DPC(&pPapConn->papco_Lock);
atalkPapConnDeQueueAssocList(pPapAddr, pPapConn);
RELEASE_SPIN_LOCK_DPC(&pPapConn->papco_Lock);
RELEASE_SPIN_LOCK(&pPapAddr->papao_Lock, OldIrql);
if (fDerefAddr)
{
// remove the Association refcount
AtalkPapAddrDereference(pPapAddr);
}
}
return error;
}
ATALK_ERROR
AtalkPapPostListen(
IN PPAP_CONNOBJ pPapConn,
IN PVOID pListenCtx,
IN GENERIC_COMPLETION CompletionRoutine
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PPAP_ADDROBJ pPapAddr = pPapConn->papco_pAssocAddr;
KIRQL OldIrql;
ATALK_ERROR error;
// This will also insert the connection object in the address objects
// list of connection which have a listen posted on them. When open
// connection requests come in, the first connection is taken off the list
// and the request satisfied.
ASSERT(VALID_PAPCO(pPapConn));
ASSERT(VALID_PAPAO(pPapAddr));
ACQUIRE_SPIN_LOCK(&pPapAddr->papao_Lock, &OldIrql);
ACQUIRE_SPIN_LOCK_DPC(&pPapConn->papco_Lock);
do
{
if ((pPapConn->papco_Flags & (PAPCO_LISTENING |
PAPCO_CONNECTING |
PAPCO_ACTIVE |
PAPCO_ASSOCIATED)) != PAPCO_ASSOCIATED)
{
error = ATALK_INVALID_CONNECTION;
break;
}
// Verify the address object is not a connect address type.
if (pPapAddr->papao_Flags & PAPAO_CONNECT)
{
error = ATALK_INVALID_PARAMETER;
break;
}
// Make the address object a listener.
pPapAddr->papao_Flags |= PAPAO_LISTENER;
pPapConn->papco_Flags |= PAPCO_LISTENING;
pPapConn->papco_ListenCtx = pListenCtx;
pPapConn->papco_ListenCompletion = CompletionRoutine;
// Insert into the listen list.
pPapConn->papco_pNextListen = pPapAddr->papao_pListenConn;
pPapAddr->papao_pListenConn = pPapConn;
// Unblock the address object.
pPapAddr->papao_Flags |= PAPAO_UNBLOCKED;
error = ATALK_NO_ERROR;
} while (FALSE);
RELEASE_SPIN_LOCK_DPC(&pPapConn->papco_Lock);
RELEASE_SPIN_LOCK(&pPapAddr->papao_Lock, OldIrql);
if (ATALK_SUCCESS(error))
{
// Now enqueue a few handlers on the address object. These will handle
// open conn/get status etc.
//
// DOC: For our implementation, until a server associates and places a
// listen on one of the connection objects, we do not become a
// listener. So the fact that we will not handle any request
// until that point is ok. Note that for a connect, the requests
// get posted on the connections atp address, which will be the same
// as the address's atp address.
if (!ATALK_SUCCESS(error = AtalkPapPrimeListener(pPapAddr)))
{
// Undo insert into the listen list.
ACQUIRE_SPIN_LOCK(&pPapAddr->papao_Lock, &OldIrql);
ACQUIRE_SPIN_LOCK_DPC(&pPapConn->papco_Lock);
atalkPapConnDeQueueListenList(pPapAddr, pPapConn);
RELEASE_SPIN_LOCK_DPC(&pPapConn->papco_Lock);
RELEASE_SPIN_LOCK(&pPapAddr->papao_Lock, OldIrql);
}
}
return error;
}
ATALK_ERROR
AtalkPapPrimeListener(
IN PPAP_ADDROBJ pPapAddr
)
/*++
Routine Description:
Enqueue a handler on the listener.
Arguments:
Return Value:
--*/
{
ATALK_ERROR error = ATALK_NO_ERROR;
KIRQL OldIrql;
BOOLEAN Unlock = TRUE;
ACQUIRE_SPIN_LOCK(&pPapAddr->papao_Lock, &OldIrql);
if ((pPapAddr->papao_Flags & PAPAO_SLS_QUEUED) == 0)
{
// Reference the address object for this handler we will be enqueuing.
AtalkPapAddrReferenceNonInterlock(pPapAddr, &error);
if (ATALK_SUCCESS(error))
{
pPapAddr->papao_Flags |= PAPAO_SLS_QUEUED;
RELEASE_SPIN_LOCK(&pPapAddr->papao_Lock, OldIrql);
Unlock = FALSE;
AtalkAtpSetReqHandler(pPapAddr->papao_pAtpAddr,
atalkPapSlsHandler,
pPapAddr);
}
}
if (Unlock)
{
RELEASE_SPIN_LOCK(&pPapAddr->papao_Lock, OldIrql);
}
return error;
}
ATALK_ERROR
AtalkPapCancelListen(
IN PPAP_CONNOBJ pPapConn
)
/*++
Routine Description:
Cancel a previously posted listen.
Arguments:
Return Value:
--*/
{
PPAP_ADDROBJ pPapAddr = pPapConn->papco_pAssocAddr;
ATALK_ERROR error = ATALK_NO_ERROR;
GENERIC_COMPLETION completionRoutine = NULL;
KIRQL OldIrql;
PVOID completionCtx = NULL;
ASSERT(VALID_PAPCO(pPapConn));
ASSERT(VALID_PAPAO(pPapAddr));
ACQUIRE_SPIN_LOCK(&pPapAddr->papao_Lock, &OldIrql);
if (!atalkPapConnDeQueueListenList(pPapAddr, pPapConn))
{
error = ATALK_INVALID_CONNECTION;
}
else
{
// We complete the listen routine
ASSERT(pPapConn->papco_Flags & PAPCO_LISTENING);
pPapConn->papco_Flags &= ~PAPCO_LISTENING;
completionRoutine = pPapConn->papco_ListenCompletion;
completionCtx = pPapConn->papco_ListenCtx;
}
RELEASE_SPIN_LOCK(&pPapAddr->papao_Lock, OldIrql);
if (*completionRoutine != NULL)
{
(*completionRoutine)(ATALK_REQUEST_CANCELLED, completionCtx);
}
return error;
}
ATALK_ERROR
AtalkPapPostConnect(
IN PPAP_CONNOBJ pPapConn,
IN PATALK_ADDR pRemoteAddr,
IN PVOID pConnectCtx,
IN GENERIC_COMPLETION CompletionRoutine
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
BOOLEAN DerefConn = FALSE;
KIRQL OldIrql;
ATALK_ERROR error = ATALK_NO_ERROR;
PBYTE pOpenPkt = NULL, pRespPkt = NULL;
PAMDL pOpenAmdl = NULL, pRespAmdl = NULL;
PPAP_ADDROBJ pPapAddr = pPapConn->papco_pAssocAddr;
ASSERT(VALID_PAPAO(pPapAddr));
ASSERT(VALID_PAPCO(pPapConn));
// Allocate a buffer to use.
if (((pOpenPkt = AtalkAllocMemory(PAP_STATUS_OFF)) == NULL) ||
((pOpenAmdl = AtalkAllocAMdl(pOpenPkt, PAP_STATUS_OFF)) == NULL) ||
((pRespPkt = AtalkAllocMemory(PAP_MAX_DATA_PACKET_SIZE)) == NULL) ||
((pRespAmdl = AtalkAllocAMdl(pRespPkt, PAP_MAX_DATA_PACKET_SIZE)) == NULL))
{
if (pOpenPkt != NULL)
AtalkFreeMemory(pOpenPkt);
if (pOpenAmdl != NULL)
AtalkFreeAMdl(pOpenAmdl);
if (pRespPkt != NULL)
AtalkFreeMemory(pRespPkt);
return ATALK_RESR_MEM;
}
ACQUIRE_SPIN_LOCK(&pPapAddr->papao_Lock, &OldIrql);
ACQUIRE_SPIN_LOCK_DPC(&pPapConn->papco_Lock);
do
{
if ((pPapConn->papco_Flags & (PAPCO_LISTENING |
PAPCO_CONNECTING |
PAPCO_ACTIVE |
PAPCO_ASSOCIATED)) != PAPCO_ASSOCIATED)
{
error = ATALK_INVALID_CONNECTION;
break;
}
// Verify the address object is not a listener address type.
if (pPapAddr->papao_Flags & PAPAO_LISTENER)
{
error = ATALK_INVALID_ADDRESS;
break;
}
// Reference the connection for the request we will be posting
AtalkPapConnReferenceByPtrNonInterlock(pPapConn, &error);
if (!ATALK_SUCCESS(error))
{
ASSERTMSG("AtalkPapPostConnect: Connection ref failed\n", 0);
break;
}
DerefConn = TRUE;
pPapConn->papco_ConnId = atalkPapGetNextConnId(pPapAddr, &error);
if (ATALK_SUCCESS(error))
{
// Make sure flags are clean.
pPapConn->papco_Flags &= ~(PAPCO_SENDDATA_RECD |
PAPCO_WRITEDATA_WAITING |
PAPCO_SEND_EOF_WRITE |
PAPCO_READDATA_PENDING |
PAPCO_REMOTE_CLOSE |
PAPCO_NONBLOCKING_READ |
PAPCO_READDATA_WAITING);
pPapConn->papco_Flags |= PAPCO_CONNECTING;
pPapConn->papco_ConnectCtx = pConnectCtx;
pPapConn->papco_ConnectCompletion = CompletionRoutine;
pPapConn->papco_pConnectRespBuf = pRespPkt;
pPapConn->papco_pConnectOpenBuf = pOpenPkt;
pPapConn->papco_ConnectRespLen = PAP_MAX_DATA_PACKET_SIZE;
pPapConn->papco_RemoteAddr = *pRemoteAddr;
pPapConn->papco_WaitTimeOut = 0; // To begin with
// For CONNECT, the connection object inherits the associated
// address objects atp address.
pPapConn->papco_pAtpAddr = pPapAddr->papao_pAtpAddr;
// Insert into the connect list.
pPapConn->papco_pNextConnect = pPapAddr->papao_pConnectConn;
pPapAddr->papao_pConnectConn = pPapConn;
pPapAddr->papao_Flags |= PAPAO_CONNECT;
}
else
{
ASSERTMSG("AtalkPapPostConnect: Unable to get conn id > 255 sess?\n", 0);
}
} while (FALSE);
RELEASE_SPIN_LOCK_DPC(&pPapConn->papco_Lock);
RELEASE_SPIN_LOCK(&pPapAddr->papao_Lock, OldIrql);
if (ATALK_SUCCESS(error))
{
error = atalkPapRepostConnect(pPapConn, pOpenAmdl, pRespAmdl);
if (ATALK_SUCCESS(error))
{
error = ATALK_PENDING;
DerefConn = FALSE;
}
else
{
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_ERR,
("AtalkPapConnect: AtalkAtpPostReq: failed %ld\n", error));
// Remove connection from the connect list and reset states.
ACQUIRE_SPIN_LOCK(&pPapAddr->papao_Lock, &OldIrql);
ACQUIRE_SPIN_LOCK_DPC(&pPapConn->papco_Lock);
pPapConn->papco_Flags &= ~PAPCO_CONNECTING;
pPapConn->papco_ConnectCtx = NULL;
pPapConn->papco_ConnectCompletion = NULL;
pPapConn->papco_pConnectRespBuf = NULL;
pPapConn->papco_ConnectRespLen = 0;
pPapConn->papco_pAtpAddr = NULL;
// Remove from the connect list.
atalkPapConnDeQueueConnectList(pPapAddr, pPapConn);
RELEASE_SPIN_LOCK_DPC(&pPapConn->papco_Lock);
RELEASE_SPIN_LOCK(&pPapAddr->papao_Lock, OldIrql);
}
}
if (!ATALK_SUCCESS(error))
{
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_ERR,
("AtalkPapConnect: failed %ld\n", error));
// Free all buffers.
ASSERT(pOpenPkt != NULL);
AtalkFreeMemory(pOpenPkt);
ASSERT(pOpenAmdl != NULL);
AtalkFreeAMdl(pOpenAmdl);
ASSERT(pRespPkt != NULL);
AtalkFreeMemory(pRespPkt);
ASSERT(pRespAmdl != NULL);
AtalkFreeAMdl(pRespAmdl);
}
if (DerefConn)
{
AtalkPapConnDereference(pPapConn);
}
return error;
}
ATALK_ERROR
AtalkPapDisconnect(
IN PPAP_CONNOBJ pPapConn,
IN ATALK_DISCONNECT_TYPE DisconnectType,
IN PVOID pDisconnectCtx,
IN GENERIC_COMPLETION CompletionRoutine
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
ATALK_ERROR tmpError;
PACTREQ pActReq;
PAMDL writeBuf;
PVOID writeCtx;
GENERIC_WRITE_COMPLETION writeCompletion = NULL;
ATALK_ERROR error = ATALK_PENDING;
KIRQL OldIrql;
BOOLEAN cancelPrimedRead = FALSE;
ASSERT(VALID_PAPCO(pPapConn));
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_INFO,
("AtalkPapDisconnect: %lx.%lx\n", pPapConn, DisconnectType));
ACQUIRE_SPIN_LOCK(&pPapConn->papco_Lock, &OldIrql);
// is it already disconnecting?
if (pPapConn->papco_Flags & PAPCO_DISCONNECTING)
{
RELEASE_SPIN_LOCK(&pPapConn->papco_Lock, OldIrql);
return(ATALK_PAP_CONN_CLOSING);
}
// When a disconnect comes in, we abort any sends waiting for a remote
// send data to come in.
if ((pPapConn->papco_Flags & (PAPCO_WRITEDATA_WAITING|PAPCO_SENDDATA_RECD)) ==
PAPCO_WRITEDATA_WAITING)
{
// In this situation, what happened is that a write was posted
// before a send data was received. As PapPostSendData response
// was never called in that case, there will be no pending reference
// on the connection.
writeCompletion = pPapConn->papco_WriteCompletion;
writeCtx = pPapConn->papco_WriteCtx;
writeBuf = pPapConn->papco_pWriteBuf;
pPapConn->papco_Flags &= ~PAPCO_WRITEDATA_WAITING;
pPapConn->papco_WriteCtx = NULL;
pPapConn->papco_pWriteBuf= NULL;
pPapConn->papco_WriteCompletion = NULL;
}
// Handle the case where a send data was received, but no data is to be sent
// just cancel the response.
if (pPapConn->papco_Flags & PAPCO_SENDDATA_RECD)
{
PATP_RESP pAtpResp = pPapConn->papco_pAtpResp;
ASSERT(VALID_ATPRS(pAtpResp));
pPapConn->papco_Flags &= ~PAPCO_SENDDATA_RECD;
RELEASE_SPIN_LOCK(&pPapConn->papco_Lock, OldIrql);
AtalkAtpCancelResp(pAtpResp);
ACQUIRE_SPIN_LOCK(&pPapConn->papco_Lock, &OldIrql);
}
// Handle the race condition between disconnect and last recv. indication.
// The fact that we have received a disconnect from the other side implies
// that we have sent out a release which in turn implies that we have all
// the data. If this is the case, then we defer disconnect till the recv.
// indication is done. Otherwise AFD gets real upset.
if ((DisconnectType == ATALK_REMOTE_DISCONNECT) &&
(pPapConn->papco_Flags & PAPCO_NONBLOCKING_READ))
{
if (AtalkAtpIsReqComplete(pPapConn->papco_pAtpAddr,
pPapConn->papco_ReadDataTid,
&pPapConn->papco_RemoteAddr))
{
pPapConn->papco_Flags |= PAPCO_DELAYED_DISCONNECT;
}
}
// Support for graceful disconnect. We only drop the received
// data when the local end does a disconnect. This will happen
// regardless of whether this routine was previously called or
// not. Also, this means that we must satisfy a read if disconnect is pending.
if ((DisconnectType == ATALK_LOCAL_DISCONNECT) ||
(DisconnectType == ATALK_TIMER_DISCONNECT))
{
pPapConn->papco_Flags |= PAPCO_REJECT_READS;
if (pPapConn->papco_Flags & PAPCO_READDATA_WAITING)
{
ASSERT(pPapConn->papco_Flags & PAPCO_NONBLOCKING_READ);
pActReq = pPapConn->papco_NbReadActReq;
// Reset the flags
pPapConn->papco_Flags &= ~(PAPCO_NONBLOCKING_READ | PAPCO_READDATA_WAITING);
RELEASE_SPIN_LOCK(&pPapConn->papco_Lock, OldIrql);
// Call the action completion routine for the prime read.
(*pActReq->ar_Completion)(ATALK_LOCAL_CLOSE, pActReq);
ACQUIRE_SPIN_LOCK(&pPapConn->papco_Lock, &OldIrql);
}
}
if ((pPapConn->papco_Flags & (PAPCO_DISCONNECTING | PAPCO_DELAYED_DISCONNECT)) == 0)
{
if (pPapConn->papco_Flags & (PAPCO_LISTENING |
PAPCO_CONNECTING |
PAPCO_ACTIVE))
{
pPapConn->papco_Flags |= PAPCO_DISCONNECTING;
if (pPapConn->papco_Flags & PAPCO_LISTENING)
{
RELEASE_SPIN_LOCK(&pPapConn->papco_Lock, OldIrql);
AtalkPapCancelListen(pPapConn);
ACQUIRE_SPIN_LOCK(&pPapConn->papco_Lock, &OldIrql);
}
else if (pPapConn->papco_Flags & PAPCO_CONNECTING)
{
RELEASE_SPIN_LOCK(&pPapConn->papco_Lock, OldIrql);
tmpError = AtalkAtpCancelReq(pPapConn->papco_pAtpAddr,
pPapConn->papco_ConnectTid,
&pPapConn->papco_RemoteAddr);
// We just stop the address. If success, it'll be dequeued from the
// connect list. If !success, its gone active. In either
// case we do not need to cleanup the ATP address. It could
// possibly be set to NULL by now as disconnect would have
// completed.
if (ATALK_SUCCESS(tmpError))
{
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_INFO,
("atalkPapDisconnect: Stopping atp address for %lx\n", pPapConn));
// AtalkAtpCleanupAddress(pPapConn->papco_pAtpAddr);
}
ACQUIRE_SPIN_LOCK(&pPapConn->papco_Lock, &OldIrql);
}
// Both of the above could have failed as the connection
// might have become active before the cancel succeeded.
// In that case (or if we were active to begin with), do
// a disconnect here.
if (pPapConn->papco_Flags & PAPCO_ACTIVE)
{
// Remember completion routines as appropriate.
if (DisconnectType == ATALK_INDICATE_DISCONNECT)
{
if (pPapConn->papco_DisconnectInform == NULL)
{
pPapConn->papco_DisconnectInform = CompletionRoutine;
pPapConn->papco_DisconnectInformCtx = pDisconnectCtx;
}
else
{
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_ERR,
("AtalkPapDisconnect: duplicate disc comp rou%lx\n", pPapConn));
error = ATALK_TOO_MANY_COMMANDS;
}
}
else if (DisconnectType == ATALK_LOCAL_DISCONNECT)
{
// Replace completion routines only if previous ones are NULL.
if (pPapConn->papco_DisconnectCompletion == NULL)
{
pPapConn->papco_DisconnectCompletion = CompletionRoutine;
pPapConn->papco_DisconnectCtx = pDisconnectCtx;
}
else
{
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_ERR,
("AtalkPapDisconnect: duplicate disc comp rou%lx\n", pPapConn));
error = ATALK_TOO_MANY_COMMANDS;
}
}
// Figure out the disconnect status and remember it in the
// connection object.
pPapConn->papco_DisconnectStatus = DISCONN_STATUS(DisconnectType);
if ((pPapConn->papco_Flags & (PAPCO_NONBLOCKING_READ|PAPCO_READDATA_WAITING)) ==
PAPCO_NONBLOCKING_READ)
{
pPapConn->papco_Flags &= ~PAPCO_NONBLOCKING_READ;
cancelPrimedRead = TRUE;
}
RELEASE_SPIN_LOCK(&pPapConn->papco_Lock, OldIrql);
// If there is a pending write, then we need to complete the write.
if (writeCompletion != NULL)
{
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_ERR,
("AtalkPapDisconect: Completing write %lx.%lx\n",
pPapConn, pPapConn->papco_DisconnectStatus));
(*writeCompletion)(pPapConn->papco_DisconnectStatus,
writeBuf,
0, // Write length
writeCtx);
}
if (cancelPrimedRead)
{
AtalkAtpCancelReq(pPapConn->papco_pAtpAddr,
pPapConn->papco_ReadDataTid,
&pPapConn->papco_RemoteAddr);
}
// Cancel the tickle request
AtalkAtpCancelReq(pPapConn->papco_pAtpAddr,
pPapConn->papco_TickleTid,
&pPapConn->papco_RemoteAddr);
// Send out disconnect packet if this was a timer or local close.
if ((DisconnectType == ATALK_LOCAL_DISCONNECT) ||
(DisconnectType == ATALK_TIMER_DISCONNECT))
{
BYTE userBytes[ATP_USERBYTES_SIZE];
USHORT tid;
userBytes[PAP_CONNECTIONID_OFF] = pPapConn->papco_ConnId;
userBytes[PAP_CMDTYPE_OFF] = PAP_CLOSE_CONN;
PUTSHORT2SHORT(&userBytes[PAP_SEQNUM_OFF], 0);
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_INFO,
("AtalkPapDisconect: Sending disc to %lx - %lx\n",
pPapConn->papco_RemoteAddr.ata_Address,
DisconnectType));
tmpError = AtalkAtpPostReq(pPapConn->papco_pAtpAddr,
&pPapConn->papco_RemoteAddr,
&tid,
0, // ALO request
NULL,
0,
userBytes,
NULL,
0,
0, // Retry count
0, // Retry interval
THIRTY_SEC_TIMER,
NULL,
NULL);
if (!ATALK_SUCCESS(tmpError))
{
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_ERR,
("AtalkPapDisconnect: post disc to rem fail%lx\n", pPapConn));
}
}
// Call the disconnect indication routine if present for a timer/
// remote disconnect.
if ((DisconnectType == ATALK_REMOTE_DISCONNECT) ||
(DisconnectType == ATALK_TIMER_DISCONNECT))
{
PVOID discCtx;
PTDI_IND_DISCONNECT discHandler = NULL;
// Acquire lock so we get a consistent handler/ctx.
ACQUIRE_SPIN_LOCK(&pPapConn->papco_pAssocAddr->papao_Lock, &OldIrql);
discHandler = pPapConn->papco_pAssocAddr->papao_DisconnectHandler;
discCtx = pPapConn->papco_pAssocAddr->papao_DisconnectHandlerCtx;
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_INFO,
("AtalkPapDisconnect: IndDisc to AFD %lx\n", pPapConn));
#if DBG
if (discHandler != NULL)
{
pPapConn->papco_Flags |= PAPCO_INDICATE_AFD_DISC;
}
#endif
RELEASE_SPIN_LOCK(&pPapConn->papco_pAssocAddr->papao_Lock, OldIrql);
if (discHandler != NULL)
{
// We use TDI_DISCONNECT_ABORT for flags. This makes
// AFD call Close for connection, but also allows a
// recv to come in as we are a buffering transport.
(*discHandler)(discCtx,
pPapConn->papco_ConnCtx,
0, // DisconnectDataLength
NULL, // DisconnectData
0, // DisconnectInfoLength
NULL, // DisconnectInfo
TDI_DISCONNECT_ABORT); // Disconnect flags.
}
}
// Stop the atp address. only if we are a server job ?
// Client side pap connections can be many-to-one atp address.
// Doesn't affect monitor.
AtalkAtpCleanupAddress(pPapConn->papco_pAtpAddr);
ACQUIRE_SPIN_LOCK(&pPapConn->papco_Lock, &OldIrql);
}
}
else
{
error = ATALK_INVALID_REQUEST;
}
}
else
{
// Do we need to remember the completion routines?
// Yes, if this is a disconnect or a indicate disconnect request,
// and our current disconnect was started due to the address object
// being closed.
if (DisconnectType == ATALK_INDICATE_DISCONNECT)
{
if (pPapConn->papco_DisconnectInform == NULL)
{
pPapConn->papco_DisconnectInform = CompletionRoutine;
pPapConn->papco_DisconnectInformCtx = pDisconnectCtx;
}
else
{
error = ATALK_TOO_MANY_COMMANDS;
}
}
else if (DisconnectType == ATALK_LOCAL_DISCONNECT)
{
// Replace completion routines only if previous ones are NULL.
if (pPapConn->papco_DisconnectCompletion == NULL)
{
pPapConn->papco_DisconnectCompletion = CompletionRoutine;
pPapConn->papco_DisconnectCtx = pDisconnectCtx;
}
else
{
error = ATALK_TOO_MANY_COMMANDS;
}
}
}
RELEASE_SPIN_LOCK(&pPapConn->papco_Lock, OldIrql);
return error;
}
ATALK_ERROR
AtalkPapRead(
IN PPAP_CONNOBJ pPapConn,
IN PAMDL pReadBuf,
IN USHORT ReadBufLen,
IN ULONG ReadFlags,
IN PVOID pReadCtx,
IN GENERIC_READ_COMPLETION CompletionRoutine
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
BYTE userBytes[ATP_USERBYTES_SIZE];
USHORT readLen, timeout;
ULONG readFlags;
PACTREQ pActReq;
BOOLEAN delayedDisConn = FALSE;
KIRQL OldIrql;
ATALK_ERROR error = ATALK_NO_ERROR;
ASSERT(VALID_PAPCO(pPapConn));
ASSERT(*CompletionRoutine != NULL);
ACQUIRE_SPIN_LOCK(&pPapConn->papco_Lock, &OldIrql);
do
{
if (ReadFlags & TDI_RECEIVE_EXPEDITED)
{
error = ATALK_INVALID_PARAMETER;
break;
}
if (pPapConn->papco_Flags & PAPCO_READDATA_PENDING)
{
error = ATALK_PAP_TOO_MANY_READS;
break;
}
// PEEK not supported for PAP
if (ReadFlags & TDI_RECEIVE_PEEK)
{
error = ATALK_PAP_INVALID_REQUEST;
break;
}
// Do we already have a pending/indicated read waiting?
// !!!NOTE!!! If we do we complete the read regardless. With winsock
// our primed read will complete and we will indicate the data, but the
// mac immediately follows with a disconnect. if the winsock client is
// unable to read the data, before the disconnect comes in, it will lose
// the last block of data.
if (pPapConn->papco_Flags & PAPCO_READDATA_WAITING)
{
pActReq = pPapConn->papco_NbReadActReq;
readLen = pPapConn->papco_NbReadLen;
readFlags = pPapConn->papco_NbReadFlags;
// Make sure buffer size is atleast that of the indicated
// data.
if (ReadBufLen < readLen)
{
error = ATALK_BUFFER_TOO_SMALL;
break;
}
// Reset the flags allowing primes to happen.
pPapConn->papco_Flags &= ~(PAPCO_NONBLOCKING_READ |
PAPCO_READDATA_WAITING);
pPapConn->papco_NbReadLen = 0;
pPapConn->papco_NbReadFlags = 0;
RELEASE_SPIN_LOCK(&pPapConn->papco_Lock, OldIrql);
// Call the action completion routine for the prime read.
(*pActReq->ar_Completion)(ATALK_NO_ERROR, pActReq);
if (ATALK_SUCCESS(error))
{
error = ((readFlags & TDI_RECEIVE_PARTIAL) ?
ATALK_PAP_PARTIAL_RECEIVE : ATALK_NO_ERROR);
}
// Call completion routine and return status success.
(*CompletionRoutine)(error,
pReadBuf,
readLen,
readFlags,
pReadCtx);
// Return pending.
return ATALK_PENDING;
}
if ((pPapConn->papco_Flags & PAPCO_ACTIVE) == 0)
{
error = ATALK_PAP_CONN_NOT_ACTIVE;
break;
}
if (ReadBufLen < (PAP_MAX_FLOW_QUANTUM*PAP_MAX_DATA_PACKET_SIZE))
{
error = ATALK_BUFFER_TOO_SMALL;
break;
}
error = ATALK_INVALID_CONNECTION;
if ((pPapConn->papco_Flags & (PAPCO_CLOSING |
PAPCO_STOPPING |
PAPCO_DISCONNECTING)) == 0)
{
AtalkPapConnReferenceByPtrNonInterlock(pPapConn, &error);
}
if (!ATALK_SUCCESS(error))
{
break;
}
pPapConn->papco_Flags |= PAPCO_READDATA_PENDING;
// Remember read information in the connection object.
pPapConn->papco_ReadCompletion = CompletionRoutine;
pPapConn->papco_ReadCtx = pReadCtx;
} while (FALSE);
RELEASE_SPIN_LOCK(&pPapConn->papco_Lock, OldIrql);
if (!ATALK_SUCCESS(error))
{
return error;
}
// Build up the user bytes to post the 'SendData' to the remote end.
userBytes[PAP_CONNECTIONID_OFF] = pPapConn->papco_ConnId;
userBytes[PAP_CMDTYPE_OFF] = PAP_SEND_DATA;
PUTSHORT2SHORT(&userBytes[PAP_SEQNUM_OFF], pPapConn->papco_NextOutgoingSeqNum);
// PAP Sequence number 0 means unsequenced.
if (++pPapConn->papco_NextOutgoingSeqNum == 0)
pPapConn->papco_NextOutgoingSeqNum = 1;
// Post the SendData request.
pPapConn->papco_RT.rt_New = AtalkGetCurrentTick();
if (pPapConn->papco_Flags & PAPCO_SERVER_JOB)
timeout = pPapConn->papco_RT.rt_Base;
else timeout = PAP_MAX_SENDDATA_REQ_INTERVAL;
error = AtalkAtpPostReq(pPapConn->papco_pAtpAddr,
&pPapConn->papco_RemoteAddr,
&pPapConn->papco_ReadDataTid,
ATP_REQ_EXACTLY_ONCE,
NULL,
0,
userBytes,
pReadBuf,
ReadBufLen,
ATP_INFINITE_RETRIES,
timeout,
THIRTY_SEC_TIMER,
atalkPapIncomingReadComplete,
pPapConn);
if (!ATALK_SUCCESS(error))
{
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_ERR,
("AtalkPapRead: AtalkAtpPostReq %ld\n", error));
ACQUIRE_SPIN_LOCK(&pPapConn->papco_Lock, &OldIrql);
// Undo the seq number change.
if (--pPapConn->papco_NextOutgoingSeqNum == 0)
pPapConn->papco_NextOutgoingSeqNum = (USHORT)0xFFFF;
pPapConn->papco_Flags &= ~PAPCO_READDATA_PENDING;
// Clear out read information in the connection object.
pPapConn->papco_ReadCompletion = NULL;
pPapConn->papco_ReadCtx = NULL;
RELEASE_SPIN_LOCK(&pPapConn->papco_Lock, OldIrql);
AtalkPapConnDereference(pPapConn);
}
else
{
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_INFO,
("AtalkPapRead: No error - AtalkAtpPostReq tid %lx\n",
pPapConn->papco_ReadDataTid));
error = ATALK_PENDING;
}
return error;
}
ATALK_ERROR
AtalkPapPrimeRead(
IN PPAP_CONNOBJ pPapConn,
IN PACTREQ pActReq
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
BYTE userBytes[ATP_USERBYTES_SIZE];
USHORT timeout;
KIRQL OldIrql;
ATALK_ERROR error = ATALK_NO_ERROR;
SHORT MaxRespBufLen;
ASSERT(VALID_PAPCO(pPapConn));
ACQUIRE_SPIN_LOCK(&pPapConn->papco_Lock, &OldIrql);
do
{
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_INFO,
("AtalkPapPrimeRead: %lx\n", pPapConn));
if (pPapConn->papco_Flags & PAPCO_NONBLOCKING_READ)
{
error = ATALK_PAP_TOO_MANY_READS;
break;
}
if (pPapConn->papco_Flags & PAPCO_REJECT_READS)
{
error = ATALK_PAP_CONN_NOT_ACTIVE;
break;
}
if ((pPapConn->papco_Flags & PAPCO_ACTIVE) == 0)
{
error = ATALK_PAP_CONN_NOT_ACTIVE;
break;
}
error = ATALK_INVALID_CONNECTION;
if ((pPapConn->papco_Flags & (PAPCO_CLOSING |
PAPCO_STOPPING |
PAPCO_DELAYED_DISCONNECT |
PAPCO_DISCONNECTING)) == 0)
{
AtalkPapConnReferenceByPtrNonInterlock(pPapConn, &error);
}
if (!ATALK_SUCCESS(error))
{
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_ERR,
("AtalkPapPrimeRead: Conn %lx Reference Failed %lx\n", pPapConn, error));
break;
}
// Remember info in the connection
pPapConn->papco_Flags |= PAPCO_NONBLOCKING_READ;
pPapConn->papco_NbReadLen = 0;
pPapConn->papco_NbReadActReq = pActReq;
} while (FALSE);
RELEASE_SPIN_LOCK(&pPapConn->papco_Lock, OldIrql);
if (!ATALK_SUCCESS(error))
{
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_INFO,
("AtalkPapPrimeRead: Failed %lx.%lx\n", error, pPapConn));
return error;
}
// Build up the user bytes to post the 'SendData' to the remote end.
userBytes[PAP_CONNECTIONID_OFF] = pPapConn->papco_ConnId;
userBytes[PAP_CMDTYPE_OFF] = PAP_SEND_DATA;
PUTSHORT2SHORT(&userBytes[PAP_SEQNUM_OFF], pPapConn->papco_NextOutgoingSeqNum);
// PAP Sequence number 0 means unsequenced.
if (++pPapConn->papco_NextOutgoingSeqNum == 0)
pPapConn->papco_NextOutgoingSeqNum = 1;
// Post the SendData request.
pPapConn->papco_RT.rt_New = AtalkGetCurrentTick();
if (pPapConn->papco_Flags & PAPCO_SERVER_JOB)
timeout = pPapConn->papco_RT.rt_Base;
else timeout = PAP_MAX_SENDDATA_REQ_INTERVAL;
MaxRespBufLen = (pPapConn->papco_pAtpAddr->atpao_MaxSinglePktSize * ATP_MAX_RESP_PKTS);
if (pActReq->ar_MdlSize > MaxRespBufLen)
{
pActReq->ar_MdlSize = MaxRespBufLen;
}
error = AtalkAtpPostReq(pPapConn->papco_pAtpAddr,
&pPapConn->papco_RemoteAddr,
&pPapConn->papco_ReadDataTid,
ATP_REQ_EXACTLY_ONCE, // ExactlyOnce request
NULL,
0,
userBytes,
pActReq->ar_pAMdl,
pActReq->ar_MdlSize,
ATP_INFINITE_RETRIES,
timeout,
THIRTY_SEC_TIMER,
atalkPapPrimedReadComplete,
pPapConn);
if (!ATALK_SUCCESS(error))
{
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_ERR,
("AtalkPapRead: AtalkAtpPostReq %ld\n", error));
// Out of resources error log.
TMPLOGERR();
ACQUIRE_SPIN_LOCK(&pPapConn->papco_Lock, &OldIrql);
// Undo the seq number change.
if (--pPapConn->papco_NextOutgoingSeqNum == 0)
pPapConn->papco_NextOutgoingSeqNum = (USHORT)0xFFFF;
pPapConn->papco_Flags &= ~PAPCO_NONBLOCKING_READ;
pPapConn->papco_NbReadActReq = NULL;
pPapConn->papco_NbReadLen = 0;
RELEASE_SPIN_LOCK(&pPapConn->papco_Lock, OldIrql);
AtalkPapConnDereference(pPapConn);
}
else
{
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_INFO,
("AtalkPapPrimedRead: No error - AtalkAtpPostReq tid %lx\n",
pPapConn->papco_ReadDataTid));
error = ATALK_PENDING;
// If a disconnect happened, cancel prime read just in case disconnect
// was unable to due to the tid having been uninitialized.
if (pPapConn->papco_Flags & PAPCO_DISCONNECTING)
{
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_ERR,
("AtalkPapPrimedRead: DISC When PRIMEREAD %lx.%lx\n",
pPapConn, pPapConn->papco_Flags));
AtalkAtpCancelReq(pPapConn->papco_pAtpAddr,
pPapConn->papco_ReadDataTid,
&pPapConn->papco_RemoteAddr);
}
}
return error;
}
ATALK_ERROR
AtalkPapWrite(
IN PPAP_CONNOBJ pPapConn,
IN PAMDL pWriteBuf,
IN USHORT WriteBufLen,
IN ULONG SendFlags,
IN PVOID pWriteCtx,
IN GENERIC_WRITE_COMPLETION CompletionRoutine
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
BOOLEAN sendDataRecd, eom;
ATALK_ERROR error;
PTDI_IND_SEND_POSSIBLE sendPossibleHandler;
PVOID sendPossibleHandlerCtx;
KIRQL OldIrql;
PPAP_ADDROBJ pPapAddr;
ASSERT(VALID_PAPCO(pPapConn));
pPapAddr = pPapConn->papco_pAssocAddr;
ASSERT(VALID_PAPAO(pPapAddr));
KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
ACQUIRE_SPIN_LOCK_DPC(&pPapConn->papco_Lock);
do
{
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_INFO,
("AtalkPapWrite: Buffer size %lx.%lx\n",
WriteBufLen, pPapConn->papco_SendFlowQuantum * PAP_MAX_DATA_PACKET_SIZE));
if (WriteBufLen > (pPapConn->papco_SendFlowQuantum * PAP_MAX_DATA_PACKET_SIZE))
{
error = ATALK_BUFFER_TOO_BIG;
ASSERTMSG("AtalkPapWrite: An invalid buffer size used for write\n",
(WriteBufLen <= (pPapConn->papco_SendFlowQuantum * PAP_MAX_DATA_PACKET_SIZE)));
break;
}
if (pPapConn->papco_Flags & PAPCO_WRITEDATA_WAITING)
{
error = ATALK_PAP_TOO_MANY_WRITES;
ASSERTMSG("AtalkPapWrite: A second write was posted\n", 0);
break;
}
if ((pPapConn->papco_Flags & PAPCO_ACTIVE) == 0)
{
error = ATALK_PAP_CONN_NOT_ACTIVE;
break;
}
// Non-blocking sends for PAP:
// Pap uses a binary event - send data credit thats sent to the remote
// end. ATP remembers the actual size of the remote entity's response
// buffer. In any case, if we do not have send credit the call would
// block, and we dont want that to happen. Also, there should be no
// pending writes on the connection to begin with.
//
if (SendFlags & TDI_SEND_EXPEDITED)
{
ASSERTMSG("AtalkPapWrite: Expedited set for pap\n", 0);
error = ATALK_INVALID_PARAMETER;
break;
}
// This goes away before the call returns. PostSendData has its
// own reference.
error = ATALK_INVALID_CONNECTION;
if ((pPapConn->papco_Flags & ( PAPCO_CLOSING |
PAPCO_STOPPING |
PAPCO_DISCONNECTING)) == 0)
{
AtalkPapConnReferenceByPtrNonInterlock(pPapConn, &error);
if (ATALK_SUCCESS(error))
{
sendDataRecd = ((pPapConn->papco_Flags & PAPCO_SENDDATA_RECD) != 0);
if (!sendDataRecd &&
(SendFlags & TDI_SEND_NON_BLOCKING))
{
// !!!NOTE!!!
// To avoid the race condition in AFD where an incoming
// send data indication setting send's possible to true
// is overwritten by this read's unwinding and setting it
// to false, we return ATALK_REQUEST_NOT_ACCEPTED, which
// will map to STATUS_REQUEST_NOT_ACCEPTED and then to
// WSAEWOULDBLOCK.
error = ATALK_REQUEST_NOT_ACCEPTED;
RELEASE_SPIN_LOCK_DPC(&pPapConn->papco_Lock);
// We have a reference on the connection we must remove.
AtalkPapConnDereference(pPapConn);
ACQUIRE_SPIN_LOCK_DPC(&pPapConn->papco_Lock);
}
}
}
if (!ATALK_SUCCESS(error))
{
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_INFO,
("AtalkPapWrite: Write failed %lx\n", error));
break;
}
error = ATALK_PENDING;
eom = (SendFlags & TDI_SEND_PARTIAL) ? FALSE : TRUE;
pPapConn->papco_Flags |= PAPCO_WRITEDATA_WAITING;
if (eom)
{
pPapConn->papco_Flags |= PAPCO_SEND_EOF_WRITE;
}
pPapConn->papco_WriteCompletion = CompletionRoutine;
pPapConn->papco_WriteCtx = pWriteCtx;
pPapConn->papco_pWriteBuf = pWriteBuf;
pPapConn->papco_WriteLen = WriteBufLen;
// Stop further writes from happening by indicating to afd.
// Call the send data event handler on the associated address with
// 0 to turn off selects on writes. We do this before we post any
// get requests, so there is no race condition.
// remember send possible handler/context.
sendPossibleHandler = pPapAddr->papao_SendPossibleHandler;
sendPossibleHandlerCtx = pPapAddr->papao_SendPossibleHandlerCtx;
} while (FALSE);
if (ATALK_SUCCESS(error))
{
if (sendDataRecd)
{
if (*sendPossibleHandler != NULL)
{
(*sendPossibleHandler)(sendPossibleHandlerCtx,
pPapConn->papco_ConnCtx,
0);
}
// atalkPostSendDataResp() will release the conn lock
error = atalkPapPostSendDataResp(pPapConn);
if (!ATALK_SUCCESS(error))
{
ACQUIRE_SPIN_LOCK_DPC(&pPapConn->papco_Lock);
pPapConn->papco_Flags &= ~PAPCO_WRITEDATA_WAITING;
RELEASE_SPIN_LOCK_DPC(&pPapConn->papco_Lock);
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_ERR,
("AtalkPapWrite: atalkPapPostSendDataResp Failed %lx\n", pPapConn));
}
}
else
{
RELEASE_SPIN_LOCK_DPC(&pPapConn->papco_Lock);
}
AtalkPapConnDereference(pPapConn);
}
else
{
RELEASE_SPIN_LOCK_DPC(&pPapConn->papco_Lock);
}
if (OldIrql != DISPATCH_LEVEL)
KeLowerIrql(OldIrql);
return error;
}
ATALK_ERROR
AtalkPapSetStatus(
IN PPAP_ADDROBJ pPapAddr,
IN PAMDL pStatusMdl, // NULL if 0-length status
IN PACTREQ pActReq
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
NTSTATUS status;
USHORT stsBufSize=0;
ULONG bytesCopied;
KIRQL OldIrql;
PBYTE pStatusBuf = NULL, pFreeStatusBuf = NULL;
ASSERT(VALID_PAPAO(pPapAddr));
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_INFO,
("AtalkPapSetStatus: Entered for Addr %lx\n", pPapAddr));
if (pStatusMdl == NULL)
{
pStatusBuf = NULL;
stsBufSize = 0;
}
else
{
// Allocate a buffer and copy the contents of the passed in
// buffer descriptor in it. Free an existing status buffer if one exists
stsBufSize = (USHORT)AtalkSizeMdlChain(pStatusMdl);
if (stsBufSize >= PAP_MAX_STATUS_SIZE)
return ATALK_BUFFER_TOO_BIG;
if (stsBufSize < 0)
return ATALK_BUFFER_TOO_SMALL;
}
if (stsBufSize > 0)
{
if ((pStatusBuf = AtalkAllocMemory(stsBufSize)) == NULL)
{
return ATALK_RESR_MEM;
}
status = TdiCopyMdlToBuffer(pStatusMdl,
0,
pStatusBuf,
0,
stsBufSize,
&bytesCopied);
ASSERT(NT_SUCCESS(status) && (bytesCopied == stsBufSize));
}
ACQUIRE_SPIN_LOCK(&pPapAddr->papao_Lock, &OldIrql);
pFreeStatusBuf = pPapAddr->papao_pStatusBuf;
pPapAddr->papao_pStatusBuf = pStatusBuf;
pPapAddr->papao_StatusSize = stsBufSize;
RELEASE_SPIN_LOCK(&pPapAddr->papao_Lock, OldIrql);
if (pFreeStatusBuf != NULL)
{
AtalkFreeMemory(pFreeStatusBuf);
}
// Call the completion routine before returning.
(*pActReq->ar_Completion)(ATALK_NO_ERROR, pActReq);
return ATALK_NO_ERROR;
}
ATALK_ERROR
AtalkPapGetStatus(
IN PPAP_ADDROBJ pPapAddr,
IN PATALK_ADDR pRemoteAddr,
IN PAMDL pStatusAmdl,
IN USHORT AmdlSize,
IN PACTREQ pActReq
)
/*++
Routine Description:
Arguments:
The Status Buffer passed in will be preceded by 4 bytes for the
unused bytes.
Return Value:
--*/
{
ATALK_ERROR error;
BYTE userBytes[ATP_USERBYTES_SIZE];
USHORT tid;
if ((pRemoteAddr->ata_Network == 0) ||
(pRemoteAddr->ata_Node == 0) ||
(pRemoteAddr->ata_Socket == 0))
{
return ATALK_SOCKET_INVALID;
}
userBytes[PAP_CONNECTIONID_OFF] = 0;
userBytes[PAP_CMDTYPE_OFF] = PAP_SEND_STATUS;
PUTSHORT2SHORT(&userBytes[PAP_SEQNUM_OFF], 0);
error = AtalkAtpPostReq(pPapAddr->papao_pAtpAddr,
pRemoteAddr,
&tid,
0, // ExactlyOnce request
NULL,
0,
userBytes,
pStatusAmdl,
AmdlSize,
PAP_GETSTATUS_REQ_RETRYCOUNT,
PAP_GETSTATUS_ATP_INTERVAL,
THIRTY_SEC_TIMER,
atalkPapIncomingStatus,
(PVOID)pActReq);
if (ATALK_SUCCESS(error))
{
error = ATALK_PENDING;
}
return error;
}
VOID
AtalkPapQuery(
IN PVOID pObject,
IN ULONG ObjectType,
IN PAMDL pAmdl,
OUT PULONG BytesWritten
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
switch (ObjectType)
{
case TDI_TRANSPORT_ADDRESS_FILE :
ASSERT(VALID_PAPAO((PPAP_ADDROBJ)pObject));
AtalkDdpQuery(AtalkPapGetDdpAddress((PPAP_ADDROBJ)pObject),
pAmdl,
BytesWritten);
break;
case TDI_CONNECTION_FILE :
{
PPAP_CONNOBJ pPapConn;
KIRQL OldIrql;
pPapConn = (PPAP_CONNOBJ)pObject;
ASSERT(VALID_PAPCO(pPapConn));
*BytesWritten = 0;
// Get the address from the associated address if any.
ACQUIRE_SPIN_LOCK(&pPapConn->papco_Lock, &OldIrql);
if (pPapConn->papco_Flags & PAPCO_ASSOCIATED)
{
AtalkDdpQuery(AtalkPapGetDdpAddress(pPapConn->papco_pAssocAddr),
pAmdl,
BytesWritten);
}
RELEASE_SPIN_LOCK(&pPapConn->papco_Lock, OldIrql);
}
break;
case TDI_CONTROL_CHANNEL_FILE :
default:
break;
}
}
LOCAL ATALK_ERROR
atalkPapRepostConnect(
IN PPAP_CONNOBJ pPapConn,
IN PAMDL pOpenAmdl,
IN PAMDL pRespAmdl
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
BYTE userBytes[ATP_USERBYTES_SIZE];
PBYTE pOpenPkt = AtalkGetAddressFromMdlSafe(pOpenAmdl, NormalPagePriority);
ATALK_ERROR error;
if (pOpenPkt == NULL) {
error = ATALK_RESR_MEM;
return error;
}
// Okay, prepare to post the OpenConn request; build up both userBytes and
// data buffer!
userBytes[PAP_CONNECTIONID_OFF] = pPapConn->papco_ConnId;
userBytes[PAP_CMDTYPE_OFF] = PAP_OPEN_CONN;
PUTSHORT2SHORT(&userBytes[PAP_SEQNUM_OFF], 0);
pOpenPkt[PAP_RESP_SOCKET_OFF] = PAPCONN_DDPSOCKET(pPapConn);
pOpenPkt[PAP_FLOWQUANTUM_OFF] = PAP_MAX_FLOW_QUANTUM;
PUTDWORD2SHORT(&pOpenPkt[PAP_WAITTIME_OFF], pPapConn->papco_WaitTimeOut);
// Post the open connection request. We do it on the atp address of the
// pap address object.
error = AtalkAtpPostReq(pPapConn->papco_pAtpAddr,
&pPapConn->papco_RemoteAddr,
&pPapConn->papco_ConnectTid,
ATP_REQ_EXACTLY_ONCE, // ExactlyOnce request
pOpenAmdl,
PAP_STATUS_OFF,
userBytes,
pRespAmdl,
PAP_MAX_DATA_PACKET_SIZE,
PAP_OPENCONN_REQ_RETRYCOUNT,
PAP_OPENCONN_INTERVAL,
THIRTY_SEC_TIMER,
atalkPapIncomingOpenReply,
pPapConn);
return error;
}
LOCAL VOID FASTCALL
atalkPapAddrDeref(
IN PPAP_ADDROBJ pPapAddr
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
KIRQL OldIrql;
BOOLEAN done = FALSE;
ACQUIRE_SPIN_LOCK(&pPapAddr->papao_Lock, &OldIrql);
ASSERT(pPapAddr->papao_RefCount > 0);
if (--pPapAddr->papao_RefCount == 0)
{
done = TRUE;
ASSERT(pPapAddr->papao_Flags & PAPAO_CLOSING);
}
RELEASE_SPIN_LOCK(&pPapAddr->papao_Lock, OldIrql);
if (done)
{
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_INFO,
("atalkPapAddrDeref: Addr %lx done with.\n", pPapAddr));
if (pPapAddr->papao_CloseComp != NULL)
{
(*pPapAddr->papao_CloseComp)(ATALK_NO_ERROR,
pPapAddr->papao_CloseCtx);
}
// Remove from the global list.
ACQUIRE_SPIN_LOCK(&atalkPapLock, &OldIrql);
AtalkUnlinkDouble(pPapAddr, papao_Next, papao_Prev);
RELEASE_SPIN_LOCK(&atalkPapLock, OldIrql);
// Free any status buffer allocated for this address object
if (pPapAddr->papao_pStatusBuf != NULL)
AtalkFreeMemory(pPapAddr->papao_pStatusBuf);
AtalkFreeMemory(pPapAddr);
AtalkUnlockPapIfNecessary();
}
}
LOCAL VOID FASTCALL
atalkPapConnRefByPtrNonInterlock(
IN PPAP_CONNOBJ pPapConn,
OUT PATALK_ERROR pError
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
*pError = ATALK_NO_ERROR;
ASSERT(VALID_PAPCO(pPapConn));
if ((pPapConn->papco_Flags & PAPCO_CLOSING) == 0)
{
ASSERT(pPapConn->papco_RefCount >= 1);
pPapConn->papco_RefCount ++;
}
else
{
*pError = ATALK_PAP_CONN_CLOSING;
}
}
LOCAL VOID
atalkPapConnRefByCtxNonInterlock(
IN PPAP_ADDROBJ pPapAddr,
IN CONNECTION_CONTEXT Ctx,
OUT PPAP_CONNOBJ * pPapConn,
OUT PATALK_ERROR pError
)
/*++
Routine Description:
!!!MUST BE CALLED WITH THE ADDRESS SPINLOCK HELD!!!
Arguments:
Return Value:
--*/
{
PPAP_CONNOBJ pPapChkConn;
ATALK_ERROR error = ATALK_PAP_CONN_NOT_FOUND;
for (pPapChkConn = pPapAddr->papao_pAssocConn;
pPapChkConn != NULL;
pPapChkConn = pPapChkConn->papco_pNextAssoc)
{
if (pPapChkConn->papco_ConnCtx == Ctx)
{
AtalkPapConnReferenceByPtr(pPapChkConn, &error);
if (ATALK_SUCCESS(error))
{
*pPapConn = pPapChkConn;
}
break;
}
}
*pError = error;
}
LOCAL VOID
atalkPapConnRefNextNc(
IN PPAP_CONNOBJ pPapConn,
IN PPAP_CONNOBJ * ppPapConnNext,
OUT PATALK_ERROR pError
)
/*++
Routine Description:
MUST BE CALLED WITH THE ASSOCIATED ADDRESS LOCK HELD!
Arguments:
Return Value:
--*/
{
PPAP_CONNOBJ pNextConn = NULL;
ATALK_ERROR error = ATALK_FAILURE;
ASSERT(VALID_PAPCO(pPapConn));
for (; pPapConn != NULL; pPapConn = pPapConn->papco_pNextActive)
{
AtalkPapConnReferenceByPtrDpc(pPapConn, &error);
if (ATALK_SUCCESS(error))
{
// Ok, this connection is referenced!
*ppPapConnNext = pPapConn;
break;
}
}
*pError = error;
}
LOCAL VOID FASTCALL
atalkPapConnDeref(
IN PPAP_CONNOBJ pPapConn
)
/*++
Routine Description:
Disconnect completion happens when the reference count goes from
2->1 if the creation reference is not already removed. If the creation
reference is already removed, it will be done when the refcount goes
from 1->0.
Arguments:
Return Value:
--*/
{
KIRQL OldIrql;
BOOLEAN fEndProcessing = FALSE;
ASSERT(VALID_PAPCO(pPapConn));
ACQUIRE_SPIN_LOCK(&pPapConn->papco_Lock, &OldIrql);
ASSERT(pPapConn->papco_RefCount > 0);
pPapConn->papco_RefCount--;
if (pPapConn->papco_RefCount > 1)
{
fEndProcessing = TRUE;
}
RELEASE_SPIN_LOCK(&pPapConn->papco_Lock, OldIrql);
if (fEndProcessing)
{
return;
}
else
{
PTDI_IND_DISCONNECT discHandler;
PVOID discCtx;
ATALK_ERROR disconnectStatus;
GENERIC_COMPLETION disconnectInform = NULL;
PVOID disconnectInformCtx = NULL;
GENERIC_COMPLETION disconnectCompletion = NULL;
PVOID cleanupCtx = NULL;
GENERIC_COMPLETION cleanupCompletion = NULL;
PVOID closeCtx = NULL;
GENERIC_COMPLETION closeCompletion = NULL;
PVOID disconnectCtx = NULL;
PATP_ADDROBJ pAtpAddr = NULL;
PPAP_ADDROBJ pPapAddr = pPapConn->papco_pAssocAddr;
BOOLEAN disconnDone = FALSE;
// We allow stopping phase to happen only after disconnecting is done.
// If disconnecting is not set and stopping is, it implies we are only
// in an associated state.
ACQUIRE_SPIN_LOCK(&pPapConn->papco_Lock, &OldIrql);
if (pPapConn->papco_Flags & PAPCO_DISCONNECTING)
{
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_INFO,
("atalkPapConnDeref: Disconnect set for %lx\n", pPapConn));
if (pPapConn->papco_RefCount == 1)
{
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_INFO,
("atalkPapConnDeref: Disconnect done %lx\n",
pPapConn));
disconnDone = TRUE;
// Avoid multiple disconnect completions/close atp addresses
// Remember all the disconnect info before we release the lock
disconnectInform = pPapConn->papco_DisconnectInform;
disconnectInformCtx = pPapConn->papco_DisconnectInformCtx;
disconnectStatus = pPapConn->papco_DisconnectStatus;
disconnectCompletion = pPapConn->papco_DisconnectCompletion;
disconnectCtx = pPapConn->papco_DisconnectCtx;
// The atp address to be closed
pAtpAddr = pPapConn->papco_pAtpAddr;
// Reset all the be null, so next request doesnt get any
pPapConn->papco_DisconnectInform = NULL;
pPapConn->papco_DisconnectInformCtx = NULL;
pPapConn->papco_DisconnectCompletion = NULL;
pPapConn->papco_DisconnectCtx = NULL;
pPapConn->papco_pAtpAddr = NULL;
}
}
if (pPapConn->papco_RefCount == 0)
{
closeCtx = pPapConn->papco_CloseCtx;
closeCompletion = pPapConn->papco_CloseComp;
pPapConn->papco_CloseCtx = NULL;
pPapConn->papco_CloseComp= NULL;
ASSERT(pPapConn->papco_Flags & PAPCO_CLOSING);
}
RELEASE_SPIN_LOCK(&pPapConn->papco_Lock, OldIrql);
if (disconnDone)
{
// Remove from the active queue.
// Reset all relevent flags.
ACQUIRE_SPIN_LOCK(&pPapAddr->papao_Lock, &OldIrql);
ACQUIRE_SPIN_LOCK_DPC(&pPapConn->papco_Lock);
atalkPapConnDeQueueActiveList(pPapAddr, pPapConn);
discHandler = pPapConn->papco_pAssocAddr->papao_DisconnectHandler;
discCtx = pPapConn->papco_pAssocAddr->papao_DisconnectHandlerCtx;
// Close the atp address if a server object. If the SERVER_JOB flag
// is set, it implies that the atp address object was open.
if ((pPapConn->papco_Flags & PAPCO_SERVER_JOB) == 0)
{
pAtpAddr = NULL;
}
// This can be done outside the spinlock section. Keep disconnection
// flag set so no other requests can get in.
// !!!NOTE!!! We keep the readdata_waiting flag so that the client
// may read the last set of data sent by the mac.
pPapConn->papco_Flags &= ~(PAPCO_LISTENING |
PAPCO_CONNECTING |
PAPCO_ACTIVE |
PAPCO_DISCONNECTING |
PAPCO_READDATA_PENDING |
PAPCO_WRITEDATA_WAITING |
PAPCO_NONBLOCKING_READ |
PAPCO_SENDDATA_RECD);
pPapConn->papco_Flags |= PAPCO_REJECT_READS;
RELEASE_SPIN_LOCK_DPC(&pPapConn->papco_Lock);
RELEASE_SPIN_LOCK(&pPapAddr->papao_Lock, OldIrql);
// Call the disconnect completion routines.
if (*disconnectInform != NULL)
{
(*disconnectInform)(disconnectStatus, disconnectInformCtx);
}
if (*disconnectCompletion != NULL)
{
(*disconnectCompletion)(disconnectStatus, disconnectCtx);
}
// Close the atp address if a server object. If the SERVER_JOB flag
// is set, it implies that the atp address object was open.
if (pAtpAddr != NULL)
{
ASSERT(VALID_ATPAO(pAtpAddr));
ASSERT(pPapConn->papco_pAtpAddr == NULL);
AtalkAtpCloseAddress(pAtpAddr, NULL, NULL);
}
}
// Check if we are done with stopping. We could either just be done with
// disconnect, or be done previously, and just need to complete the stop
// now.
ACQUIRE_SPIN_LOCK(&pPapConn->papco_Lock, &OldIrql);
if ((pPapConn->papco_Flags & PAPCO_STOPPING) != 0)
{
BOOLEAN fDisassoc = FALSE;
// See if we do the cleanup irp completion.
if (pPapConn->papco_RefCount == 1)
{
cleanupCtx = pPapConn->papco_CleanupCtx;
cleanupCompletion = pPapConn->papco_CleanupComp;
pPapConn->papco_CleanupComp = NULL;
pPapConn->papco_CleanupCtx = NULL;
pPapConn->papco_Flags &= ~PAPCO_STOPPING;
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_INFO,
("atalkPapConnDeref: Cleanup on %lx.%lx\n",
pPapConn, cleanupCtx));
}
if ((pPapConn->papco_Flags & ( PAPCO_LISTENING |
PAPCO_CONNECTING |
PAPCO_ACTIVE)) == 0)
{
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_INFO,
("atalkPapConnDeref: Stopping - do disassoc for %lx\n", pPapConn));
fDisassoc = ((pPapConn->papco_Flags & PAPCO_ASSOCIATED) != 0);
}
RELEASE_SPIN_LOCK(&pPapConn->papco_Lock, OldIrql);
// Call the disassociate routine. This should just fail, if the
// connection is still active or any other state than just
// plain associated. This will also reset the stopping flag.
if (fDisassoc)
{
AtalkPapDissociateAddress(pPapConn);
}
}
else
{
RELEASE_SPIN_LOCK(&pPapConn->papco_Lock, OldIrql);
}
// Complete cleanup at the end.
if (*cleanupCompletion != NULL)
(*cleanupCompletion)(ATALK_NO_ERROR, cleanupCtx);
if (*closeCompletion != NULL)
{
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_INFO,
("atalkPapConnDeref: Close done for %lx\n", pPapConn));
// Call the close completion routines
(*closeCompletion)(ATALK_NO_ERROR, closeCtx);
// Remove from the global list.
ACQUIRE_SPIN_LOCK(&atalkPapLock, &OldIrql);
AtalkUnlinkDouble(pPapConn, papco_Next, papco_Prev);
RELEASE_SPIN_LOCK(&atalkPapLock, OldIrql);
// Free up the connection memory.
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_INFO,
("atalkPapConnDeref: Freeing up connection %lx\n", pPapConn));
AtalkUnlockPapIfNecessary();
AtalkFreeMemory(pPapConn);
}
}
}
LOCAL ATALK_ERROR FASTCALL
atalkPapPostSendDataResp(
IN PPAP_CONNOBJ pPapConn
)
/*++
Routine Description:
Timing Thoughts: We could get here from PapWrite *only* if we had already
received a senddata. And so we will not get here from a send data being received
as it will not be accepted while we have a previous send data pending. The
other way around- we will only get here from a send data coming in if we
have a write pending. So we will never get here from write as we would not
have had a send data pending at that time.
If the connection reference fails, that means we are shutting down, and
the shutdown code would already have called the write completion with an
error. We just get outta here.
THIS IS CALLED WITH papco_Lock held !!!
Arguments:
Return Value:
--*/
{
ATALK_ERROR error;
PATP_RESP pAtpResp;
BYTE userBytes[ATP_USERBYTES_SIZE];
ASSERT(VALID_PAPCO(pPapConn));
// ACQUIRE_SPIN_LOCK_DPC(&pPapConn->papco_Lock);
// If we are disconnecting or received a remote disconnect, get out.
// The disconnect routine would have already cancelled the response.
error = ATALK_FAILURE;
if ((pPapConn->papco_Flags & (PAPCO_CLOSING |
PAPCO_STOPPING |
PAPCO_DELAYED_DISCONNECT |
PAPCO_RECVD_DISCONNECT |
PAPCO_DISCONNECTING)) == 0)
{
ASSERT ((pPapConn->papco_Flags & (PAPCO_SENDDATA_RECD | PAPCO_WRITEDATA_WAITING)) ==
(PAPCO_SENDDATA_RECD | PAPCO_WRITEDATA_WAITING));
userBytes[PAP_CONNECTIONID_OFF] = pPapConn->papco_ConnId;
userBytes[PAP_CMDTYPE_OFF] = PAP_DATA;
// If EOF, need a non-zero value in the first byte.
PUTSHORT2SHORT(&userBytes[PAP_EOFFLAG_OFF], 0);
if (pPapConn->papco_Flags & PAPCO_SEND_EOF_WRITE)
userBytes[PAP_EOFFLAG_OFF] = TRUE;
pAtpResp = pPapConn->papco_pAtpResp;
// Reference connection for this send.
AtalkPapConnReferenceByPtrNonInterlock(pPapConn, &error);
if (ATALK_SUCCESS(error))
{
AtalkAtpRespReferenceByPtrDpc(pAtpResp, &error);
}
else
{
// Connection reference failed!
// Pending write would have been completed by the closing routine.
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_ERR,
("AtalkPapPostSendData: Conn ref failed for %lx.%lx\n",
pPapConn, pPapConn->papco_Flags));
// This should never happen as we check closing flag above and the reference
// shouldn't fail for any other reason.
KeBugCheck(0);
}
}
else
{
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_ERR,
("AtalkPapPostSendData: HIT RACE CONDITION conn %lx Resp %lx\n",
pPapConn, pPapConn->papco_pAtpResp));
}
RELEASE_SPIN_LOCK_DPC(&pPapConn->papco_Lock);
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_INFO,
("AtalkPapPostSendData: conn %lx Resp %lx\n",
pPapConn, pPapConn->papco_pAtpResp));
if (ATALK_SUCCESS(error))
{
// Post the response.
error = AtalkAtpPostResp(pAtpResp,
&pPapConn->papco_SendDataSrc,
pPapConn->papco_pWriteBuf,
pPapConn->papco_WriteLen,
userBytes,
atalkPapSendDataRel,
pPapConn);
AtalkAtpRespDereference(pAtpResp);
if (!ATALK_SUCCESS(error))
{
// Simulate completion with an error.
atalkPapSendDataRel(error, pPapConn);
}
error = ATALK_PENDING;
}
return error;
}
LOCAL VOID
atalkPapIncomingReadComplete(
IN ATALK_ERROR ErrorCode,
IN PPAP_CONNOBJ pPapConn, // Our context
IN PAMDL pReqAmdl,
IN PAMDL pReadAmdl,
IN USHORT ReadLen,
IN PBYTE ReadUserBytes
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
GENERIC_READ_COMPLETION pReadCompletion;
PVOID pReadCtx;
KIRQL OldIrql;
ULONG readFlags = TDI_RECEIVE_PARTIAL;
ASSERT(VALID_PAPCO(pPapConn));
if (ATALK_SUCCESS(ErrorCode))
{
// When a read completes, tag that we have heard something from the other side
pPapConn->papco_LastContactTime = AtalkGetCurrentTick();
if ((ReadUserBytes[PAP_CONNECTIONID_OFF] != pPapConn->papco_ConnId) ||
(ReadUserBytes[PAP_CMDTYPE_OFF] != PAP_DATA))
{
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_ERR,
("atalkPapIncomingReadComplete: ReadUserBytes %lx. %lx\n",
*((PULONG)ReadUserBytes), pPapConn->papco_ConnId));
ErrorCode = ATALK_PAP_INVALID_USERBYTES;
}
if (ReadUserBytes[PAP_EOFFLAG_OFF] != 0)
{
readFlags = 0;
}
}
else
{
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_ERR,
("atalkPapIncomingReadComplete: Error %lx for pPapConn %lx\n",
ErrorCode, pPapConn));
}
ASSERT(pReqAmdl == NULL);
// Estimate the retry interval for next time.
if (pPapConn->papco_Flags & PAPCO_SERVER_JOB)
{
pPapConn->papco_RT.rt_New = AtalkGetCurrentTick() - pPapConn->papco_RT.rt_New;
AtalkCalculateNewRT(&pPapConn->papco_RT);
}
#ifdef PROFILING
{
KIRQL OldIrql;
ACQUIRE_SPIN_LOCK(&AtalkStatsLock, &OldIrql);
AtalkStatistics.stat_LastPapRTT = (ULONG)(pPapConn->papco_RT.rt_Base);
if ((ULONG)(pPapConn->papco_RT.rt_Base) > AtalkStatistics.stat_MaxPapRTT)
AtalkStatistics.stat_MaxPapRTT = (ULONG)(pPapConn->papco_RT.rt_Base);
RELEASE_SPIN_LOCK(&AtalkStatsLock, OldIrql);
}
#endif
// Before we look at the incoming error code, see if we can mark in the
// job structure that the other sides sendData is complete.
ACQUIRE_SPIN_LOCK(&pPapConn->papco_Lock, &OldIrql);
pPapConn->papco_Flags &= ~PAPCO_READDATA_PENDING;
pReadCompletion = pPapConn->papco_ReadCompletion;
pReadCtx = pPapConn->papco_ReadCtx;
ASSERT(pReadCompletion != NULL);
RELEASE_SPIN_LOCK(&pPapConn->papco_Lock, OldIrql);
(*pReadCompletion)(ErrorCode, pReadAmdl, ReadLen, readFlags, pReadCtx);
// Deref the connection object.
AtalkPapConnDereference(pPapConn);
}
LOCAL VOID
atalkPapPrimedReadComplete(
IN ATALK_ERROR ErrorCode,
IN PPAP_CONNOBJ pPapConn, // Our context
IN PAMDL pReqAmdl,
IN PAMDL pReadAmdl,
IN USHORT ReadLen,
IN PBYTE ReadUserBytes
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PTDI_IND_RECEIVE recvHandler;
PVOID recvHandlerCtx;
PIRP recvIrp;
NTSTATUS ntStatus;
ULONG bytesTaken;
PBYTE pReadBuf;
KIRQL OldIrql;
PACTREQ pActReq;
BOOLEAN completeRead = FALSE, delayedDisConn = FALSE;
ULONG readFlags = (TDI_RECEIVE_PARTIAL | TDI_RECEIVE_NORMAL);
ASSERT(VALID_PAPCO(pPapConn));
if (ATALK_SUCCESS(ErrorCode))
{
if ((ReadUserBytes[PAP_CONNECTIONID_OFF] != pPapConn->papco_ConnId) ||
(ReadUserBytes[PAP_CMDTYPE_OFF] != PAP_DATA))
{
// This will mean a primed read completes with disconnect indication to afd!!
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_ERR,
("atalkPapIncomingReadComplete: ReadUserBytes %lx. %lx\n",
*((PULONG)ReadUserBytes), pPapConn->papco_ConnId));
ErrorCode = ATALK_PAP_INVALID_USERBYTES;
}
if (ReadUserBytes[PAP_EOFFLAG_OFF] != 0)
{
readFlags = TDI_RECEIVE_NORMAL;
}
}
else if (ErrorCode == ATALK_ATP_REQ_CANCELLED)
{
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_ERR,
("atalkPapIncomingReadComplete: Request cancelled\n"));
completeRead = TRUE;
}
else
{
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_ERR,
("atalkPapIncomingReadComplete: Error %lx for pPapConn %lx\n",
ErrorCode, pPapConn));
}
ASSERT(pReqAmdl == NULL);
#ifdef PROFILING
{
KIRQL OldIrql;
ACQUIRE_SPIN_LOCK(&AtalkStatsLock, &OldIrql);
AtalkStatistics.stat_LastPapRTT = (ULONG)(pPapConn->papco_RT.rt_Base);
if ((ULONG)(pPapConn->papco_RT.rt_Base) > AtalkStatistics.stat_MaxPapRTT)
AtalkStatistics.stat_MaxPapRTT = (ULONG)(pPapConn->papco_RT.rt_Base);
RELEASE_SPIN_LOCK(&AtalkStatsLock, OldIrql);
}
#endif
// Remember the info in the connection object.
ACQUIRE_SPIN_LOCK(&pPapConn->papco_Lock, &OldIrql);
pActReq = pPapConn->papco_NbReadActReq;
recvHandler = pPapConn->papco_pAssocAddr->papao_RecvHandler;
recvHandlerCtx = pPapConn->papco_pAssocAddr->papao_RecvHandlerCtx;
if (ATALK_SUCCESS(ErrorCode))
{
// When a read completes, tag that we have heard something from the other side
pPapConn->papco_LastContactTime = AtalkGetCurrentTick();
// Estimate the retry interval for next time.
if (pPapConn->papco_Flags & PAPCO_SERVER_JOB)
{
pPapConn->papco_RT.rt_New = AtalkGetCurrentTick() - pPapConn->papco_RT.rt_New;
AtalkCalculateNewRT(&pPapConn->papco_RT);
}
pPapConn->papco_Flags |= PAPCO_READDATA_WAITING;
if (pPapConn->papco_Flags & PAPCO_RECVD_DISCONNECT)
{
// AFD gets awfully upset when a read is indicated after a disconnect
recvHandler = NULL;
}
pPapConn->papco_NbReadLen = ReadLen;
pPapConn->papco_NbReadFlags = readFlags;
// Get the system address for the mdl
pReadBuf = (PBYTE)MmGetSystemAddressForMdlSafe(
pActReq->ar_pAMdl,
NormalPagePriority);
if (pReadBuf == NULL)
{
pPapConn->papco_Flags &= ~PAPCO_NONBLOCKING_READ;
pPapConn->papco_NbReadActReq = NULL;
pPapConn->papco_NbReadLen = 0;
// Do not indicate a receive
recvHandler = NULL;
// Complete the read
completeRead = TRUE;
}
}
else
{
pPapConn->papco_Flags &= ~PAPCO_NONBLOCKING_READ;
pPapConn->papco_NbReadActReq = NULL;
pPapConn->papco_NbReadLen = 0;
pReadBuf = NULL;
// Do not indicate a receive
recvHandler = NULL;
// Complete the read
completeRead = TRUE;
}
RELEASE_SPIN_LOCK(&pPapConn->papco_Lock, OldIrql);
// Call the indication routine on the address object..
if (*recvHandler != NULL)
{
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_WARN,
("atalkPapPrimedReadComplete: Indicating when disconnecting!\n"));
ntStatus = (*recvHandler)(recvHandlerCtx,
pPapConn->papco_ConnCtx,
readFlags,
pPapConn->papco_NbReadLen,
pPapConn->papco_NbReadLen,
&bytesTaken,
pReadBuf,
&recvIrp);
ASSERT((bytesTaken == 0) || (bytesTaken == ReadLen));
if (ntStatus == STATUS_MORE_PROCESSING_REQUIRED)
{
if (recvIrp != NULL)
{
// Post the receive as if it came from the io system
ntStatus = AtalkDispatchInternalDeviceControl(
(PDEVICE_OBJECT)AtalkDeviceObject[ATALK_DEV_PAP],
recvIrp);
ASSERT(ntStatus == STATUS_PENDING);
}
else
{
ASSERTMSG("atalkPapPrimedReadComplete: No receive irp!\n", 0);
KeBugCheck(0);
}
}
else if (ntStatus == STATUS_SUCCESS)
{
// !!!NOTE!!!
// Its possible that a disconnect happened and completed
// the pending read already. And so AFD is returning us this
// for the indication as the connection is already disconnected.
if (bytesTaken != 0)
{
// Assume all of the data was read.
ASSERT(bytesTaken == ReadLen);
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_INFO,
("atalkPapPrimedReadComplete: All bytes read %lx\n",
bytesTaken));
// We are done with the primed read.
ACQUIRE_SPIN_LOCK(&pPapConn->papco_Lock, &OldIrql);
if (pPapConn->papco_Flags & PAPCO_NONBLOCKING_READ)
{
pPapConn->papco_Flags &= ~(PAPCO_NONBLOCKING_READ |
PAPCO_READDATA_WAITING);
pPapConn->papco_NbReadActReq = NULL;
pPapConn->papco_NbReadLen = 0;
// Complete the read
completeRead = TRUE;
}
RELEASE_SPIN_LOCK(&pPapConn->papco_Lock, OldIrql);
}
}
else if (ntStatus == STATUS_DATA_NOT_ACCEPTED)
{
// Client may have posted a receive in the indication. Or
// it will post a receive later on. Do nothing here.
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_INFO,
("atalkPapPrimedReadComplete: Indication status %lx\n", ntStatus));
}
}
ACQUIRE_SPIN_LOCK(&pPapConn->papco_Lock, &OldIrql);
if (pPapConn->papco_Flags & PAPCO_DELAYED_DISCONNECT)
{
delayedDisConn = TRUE;
pPapConn->papco_Flags &= ~PAPCO_DELAYED_DISCONNECT;
}
RELEASE_SPIN_LOCK(&pPapConn->papco_Lock, OldIrql);
// Complete the action request.
if (completeRead)
{
// Call the action completion routine for the prime read.
(*pActReq->ar_Completion)(ATALK_NO_ERROR, pActReq);
}
// Finally if we have a delayed disconnect, complete the stuff
if (delayedDisConn)
{
AtalkPapDisconnect(pPapConn,
ATALK_REMOTE_DISCONNECT,
NULL,
NULL);
}
// Deref the connection object.
AtalkPapConnDereference(pPapConn);
}
LOCAL VOID
atalkPapIncomingStatus(
IN ATALK_ERROR ErrorCode,
IN PACTREQ pActReq, // Our Ctx
IN PAMDL pReqAmdl,
IN PAMDL pStatusAmdl,
IN USHORT StatusLen,
IN PBYTE ReadUserBytes
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
// Call the action completion routine
(*pActReq->ar_Completion)(ErrorCode, pActReq);
}
LOCAL VOID FASTCALL
atalkPapSendDataRel(
IN ATALK_ERROR ErrorCode,
IN PPAP_CONNOBJ pPapConn
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PAMDL pWriteBuf;
SHORT writeLen;
GENERIC_WRITE_COMPLETION writeCompletion;
PVOID writeCtx;
PATP_RESP pAtpResp;
KIRQL OldIrql;
ASSERT(VALID_PAPCO(pPapConn));
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_INFO,
("atalkPapSendDataRel: Error %lx for pPapConn %lx\n", ErrorCode, pPapConn));
// If this was cancelled, then we turn the error into a no-error.
if (ErrorCode == ATALK_ATP_RESP_CANCELLED)
ErrorCode = ATALK_NO_ERROR;
ACQUIRE_SPIN_LOCK(&pPapConn->papco_Lock, &OldIrql);
pPapConn->papco_Flags &= ~(PAPCO_WRITEDATA_WAITING |
PAPCO_SENDDATA_RECD |
PAPCO_SEND_EOF_WRITE);
pWriteBuf = pPapConn->papco_pWriteBuf;
writeLen = pPapConn->papco_WriteLen;
writeCompletion = pPapConn->papco_WriteCompletion;
writeCtx = pPapConn->papco_WriteCtx;
pAtpResp = pPapConn->papco_pAtpResp;
pPapConn->papco_pAtpResp = NULL;
ASSERT (pAtpResp != NULL);
// Reinitialize all the variables.
pPapConn->papco_WriteLen = 0;
pPapConn->papco_pWriteBuf = NULL;
pPapConn->papco_WriteCtx = NULL;
pPapConn->papco_WriteCompletion = NULL;
RELEASE_SPIN_LOCK(&pPapConn->papco_Lock, OldIrql);
(*writeCompletion)(ErrorCode, pWriteBuf, writeLen, writeCtx);
// Dereference the atp response structure now, but not if a response was never posted
if (ErrorCode != ATALK_ATP_RESP_CLOSING)
{
AtalkAtpRespDereference(pAtpResp);
}
else
{
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_ERR,
("atalkPapSendDataRel: Resp cancelled before post %lx\n", pPapConn));
}
// Dereference the connection
AtalkPapConnDereference(pPapConn);
}
LOCAL VOID
atalkPapSlsHandler(
IN ATALK_ERROR ErrorCode,
IN PPAP_ADDROBJ pPapAddr, // Listener (our context)
IN PATP_RESP pAtpResp,
IN PATALK_ADDR pSrcAddr, // Address of requestor
IN USHORT PktLen,
IN PBYTE pPkt,
IN PBYTE pUserBytes
)
/*++
Routine Description:
Handler for incoming requests on the Sls. It handles session opens and get
status on the session.
Arguments:
Return Value:
--*/
{
BYTE connId, cmdType;
BYTE userBytes[ATP_USERBYTES_SIZE];
PBYTE pRespPkt;
PAMDL pRespAmdl;
SHORT respLen;
PPAP_SEND_STATUS_REL pSendSts;
BOOLEAN DerefAddr = FALSE;
ATALK_ERROR error = ATALK_NO_ERROR;
if (!ATALK_SUCCESS(ErrorCode))
{
// Remove the reference on the address object since atp socket is closing
if (ErrorCode == ATALK_ATP_CLOSING)
{
// Remove reference on the connection since socket is closing
AtalkPapAddrDereference(pPapAddr);
}
return;
}
// Try to reference the address. If we fail, its probably closing, so
// get out. This reference will stay around for the duration of this call.
AtalkPapAddrReference(pPapAddr, &error);
if (!ATALK_SUCCESS(error))
{
if (pAtpResp != NULL)
{
AtalkAtpCancelResp(pAtpResp);
}
return;
}
// If we have a send status request, and we manage to
// successfully post it, we reset this. And then it goes away in
// the release routine.
DerefAddr = TRUE;
connId = pUserBytes[PAP_CONNECTIONID_OFF];
cmdType = pUserBytes[PAP_CMDTYPE_OFF];
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_INFO,
("atalkPapSlsHandler: Received request %x, tid %x\n",
cmdType, (pAtpResp != NULL) ? pAtpResp->resp_Tid : 0));
switch(cmdType)
{
case PAP_OPEN_CONN:
// Ensure packet length is ok.
// Accept the connection. This will send any error replies as appropriate.
if ((PktLen < PAP_STATUS_OFF) ||
!atalkPapConnAccept(pPapAddr,
pSrcAddr,
pPkt,
connId,
pAtpResp))
{
AtalkAtpCancelResp(pAtpResp);
}
break;
case PAP_SEND_STATUS:
userBytes[PAP_CONNECTIONID_OFF] = 0;
userBytes[PAP_CMDTYPE_OFF] = PAP_STATUS_REPLY;
PUTSHORT2SHORT(&userBytes[PAP_SEQNUM_OFF], 0);
// We need to put in the status with the spinlock of the
// address object held.
ACQUIRE_SPIN_LOCK_DPC(&pPapAddr->papao_Lock);
do
{
// Get a buffer we can send the response with.
ASSERTMSG("atalkPapSlsHandler: Status size incorrec\n",
(pPapAddr->papao_StatusSize >= 0));
if ((pSendSts = AtalkAllocMemory(sizeof(PAP_SEND_STATUS_REL) +
pPapAddr->papao_StatusSize))== NULL)
{
error = ATALK_RESR_MEM;
break;
}
respLen = PAP_STATUS_OFF + 1;
ASSERT(pPapAddr->papao_StatusSize <= PAP_MAX_STATUS_SIZE);
pRespPkt = pSendSts->papss_StatusBuf;
pRespPkt[PAP_STATUS_OFF] = (BYTE)pPapAddr->papao_StatusSize;
// Zero out the unused portion of the buffer
PUTDWORD2DWORD(pRespPkt, 0);
if (pPapAddr->papao_StatusSize > 0)
{
RtlCopyMemory(pRespPkt + 1 + PAP_STATUS_OFF,
pPapAddr->papao_pStatusBuf,
pPapAddr->papao_StatusSize);
respLen += pPapAddr->papao_StatusSize;
ASSERT(respLen <= PAP_MAX_DATA_PACKET_SIZE);
}
// Build an mdl for the length that we are using.
if ((pRespAmdl = AtalkAllocAMdl(pRespPkt, respLen)) == NULL)
{
error = ATALK_RESR_MEM;
AtalkFreeMemory(pSendSts);
break;
}
pSendSts->papss_pAmdl = pRespAmdl;
pSendSts->papss_pPapAddr = pPapAddr;
pSendSts->papss_pAtpResp = pAtpResp;
} while (FALSE);
RELEASE_SPIN_LOCK_DPC(&pPapAddr->papao_Lock);
if (!ATALK_SUCCESS(error))
{
AtalkAtpCancelResp(pAtpResp);
break;
}
ASSERT(pSendSts != NULL);
ASSERT((pRespAmdl != NULL) && (respLen > 0));
error = AtalkAtpPostResp(pAtpResp,
pSrcAddr,
pRespAmdl,
respLen,
userBytes,
atalkPapStatusRel,
pSendSts);
// atalkPapStatusRel Dereferences the address.
DerefAddr = FALSE;
if (!ATALK_SUCCESS(error))
{
atalkPapStatusRel(error, pSendSts);
}
break;
default:
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_ERR,
("atalkPapSlsHandler: Invalid request %x\n", cmdType));
AtalkAtpCancelResp(pAtpResp);
break;
}
// Remove reference added at the beginning.
if (DerefAddr)
{
AtalkPapAddrDereference(pPapAddr);
}
}
LOCAL VOID
atalkPapIncomingReq(
IN ATALK_ERROR ErrorCode,
IN PPAP_CONNOBJ pPapConn, // Connection (our context)
IN PATP_RESP pAtpResp,
IN PATALK_ADDR pSrcAddr, // Address of requestor
IN USHORT PktLen,
IN PBYTE pPkt,
IN PBYTE pUserBytes
)
/*++
Routine Description:
This handles requests on a connection, SendData, Tickles, and Close.
Arguments:
Return Value:
--*/
{
ATALK_ERROR error;
BYTE connId, cmdType;
USHORT seqNum;
PTDI_IND_SEND_POSSIBLE sendPossibleHandler;
PVOID sendPossibleHandlerCtx;
BOOLEAN DerefConn = FALSE;
BOOLEAN cancelResp = TRUE;
ASSERT(VALID_PAPCO(pPapConn));
do
{
if (!ATALK_SUCCESS(ErrorCode))
{
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_WARN,
("atalkPapIncomingReq: pPapConn %lx, ErrorCode %ld, exit.\n",
pPapConn, ErrorCode));
if (ErrorCode == ATALK_ATP_CLOSING)
{
// Remove reference on the connection since socket is closing
AtalkPapConnDereference(pPapConn);
break;
}
}
ACQUIRE_SPIN_LOCK_DPC(&pPapConn->papco_Lock);
if ((pPapConn->papco_Flags & (PAPCO_ACTIVE |
PAPCO_STOPPING |
PAPCO_LOCAL_DISCONNECT|
PAPCO_DISCONNECTING |
PAPCO_CLOSING)) == PAPCO_ACTIVE)
{
AtalkPapConnReferenceByPtrNonInterlock(pPapConn, &error);
}
else
{
error = ATALK_INVALID_CONNECTION;
}
RELEASE_SPIN_LOCK_DPC(&pPapConn->papco_Lock);
if (!ATALK_SUCCESS(error))
{
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_ERR,
("atalkPapIncomingReq: Ref FAILED/DISC %lx.%lx\n",
pPapConn, pPapConn->papco_Flags));
break;
}
// Remember to remove connection referenced above
DerefConn = TRUE;
connId = pUserBytes[PAP_CONNECTIONID_OFF];
cmdType = pUserBytes[PAP_CMDTYPE_OFF];
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_INFO,
("atalkPapIncomingReq: Received request %x, tid %x\n",
cmdType, (pAtpResp != NULL) ? pAtpResp->resp_Tid : 0));
if (connId != pPapConn->papco_ConnId)
{
// Just drop this packet. Cancel response though in case this is
// a xo request
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_INFO,
("atalkPapIncomingReq: ConnId %lx recd %lx\n",
pPapConn->papco_ConnId, connId));
break;
}
pPapConn->papco_LastContactTime = AtalkGetCurrentTick();
switch (cmdType)
{
case PAP_SEND_DATA:
GETSHORT2SHORT(&seqNum, &pUserBytes[PAP_SEQNUM_OFF]);
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_INFO,
("atalkPapIncomingReq: send data %lx recd %lx\n",
seqNum, pPapConn->papco_ConnId));
ACQUIRE_SPIN_LOCK_DPC(&pPapConn->papco_Lock);
if ((seqNum == 0) &&
(pPapConn->papco_Flags & PAPCO_SENDDATA_RECD))
{
// We have an unsequenced incoming sendData request before we've
// finished with the previous one (i.e gotten a release for it).
// We don't clear the PAPCO_SENDDATA_RECD flag until we receive
// a release or time out. Also, we cannot assume an implied
// release as we can with sequenced requests. So we just cancel
// the response so we can be notified of a retry of the send
// data request again
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_ERR,
("atalkPapIncomingReq: Dropping unseq send data %lx\n", pAtpResp));
RELEASE_SPIN_LOCK_DPC(&pPapConn->papco_Lock);
break;
}
if (seqNum != 0)
{
// Sequenced send data. Verify the seq num.
if (seqNum != (USHORT)(pPapConn->papco_NextIncomingSeqNum))
{
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_ERR,
("atalkPapIncomingReq: Out of Seq - current %lx, incoming %lx on %lx\n",
pPapConn->papco_NextIncomingSeqNum, seqNum, pPapConn));
// Cancel our response.
RELEASE_SPIN_LOCK_DPC(&pPapConn->papco_Lock);
break;
}
if (pPapConn->papco_Flags & PAPCO_SENDDATA_RECD)
{
USHORT tid;
// We have a send data before the previous one has completed.
// As PAP is a one-request-at-a-time protocol, we can assume
// that the release for previous transaction is lost. This
// gets rid of the 30 second pauses when a release is dropped.
// Cancel our response. Note this implies that a response
// had been posted by the pap client. In SendRel then, we
// convert the response cancelled error to no error.
ASSERT (pPapConn->papco_pAtpResp != NULL);
tid = pPapConn->papco_pAtpResp->resp_Tid;
RELEASE_SPIN_LOCK_DPC(&pPapConn->papco_Lock);
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_WARN,
("atalkPapIncomingReq: Cancelling response tid %x\n", tid));
// CAUTION: We cannot use AtalkAtpCancelResp() since we have no
// reference to the resp structure and involves a potential
// race condition. Play safe and cancel by tid instead.
error = AtalkAtpCancelRespByTid(pPapConn->papco_pAtpAddr,
&pPapConn->papco_SendDataSrc,
tid);
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_INFO,
("atalkPapIncomingReq: Cancel error %lx\n", error));
// If we were unable to cancel the response that means that
// it already timed out or got a release. We dont want to
// get into a situation where we would be messing with variables
// accessed both in the SendDataRel and here. So we just
// cancel this response and hope to get it again.
if (!ATALK_SUCCESS(error))
{
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_WARN,
("atalkPapIncomingReq: Cancel old resp tid %x (%ld)\n",
tid, error));
break;
}
ACQUIRE_SPIN_LOCK_DPC(&pPapConn->papco_Lock);
pPapConn->papco_pAtpResp = NULL;
pPapConn->papco_Flags &= ~(PAPCO_SENDDATA_RECD | PAPCO_WRITEDATA_WAITING);
}
// Increment the sequence number. If we loop to 0, set to 1.
if (++pPapConn->papco_NextIncomingSeqNum == 0)
{
pPapConn->papco_NextIncomingSeqNum = 1;
}
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_INFO,
("atalkPapIncomingReq: Recd %lx Next %lx\n",
seqNum, pPapConn->papco_NextIncomingSeqNum));
}
else
{
// Unsequenced send data received. Handle it.
ASSERT (seqNum != 0);
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_INFO,
("atalkPapIncomingReq: Unsequenced send data recd!\n"));
}
cancelResp = FALSE;
ASSERT((pPapConn->papco_Flags & PAPCO_SENDDATA_RECD) == 0);
pPapConn->papco_Flags |= PAPCO_SENDDATA_RECD;
pPapConn->papco_pAtpResp = pAtpResp;
// The mac may not send its 'SendData' from its responding socket
// (the one we are tickling and have noted as papco_RemoteAddr), we need
// to address the response to the socket that the request originated on.
pPapConn->papco_SendDataSrc = *pSrcAddr;
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_INFO,
("atalkPapIncomingReq: send data src %lx.%lx.%lx\n",
pSrcAddr->ata_Network, pSrcAddr->ata_Node, pSrcAddr->ata_Socket));
// remember send possible handler/context.
sendPossibleHandler = pPapConn->papco_pAssocAddr->papao_SendPossibleHandler;
sendPossibleHandlerCtx = pPapConn->papco_pAssocAddr->papao_SendPossibleHandlerCtx;
if (pPapConn->papco_Flags & PAPCO_WRITEDATA_WAITING)
{
// RELEASE_SPIN_LOCK_DPC(&pPapConn->papco_Lock);
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_INFO,
("atalkPapIncomingReq: Posting send data resp\n"));
// atalkPostSendDataResp() will release the conn lock
atalkPapPostSendDataResp(pPapConn);
}
else
{
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_INFO,
("atalkPapIncomingReq: No WriteData waiting\n"));
if (sendPossibleHandler != NULL)
{
(*sendPossibleHandler)(sendPossibleHandlerCtx,
pPapConn->papco_ConnCtx,
pPapConn->papco_SendFlowQuantum*PAP_MAX_DATA_PACKET_SIZE);
}
RELEASE_SPIN_LOCK_DPC(&pPapConn->papco_Lock);
}
break;
case PAP_CLOSE_CONN:
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_INFO,
("atalkPapIncomingReq: Close conn recvd. for connection %lx\n",
pPapConn));
ACQUIRE_SPIN_LOCK_DPC(&pPapConn->papco_Lock);
pPapConn->papco_Flags |= (PAPCO_REMOTE_DISCONNECT | PAPCO_RECVD_DISCONNECT);
RELEASE_SPIN_LOCK_DPC(&pPapConn->papco_Lock);
// Post the close connection reply.
cancelResp = FALSE;
pUserBytes[PAP_CMDTYPE_OFF] = PAP_CLOSE_CONN_REPLY;
AtalkAtpPostResp(pAtpResp,
pSrcAddr,
NULL, // Response buffer
0,
pUserBytes,
AtalkAtpGenericRespComplete,
pAtpResp);
// PapDisconnect will call the disconnect indication routine if set.
AtalkPapDisconnect(pPapConn,
ATALK_REMOTE_DISCONNECT,
NULL,
NULL);
break;
case PAP_TICKLE:
// We've already registered contact.
// Cancel this response since we never respond to it and we want this to go away
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_INFO,
("atalkPapIncomingReq: Recvd. tickle - resp %lx\n", pAtpResp));
cancelResp = TRUE;
break;
default:
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_ERR,
("atalkPapIncomingReq: Invalid command %x\n", cmdType));
cancelResp = TRUE;
break;
}
} while (FALSE);
if (cancelResp & (pAtpResp != NULL))
{
AtalkAtpCancelResp(pAtpResp);
}
if (DerefConn)
{
// Remove reference added at the beginning.
AtalkPapConnDereference(pPapConn);
}
}
LOCAL VOID
atalkPapIncomingOpenReply(
IN ATALK_ERROR ErrorCode,
IN PPAP_CONNOBJ pPapConn, // Our context
IN PAMDL pReqAmdl,
IN PAMDL pReadAmdl,
IN USHORT ReadLen,
IN PBYTE ReadUserBytes
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
ULONG index;
KIRQL OldIrql;
ATALK_ERROR error;
USHORT statusLen, i, connectStatus;
BYTE userBytes[ATP_USERBYTES_SIZE];
PBYTE pRespPkt, pOpenPkt;
PTDI_IND_SEND_POSSIBLE sendPossibleHandler;
PVOID sendPossibleHandlerCtx;
PPAP_ADDROBJ pPapAddr = pPapConn->papco_pAssocAddr;
ASSERT(VALID_PAPCO(pPapConn));
pRespPkt = pPapConn->papco_pConnectRespBuf;
pOpenPkt = pPapConn->papco_pConnectOpenBuf;
ASSERT(pRespPkt != NULL);
ASSERT(pOpenPkt != NULL);
if (ATALK_SUCCESS(ErrorCode))
{
// Well, lets see what kind of response we got; take a look at both the
// response user-bytes and the response buffer. Note that we now allow
// the LaserWriter IIg to leave the status string off altogether,
// rather than the [correct] zero-sized string.
do
{
// The reply length should be atleast the minimum and it should be
// an open-reply we are looking at.
if ((ReadLen < PAP_STATUS_OFF) ||
(ReadUserBytes[PAP_CMDTYPE_OFF] != PAP_OPEN_CONNREPLY))
{
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_ERR,
("atalkPapIncomingOpenReply: Invalid read len or cmd %x/%x\n",
ReadLen, ReadUserBytes[PAP_CMDTYPE_OFF]));
ErrorCode = ATALK_REMOTE_CLOSE;
break;
}
if (ReadLen == PAP_STATUS_OFF)
{
statusLen = 0; // Missing, from the LaserWriter IIg
}
else
{
// Verify length.
statusLen = pRespPkt[PAP_STATUS_OFF];
if ((statusLen != 0) &&
((statusLen + 1 + PAP_STATUS_OFF) > ReadLen))
{
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_ERR,
("atalkPapIncomingOpenReply: Invalid status len %lx\n", ReadLen));
ErrorCode = ATALK_REMOTE_CLOSE;
break;
}
}
// Check for connect result.
GETSHORT2SHORT(&connectStatus, &pRespPkt[PAP_RESULT_OFF]);
// Check for open reply code in packet. Do not check the
// connectionid unless the response is success. Some rips
// are known to send a bogus connectionid if the return
// code is 'busy'.
if ((connectStatus == 0) &&
(ReadUserBytes[PAP_CONNECTIONID_OFF] != pPapConn->papco_ConnId))
{
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_ERR,
("atalkPapIncomingOpenReply: Invalid connid %x, expected %x\n",
ReadUserBytes[PAP_CONNECTIONID_OFF], pPapConn->papco_ConnId));
ErrorCode = ATALK_REMOTE_CLOSE;
break;
}
if (connectStatus != 0)
{
ATALK_ERROR ReconnectError;
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_WARN,
("atalkPapIncomingOpenReply: server busy %lx\n", connectStatus));
ErrorCode = ATALK_PAP_SERVER_BUSY;
// If we have not yet reached the max. timeout, retry
if (pPapConn->papco_WaitTimeOut < PAP_MAX_WAIT_TIMEOUT)
{
pPapConn->papco_WaitTimeOut ++;
ReconnectError = atalkPapRepostConnect(pPapConn, pReqAmdl, pReadAmdl);
if (ATALK_SUCCESS(ReconnectError))
return; // Exit now
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_WARN,
("atalkPapIncomingOpenReply: Post reconnect failed %lx\n", ReconnectError));
}
break;
}
// Switch the socket of the remote address to be the one specified
// by the remote end.
pPapConn->papco_RemoteAddr.ata_Socket = pRespPkt[PAP_RESP_SOCKET_OFF];
pPapConn->papco_SendFlowQuantum = pRespPkt[PAP_FLOWQUANTUM_OFF];
if (pPapConn->papco_SendFlowQuantum > PAP_MAX_FLOW_QUANTUM)
{
pPapConn->papco_SendFlowQuantum = PAP_MAX_FLOW_QUANTUM;
}
} while (FALSE);
if (ATALK_SUCCESS(ErrorCode))
{
// Build up userBytes to start tickling the other end.
userBytes[PAP_CONNECTIONID_OFF] = pPapConn->papco_ConnId;
userBytes[PAP_CMDTYPE_OFF] = PAP_TICKLE;
PUTSHORT2SHORT(&userBytes[PAP_SEQNUM_OFF], 0);
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_INFO,
("atalkPapIncomingOpenReply: id %lx Rem %lx.%lx.%lx RespSkt %lx\n",
pPapConn->papco_ConnId, pPapConn->papco_RemoteAddr.ata_Network,
pPapConn->papco_RemoteAddr.ata_Node, pPapConn->papco_RemoteAddr.ata_Socket,
PAPCONN_DDPSOCKET(pPapConn)));
error = AtalkAtpPostReq(pPapConn->papco_pAtpAddr,
&pPapConn->papco_RemoteAddr,
&pPapConn->papco_TickleTid,
0, // ALO transaction
NULL,
0,
userBytes,
NULL,
0,
ATP_INFINITE_RETRIES,
PAP_TICKLE_INTERVAL,
THIRTY_SEC_TIMER,
NULL,
NULL);
ASSERT(ATALK_SUCCESS(error));
index = PAP_HASH_ID_ADDR(pPapConn->papco_ConnId, &pPapConn->papco_RemoteAddr);
// Move the connection from the connect list to the active list.
ACQUIRE_SPIN_LOCK(&pPapAddr->papao_Lock, &OldIrql);
ACQUIRE_SPIN_LOCK_DPC(&pPapConn->papco_Lock);
// Make sure flags are clean.
pPapConn->papco_Flags &= ~(PAPCO_SENDDATA_RECD |
PAPCO_WRITEDATA_WAITING |
PAPCO_SEND_EOF_WRITE |
PAPCO_READDATA_PENDING |
PAPCO_REMOTE_CLOSE |
PAPCO_NONBLOCKING_READ |
PAPCO_READDATA_WAITING);
pPapConn->papco_Flags &= ~PAPCO_CONNECTING;
pPapConn->papco_Flags |= PAPCO_ACTIVE;
atalkPapConnDeQueueConnectList(pPapAddr, pPapConn);
// Thread the connection object into addr lookup by session id.
pPapConn->papco_pNextActive = pPapAddr->papao_pActiveHash[index];
pPapAddr->papao_pActiveHash[index] = pPapConn;
// Reference for the request handler
AtalkPapConnReferenceByPtrNonInterlock(pPapConn, &error);
// Call the send data event handler on the associated address with
// 0 to turn off selects on writes. We do this before we post any
// get requests, so there is no race condition.
// remember send possible handler/context.
sendPossibleHandler = pPapAddr->papao_SendPossibleHandler;
sendPossibleHandlerCtx = pPapAddr->papao_SendPossibleHandlerCtx;
RELEASE_SPIN_LOCK_DPC(&pPapConn->papco_Lock);
RELEASE_SPIN_LOCK(&pPapAddr->papao_Lock, OldIrql);
if (sendPossibleHandler != NULL)
{
(*sendPossibleHandler)(sendPossibleHandlerCtx,
pPapConn->papco_ConnCtx,
0);
}
// Set the request handler on this connection.
// It will handle tickle's, close's and sendData's.
AtalkAtpSetReqHandler(pPapConn->papco_pAtpAddr,
atalkPapIncomingReq,
pPapConn);
pPapConn->papco_LastContactTime = AtalkGetCurrentTick();
}
else
{
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_ERR,
("atalkPapIncomingOpenReply: Incoming connect fail %lx\n", ErrorCode));
// Move the connection out of the connect list.
ACQUIRE_SPIN_LOCK(&pPapAddr->papao_Lock, &OldIrql);
ACQUIRE_SPIN_LOCK_DPC(&pPapConn->papco_Lock);
pPapConn->papco_Flags &= ~PAPCO_CONNECTING;
atalkPapConnDeQueueConnectList(pPapAddr, pPapConn);
RELEASE_SPIN_LOCK_DPC(&pPapConn->papco_Lock);
RELEASE_SPIN_LOCK(&pPapAddr->papao_Lock, OldIrql);
}
}
else
{
// Move the connection out of the connect list.
ACQUIRE_SPIN_LOCK(&pPapAddr->papao_Lock, &OldIrql);
ACQUIRE_SPIN_LOCK_DPC(&pPapConn->papco_Lock);
atalkPapConnDeQueueConnectList(pPapAddr, pPapConn);
pPapConn->papco_Flags &= ~PAPCO_CONNECTING;
RELEASE_SPIN_LOCK_DPC(&pPapConn->papco_Lock);
RELEASE_SPIN_LOCK(&pPapAddr->papao_Lock, OldIrql);
}
// Free the buffers.
AtalkFreeMemory(pRespPkt);
AtalkFreeMemory(pOpenPkt);
AtalkFreeAMdl(pReadAmdl);
AtalkFreeAMdl(pReqAmdl);
// Call the completion routine.
(*pPapConn->papco_ConnectCompletion)(ErrorCode, pPapConn->papco_ConnectCtx);
// Remove reference for this handler.
AtalkPapConnDereference(pPapConn);
}
LOCAL VOID FASTCALL
atalkPapIncomingRel(
IN ATALK_ERROR ErrorCode,
IN PPAP_OPEN_REPLY_REL pOpenReply
)
/*++
Routine Description:
Handler for incoming release for reply
Arguments:
Return Value:
--*/
{
KIRQL OldIrql;
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_INFO,
("atalkPapIncomingRel: Called %lx for pOpenReply %lx\n",
ErrorCode, pOpenReply));
ASSERT(pOpenReply != NULL);
ASSERT(pOpenReply->papor_pRespAmdl != NULL);
// Dereference the atp response structure now
AtalkAtpRespDereference(pOpenReply->papor_pAtpResp);
AtalkFreeAMdl(pOpenReply->papor_pRespAmdl);
AtalkFreeMemory(pOpenReply);
}
LOCAL VOID FASTCALL
atalkPapStatusRel(
IN ATALK_ERROR ErrorCode,
IN PPAP_SEND_STATUS_REL pSendSts
)
/*++
Routine Description:
Handler for incoming release for reply
Arguments:
Return Value:
--*/
{
KIRQL OldIrql;
UNREFERENCED_PARAMETER(ErrorCode);
ASSERT(pSendSts != NULL);
ASSERT(pSendSts->papss_pAmdl != NULL);
// Dereference the atp response structure now
AtalkAtpRespDereference(pSendSts->papss_pAtpResp);
AtalkPapAddrDereference(pSendSts->papss_pPapAddr);
AtalkFreeAMdl(pSendSts->papss_pAmdl);
AtalkFreeMemory(pSendSts);
}
#define SLS_OPEN_RESP_SOCKET 0x0001
#define SLS_OPEN_RESP_PKT 0x0002
#define SLS_OPEN_RESP_MDL 0x0004
#define SLS_OPEN_CONN_REF 0x0008
#define SLS_ACCEPT_IRP 0x0010
#define SLS_CONN_REQ_REFS 0x0020
#define SLS_CONN_TIMER_REF 0x0040
#define SLS_LISTEN_DEQUEUED 0x0080
BOOLEAN
atalkPapConnAccept(
IN PPAP_ADDROBJ pPapAddr, // Listener
IN PATALK_ADDR pSrcAddr, // Address of requestor
IN PBYTE pPkt,
IN BYTE ConnId,
IN PATP_RESP pAtpResp
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
ATALK_ERROR tmpError;
BYTE userBytes[ATP_USERBYTES_SIZE];
PBYTE pRespPkt;
PAMDL pRespAmdl;
PATP_ADDROBJ pRespondingAtpAddr;
PPAP_CONNOBJ pPapConn;
ULONG index;
SHORT respLen, i;
PPAP_OPEN_REPLY_REL pOpenReply;
GENERIC_COMPLETION listenCompletion;
PVOID listenCtx;
KIRQL OldIrql;
PIRP acceptIrp;
PTDI_IND_SEND_POSSIBLE sendPossibleHandler;
PVOID sendPossibleHandlerCtx;
USHORT openResr = 0;
ATALK_ERROR error = ATALK_NO_ERROR;
BOOLEAN indicate = FALSE;
BOOLEAN relAddrLock = FALSE;
BOOLEAN DerefAddr = FALSE;
BOOLEAN sendOpenErr = FALSE;
// Get a buffer we can send the response with.
if ((pOpenReply = (PPAP_OPEN_REPLY_REL)
AtalkAllocMemory(sizeof(PAP_OPEN_REPLY_REL))) == NULL)
{
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_ERR,
("atalkPapConnAccept: Could not allocate resp packet\n"));
return FALSE;
}
// NOTE! pOpenReply contains a max sized packet. Get a ptr to work with.
pRespPkt = pOpenReply->papor_pRespPkt;
openResr |= SLS_OPEN_RESP_PKT;
// Build up the response packet. If we encounter an error later on,
// just set the error code in packet.
userBytes[PAP_CONNECTIONID_OFF] = ConnId;
userBytes[PAP_CMDTYPE_OFF] = PAP_OPEN_CONNREPLY;
PUTSHORT2SHORT(&userBytes[PAP_SEQNUM_OFF], PAP_NO_ERROR);
// !!!NOTE!!!
// The socket will be set after the connection is determined.
// This will only happen in the non-error case.
pRespPkt[PAP_FLOWQUANTUM_OFF] = PAP_MAX_FLOW_QUANTUM;
PUTSHORT2SHORT(&pRespPkt[PAP_RESULT_OFF], 0);
ACQUIRE_SPIN_LOCK(&pPapAddr->papao_Lock, &OldIrql);
relAddrLock = TRUE;
do
{
// We need to put in the status with the spinlock of the
// address object held.
pRespPkt[PAP_STATUS_OFF] = (BYTE)pPapAddr->papao_StatusSize;
ASSERT((pPapAddr->papao_StatusSize >= 0) &&
(pPapAddr->papao_StatusSize <= PAP_MAX_STATUS_SIZE));
if (pPapAddr->papao_StatusSize > 0)
{
ASSERT(pPapAddr->papao_pStatusBuf != NULL);
RtlCopyMemory(pRespPkt+PAP_STATUS_OFF+1,
pPapAddr->papao_pStatusBuf,
pPapAddr->papao_StatusSize);
}
respLen = PAP_STATUS_OFF + pPapAddr->papao_StatusSize + 1;
ASSERT(respLen <= PAP_MAX_DATA_PACKET_SIZE);
// Build an mdl for the length that we are using.
if ((pRespAmdl = AtalkAllocAMdl(pRespPkt, respLen)) == NULL)
{
error = ATALK_RESR_MEM;
break;
}
pOpenReply->papor_pRespAmdl = pRespAmdl;
pOpenReply->papor_pAtpResp = pAtpResp;
openResr |= SLS_OPEN_RESP_MDL;
// Send an open status whatever happens now. If ATALK_SUCCESS(error)
// we send out a connection accept packets. Assume blocked. If
// unblocked and we are able to get a connection object, error will
// be set to success.
sendOpenErr = TRUE;
error = ATALK_RESR_MEM;
// If PapUnblockedState - either there is a GetNextJob posted, or
// an incoming event handler is set on the listener.
if (pPapAddr->papao_Flags & PAPAO_UNBLOCKED)
{
PTDI_IND_CONNECT indicationRoutine;
PVOID indicationCtx;
NTSTATUS status;
CONNECTION_CONTEXT ConnCtx;
TA_APPLETALK_ADDRESS tdiAddr;
// either a getnextjob() or a listener is set.
// depending on which one it is, dequeue or remember the listener.
if (pPapAddr->papao_pListenConn != NULL)
{
// There a connection with a pending listen. use it.
pPapConn = pPapAddr->papao_pListenConn;
if (((pPapAddr->papao_pListenConn = pPapConn->papco_pNextListen) == NULL) &&
(pPapAddr->papao_ConnHandler == NULL))
{
// We have no more listens pending! No event handler either.
pPapAddr->papao_Flags &= ~PAPAO_UNBLOCKED;
#if DBG
pPapAddr->papao_Flags |= PAPAO_BLOCKING;
#endif
}
ASSERT(VALID_PAPCO(pPapConn));
// Reference the connection object with a listen posted on it.
AtalkPapConnReferenceByPtrDpc(pPapConn, &error);
if (!ATALK_SUCCESS(error))
{
break;
}
// Get the listen completion information
listenCompletion = pPapConn->papco_ListenCompletion;
listenCtx = pPapConn->papco_ListenCtx;
openResr |= (SLS_OPEN_CONN_REF | SLS_LISTEN_DEQUEUED);
}
else if ((indicationRoutine = pPapAddr->papao_ConnHandler) != NULL)
{
indicationCtx = pPapAddr->papao_ConnHandlerCtx;
indicate = TRUE;
// Convert remote atalk address to tdi address
ATALKADDR_TO_TDI(&tdiAddr, pSrcAddr);
RELEASE_SPIN_LOCK(&pPapAddr->papao_Lock, OldIrql);
relAddrLock = FALSE;
acceptIrp = NULL;
status = (*indicationRoutine)(indicationCtx,
sizeof(tdiAddr),
(PVOID)&tdiAddr,
0, // User data length
NULL, // User data
0, // Option length
NULL, // Options
&ConnCtx,
&acceptIrp);
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_INFO,
("atalkPapConnAccept: indicate status %lx\n", status));
if (status == STATUS_MORE_PROCESSING_REQUIRED)
{
ASSERT(acceptIrp != NULL);
if (acceptIrp != NULL)
{
openResr |= SLS_ACCEPT_IRP;
}
// Find the connection and accept the connection using that
// connection object.
ACQUIRE_SPIN_LOCK(&pPapAddr->papao_Lock, &OldIrql);
relAddrLock = TRUE;
AtalkPapConnReferenceByCtxNonInterlock(pPapAddr,
ConnCtx,
&pPapConn,
&error);
if (!ATALK_SUCCESS(error))
{
break;
}
openResr |= SLS_OPEN_CONN_REF;
}
else
{
ASSERT(acceptIrp == NULL);
error = ATALK_RESR_MEM;
break;
}
}
else
{
// The UNBLOCKED bit was set.
ASSERT(0);
KeBugCheck(0);
}
}
if (openResr & SLS_OPEN_CONN_REF)
{
if (relAddrLock)
{
RELEASE_SPIN_LOCK(&pPapAddr->papao_Lock, OldIrql);
relAddrLock = FALSE;
}
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_INFO,
("atalkPapConnAccept: Creating an Atp address\n"));
// Now on NT, we will mostly (always) be using the indication, as PAP
// will be exposed only through winsock.
error = AtalkAtpOpenAddress(AtalkDefaultPort,
0,
NULL,
PAP_MAX_DATA_PACKET_SIZE,
PAP_SEND_USER_BYTES_ALL,
NULL,
TRUE, // CACHE address
&pRespondingAtpAddr);
if (!ATALK_SUCCESS(error))
{
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_ERR,
("atalkPapConnAccept: Error open atp resp socket %lx\n", error));
break;
}
openResr |= SLS_OPEN_RESP_SOCKET;
// Common for both listen and indicate. The connection object
// should be referenced.
// Store the information in the connection structure given by
// the connection object thats passed back in the indication
// or is part of the getnextjob structure.
pPapConn->papco_Flags |= PAPCO_SERVER_JOB;
pPapConn->papco_RemoteAddr = *pSrcAddr;
pPapConn->papco_RemoteAddr.ata_Socket = pPkt[PAP_RESP_SOCKET_OFF];
pPapConn->papco_ConnId = ConnId;
pPapConn->papco_SendFlowQuantum = pPkt[PAP_FLOWQUANTUM_OFF];
pPapConn->papco_LastContactTime = AtalkGetCurrentTick();
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_INFO,
("atalkPapConnAccept: ConnId %lx Rem %lx.%lx.%lx\n",
ConnId, pSrcAddr->ata_Network, pSrcAddr->ata_Node,
pPkt[PAP_RESP_SOCKET_OFF]));
ASSERT(pPapConn->papco_SendFlowQuantum > 0);
if (pPapConn->papco_SendFlowQuantum > PAP_MAX_FLOW_QUANTUM)
pPapConn->papco_SendFlowQuantum = PAP_MAX_FLOW_QUANTUM;
// Thread the connection object into addr lookup by session id.
index = PAP_HASH_ID_ADDR(ConnId, &pPapConn->papco_RemoteAddr);
ACQUIRE_SPIN_LOCK(&pPapAddr->papao_Lock, &OldIrql);
relAddrLock = TRUE;
ACQUIRE_SPIN_LOCK_DPC(&pPapConn->papco_Lock);
// Try to reference the connection for the request handler that we
// are going to set
AtalkPapConnReferenceByPtrNonInterlock(pPapConn, &error);
if (!ATALK_SUCCESS(error))
{
ASSERT(0);
RELEASE_SPIN_LOCK_DPC(&pPapConn->papco_Lock);
break;
}
openResr |= (SLS_CONN_REQ_REFS | SLS_CONN_TIMER_REF);
// The connection object could be re-used by AFD. Make sure it is
// in the right state
pPapConn->papco_NextOutgoingSeqNum = 1; // Set to 1, not 0.
pPapConn->papco_NextIncomingSeqNum = 1; // Next expected incoming.
AtalkInitializeRT(&pPapConn->papco_RT,
PAP_INIT_SENDDATA_REQ_INTERVAL,
PAP_MIN_SENDDATA_REQ_INTERVAL,
PAP_MAX_SENDDATA_REQ_INTERVAL);
pPapConn->papco_Flags &= ~(PAPCO_LISTENING |
PAPCO_DELAYED_DISCONNECT |
PAPCO_DISCONNECTING |
PAPCO_RECVD_DISCONNECT |
PAPCO_LOCAL_DISCONNECT |
PAPCO_REMOTE_DISCONNECT |
PAPCO_SENDDATA_RECD |
PAPCO_WRITEDATA_WAITING |
PAPCO_SEND_EOF_WRITE |
PAPCO_READDATA_PENDING |
PAPCO_NONBLOCKING_READ |
PAPCO_READDATA_WAITING |
#if DBG
PAPCO_CLEANUP |
PAPCO_INDICATE_AFD_DISC |
#endif
PAPCO_REMOTE_CLOSE);
pPapConn->papco_Flags |= PAPCO_ACTIVE;
pPapConn->papco_pNextActive = pPapAddr->papao_pActiveHash[index];
pPapAddr->papao_pActiveHash[index] = pPapConn;
// Remember the responding socket.
pPapConn->papco_pAtpAddr = pRespondingAtpAddr;
// Set the socket in the packet we'll be sending.
pRespPkt[PAP_RESP_SOCKET_OFF] = PAPCONN_DDPSOCKET(pPapConn);
// Call the send data event handler on the associated address with
// 0 to turn off selects on writes. We do this before we post any
// get requests, so there is no race condition.
// remember send possible handler/context.
sendPossibleHandler = pPapAddr->papao_SendPossibleHandler;
sendPossibleHandlerCtx = pPapAddr->papao_SendPossibleHandlerCtx;
RELEASE_SPIN_LOCK_DPC(&pPapConn->papco_Lock);
}
} while (FALSE);
if (relAddrLock)
{
RELEASE_SPIN_LOCK(&pPapAddr->papao_Lock, OldIrql);
}
// This reference needs to go regardless.
if (openResr & SLS_OPEN_CONN_REF)
{
// Remove the reference for the listen dequeued/indication accept.
AtalkPapConnDereference(pPapConn);
}
if (sendOpenErr)
{
if (!ATALK_SUCCESS(error))
{
// Send error status.
PUTSHORT2SHORT(&pRespPkt[PAP_RESULT_OFF], PAP_PRINTER_BUSY);
}
else
{
// Set the request handler on this connection. It will handle
// tickle's, close's and sendData's. Do this before we send
// the open reply so that we dont miss the first send data.
openResr &= ~SLS_CONN_REQ_REFS;
AtalkAtpSetReqHandler(pPapConn->papco_pAtpAddr,
atalkPapIncomingReq,
pPapConn);
}
if (ATALK_SUCCESS(AtalkAtpPostResp(pAtpResp,
pSrcAddr,
pRespAmdl,
respLen,
userBytes,
atalkPapIncomingRel,
pOpenReply)))
{
// We want the completion to free up the buffer/mdl.
openResr &= ~(SLS_OPEN_RESP_PKT | SLS_OPEN_RESP_MDL);
}
}
if (ATALK_SUCCESS(error))
{
// We better have sent the open reply.
ASSERT(sendOpenErr);
ASSERT(VALID_ATPAO(pPapConn->papco_pAtpAddr));
if ((openResr & (SLS_ACCEPT_IRP & SLS_OPEN_CONN_REF)) ==
(SLS_ACCEPT_IRP & SLS_OPEN_CONN_REF))
{
// Only if we got a referenced connection through an accept
// do we call the send possible.
if (sendPossibleHandler != NULL)
{
(*sendPossibleHandler)(sendPossibleHandlerCtx,
pPapConn->papco_ConnCtx,
0);
}
}
// Build up userBytes to start tickling the other end.
userBytes[PAP_CONNECTIONID_OFF] = ConnId;
userBytes[PAP_CMDTYPE_OFF] = PAP_TICKLE;
PUTSHORT2SHORT(&userBytes[PAP_SEQNUM_OFF], 0);
tmpError = AtalkAtpPostReq(pPapConn->papco_pAtpAddr,
&pPapConn->papco_RemoteAddr,
&pPapConn->papco_TickleTid,
0, // AtLeastOnce
NULL,
0,
userBytes,
NULL,
0,
ATP_INFINITE_RETRIES,
PAP_TICKLE_INTERVAL,
THIRTY_SEC_TIMER,
NULL,
NULL);
ASSERT(ATALK_SUCCESS(tmpError));
pPapConn->papco_LastContactTime = AtalkGetCurrentTick();
}
else
{
// Release all resources
if (openResr & SLS_OPEN_RESP_SOCKET)
{
AtalkAtpCloseAddress(pRespondingAtpAddr, NULL, NULL);
}
if (openResr & SLS_OPEN_RESP_MDL)
{
AtalkFreeAMdl(pOpenReply->papor_pRespAmdl);
}
if (openResr & SLS_OPEN_RESP_PKT)
{
AtalkFreeMemory(pOpenReply);
}
if (openResr & SLS_CONN_TIMER_REF)
{
AtalkPapConnDereference(pPapConn);
}
}
if (openResr & SLS_LISTEN_DEQUEUED)
{
ASSERT(!indicate);
ASSERT(listenCompletion != NULL);
(*listenCompletion)(error, listenCtx);
}
if (openResr & SLS_ACCEPT_IRP)
{
acceptIrp->IoStatus.Information = 0;
ASSERT (error != ATALK_PENDING);
TdiCompleteRequest(acceptIrp, AtalkErrorToNtStatus(error));
}
return sendOpenErr;
}
LOCAL LONG FASTCALL
atalkPapConnMaintenanceTimer(
IN PTIMERLIST pTimer,
IN BOOLEAN TimerShuttingDown
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PPAP_CONNOBJ pPapConn;
ATALK_ERROR error;
BOOLEAN Close;
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_INFO,
("atalkPapConnMaintenanceTimer: Entered \n"));
if (TimerShuttingDown)
return ATALK_TIMER_NO_REQUEUE;
ACQUIRE_SPIN_LOCK_DPC(&atalkPapLock);
// Walk the list of connections on the global list and shut down
// ones that have not tickle'd for a while
for (pPapConn = atalkPapConnList; pPapConn != NULL; NOTHING)
{
ASSERT(VALID_PAPCO(pPapConn));
Close = FALSE;
ACQUIRE_SPIN_LOCK_DPC(&pPapConn->papco_Lock);
if (((pPapConn->papco_Flags & (PAPCO_ACTIVE |
PAPCO_CLOSING |
PAPCO_STOPPING |
PAPCO_DELAYED_DISCONNECT |
PAPCO_DISCONNECTING)) == PAPCO_ACTIVE) &&
((AtalkGetCurrentTick() - pPapConn->papco_LastContactTime) > PAP_CONNECTION_INTERVAL))
{
// Connection has expired.
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_ERR,
("atalkPapConnMaintenanceTimer: Connection %lx.%lx expired\n",
pPapConn, pPapConn->papco_ConnId));
Close = TRUE;
}
RELEASE_SPIN_LOCK_DPC(&pPapConn->papco_Lock);
if (Close)
{
AtalkPapConnReferenceByPtrDpc(pPapConn, &error);
if (ATALK_SUCCESS(error))
{
RELEASE_SPIN_LOCK_DPC(&atalkPapLock);
AtalkPapDisconnect(pPapConn,
ATALK_TIMER_DISCONNECT,
NULL,
NULL);
AtalkPapConnDereference(pPapConn);
ACQUIRE_SPIN_LOCK_DPC(&atalkPapLock);
pPapConn = atalkPapConnList;
}
}
if (!Close)
{
pPapConn = pPapConn->papco_Next;
}
}
RELEASE_SPIN_LOCK_DPC(&atalkPapLock);
return ATALK_TIMER_REQUEUE;
}
LOCAL BYTE
atalkPapGetNextConnId(
IN PPAP_ADDROBJ pPapAddr,
OUT PATALK_ERROR pError
)
/*++
Routine Description:
CALLED WITH THE ADDRESS SPIN LOCK HELD!
Arguments:
Return Value:
--*/
{
PPAP_CONNOBJ pPapConn;
USHORT i;
BYTE startConnId, connId;
ATALK_ERROR error = ATALK_NO_ERROR;
startConnId = connId = ++pPapAddr->papao_NextConnId;
while (TRUE)
{
for (i = 0; i < PAP_CONN_HASH_SIZE; i++)
{
for (pPapConn = pPapAddr->papao_pActiveHash[i];
((pPapConn != NULL) && (pPapConn->papco_ConnId != connId));
pPapConn = pPapConn->papco_pNextActive)
NOTHING;
if (pPapConn != NULL)
break;
}
if (pPapConn == NULL)
{
pPapAddr->papao_NextConnId = connId+1;
break;
}
else
{
if (connId == (startConnId - 1))
{
ASSERT(0);
// We wrapped around and there are no more conn ids.
error = ATALK_RESR_MEM;
break;
}
connId++;
}
}
*pError = error;
return(ATALK_SUCCESS(error) ? connId : 0);
}
LOCAL VOID
atalkPapQueueAddrGlobalList(
IN PPAP_ADDROBJ pPapAddr
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
KIRQL OldIrql;
ACQUIRE_SPIN_LOCK(&atalkPapLock, &OldIrql);
AtalkLinkDoubleAtHead(atalkPapAddrList, pPapAddr, papao_Next, papao_Prev);
RELEASE_SPIN_LOCK(&atalkPapLock, OldIrql);
}
LOCAL VOID
atalkPapConnDeQueueAssocList(
IN PPAP_ADDROBJ pPapAddr,
IN PPAP_CONNOBJ pPapConn
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PPAP_CONNOBJ pPapRemConn, *ppPapRemConn;
for (ppPapRemConn = &pPapAddr->papao_pAssocConn;
((pPapRemConn = *ppPapRemConn) != NULL);
NOTHING)
{
if (pPapRemConn == pPapConn)
{
*ppPapRemConn = pPapConn->papco_pNextAssoc;
break;
}
ppPapRemConn = &pPapRemConn->papco_pNextAssoc;
}
}
LOCAL VOID
atalkPapConnDeQueueConnectList(
IN PPAP_ADDROBJ pPapAddr,
IN PPAP_CONNOBJ pPapConn
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PPAP_CONNOBJ pPapRemConn, *ppPapRemConn;
ASSERT(pPapAddr->papao_Flags & PAPAO_CONNECT);
for (ppPapRemConn = &pPapAddr->papao_pConnectConn;
((pPapRemConn = *ppPapRemConn) != NULL);
NOTHING)
{
if (pPapRemConn == pPapConn)
{
*ppPapRemConn = pPapConn->papco_pNextConnect;
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_INFO,
("atalkPapConnDeQueueConnectList: Removed connect conn %lx\n", pPapConn));
break;
}
ppPapRemConn = &pPapRemConn->papco_pNextConnect;
}
}
LOCAL BOOLEAN
atalkPapConnDeQueueListenList(
IN PPAP_ADDROBJ pPapAddr,
IN PPAP_CONNOBJ pPapConn
)
/*++
Routine Description:
!!!MUST BE CALLED WITH PAP ADDRESS LOCK HELD!!!
Arguments:
Return Value:
--*/
{
PPAP_CONNOBJ pPapRemConn, *ppPapRemConn;
BOOLEAN removed = FALSE;
ASSERT(pPapAddr->papao_Flags & PAPAO_LISTENER);
for (ppPapRemConn = &pPapAddr->papao_pListenConn;
((pPapRemConn = *ppPapRemConn) != NULL);
NOTHING)
{
if (pPapRemConn == pPapConn)
{
removed = TRUE;
*ppPapRemConn = pPapConn->papco_pNextListen;
// If no more listens, then we set the address object to blocked
// state.
if ((pPapAddr->papao_pListenConn == NULL) &&
(pPapAddr->papao_ConnHandler == NULL))
{
pPapAddr->papao_Flags &= ~PAPAO_UNBLOCKED;
#if DBG
pPapAddr->papao_Flags |= PAPAO_BLOCKING;
#endif
}
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_INFO,
("atalkPapConnDeQueueListenList: Removed listen conn %lx\n", pPapConn));
break;
}
ppPapRemConn = &pPapRemConn->papco_pNextListen;
}
return removed;
}
LOCAL VOID
atalkPapConnDeQueueActiveList(
IN PPAP_ADDROBJ pPapAddr,
IN PPAP_CONNOBJ pPapConn
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PPAP_CONNOBJ pPapRemConn, *ppPapRemConn;
ULONG index;
index = PAP_HASH_ID_ADDR(pPapConn->papco_ConnId, &pPapConn->papco_RemoteAddr);
for (ppPapRemConn = &pPapAddr->papao_pActiveHash[index];
((pPapRemConn = *ppPapRemConn) != NULL);
NOTHING)
{
if (pPapRemConn == pPapConn)
{
*ppPapRemConn = pPapConn->papco_pNextActive;
DBGPRINT(DBG_COMP_PAP, DBG_LEVEL_INFO,
("atalkPapConnDeQueueActiveList: Removed active conn %lx\n", pPapConn));
break;
}
ppPapRemConn = &pPapRemConn->papco_pNextActive;
}
}