You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
7150 lines
171 KiB
7150 lines
171 KiB
/*++
|
|
|
|
Copyright (c) 1992 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
adsp.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the ADSP protocol.
|
|
|
|
Author:
|
|
|
|
Jameel Hyder ([email protected])
|
|
Nikhil Kamkolkar ([email protected])
|
|
|
|
Revision History:
|
|
30 Mar 1993 Initial Version
|
|
|
|
Notes: Tab stop: 4
|
|
--*/
|
|
|
|
#include <atalk.h>
|
|
#pragma hdrstop
|
|
#define FILENUM ADSP
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(INIT, AtalkInitAdspInitialize)
|
|
#pragma alloc_text(PAGE, AtalkAdspCreateAddress)
|
|
#pragma alloc_text(PAGEADSP, AtalkAdspCreateConnection)
|
|
#pragma alloc_text(PAGEADSP, AtalkAdspCleanupAddress)
|
|
#pragma alloc_text(PAGEADSP, AtalkAdspCloseAddress)
|
|
#pragma alloc_text(PAGEADSP, AtalkAdspCloseConnection)
|
|
#pragma alloc_text(PAGEADSP, AtalkAdspCleanupConnection)
|
|
#pragma alloc_text(PAGEADSP, AtalkAdspAssociateAddress)
|
|
#pragma alloc_text(PAGEADSP, AtalkAdspDissociateAddress)
|
|
#pragma alloc_text(PAGEADSP, AtalkAdspPostListen)
|
|
#pragma alloc_text(PAGEADSP, AtalkAdspCancelListen)
|
|
#pragma alloc_text(PAGEADSP, AtalkAdspPostConnect)
|
|
#pragma alloc_text(PAGEADSP, AtalkAdspDisconnect)
|
|
#pragma alloc_text(PAGEADSP, AtalkAdspRead)
|
|
#pragma alloc_text(PAGEADSP, AtalkAdspProcessQueuedSend)
|
|
#pragma alloc_text(PAGEADSP, AtalkAdspWrite)
|
|
#pragma alloc_text(PAGEADSP, AtalkAdspQuery)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspPacketIn)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspHandleOpenControl)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspHandleAttn)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspHandlePiggyBackAck)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspHandleControl)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspHandleData)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspHandleOpenReq)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspListenIndicateNonInterlock)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspSendExpedited)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspSendOpenControl)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspSendControl)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspSendDeny)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspSendAttn)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspSendData)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspRecvData)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspRecvAttn)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspConnSendComplete)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspAddrSendComplete)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspSendAttnComplete)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspSendDataComplete)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspConnMaintenanceTimer)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspRetransmitTimer)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspAttnRetransmitTimer)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspOpenTimer)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspAddrRefNonInterlock)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspConnRefByPtrNonInterlock)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspConnRefByCtxNonInterlock)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspConnRefBySrcAddr)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspConnRefNextNc)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspMaxSendSize)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspMaxNextReadSize)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspDescribeFromBufferQueue)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspBufferQueueSize)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspMessageSize)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspAllocCopyChunk)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspGetLookahead)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspAddToBufferQueue)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspReadFromBufferQueue)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspDiscardFromBufferQueue)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspBufferChunkReference)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspBufferChunkDereference)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspDecodeHeader)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspGetNextConnId)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspConnDeQueueAssocList)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspConnDeQueueConnectList)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspConnDeQueueListenList)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspConnDeQueueActiveList)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspAddrDeQueueGlobalList)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspAddrDeQueueGlobalList)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspConnDeQueueGlobalList)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspAddrDeQueueOpenReq)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspIsDuplicateOpenReq)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspGenericComplete)
|
|
#pragma alloc_text(PAGEADSP, atalkAdspConnFindInConnect)
|
|
#endif
|
|
|
|
//
|
|
// The model for ADSP 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
|
|
AtalkInitAdspInitialize(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
INITIALIZE_SPIN_LOCK(&atalkAdspLock);
|
|
}
|
|
|
|
|
|
|
|
|
|
ATALK_ERROR
|
|
AtalkAdspCreateAddress(
|
|
IN PATALK_DEV_CTX pDevCtx OPTIONAL,
|
|
IN BYTE SocketType,
|
|
OUT PADSP_ADDROBJ * ppAdspAddr
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PADSP_ADDROBJ pAdspAddr = NULL;
|
|
ATALK_ERROR error;
|
|
|
|
do
|
|
{
|
|
// Allocate memory for the Adsp address object
|
|
if ((pAdspAddr = AtalkAllocZeroedMemory(sizeof(ADSP_ADDROBJ))) == NULL)
|
|
{
|
|
error = ATALK_RESR_MEM;
|
|
break;
|
|
}
|
|
|
|
// Create a Ddp Socket on the port
|
|
error = AtalkDdpOpenAddress(AtalkDefaultPort,
|
|
0, // Dynamic socket
|
|
NULL,
|
|
atalkAdspPacketIn,
|
|
pAdspAddr, // Context for packet in.
|
|
DDPPROTO_ADSP,
|
|
pDevCtx,
|
|
&pAdspAddr->adspao_pDdpAddr);
|
|
|
|
if (!ATALK_SUCCESS(error))
|
|
{
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_ERR,
|
|
("AtalkAdspCreateAddress: AtalkDdpOpenAddress fail %ld\n", error));
|
|
break;
|
|
}
|
|
|
|
// Initialize the Adsp address object
|
|
pAdspAddr->adspao_Signature = ADSPAO_SIGNATURE;
|
|
|
|
INITIALIZE_SPIN_LOCK(&pAdspAddr->adspao_Lock);
|
|
|
|
// Is this a message mode socket?
|
|
if (SocketType != SOCKET_TYPE_STREAM)
|
|
{
|
|
pAdspAddr->adspao_Flags |= ADSPAO_MESSAGE;
|
|
}
|
|
|
|
// Creation reference
|
|
pAdspAddr->adspao_RefCount = 1;
|
|
|
|
} while (FALSE);
|
|
|
|
if (ATALK_SUCCESS(error))
|
|
{
|
|
// Insert into the global address list.
|
|
atalkAdspAddrQueueGlobalList(pAdspAddr);
|
|
|
|
*ppAdspAddr = pAdspAddr;
|
|
}
|
|
else if (pAdspAddr != NULL)
|
|
{
|
|
AtalkFreeMemory(pAdspAddr);
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
|
|
|
|
ATALK_ERROR
|
|
AtalkAdspCleanupAddress(
|
|
IN PADSP_ADDROBJ pAdspAddr
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
USHORT i;
|
|
KIRQL OldIrql;
|
|
PADSP_CONNOBJ pAdspConn, pAdspConnNext;
|
|
ATALK_ERROR error;
|
|
|
|
ACQUIRE_SPIN_LOCK(&pAdspAddr->adspao_Lock, &OldIrql);
|
|
|
|
// Shutdown all connections on this address object.
|
|
for (i = 0; i < ADSP_CONN_HASH_SIZE; i++)
|
|
{
|
|
if ((pAdspConn = pAdspAddr->adspao_pActiveHash[i]) == NULL)
|
|
{
|
|
// If empty, go on to the next index in hash table.
|
|
continue;
|
|
}
|
|
|
|
// Includes the one we are starting with.
|
|
atalkAdspConnRefNextNc(pAdspConn, &pAdspConnNext, &error);
|
|
if (!ATALK_SUCCESS(error))
|
|
{
|
|
// No connections left on this index. Go to the next one.
|
|
continue;
|
|
}
|
|
|
|
while (TRUE)
|
|
{
|
|
if ((pAdspConn = pAdspConnNext) == NULL)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if ((pAdspConnNext = pAdspConn->adspco_pNextActive) != NULL)
|
|
{
|
|
atalkAdspConnRefNextNc(pAdspConnNext, &pAdspConnNext, &error);
|
|
if (!ATALK_SUCCESS(error))
|
|
{
|
|
// No requests left on this index. Go to the next one.
|
|
pAdspConnNext = NULL;
|
|
}
|
|
}
|
|
|
|
// Shutdown this connection
|
|
RELEASE_SPIN_LOCK(&pAdspAddr->adspao_Lock, OldIrql);
|
|
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("AtalkAdspCloseAddress: Stopping conn %lx\n", pAdspConn));
|
|
|
|
AtalkAdspCleanupConnection(pAdspConn);
|
|
|
|
AtalkAdspConnDereference(pAdspConn);
|
|
ACQUIRE_SPIN_LOCK(&pAdspAddr->adspao_Lock, &OldIrql);
|
|
}
|
|
}
|
|
RELEASE_SPIN_LOCK(&pAdspAddr->adspao_Lock, OldIrql);
|
|
|
|
return ATALK_NO_ERROR;
|
|
}
|
|
|
|
|
|
|
|
|
|
ATALK_ERROR
|
|
AtalkAdspCloseAddress(
|
|
IN PADSP_ADDROBJ pAdspAddr,
|
|
IN GENERIC_COMPLETION CompletionRoutine,
|
|
IN PVOID pCloseCtx
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
KIRQL OldIrql;
|
|
PADSP_CONNOBJ pAdspConn;
|
|
PADSP_CONNOBJ pAdspConnNext;
|
|
DWORD dwAssocRefCounts=0;
|
|
|
|
|
|
ACQUIRE_SPIN_LOCK(&pAdspAddr->adspao_Lock, &OldIrql);
|
|
if (pAdspAddr->adspao_Flags & ADSPAO_CLOSING)
|
|
{
|
|
// We are already closing! This should never happen!
|
|
ASSERT(0);
|
|
}
|
|
pAdspAddr->adspao_Flags |= ADSPAO_CLOSING;
|
|
|
|
// Set the completion info.
|
|
pAdspAddr->adspao_CloseComp = CompletionRoutine;
|
|
pAdspAddr->adspao_CloseCtx = pCloseCtx;
|
|
|
|
// Implicitly dissociate any connection objects
|
|
for (pAdspConn = pAdspAddr->adspao_pAssocConn;
|
|
pAdspConn != NULL;
|
|
pAdspConn = pAdspConnNext)
|
|
{
|
|
ACQUIRE_SPIN_LOCK_DPC(&pAdspConn->adspco_Lock);
|
|
pAdspConnNext = pAdspConn->adspco_pNextAssoc;
|
|
|
|
// reset associated flag
|
|
if (pAdspConn->adspco_Flags & ADSPCO_ASSOCIATED)
|
|
{
|
|
dwAssocRefCounts++;
|
|
pAdspConn->adspco_Flags &= ~ADSPCO_ASSOCIATED;
|
|
}
|
|
|
|
pAdspConn->adspco_pAssocAddr = NULL;
|
|
|
|
RELEASE_SPIN_LOCK_DPC(&pAdspConn->adspco_Lock);
|
|
}
|
|
|
|
// ok to subtract: at least Creation refcount is still around
|
|
pAdspAddr->adspao_RefCount -= dwAssocRefCounts;
|
|
|
|
RELEASE_SPIN_LOCK(&pAdspAddr->adspao_Lock, OldIrql);
|
|
|
|
// Remove the creation reference count
|
|
AtalkAdspAddrDereference(pAdspAddr);
|
|
|
|
return ATALK_PENDING;
|
|
}
|
|
|
|
|
|
|
|
|
|
ATALK_ERROR
|
|
AtalkAdspCreateConnection(
|
|
IN PVOID pConnCtx, // Context to associate with the session
|
|
IN PATALK_DEV_CTX pDevCtx OPTIONAL,
|
|
OUT PADSP_CONNOBJ * ppAdspConn
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Create an ADSP 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:
|
|
|
|
|
|
--*/
|
|
{
|
|
KIRQL OldIrql;
|
|
PADSP_CONNOBJ pAdspConn;
|
|
|
|
// Allocate memory for a connection object
|
|
if ((pAdspConn = AtalkAllocZeroedMemory(sizeof(ADSP_CONNOBJ))) == NULL)
|
|
{
|
|
return ATALK_RESR_MEM;
|
|
}
|
|
|
|
pAdspConn->adspco_Signature = ADSPCO_SIGNATURE;
|
|
|
|
INITIALIZE_SPIN_LOCK(&pAdspConn->adspco_Lock);
|
|
pAdspConn->adspco_ConnCtx = pConnCtx;
|
|
// pAdspConn->adspco_Flags = 0;
|
|
pAdspConn->adspco_RefCount = 1; // Creation reference
|
|
|
|
*ppAdspConn = pAdspConn;
|
|
|
|
// Delay remote disconnects to avoid race condn. between rcv/disconnect since
|
|
// this can cause AFD to get extremely unhappy.
|
|
AtalkTimerInitialize(&pAdspConn->adspco_DisconnectTimer,
|
|
atalkAdspDisconnectTimer,
|
|
ADSP_DISCONNECT_DELAY);
|
|
|
|
// Insert into the global connection list.
|
|
ACQUIRE_SPIN_LOCK(&atalkAdspLock, &OldIrql);
|
|
pAdspConn->adspco_pNextGlobal = atalkAdspConnList;
|
|
atalkAdspConnList = pAdspConn;
|
|
RELEASE_SPIN_LOCK(&atalkAdspLock, OldIrql);
|
|
|
|
return ATALK_NO_ERROR;
|
|
}
|
|
|
|
|
|
|
|
|
|
ATALK_ERROR
|
|
AtalkAdspCloseConnection(
|
|
IN PADSP_CONNOBJ pAdspConn,
|
|
IN GENERIC_COMPLETION CompletionRoutine,
|
|
IN PVOID pCloseCtx
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Shutdown a session.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
KIRQL OldIrql;
|
|
|
|
ASSERT(VALID_ADSPCO(pAdspConn));
|
|
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("AtalkAdspStopConnection: Close for %lx.%lx\n", pAdspConn, pAdspConn->adspco_Flags));
|
|
|
|
ACQUIRE_SPIN_LOCK(&pAdspConn->adspco_Lock, &OldIrql);
|
|
if (pAdspConn->adspco_Flags & ADSPCO_CLOSING)
|
|
{
|
|
// We are already closing! This should never happen!
|
|
KeBugCheck(0);
|
|
}
|
|
pAdspConn->adspco_Flags |= ADSPCO_CLOSING;
|
|
|
|
// Set the completion info.
|
|
pAdspConn->adspco_CloseComp = CompletionRoutine;
|
|
pAdspConn->adspco_CloseCtx = pCloseCtx;
|
|
RELEASE_SPIN_LOCK(&pAdspConn->adspco_Lock, OldIrql);
|
|
|
|
// Remove the creation reference count
|
|
AtalkAdspConnDereference(pAdspConn);
|
|
return ATALK_PENDING;
|
|
}
|
|
|
|
|
|
|
|
|
|
ATALK_ERROR
|
|
AtalkAdspCleanupConnection(
|
|
IN PADSP_CONNOBJ pAdspConn
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Shutdown a session.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
KIRQL OldIrql;
|
|
BOOLEAN stopping = FALSE;
|
|
ATALK_ERROR error = ATALK_NO_ERROR;
|
|
|
|
ASSERT(VALID_ADSPCO(pAdspConn));
|
|
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("AtalkAdspStopConnection: Cleanup for %lx.%lx\n", pAdspConn, pAdspConn->adspco_Flags));
|
|
|
|
ACQUIRE_SPIN_LOCK(&pAdspConn->adspco_Lock, &OldIrql);
|
|
if ((pAdspConn->adspco_Flags & ADSPCO_STOPPING) == 0)
|
|
{
|
|
// So Deref can complete cleanup irp.
|
|
pAdspConn->adspco_Flags |= ADSPCO_STOPPING;
|
|
|
|
// If already effectively stopped, just return.
|
|
if (pAdspConn->adspco_Flags & ADSPCO_ASSOCIATED)
|
|
{
|
|
stopping = TRUE;
|
|
}
|
|
else
|
|
{
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_ERR,
|
|
("AtalkAdspStopConnection: Called for a stopped conn %lx.%lx\n",
|
|
pAdspConn, pAdspConn->adspco_Flags));
|
|
}
|
|
}
|
|
RELEASE_SPIN_LOCK(&pAdspConn->adspco_Lock, OldIrql);
|
|
|
|
// Close the DDP Address Object if this was a server connection and
|
|
// opened its own socket.
|
|
if (stopping)
|
|
{
|
|
// 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 = AtalkAdspDisconnect(pAdspConn,
|
|
ATALK_LOCAL_DISCONNECT,
|
|
NULL,
|
|
NULL);
|
|
|
|
// We were already disconnected.
|
|
if (error == ATALK_INVALID_REQUEST)
|
|
{
|
|
AtalkAdspDissociateAddress(pAdspConn);
|
|
}
|
|
}
|
|
|
|
return ATALK_NO_ERROR;
|
|
}
|
|
|
|
|
|
|
|
|
|
ATALK_ERROR
|
|
AtalkAdspAssociateAddress(
|
|
IN PADSP_ADDROBJ pAdspAddr,
|
|
IN PADSP_CONNOBJ pAdspConn
|
|
)
|
|
/*++
|
|
|
|
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_ADSPAO(pAdspAddr));
|
|
ASSERT(VALID_ADSPCO(pAdspConn));
|
|
|
|
ACQUIRE_SPIN_LOCK(&pAdspAddr->adspao_Lock, &OldIrql);
|
|
ACQUIRE_SPIN_LOCK_DPC(&pAdspConn->adspco_Lock);
|
|
|
|
error = ATALK_ALREADY_ASSOCIATED;
|
|
if ((pAdspConn->adspco_Flags & ADSPCO_ASSOCIATED) == 0)
|
|
{
|
|
error = ATALK_NO_ERROR;
|
|
|
|
// Link it in.
|
|
pAdspConn->adspco_pNextAssoc = pAdspAddr->adspao_pAssocConn;
|
|
pAdspAddr->adspao_pAssocConn = pAdspConn;
|
|
|
|
// Remove not associated flag.
|
|
pAdspConn->adspco_Flags |= ADSPCO_ASSOCIATED;
|
|
pAdspConn->adspco_pAssocAddr = pAdspAddr;
|
|
|
|
// put Association refcount
|
|
pAdspAddr->adspao_RefCount++;
|
|
}
|
|
|
|
RELEASE_SPIN_LOCK_DPC(&pAdspConn->adspco_Lock);
|
|
RELEASE_SPIN_LOCK(&pAdspAddr->adspao_Lock, OldIrql);
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
|
|
|
|
ATALK_ERROR
|
|
AtalkAdspDissociateAddress(
|
|
IN PADSP_CONNOBJ pAdspConn
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PADSP_ADDROBJ pAdspAddr;
|
|
KIRQL OldIrql;
|
|
ATALK_ERROR error = ATALK_NO_ERROR;
|
|
|
|
ASSERT(VALID_ADSPCO(pAdspConn));
|
|
|
|
ACQUIRE_SPIN_LOCK(&pAdspConn->adspco_Lock, &OldIrql);
|
|
if ((pAdspConn->adspco_Flags & (ADSPCO_LISTENING |
|
|
ADSPCO_CONNECTING |
|
|
ADSPCO_ACTIVE |
|
|
ADSPCO_ASSOCIATED)) != ADSPCO_ASSOCIATED)
|
|
{
|
|
// ASSERTMSG("AtalkAdspDissociateAddress: Disassociate not valid\n", 0);
|
|
error = ATALK_INVALID_CONNECTION;
|
|
}
|
|
else
|
|
{
|
|
pAdspAddr = pAdspConn->adspco_pAssocAddr ;
|
|
|
|
if (pAdspAddr == NULL)
|
|
{
|
|
ASSERT(0);
|
|
error = ATALK_CANNOT_DISSOCIATE;
|
|
}
|
|
|
|
// Set not associated flag. Don't reset the stopping flag.
|
|
pAdspConn->adspco_Flags &= ~ADSPCO_ASSOCIATED;
|
|
|
|
// don't null it out yet: we'll do when we disconnect the connection
|
|
// pAdspConn->adspco_pAssocAddr = NULL;
|
|
}
|
|
RELEASE_SPIN_LOCK(&pAdspConn->adspco_Lock, OldIrql);
|
|
|
|
// Unlink it if ok.
|
|
if (ATALK_SUCCESS(error))
|
|
{
|
|
ACQUIRE_SPIN_LOCK(&pAdspAddr->adspao_Lock, &OldIrql);
|
|
ACQUIRE_SPIN_LOCK_DPC(&pAdspConn->adspco_Lock);
|
|
atalkAdspConnDeQueueAssocList(pAdspAddr, pAdspConn);
|
|
RELEASE_SPIN_LOCK_DPC(&pAdspConn->adspco_Lock);
|
|
RELEASE_SPIN_LOCK(&pAdspAddr->adspao_Lock, OldIrql);
|
|
|
|
// remove the Association refcount
|
|
AtalkAdspAddrDereference(pAdspAddr);
|
|
}
|
|
return error;
|
|
}
|
|
|
|
|
|
|
|
|
|
ATALK_ERROR
|
|
AtalkAdspPostListen(
|
|
IN PADSP_CONNOBJ pAdspConn,
|
|
IN PVOID pListenCtx,
|
|
IN GENERIC_COMPLETION CompletionRoutine
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PADSP_ADDROBJ pAdspAddr = pAdspConn->adspco_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_ADSPCO(pAdspConn));
|
|
ASSERT(VALID_ADSPAO(pAdspAddr));
|
|
|
|
ACQUIRE_SPIN_LOCK(&pAdspAddr->adspao_Lock, &OldIrql);
|
|
ACQUIRE_SPIN_LOCK_DPC(&pAdspConn->adspco_Lock);
|
|
do
|
|
{
|
|
if ((pAdspConn->adspco_Flags & (ADSPCO_LISTENING |
|
|
ADSPCO_CONNECTING |
|
|
ADSPCO_HALF_ACTIVE |
|
|
ADSPCO_ACTIVE |
|
|
ADSPCO_ASSOCIATED)) != ADSPCO_ASSOCIATED)
|
|
{
|
|
error = ATALK_INVALID_CONNECTION;
|
|
break;
|
|
}
|
|
|
|
// Verify the address object is not a connect address type.
|
|
if (pAdspAddr->adspao_Flags & ADSPAO_CONNECT)
|
|
{
|
|
error = ATALK_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
// Make the address object a listener.
|
|
pAdspAddr->adspao_Flags |= ADSPAO_LISTENER;
|
|
|
|
pAdspConn->adspco_Flags |= ADSPCO_LISTENING;
|
|
pAdspConn->adspco_ListenCtx = pListenCtx;
|
|
pAdspConn->adspco_ListenCompletion = CompletionRoutine;
|
|
|
|
// Insert into the listen list.
|
|
pAdspConn->adspco_pNextListen = pAdspAddr->adspao_pListenConn;
|
|
pAdspAddr->adspao_pListenConn = pAdspConn;
|
|
|
|
// Inherits the address objects ddp address
|
|
pAdspConn->adspco_pDdpAddr = pAdspAddr->adspao_pDdpAddr;
|
|
|
|
// Initialize pended sends list
|
|
InitializeListHead(&pAdspConn->adspco_PendedSends);
|
|
|
|
error = ATALK_PENDING;
|
|
} while (FALSE);
|
|
RELEASE_SPIN_LOCK_DPC(&pAdspConn->adspco_Lock);
|
|
RELEASE_SPIN_LOCK(&pAdspAddr->adspao_Lock, OldIrql);
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
|
|
|
|
ATALK_ERROR
|
|
AtalkAdspCancelListen(
|
|
IN PADSP_CONNOBJ pAdspConn
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Cancel a previously posted listen.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PADSP_ADDROBJ pAdspAddr = pAdspConn->adspco_pAssocAddr;
|
|
KIRQL OldIrql;
|
|
ATALK_ERROR error = ATALK_NO_ERROR;
|
|
GENERIC_COMPLETION completionRoutine = NULL;
|
|
PVOID completionCtx = NULL;
|
|
|
|
ASSERT(VALID_ADSPCO(pAdspConn));
|
|
ASSERT(VALID_ADSPAO(pAdspAddr));
|
|
ACQUIRE_SPIN_LOCK(&pAdspAddr->adspao_Lock, &OldIrql);
|
|
if (!atalkAdspConnDeQueueListenList(pAdspAddr, pAdspConn))
|
|
{
|
|
error = ATALK_INVALID_CONNECTION;
|
|
}
|
|
else
|
|
{
|
|
// We complete the listen routine
|
|
ASSERT(pAdspConn->adspco_Flags & ADSPCO_LISTENING);
|
|
pAdspConn->adspco_Flags &= ~ADSPCO_LISTENING;
|
|
completionRoutine = pAdspConn->adspco_ListenCompletion;
|
|
completionCtx = pAdspConn->adspco_ListenCtx;
|
|
}
|
|
RELEASE_SPIN_LOCK(&pAdspAddr->adspao_Lock, OldIrql);
|
|
|
|
if (*completionRoutine != NULL)
|
|
{
|
|
(*completionRoutine)(ATALK_REQUEST_CANCELLED, completionCtx);
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
|
|
|
|
ATALK_ERROR
|
|
AtalkAdspPostConnect(
|
|
IN PADSP_CONNOBJ pAdspConn,
|
|
IN PATALK_ADDR pRemote_Addr,
|
|
IN PVOID pConnectCtx,
|
|
IN GENERIC_COMPLETION CompletionRoutine
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
ATALK_ERROR error;
|
|
KIRQL OldIrql;
|
|
BOOLEAN DerefConn = FALSE;
|
|
PADSP_ADDROBJ pAdspAddr = pAdspConn->adspco_pAssocAddr;
|
|
|
|
ASSERT(VALID_ADSPCO(pAdspConn));
|
|
ASSERT(VALID_ADSPAO(pAdspAddr));
|
|
ACQUIRE_SPIN_LOCK(&pAdspAddr->adspao_Lock, &OldIrql);
|
|
ACQUIRE_SPIN_LOCK_DPC(&pAdspConn->adspco_Lock);
|
|
do
|
|
{
|
|
if ((pAdspConn->adspco_Flags & (ADSPCO_LISTENING |
|
|
ADSPCO_CONNECTING |
|
|
ADSPCO_HALF_ACTIVE |
|
|
ADSPCO_ACTIVE |
|
|
ADSPCO_ASSOCIATED)) != ADSPCO_ASSOCIATED)
|
|
{
|
|
error = ATALK_INVALID_CONNECTION;
|
|
break;
|
|
}
|
|
|
|
// Verify the address object is not a listener address type.
|
|
if (pAdspAddr->adspao_Flags & ADSPAO_LISTENER)
|
|
{
|
|
error = ATALK_INVALID_ADDRESS;
|
|
break;
|
|
}
|
|
|
|
// Reference the connection for this call and for the timer.
|
|
AtalkAdspConnReferenceByPtrNonInterlock(pAdspConn, 2, &error);
|
|
if (!ATALK_SUCCESS(error))
|
|
{
|
|
ASSERTMSG("AtalkAdspPostConnect: Connection ref failed\n", 0);
|
|
break;
|
|
}
|
|
|
|
DerefConn = TRUE;
|
|
|
|
pAdspConn->adspco_LocalConnId = atalkAdspGetNextConnId(pAdspAddr,
|
|
&error);
|
|
|
|
if (ATALK_SUCCESS(error))
|
|
{
|
|
pAdspConn->adspco_Flags |= (ADSPCO_CONNECTING | ADSPCO_OPEN_TIMER);
|
|
pAdspConn->adspco_ConnectCtx = pConnectCtx;
|
|
pAdspConn->adspco_ConnectCompletion = CompletionRoutine;
|
|
pAdspConn->adspco_RemoteAddr = *pRemote_Addr;
|
|
pAdspConn->adspco_ConnectAttempts = ADSP_MAX_OPEN_ATTEMPTS;
|
|
|
|
// Insert into the connect list.
|
|
pAdspConn->adspco_pNextConnect = pAdspAddr->adspao_pConnectConn;
|
|
pAdspAddr->adspao_pConnectConn = pAdspConn;
|
|
pAdspAddr->adspao_Flags |= ADSPAO_CONNECT;
|
|
|
|
pAdspConn->adspco_RecvWindow=
|
|
pAdspConn->adspco_SendQueueMax =
|
|
pAdspConn->adspco_RecvQueueMax = ADSP_DEF_SEND_RX_WINDOW_SIZE;
|
|
|
|
// Inherits the address objects ddp address
|
|
pAdspConn->adspco_pDdpAddr = pAdspAddr->adspao_pDdpAddr;
|
|
|
|
// Initialize pended sends list
|
|
InitializeListHead(&pAdspConn->adspco_PendedSends);
|
|
|
|
// Start the open timer
|
|
AtalkTimerInitialize(&pAdspConn->adspco_OpenTimer,
|
|
atalkAdspOpenTimer,
|
|
ADSP_OPEN_INTERVAL);
|
|
AtalkTimerScheduleEvent(&pAdspConn->adspco_OpenTimer);
|
|
}
|
|
else
|
|
{
|
|
ASSERTMSG("AtalkAdspPostConnect: Unable to get conn id\n", 0);
|
|
error = ATALK_RESR_MEM;
|
|
RES_LOG_ERROR();
|
|
break;
|
|
}
|
|
|
|
} while (FALSE);
|
|
RELEASE_SPIN_LOCK_DPC(&pAdspConn->adspco_Lock);
|
|
RELEASE_SPIN_LOCK(&pAdspAddr->adspao_Lock, OldIrql);
|
|
|
|
if (ATALK_SUCCESS(error))
|
|
{
|
|
// Send connect packet to the remote end. This will add its
|
|
// own references.
|
|
atalkAdspSendOpenControl(pAdspConn);
|
|
|
|
error = ATALK_PENDING;
|
|
}
|
|
else
|
|
{
|
|
if (DerefConn)
|
|
{
|
|
// Remove the reference for timer only if error.
|
|
AtalkAdspConnDereference(pAdspConn);
|
|
}
|
|
}
|
|
|
|
if (DerefConn)
|
|
{
|
|
// Remove the reference for call
|
|
AtalkAdspConnDereference(pAdspConn);
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
#define atalkAdspCompleteQueuedSends(_pAdspConn, _error) \
|
|
{ \
|
|
ULONG writeBufLen; \
|
|
PVOID writeCtx; \
|
|
\
|
|
while (!IsListEmpty(&(_pAdspConn)->adspco_PendedSends)) \
|
|
{ \
|
|
writeCtx = LIST_ENTRY_TO_WRITECTX((_pAdspConn)->adspco_PendedSends.Flink); \
|
|
writeBufLen = WRITECTX_SIZE(writeCtx); \
|
|
\
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_WARN, \
|
|
("AtalkAdspCompleteQueuedSends: %lx WriteLen %x\n", \
|
|
writeCtx, writeBufLen)); \
|
|
\
|
|
RemoveEntryList(WRITECTX_LINKAGE(writeCtx)); \
|
|
\
|
|
RELEASE_SPIN_LOCK(&(_pAdspConn)->adspco_Lock, OldIrql); \
|
|
atalkTdiGenericWriteComplete(_error, \
|
|
(PAMDL)(WRITECTX_TDI_BUFFER(writeCtx)), \
|
|
(USHORT)writeBufLen, \
|
|
WRITECTX(writeCtx)); \
|
|
ACQUIRE_SPIN_LOCK(&(_pAdspConn)->adspco_Lock, &OldIrql); \
|
|
} \
|
|
}
|
|
|
|
|
|
ATALK_ERROR
|
|
AtalkAdspDisconnect(
|
|
IN PADSP_CONNOBJ pAdspConn,
|
|
IN ATALK_DISCONNECT_TYPE DisconnectType,
|
|
IN PVOID pDisconnectCtx,
|
|
IN GENERIC_COMPLETION DisconnectRoutine
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PAMDL readBuf = NULL,
|
|
exReadBuf = NULL;
|
|
GENERIC_READ_COMPLETION readCompletion = NULL,
|
|
exReadCompletion = NULL;
|
|
PVOID readCtx = NULL,
|
|
exReadCtx = NULL;
|
|
PAMDL exWriteBuf = NULL;
|
|
GENERIC_WRITE_COMPLETION exWriteCompletion = NULL;
|
|
PVOID exWriteCtx = NULL;
|
|
PBYTE exWriteChBuf = NULL,
|
|
exRecdBuf = NULL;
|
|
GENERIC_COMPLETION completionRoutine = NULL;
|
|
PVOID completionCtx = NULL;
|
|
ATALK_ERROR error = ATALK_PENDING;
|
|
BOOLEAN connTimerCancelled = FALSE,
|
|
openTimerCancelled = FALSE,
|
|
sendAttnTimerCancelled = FALSE,
|
|
rexmitTimerCancelled = FALSE,
|
|
connectCancelled = FALSE;
|
|
KIRQL OldIrql;
|
|
|
|
ASSERT(VALID_ADSPCO(pAdspConn));
|
|
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("AtalkAdspDisconnectConnection: %lx.%lx\n", pAdspConn, DisconnectType));
|
|
|
|
ACQUIRE_SPIN_LOCK(&pAdspConn->adspco_Lock, &OldIrql);
|
|
|
|
// 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. Note that attentions are not acknowledged until our client
|
|
// reads them, so there isnt an issue with them. Also, this means
|
|
// that we must satisfy a read if disconnect is pending.
|
|
if ((DisconnectType == ATALK_LOCAL_DISCONNECT) ||
|
|
(DisconnectType == ATALK_TIMER_DISCONNECT))
|
|
{
|
|
atalkAdspDiscardFromBufferQueue(&pAdspConn->adspco_RecvQueue,
|
|
atalkAdspBufferQueueSize(&pAdspConn->adspco_RecvQueue),
|
|
NULL,
|
|
DISCONN_STATUS(DisconnectType),
|
|
NULL);
|
|
}
|
|
|
|
if ((pAdspConn->adspco_Flags & ADSPCO_DISCONNECTING) == 0)
|
|
{
|
|
if ((pAdspConn->adspco_Flags & (ADSPCO_LISTENING |
|
|
ADSPCO_CONNECTING |
|
|
ADSPCO_HALF_ACTIVE |
|
|
ADSPCO_ACTIVE)) == 0)
|
|
{
|
|
error = ATALK_INVALID_REQUEST;
|
|
}
|
|
else
|
|
{
|
|
pAdspConn->adspco_Flags |= ADSPCO_DISCONNECTING;
|
|
if (DisconnectType == ATALK_LOCAL_DISCONNECT)
|
|
pAdspConn->adspco_Flags |= ADSPCO_LOCAL_DISCONNECT;
|
|
if (DisconnectType == ATALK_REMOTE_DISCONNECT)
|
|
pAdspConn->adspco_Flags |= ADSPCO_REMOTE_DISCONNECT;
|
|
|
|
if (pAdspConn->adspco_Flags & ADSPCO_LISTENING)
|
|
{
|
|
RELEASE_SPIN_LOCK(&pAdspConn->adspco_Lock, OldIrql);
|
|
AtalkAdspCancelListen(pAdspConn);
|
|
ACQUIRE_SPIN_LOCK(&pAdspConn->adspco_Lock, &OldIrql);
|
|
}
|
|
else if (pAdspConn->adspco_Flags & ADSPCO_CONNECTING)
|
|
{
|
|
// Cancel open timer
|
|
ASSERT(pAdspConn->adspco_Flags & ADSPCO_OPEN_TIMER);
|
|
openTimerCancelled = AtalkTimerCancelEvent(&pAdspConn->adspco_OpenTimer,
|
|
NULL);
|
|
|
|
completionRoutine = pAdspConn->adspco_ConnectCompletion;
|
|
completionCtx = pAdspConn->adspco_ConnectCtx;
|
|
|
|
// We can only be here if the connect is not done yet. Complete
|
|
// as if timer is done, always.
|
|
pAdspConn->adspco_DisconnectStatus = ATALK_TIMEOUT;
|
|
RELEASE_SPIN_LOCK(&pAdspConn->adspco_Lock, OldIrql);
|
|
connectCancelled = atalkAdspConnDeQueueConnectList(pAdspConn->adspco_pAssocAddr,
|
|
pAdspConn);
|
|
|
|
if (!connectCancelled)
|
|
{
|
|
completionRoutine = NULL;
|
|
completionCtx = NULL;
|
|
}
|
|
|
|
ACQUIRE_SPIN_LOCK(&pAdspConn->adspco_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 (pAdspConn->adspco_Flags & (ADSPCO_HALF_ACTIVE | ADSPCO_ACTIVE))
|
|
{
|
|
// Get the completion routines for a pending accept
|
|
if (pAdspConn->adspco_Flags & ADSPCO_ACCEPT_IRP)
|
|
{
|
|
completionRoutine = pAdspConn->adspco_ListenCompletion;
|
|
completionCtx = pAdspConn->adspco_ListenCtx;
|
|
|
|
// Dequeue the open request that must be queued on
|
|
// this connection object to filter duplicates.
|
|
|
|
pAdspConn->adspco_Flags &= ~ADSPCO_ACCEPT_IRP;
|
|
}
|
|
|
|
// First cancel the conection maintainance timer. Only if
|
|
// we are not called from the timer.
|
|
if ((DisconnectType != ATALK_TIMER_DISCONNECT) &&
|
|
(connTimerCancelled =
|
|
AtalkTimerCancelEvent(&pAdspConn->adspco_ConnTimer,
|
|
NULL)))
|
|
{
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_WARN,
|
|
("AtalkAdspDisconnect: Cancelled timer successfully\n"));
|
|
}
|
|
|
|
// Cancel retransmit timer if started. Could be called from
|
|
// OpenTimer.
|
|
if (pAdspConn->adspco_Flags & ADSPCO_RETRANSMIT_TIMER)
|
|
{
|
|
rexmitTimerCancelled =
|
|
AtalkTimerCancelEvent(&pAdspConn->adspco_RetransmitTimer,
|
|
NULL);
|
|
}
|
|
|
|
// Remember completion routines as appropriate.
|
|
if (DisconnectType == ATALK_INDICATE_DISCONNECT)
|
|
{
|
|
if (pAdspConn->adspco_DisconnectInform == NULL)
|
|
{
|
|
pAdspConn->adspco_DisconnectInform = DisconnectRoutine;
|
|
pAdspConn->adspco_DisconnectInformCtx = pDisconnectCtx;
|
|
}
|
|
else
|
|
{
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_ERR,
|
|
("AtalkAdspDisconnect: duplicate disc comp rou%lx\n", pAdspConn));
|
|
|
|
error = ATALK_TOO_MANY_COMMANDS;
|
|
}
|
|
}
|
|
else if (DisconnectType == ATALK_LOCAL_DISCONNECT)
|
|
{
|
|
// Replace completion routines only if previous ones are
|
|
// NULL.
|
|
if (*pAdspConn->adspco_DisconnectCompletion == NULL)
|
|
{
|
|
pAdspConn->adspco_DisconnectCompletion = DisconnectRoutine;
|
|
pAdspConn->adspco_DisconnectCtx = pDisconnectCtx;
|
|
}
|
|
else
|
|
{
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_ERR,
|
|
("AtalkAdspDisconnect: duplicate disc comp rou%lx\n", pAdspConn));
|
|
|
|
error = ATALK_TOO_MANY_COMMANDS;
|
|
}
|
|
}
|
|
|
|
// Figure out the disconnect status and remember it in the
|
|
// connection object.
|
|
pAdspConn->adspco_DisconnectStatus = DISCONN_STATUS(DisconnectType);
|
|
|
|
if (pAdspConn->adspco_Flags & ADSPCO_ATTN_DATA_RECD)
|
|
{
|
|
exRecdBuf = pAdspConn->adspco_ExRecdData;
|
|
pAdspConn->adspco_Flags &= ~ADSPCO_ATTN_DATA_RECD;
|
|
}
|
|
|
|
// Is there a pending send attention?
|
|
if (pAdspConn->adspco_Flags & ADSPCO_EXSEND_IN_PROGRESS)
|
|
{
|
|
exWriteCompletion = pAdspConn->adspco_ExWriteCompletion;
|
|
exWriteCtx = pAdspConn->adspco_ExWriteCtx;
|
|
exWriteBuf = pAdspConn->adspco_ExWriteBuf;
|
|
exWriteChBuf = pAdspConn->adspco_ExWriteChBuf;
|
|
|
|
ASSERT(exWriteChBuf != NULL);
|
|
sendAttnTimerCancelled =
|
|
AtalkTimerCancelEvent(&pAdspConn->adspco_ExRetryTimer,
|
|
NULL);
|
|
|
|
pAdspConn->adspco_Flags &= ~ADSPCO_EXSEND_IN_PROGRESS;
|
|
}
|
|
|
|
|
|
// Are there any pending receives?
|
|
if (pAdspConn->adspco_Flags & ADSPCO_READ_PENDING)
|
|
{
|
|
readBuf = pAdspConn->adspco_ReadBuf;
|
|
readCompletion = pAdspConn->adspco_ReadCompletion;
|
|
readCtx = pAdspConn->adspco_ReadCtx;
|
|
|
|
pAdspConn->adspco_Flags &= ~ADSPCO_READ_PENDING;
|
|
}
|
|
|
|
if (pAdspConn->adspco_Flags & ADSPCO_EXREAD_PENDING)
|
|
{
|
|
exReadBuf = pAdspConn->adspco_ExReadBuf;
|
|
exReadCompletion = pAdspConn->adspco_ExReadCompletion;
|
|
exReadCtx = pAdspConn->adspco_ExReadCtx;
|
|
|
|
pAdspConn->adspco_Flags &= ~ADSPCO_EXREAD_PENDING;
|
|
}
|
|
|
|
// Discard the send queue. This will complete pending sends.
|
|
atalkAdspDiscardFromBufferQueue(&pAdspConn->adspco_SendQueue,
|
|
atalkAdspBufferQueueSize(&pAdspConn->adspco_SendQueue),
|
|
NULL,
|
|
pAdspConn->adspco_DisconnectStatus,
|
|
pAdspConn);
|
|
|
|
atalkAdspCompleteQueuedSends(pAdspConn,
|
|
pAdspConn->adspco_DisconnectStatus);
|
|
|
|
RELEASE_SPIN_LOCK(&pAdspConn->adspco_Lock, OldIrql);
|
|
|
|
// Send out disconnect packet if this was a timer or local close.
|
|
if ((DisconnectType == ATALK_LOCAL_DISCONNECT) ||
|
|
(DisconnectType == ATALK_TIMER_DISCONNECT))
|
|
{
|
|
|
|
ACQUIRE_SPIN_LOCK(&pAdspConn->adspco_Lock, &OldIrql);
|
|
atalkAdspSendControl(pAdspConn,
|
|
ADSP_CONTROL_FLAG + ADSP_CLOSE_CONN_CODE);
|
|
RELEASE_SPIN_LOCK(&pAdspConn->adspco_Lock, OldIrql);
|
|
}
|
|
|
|
// Call the send attention completion
|
|
if (*exWriteCompletion != NULL)
|
|
{
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkDisconnect: ExWrite\n"));
|
|
|
|
(*exWriteCompletion)(pAdspConn->adspco_DisconnectStatus,
|
|
exWriteBuf,
|
|
0,
|
|
exWriteCtx);
|
|
|
|
AtalkFreeMemory(exWriteChBuf);
|
|
}
|
|
|
|
// If we had received an attention packet, and had
|
|
// saved it away, free it.
|
|
if (exRecdBuf != NULL)
|
|
{
|
|
AtalkFreeMemory(exRecdBuf);
|
|
}
|
|
|
|
if (*readCompletion != NULL)
|
|
{
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkDisconnect: Read %lx\n", pAdspConn->adspco_DisconnectStatus));
|
|
|
|
(*readCompletion)(pAdspConn->adspco_DisconnectStatus,
|
|
readBuf,
|
|
0,
|
|
0,
|
|
readCtx);
|
|
|
|
// Deref connection for the read
|
|
AtalkAdspConnDereference(pAdspConn);
|
|
}
|
|
|
|
if (*exReadCompletion != NULL)
|
|
{
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkDisconnect: ExRead\n"));
|
|
|
|
(*exReadCompletion)(pAdspConn->adspco_DisconnectStatus,
|
|
exReadBuf,
|
|
0,
|
|
0,
|
|
exReadCtx);
|
|
|
|
// Deref connection for the read
|
|
AtalkAdspConnDereference(pAdspConn);
|
|
}
|
|
|
|
// Call the disconnect indication routine if present for a timer/
|
|
// remote disconnect.
|
|
if ((DisconnectType == ATALK_REMOTE_DISCONNECT) ||
|
|
(DisconnectType == ATALK_TIMER_DISCONNECT))
|
|
{
|
|
PTDI_IND_DISCONNECT discHandler;
|
|
PVOID discCtx;
|
|
PADSP_ADDROBJ pAdspAddr = pAdspConn->adspco_pAssocAddr;
|
|
|
|
ASSERT(VALID_ADSPAO(pAdspAddr));
|
|
|
|
// Acquire lock so we get a consistent handler/ctx.
|
|
ACQUIRE_SPIN_LOCK(&pAdspAddr->adspao_Lock, &OldIrql);
|
|
discHandler = pAdspAddr->adspao_DisconnectHandler;
|
|
discCtx = pAdspAddr->adspao_DisconnectHandlerCtx;
|
|
RELEASE_SPIN_LOCK(&pAdspAddr->adspao_Lock, OldIrql);
|
|
|
|
if (*discHandler != NULL)
|
|
{
|
|
(*discHandler)(discCtx,
|
|
pAdspConn->adspco_ConnCtx,
|
|
0, // DisconnectDataLength
|
|
NULL, // DisconnectData
|
|
0, // DisconnectInfoLength
|
|
NULL, // DisconnectInfo
|
|
TDI_DISCONNECT_ABORT); // Disconnect flags.
|
|
}
|
|
}
|
|
|
|
// Stop the ddp address.
|
|
AtalkDdpCleanupAddress(pAdspConn->adspco_pDdpAddr);
|
|
ACQUIRE_SPIN_LOCK(&pAdspConn->adspco_Lock, &OldIrql);
|
|
}
|
|
}
|
|
}
|
|
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 (pAdspConn->adspco_DisconnectInform == NULL)
|
|
{
|
|
pAdspConn->adspco_DisconnectInform = DisconnectRoutine;
|
|
pAdspConn->adspco_DisconnectInformCtx = pDisconnectCtx;
|
|
}
|
|
else
|
|
{
|
|
error = ATALK_TOO_MANY_COMMANDS;
|
|
}
|
|
}
|
|
else if (DisconnectType == ATALK_LOCAL_DISCONNECT)
|
|
{
|
|
// Replace completion routines only if previous ones are
|
|
// NULL.
|
|
if (*pAdspConn->adspco_DisconnectCompletion == NULL)
|
|
{
|
|
pAdspConn->adspco_DisconnectCompletion = DisconnectRoutine;
|
|
pAdspConn->adspco_DisconnectCtx = pDisconnectCtx;
|
|
}
|
|
else
|
|
{
|
|
error = ATALK_TOO_MANY_COMMANDS;
|
|
}
|
|
}
|
|
}
|
|
RELEASE_SPIN_LOCK(&pAdspConn->adspco_Lock, OldIrql);
|
|
|
|
// If there was a completion routine to call, call it now.
|
|
if (*completionRoutine != NULL)
|
|
{
|
|
(*completionRoutine)(pAdspConn->adspco_DisconnectStatus,
|
|
completionCtx);
|
|
}
|
|
|
|
// If we cancelled any timers, remove their references.
|
|
if (connTimerCancelled)
|
|
{
|
|
AtalkAdspConnDereference(pAdspConn);
|
|
}
|
|
|
|
if (sendAttnTimerCancelled)
|
|
{
|
|
AtalkAdspConnDereference(pAdspConn);
|
|
}
|
|
|
|
if (openTimerCancelled)
|
|
{
|
|
AtalkAdspConnDereference(pAdspConn);
|
|
}
|
|
|
|
if (rexmitTimerCancelled)
|
|
{
|
|
AtalkAdspConnDereference(pAdspConn);
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
|
|
|
|
ATALK_ERROR
|
|
AtalkAdspRead(
|
|
IN PADSP_CONNOBJ pAdspConn,
|
|
IN PAMDL pReadBuf,
|
|
IN USHORT ReadBufLen,
|
|
IN ULONG ReadFlags,
|
|
IN PVOID pReadCtx,
|
|
IN GENERIC_READ_COMPLETION CompletionRoutine
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
KIRQL OldIrql;
|
|
ATALK_ERROR error;
|
|
|
|
ASSERT(VALID_ADSPCO(pAdspConn));
|
|
|
|
ACQUIRE_SPIN_LOCK(&pAdspConn->adspco_Lock, &OldIrql);
|
|
do
|
|
{
|
|
// We allow reads when disconnecting if the receive data
|
|
// queue is non-empty. Since none of the receive chunks ref
|
|
// the connection, the active flag and the disconnect
|
|
// flags could have gone away. So we cue of the receive buffer
|
|
// size. We also dont allow exdata recvs unless we are active.
|
|
if (((pAdspConn->adspco_Flags & (ADSPCO_CLOSING | ADSPCO_STOPPING))) ||
|
|
((((pAdspConn->adspco_Flags & ADSPCO_ACTIVE) == 0) ||
|
|
(pAdspConn->adspco_Flags & ADSPCO_DISCONNECTING)) &&
|
|
(((atalkAdspBufferQueueSize(&pAdspConn->adspco_RecvQueue) == 0)) ||
|
|
(ReadFlags & TDI_RECEIVE_EXPEDITED))))
|
|
{
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_WARN,
|
|
("AtalkAdspRead: Failing on %lx Flg %lx.%lx\n",
|
|
pAdspConn, pAdspConn->adspco_Flags, ReadFlags));
|
|
|
|
error = ATALK_ADSP_CONN_NOT_ACTIVE;
|
|
break;
|
|
}
|
|
|
|
// Depending on the kind of read we are posting...
|
|
if (((ReadFlags & TDI_RECEIVE_NORMAL) &&
|
|
(pAdspConn->adspco_Flags & ADSPCO_READ_PENDING)) ||
|
|
((ReadFlags & TDI_RECEIVE_EXPEDITED) &&
|
|
(pAdspConn->adspco_Flags & ADSPCO_EXREAD_PENDING)))
|
|
{
|
|
error = ATALK_TOO_MANY_COMMANDS;
|
|
break;
|
|
}
|
|
|
|
AtalkAdspConnReferenceByPtrNonInterlock(pAdspConn, 1, &error);
|
|
if (!ATALK_SUCCESS(error))
|
|
{
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_ERR,
|
|
("AtalkAdspRead: ConnRef Failing on %lx Flg %lx.%lx\n",
|
|
pAdspConn, pAdspConn->adspco_Flags, ReadFlags));
|
|
break;
|
|
}
|
|
|
|
// Remember the read completion information
|
|
if (ReadFlags & TDI_RECEIVE_NORMAL)
|
|
{
|
|
pAdspConn->adspco_Flags |= ADSPCO_READ_PENDING;
|
|
pAdspConn->adspco_ReadFlags = ReadFlags;
|
|
pAdspConn->adspco_ReadBuf = pReadBuf;
|
|
pAdspConn->adspco_ReadBufLen = ReadBufLen;
|
|
pAdspConn->adspco_ReadCompletion = CompletionRoutine;
|
|
pAdspConn->adspco_ReadCtx = pReadCtx;
|
|
}
|
|
else
|
|
{
|
|
ASSERT(ReadFlags & TDI_RECEIVE_EXPEDITED);
|
|
pAdspConn->adspco_Flags |= ADSPCO_EXREAD_PENDING;
|
|
pAdspConn->adspco_ExReadFlags = ReadFlags;
|
|
pAdspConn->adspco_ExReadBuf = pReadBuf;
|
|
pAdspConn->adspco_ExReadBufLen = ReadBufLen;
|
|
pAdspConn->adspco_ExReadCompletion = CompletionRoutine;
|
|
pAdspConn->adspco_ExReadCtx = pReadCtx;
|
|
}
|
|
} while (FALSE);
|
|
|
|
if (ATALK_SUCCESS(error))
|
|
{
|
|
// Try to complete the read. This will also handle received
|
|
// attention data.
|
|
atalkAdspRecvData(pAdspConn);
|
|
error = ATALK_PENDING;
|
|
}
|
|
|
|
RELEASE_SPIN_LOCK(&pAdspConn->adspco_Lock, OldIrql);
|
|
return error;
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
AtalkAdspProcessQueuedSend(
|
|
IN PADSP_CONNOBJ pAdspConn
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
ULONG sendSize, windowSize, writeBufLen, writeFlags;
|
|
KIRQL OldIrql;
|
|
PVOID writeCtx;
|
|
ATALK_ERROR error;
|
|
BOOLEAN eom;
|
|
PBUFFER_CHUNK pChunk = NULL;
|
|
|
|
PTDI_IND_SEND_POSSIBLE sendPossibleHandler;
|
|
PVOID sendPossibleHandlerCtx;
|
|
|
|
// Walk through pended list.
|
|
while (!IsListEmpty(&pAdspConn->adspco_PendedSends))
|
|
{
|
|
writeCtx = LIST_ENTRY_TO_WRITECTX(pAdspConn->adspco_PendedSends.Flink);
|
|
writeBufLen = WRITECTX_SIZE(writeCtx);
|
|
writeFlags = WRITECTX_FLAGS(writeCtx);
|
|
|
|
eom = (writeFlags & TDI_SEND_PARTIAL) ? FALSE : TRUE;
|
|
eom = (eom && (pAdspConn->adspco_pAssocAddr->adspao_Flags & ADSPAO_MESSAGE));
|
|
|
|
windowSize = (LONG)(pAdspConn->adspco_SendWindowSeq -
|
|
pAdspConn->adspco_SendSeq +
|
|
(LONG)1);
|
|
|
|
sendSize = MIN(atalkAdspMaxSendSize(pAdspConn), windowSize);
|
|
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_WARN,
|
|
("AtalkAdspProcessQueuedSend: %lx SendSize %lx, WriteBufLen %x Flags %lx\n",
|
|
writeCtx, sendSize, writeBufLen, writeFlags));
|
|
|
|
// While looping through requests, we might exhaust window.
|
|
if (sendSize == 0)
|
|
{
|
|
// Call send possible handler indicating sends are not ok.
|
|
// Needs to be within spinlock to avoid raceconditions where
|
|
// an ack has come in and opened the window. And it needs to
|
|
// be before atalkAdspSendData() as that will release the lock.
|
|
sendPossibleHandler =
|
|
pAdspConn->adspco_pAssocAddr->adspao_SendPossibleHandler;
|
|
sendPossibleHandlerCtx =
|
|
pAdspConn->adspco_pAssocAddr->adspao_SendPossibleHandlerCtx;
|
|
|
|
if (*sendPossibleHandler != NULL)
|
|
{
|
|
(*sendPossibleHandler)(sendPossibleHandlerCtx,
|
|
pAdspConn->adspco_ConnCtx,
|
|
0);
|
|
|
|
pAdspConn->adspco_Flags |= ADSPCO_SEND_WINDOW_CLOSED;
|
|
}
|
|
break;
|
|
}
|
|
|
|
// !!! The client can do a send with 0 bytes and eom only also.
|
|
if ((ULONG)(writeBufLen + BYTECOUNT(eom)) > sendSize)
|
|
{
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_WARN,
|
|
("AtalkAdspProcessQueuedSend: WriteBufLen %lx > sendsize %lx\n",
|
|
writeBufLen, sendSize));
|
|
|
|
// Adjust send to send as much as it can. Winsock loop will pend
|
|
// it again with remaining data.
|
|
writeBufLen = (USHORT)(sendSize - BYTECOUNT(eom));
|
|
|
|
// If we hit the weird case where now we are trying to send 0 bytes and
|
|
// no eom, while the actual send does have an eom, then we just wait
|
|
// for window to open up more.
|
|
if (eom && (writeBufLen == 0))
|
|
{
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_ERR,
|
|
("AtalkAdspProcessQueuedSend: WriteBufLen %lx.%d.%lx %lx\n",
|
|
writeBufLen, eom, sendSize, pAdspConn));
|
|
break;
|
|
}
|
|
|
|
ASSERT(writeBufLen > 0);
|
|
eom = FALSE;
|
|
}
|
|
|
|
ASSERT((writeBufLen > 0) || eom);
|
|
|
|
// Yippee, can send it now. Either it goes in send queue or is completed
|
|
// right away.
|
|
RemoveEntryList(WRITECTX_LINKAGE(writeCtx));
|
|
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_WARN,
|
|
("AtalkAdspProcessQueuedSend: Processing queued send %lx.%lx\n",
|
|
pAdspConn, writeCtx));
|
|
|
|
// Think positive! Assume everything will go well and allocate
|
|
// the buffer chunk that would be needed for this data. Copy the
|
|
// data into the buffer chunk. We cant do this in the beginning as
|
|
// we need to get WriteBufLen set up.
|
|
pChunk = (PBUFFER_CHUNK)
|
|
atalkAdspAllocCopyChunk((PAMDL)(WRITECTX_TDI_BUFFER(writeCtx)),
|
|
(USHORT)writeBufLen,
|
|
eom,
|
|
FALSE);
|
|
|
|
error = ATALK_RESR_MEM;
|
|
if (pChunk != NULL)
|
|
{
|
|
// Set the completion information in the chunk. This will
|
|
// be called when the last reference on the chunk goes away.
|
|
pChunk->bc_Flags |= BC_SEND;
|
|
pChunk->bc_WriteBuf = (PAMDL)(WRITECTX_TDI_BUFFER(writeCtx));
|
|
pChunk->bc_WriteCompletion = atalkTdiGenericWriteComplete;
|
|
pChunk->bc_WriteCtx = writeCtx;
|
|
pChunk->bc_ConnObj = pAdspConn;
|
|
|
|
atalkAdspAddToBufferQueue(&pAdspConn->adspco_SendQueue,
|
|
pChunk,
|
|
&pAdspConn->adspco_NextSendQueue);
|
|
|
|
// Try to send the data
|
|
atalkAdspSendData(pAdspConn);
|
|
error = ATALK_PENDING;
|
|
}
|
|
|
|
if (!ATALK_SUCCESS(error))
|
|
{
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_ERR,
|
|
("AtalkAdspProcessQueuedSend: Error queued send %lx.%lx\n",
|
|
pAdspConn, writeCtx));
|
|
|
|
#if DBG
|
|
(&pAdspConn->adspco_Lock)->FileLineLock |= 0x80000000;
|
|
#endif
|
|
// Complete send request with insufficient resources error.
|
|
RELEASE_SPIN_LOCK_DPC(&pAdspConn->adspco_Lock);
|
|
atalkTdiGenericWriteComplete(error,
|
|
(PAMDL)(WRITECTX_TDI_BUFFER(writeCtx)),
|
|
(USHORT)writeBufLen,
|
|
WRITECTX(writeCtx));
|
|
ACQUIRE_SPIN_LOCK_DPC(&pAdspConn->adspco_Lock);
|
|
#if DBG
|
|
(&pAdspConn->adspco_Lock)->FileLineLock &= ~0x80000000;
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
ATALK_ERROR
|
|
AtalkAdspWrite(
|
|
IN PADSP_CONNOBJ pAdspConn,
|
|
IN PAMDL pWriteBuf,
|
|
IN USHORT WriteBufLen,
|
|
IN ULONG SendFlags,
|
|
IN PVOID pWriteCtx,
|
|
IN GENERIC_WRITE_COMPLETION CompletionRoutine
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
ATALK_ERROR error;
|
|
BOOLEAN eom;
|
|
ULONG sendSize, windowSize;
|
|
PTDI_IND_SEND_POSSIBLE sendPossibleHandler;
|
|
PVOID sendPossibleHandlerCtx;
|
|
KIRQL OldIrql;
|
|
PBUFFER_CHUNK pChunk = NULL;
|
|
BOOLEAN DerefConn = FALSE,
|
|
callComp = FALSE;
|
|
|
|
|
|
ASSERT(VALID_ADSPCO(pAdspConn));
|
|
|
|
eom = (SendFlags & TDI_SEND_PARTIAL) ? FALSE : TRUE;
|
|
if ((WriteBufLen == 0) && !eom)
|
|
{
|
|
return ATALK_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
if (SendFlags & TDI_SEND_EXPEDITED)
|
|
{
|
|
return (atalkAdspSendExpedited(pAdspConn,
|
|
pWriteBuf,
|
|
WriteBufLen,
|
|
SendFlags,
|
|
pWriteCtx,
|
|
CompletionRoutine));
|
|
}
|
|
|
|
// We atleast have one byte of data or eom to send.
|
|
ASSERT(eom || (WriteBufLen != 0));
|
|
|
|
ACQUIRE_SPIN_LOCK(&pAdspConn->adspco_Lock, &OldIrql);
|
|
do
|
|
{
|
|
if ((pAdspConn->adspco_Flags & (ADSPCO_ACTIVE |
|
|
ADSPCO_CLOSING |
|
|
ADSPCO_STOPPING |
|
|
ADSPCO_DISCONNECTING)) != ADSPCO_ACTIVE)
|
|
{
|
|
error = ATALK_ADSP_CONN_NOT_ACTIVE;
|
|
break;
|
|
}
|
|
|
|
if (pAdspConn->adspco_Flags & ADSPCO_SEND_IN_PROGRESS)
|
|
{
|
|
error = ATALK_TOO_MANY_COMMANDS;
|
|
break;
|
|
}
|
|
|
|
AtalkAdspConnReferenceByPtrNonInterlock(pAdspConn, 1, &error);
|
|
if (!ATALK_SUCCESS(error))
|
|
{
|
|
break;
|
|
}
|
|
|
|
eom = (eom && (pAdspConn->adspco_pAssocAddr->adspao_Flags & ADSPAO_MESSAGE));
|
|
|
|
DerefConn = TRUE;
|
|
|
|
windowSize = (LONG)(pAdspConn->adspco_SendWindowSeq -
|
|
pAdspConn->adspco_SendSeq +
|
|
(LONG)1);
|
|
|
|
sendSize = MIN(atalkAdspMaxSendSize(pAdspConn), windowSize);
|
|
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("AtalkAdspWrite: SendSize %lx, WriteBufLen %x\n", sendSize, WriteBufLen));
|
|
|
|
// For a blocking send, queue in any sends that exceed window size.
|
|
if ((SendFlags & TDI_SEND_NON_BLOCKING) == 0)
|
|
{
|
|
if ((!IsListEmpty(&pAdspConn->adspco_PendedSends)) ||
|
|
(sendSize < (WriteBufLen + BYTECOUNT(eom))))
|
|
{
|
|
// Stop sends whenever a send gets queued.
|
|
sendPossibleHandler =
|
|
pAdspConn->adspco_pAssocAddr->adspao_SendPossibleHandler;
|
|
sendPossibleHandlerCtx =
|
|
pAdspConn->adspco_pAssocAddr->adspao_SendPossibleHandlerCtx;
|
|
|
|
if (*sendPossibleHandler != NULL)
|
|
{
|
|
(*sendPossibleHandler)(sendPossibleHandlerCtx,
|
|
pAdspConn->adspco_ConnCtx,
|
|
0);
|
|
|
|
pAdspConn->adspco_Flags |= ADSPCO_SEND_WINDOW_CLOSED;
|
|
}
|
|
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("AtalkAdspWrite: Wdw %lx, WriteLen %x on BLOCKING QUEUEING !\n",
|
|
sendSize, WriteBufLen));
|
|
|
|
InsertTailList(&pAdspConn->adspco_PendedSends, WRITECTX_LINKAGE(pWriteCtx));
|
|
|
|
if (sendSize > 0)
|
|
{
|
|
AtalkAdspProcessQueuedSend(pAdspConn);
|
|
}
|
|
error = ATALK_PENDING;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// If there are pended blocking sends complete them with
|
|
// ATALK_REQUEST_NOT_ACCEPTED (WSAEWOULDBLOCK).
|
|
//
|
|
// !!!This is data corruption, but app shouldn't be doing this.
|
|
//
|
|
if (!IsListEmpty(&pAdspConn->adspco_PendedSends))
|
|
{
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_ERR,
|
|
("AtalkAdspWrite: ABORTING PENDED SENDS CORRUPTION %lx\n", pAdspConn));
|
|
|
|
atalkAdspCompleteQueuedSends(pAdspConn, ATALK_REQUEST_NOT_ACCEPTED);
|
|
}
|
|
}
|
|
|
|
if (sendSize == 0)
|
|
{
|
|
// Call send possible handler indicating sends are not ok.
|
|
// Needs to be within spinlock to avoid raceconditions where
|
|
// an ack has come in and opened the window. And it needs to
|
|
// be before atalkAdspSendData() as that will release the lock.
|
|
sendPossibleHandler =
|
|
pAdspConn->adspco_pAssocAddr->adspao_SendPossibleHandler;
|
|
sendPossibleHandlerCtx =
|
|
pAdspConn->adspco_pAssocAddr->adspao_SendPossibleHandlerCtx;
|
|
|
|
if (*sendPossibleHandler != NULL)
|
|
{
|
|
(*sendPossibleHandler)(sendPossibleHandlerCtx,
|
|
pAdspConn->adspco_ConnCtx,
|
|
0);
|
|
|
|
pAdspConn->adspco_Flags |= ADSPCO_SEND_WINDOW_CLOSED;
|
|
}
|
|
|
|
if (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_DEVICE_NOT_READY;
|
|
|
|
error = ATALK_REQUEST_NOT_ACCEPTED;
|
|
}
|
|
|
|
// We have no open send window, try to send data in the retransmit
|
|
// queue.
|
|
atalkAdspSendData(pAdspConn);
|
|
break;
|
|
}
|
|
|
|
// Because of the sequence numbers, we need to copy the data
|
|
// into our buffers while holding the spinlock. If we cant send it all
|
|
// send as much as we can.
|
|
|
|
// !!! TDI doesn't count the eom as taking up a count, so we need to
|
|
// make allowances for that. If we are able to send just the data
|
|
// but not the eom, we should send one less byte than requested, so
|
|
// the client retries again.
|
|
|
|
// !!! The client can do a send with 0 bytes and eom only also.
|
|
if ((ULONG)(WriteBufLen + BYTECOUNT(eom)) > sendSize)
|
|
{
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("AtalkAdspSend: WriteBufLen being decreased %x.%lx\n",
|
|
WriteBufLen, sendSize-BYTECOUNT(eom)));
|
|
|
|
WriteBufLen = (USHORT)(sendSize - BYTECOUNT(eom));
|
|
eom = FALSE;
|
|
}
|
|
|
|
if ((WriteBufLen == 0) && !eom)
|
|
{
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_ERR,
|
|
("AtalkAdspSend: SEND 0 bytes NO EOM %lx\n", pAdspConn));
|
|
|
|
callComp = TRUE;
|
|
error = ATALK_PENDING;
|
|
break;
|
|
}
|
|
|
|
// pAdspConn->adspco_Flags |= ADSPCO_SEND_IN_PROGRESS;
|
|
// If we release the spin lock here we have a race condition
|
|
// where the sendsize is still not accounting for this send,
|
|
// and so another posted send could come in when it really
|
|
// shouldn't. We avoid it using the flag above, which when
|
|
// set will prevent further sends from happening.
|
|
// RELEASE_SPIN_LOCK(&pAdspConn->adspco_Lock, OldIrql);
|
|
|
|
// Think positive! Assume everything will go well and allocate
|
|
// the buffer chunk that would be needed for this data. Copy the
|
|
// data into the buffer chunk. We cant do this in the beginning as
|
|
// we need to get WriteBufLen set up.
|
|
pChunk = (PBUFFER_CHUNK)atalkAdspAllocCopyChunk(pWriteBuf,
|
|
WriteBufLen,
|
|
eom,
|
|
FALSE);
|
|
|
|
// ACQUIRE_SPIN_LOCK(&pAdspConn->adspco_Lock, &OldIrql);
|
|
// pAdspConn->adspco_Flags &= ~ADSPCO_SEND_IN_PROGRESS;
|
|
|
|
error = ATALK_RESR_MEM;
|
|
if (pChunk != NULL)
|
|
{
|
|
// Set the completion information in the chunk. This will
|
|
// be called when the last reference on the chunk goes away.
|
|
pChunk->bc_Flags |= BC_SEND;
|
|
pChunk->bc_WriteBuf = pWriteBuf;
|
|
pChunk->bc_WriteCompletion = CompletionRoutine;
|
|
pChunk->bc_WriteCtx = pWriteCtx;
|
|
pChunk->bc_ConnObj = pAdspConn;
|
|
|
|
atalkAdspAddToBufferQueue(&pAdspConn->adspco_SendQueue,
|
|
pChunk,
|
|
&pAdspConn->adspco_NextSendQueue);
|
|
|
|
// Try to send the data
|
|
atalkAdspSendData(pAdspConn);
|
|
error = ATALK_PENDING;
|
|
}
|
|
} while (FALSE);
|
|
|
|
RELEASE_SPIN_LOCK(&pAdspConn->adspco_Lock, OldIrql);
|
|
|
|
if ((error == ATALK_PENDING) && callComp)
|
|
{
|
|
ASSERT(WriteBufLen == 0);
|
|
ASSERT(pChunk == NULL);
|
|
|
|
(*CompletionRoutine)(ATALK_NO_ERROR,
|
|
pWriteBuf,
|
|
WriteBufLen,
|
|
pWriteCtx);
|
|
}
|
|
else if (!ATALK_SUCCESS(error) && (pChunk != NULL))
|
|
{
|
|
AtalkFreeMemory(pChunk);
|
|
}
|
|
|
|
if (DerefConn)
|
|
{
|
|
AtalkAdspConnDereference(pAdspConn);
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
AtalkAdspQuery(
|
|
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_ADSPAO((PADSP_ADDROBJ)pObject));
|
|
AtalkDdpQuery(AtalkAdspGetDdpAddress((PADSP_ADDROBJ)pObject),
|
|
pAmdl,
|
|
BytesWritten);
|
|
|
|
break;
|
|
|
|
case TDI_CONNECTION_FILE :
|
|
{
|
|
KIRQL OldIrql;
|
|
PADSP_CONNOBJ pAdspConn;
|
|
|
|
pAdspConn = (PADSP_CONNOBJ)pObject;
|
|
ASSERT(VALID_ADSPCO(pAdspConn));
|
|
|
|
*BytesWritten = 0;
|
|
// Get the address from the associated address if any.
|
|
ACQUIRE_SPIN_LOCK(&pAdspConn->adspco_Lock, &OldIrql);
|
|
if (pAdspConn->adspco_Flags & ADSPCO_ASSOCIATED)
|
|
{
|
|
AtalkDdpQuery(AtalkAdspGetDdpAddress(pAdspConn->adspco_pAssocAddr),
|
|
pAmdl,
|
|
BytesWritten);
|
|
}
|
|
RELEASE_SPIN_LOCK(&pAdspConn->adspco_Lock, OldIrql);
|
|
}
|
|
break;
|
|
|
|
case TDI_CONTROL_CHANNEL_FILE :
|
|
default:
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// ADSP PACKET IN (HANDLE ROUTINES)
|
|
//
|
|
|
|
VOID
|
|
atalkAdspPacketIn(
|
|
IN PPORT_DESCRIPTOR pPortDesc,
|
|
IN PDDP_ADDROBJ pDdpAddr,
|
|
IN PBYTE pPkt,
|
|
IN USHORT PktLen,
|
|
IN PATALK_ADDR pSrcAddr,
|
|
IN PATALK_ADDR pDestAddr,
|
|
IN ATALK_ERROR ErrorCode,
|
|
IN BYTE DdpType,
|
|
IN PADSP_ADDROBJ pAdspAddr,
|
|
IN BOOLEAN OptimizedPath,
|
|
IN PVOID OptimizeCtx
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
ATALK_ERROR error;
|
|
PADSP_CONNOBJ pAdspConn;
|
|
USHORT remoteConnId;
|
|
ULONG remoteFirstByteSeq, remoteNextRecvSeq;
|
|
LONG remoteRecvWindow;
|
|
BYTE descriptor, controlCode;
|
|
BOOLEAN DerefConn = FALSE;
|
|
|
|
do
|
|
{
|
|
if ((!ATALK_SUCCESS(ErrorCode)) ||
|
|
(DdpType != DDPPROTO_ADSP) ||
|
|
(PktLen < ADSP_DATA_OFF))
|
|
{
|
|
ASSERT(0);
|
|
break;
|
|
}
|
|
|
|
// Decode the header.
|
|
atalkAdspDecodeHeader(pPkt,
|
|
&remoteConnId,
|
|
&remoteFirstByteSeq,
|
|
&remoteNextRecvSeq,
|
|
&remoteRecvWindow,
|
|
&descriptor);
|
|
|
|
controlCode = (descriptor & ADSP_CONTROL_MASK);
|
|
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspPacketIn: Recd packet %lx.%x\n", remoteConnId, descriptor));
|
|
|
|
|
|
// If this is a open connection request we handle it in here,
|
|
// else we find the connection it is meant for and pass it on.
|
|
if ((descriptor & ADSP_CONTROL_FLAG) &&
|
|
(controlCode == ADSP_OPENCONN_REQ_CODE))
|
|
{
|
|
// Handle the open connection.
|
|
if (PktLen < (ADSP_NEXT_ATTEN_SEQNUM_OFF + sizeof(ULONG)))
|
|
{
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_ERR,
|
|
("atalkAdspPacketIn: Incorrect len for pkt\n"));
|
|
|
|
break;
|
|
}
|
|
|
|
atalkAdspHandleOpenReq(pAdspAddr,
|
|
pPkt,
|
|
PktLen,
|
|
pSrcAddr,
|
|
remoteConnId,
|
|
remoteFirstByteSeq,
|
|
remoteNextRecvSeq,
|
|
remoteRecvWindow,
|
|
descriptor);
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
if ((descriptor & ADSP_CONTROL_FLAG) &&
|
|
(controlCode > ADSP_OPENCONN_REQ_CODE) &&
|
|
(controlCode <= ADSP_OPENCONN_DENY_CODE))
|
|
{
|
|
// Handle the open connection.
|
|
if (PktLen < (ADSP_NEXT_ATTEN_SEQNUM_OFF + sizeof(ULONG)))
|
|
{
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_ERR,
|
|
("atalkAdspPacketIn: Incorrect len for pkt\n"));
|
|
break;
|
|
}
|
|
|
|
atalkAdspHandleOpenControl(pAdspAddr,
|
|
pPkt,
|
|
PktLen,
|
|
pSrcAddr,
|
|
remoteConnId,
|
|
remoteFirstByteSeq,
|
|
remoteNextRecvSeq,
|
|
remoteRecvWindow,
|
|
descriptor);
|
|
|
|
break;
|
|
}
|
|
|
|
// This was not an open connection request, find the connection
|
|
// this is meant for.
|
|
ACQUIRE_SPIN_LOCK_DPC(&pAdspAddr->adspao_Lock);
|
|
atalkAdspConnRefBySrcAddr(pAdspAddr,
|
|
pSrcAddr,
|
|
remoteConnId,
|
|
&pAdspConn,
|
|
&error);
|
|
RELEASE_SPIN_LOCK_DPC(&pAdspAddr->adspao_Lock);
|
|
|
|
if (!ATALK_SUCCESS(error))
|
|
{
|
|
// Not one of our active/half open connections.
|
|
break;
|
|
}
|
|
|
|
DerefConn = TRUE;
|
|
pAdspConn->adspco_LastContactTime = AtalkGetCurrentTick();
|
|
|
|
if (descriptor & ADSP_ATTEN_FLAG)
|
|
{
|
|
// Handle attention packets
|
|
atalkAdspHandleAttn(pAdspConn,
|
|
pPkt,
|
|
PktLen,
|
|
pSrcAddr,
|
|
remoteFirstByteSeq,
|
|
remoteNextRecvSeq,
|
|
remoteRecvWindow,
|
|
descriptor);
|
|
break;
|
|
}
|
|
|
|
// Check if we got a piggybacked ack. This will call the
|
|
// send possible handler too if the send window opens up.
|
|
// It will also change the send sequence number.
|
|
atalkAdspHandlePiggyBackAck(pAdspConn,
|
|
remoteNextRecvSeq,
|
|
remoteRecvWindow);
|
|
|
|
if (descriptor & ADSP_CONTROL_FLAG)
|
|
{
|
|
// Handle the other control packets
|
|
atalkAdspHandleControl(pAdspConn,
|
|
pPkt,
|
|
PktLen,
|
|
pSrcAddr,
|
|
remoteFirstByteSeq,
|
|
remoteNextRecvSeq,
|
|
remoteRecvWindow,
|
|
descriptor);
|
|
|
|
break;
|
|
}
|
|
|
|
// If we got something that didnt fit any of the above, we might
|
|
// have some data.
|
|
atalkAdspHandleData(pAdspConn,
|
|
pPkt,
|
|
PktLen,
|
|
pSrcAddr,
|
|
remoteFirstByteSeq,
|
|
remoteNextRecvSeq,
|
|
remoteRecvWindow,
|
|
descriptor);
|
|
} while (FALSE);
|
|
|
|
if (DerefConn)
|
|
{
|
|
AtalkAdspConnDereference(pAdspConn);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
LOCAL VOID
|
|
atalkAdspHandleOpenControl(
|
|
IN PADSP_ADDROBJ pAdspAddr,
|
|
IN PBYTE pPkt,
|
|
IN USHORT PktLen,
|
|
IN PATALK_ADDR pSrcAddr,
|
|
IN USHORT RemoteConnId,
|
|
IN ULONG RemoteFirstByteSeq,
|
|
IN ULONG RemoteNextRecvSeq,
|
|
IN ULONG RemoteRecvWindow,
|
|
IN BYTE Descriptor
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
!!!WE ONLY SUPPORT THE LISTENER PARADIGM FOR CONNECTION ESTABLISHMENT!!!
|
|
!!!A OpenConnectionRequest will always open a new connection! Remote !!!
|
|
!!!MUST send a Open Connection Request & Acknowledgement !!!
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PADSP_CONNOBJ pAdspConn;
|
|
BYTE controlCode;
|
|
USHORT adspVersionStamp, destConnId;
|
|
KIRQL OldIrql;
|
|
ULONG recvAttnSeq;
|
|
PADSP_OPEN_REQ pOpenReq = NULL;
|
|
ATALK_ERROR error = ATALK_NO_ERROR;
|
|
GENERIC_COMPLETION completionRoutine = NULL;
|
|
PVOID completionCtx = NULL;
|
|
BOOLEAN sendAck = FALSE,
|
|
openTimerCancelled = FALSE,
|
|
relAddrLock = FALSE;
|
|
PTDI_IND_SEND_POSSIBLE sendPossibleHandler = NULL;
|
|
PVOID sendPossibleHandlerCtx;
|
|
|
|
controlCode = (Descriptor & ADSP_CONTROL_MASK);
|
|
|
|
ASSERT(controlCode != ADSP_OPENCONN_REQ_CODE);
|
|
|
|
// Get the other information from the adsp header
|
|
GETSHORT2SHORT(&adspVersionStamp,
|
|
pPkt + ADSP_VERSION_STAMP_OFF);
|
|
|
|
GETSHORT2SHORT(&destConnId,
|
|
pPkt + ADSP_DEST_CONNID_OFF);
|
|
|
|
GETDWORD2DWORD(&recvAttnSeq,
|
|
pPkt + ADSP_NEXT_ATTEN_SEQNUM_OFF);
|
|
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspHandleOpenControl: OpenControl %lx.%lx.%lx.%lx.%lx\n",
|
|
RemoteConnId, RemoteFirstByteSeq, RemoteNextRecvSeq, RemoteRecvWindow, recvAttnSeq));
|
|
|
|
// Drop request if version isnt right.
|
|
if (adspVersionStamp != ADSP_VERSION)
|
|
{
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_ERR,
|
|
("atalkAdspPacketIn: Version incorrect\n"));
|
|
|
|
return;
|
|
}
|
|
|
|
// Find the connection, since this could be a deny, we cant
|
|
// use the remote values as they would not be valid. The
|
|
// connection should be in the connecting list for a reqandack/deny.
|
|
// For ack the remote values should be valid and the
|
|
// connection will be in the active list with the flags indicating
|
|
// that it is only half open.
|
|
|
|
ACQUIRE_SPIN_LOCK(&pAdspAddr->adspao_Lock, &OldIrql);
|
|
relAddrLock = TRUE;
|
|
|
|
if (controlCode == ADSP_OPENCONN_ACK_CODE)
|
|
{
|
|
// The connection will be in the active list.
|
|
atalkAdspConnRefBySrcAddr(pAdspAddr,
|
|
pSrcAddr,
|
|
RemoteConnId,
|
|
&pAdspConn,
|
|
&error);
|
|
}
|
|
else
|
|
{
|
|
atalkAdspConnFindInConnect(pAdspAddr,
|
|
destConnId,
|
|
pSrcAddr,
|
|
&pAdspConn,
|
|
&error);
|
|
}
|
|
|
|
if (ATALK_SUCCESS(error))
|
|
{
|
|
ACQUIRE_SPIN_LOCK_DPC(&pAdspConn->adspco_Lock);
|
|
|
|
switch (controlCode)
|
|
{
|
|
case ADSP_OPENCONN_DENY_CODE:
|
|
|
|
// Cancel open timer if this was a CONNECTING connection. If
|
|
// we had send out a ACK&REQ and then received a DENY just drop
|
|
// this and let the connection age out.
|
|
if ((pAdspConn->adspco_Flags & ADSPCO_CONNECTING) &&
|
|
((pAdspConn->adspco_Flags & ADSPCO_DISCONNECTING) == 0))
|
|
{
|
|
ASSERT(pAdspConn->adspco_Flags & ADSPCO_OPEN_TIMER);
|
|
|
|
// Turn of the connecting flag as we are completing the request.
|
|
// If OpenTimer calls disconnect, then we wont end up trying to
|
|
// complete the request twice.
|
|
pAdspConn->adspco_Flags &= ~ADSPCO_CONNECTING;
|
|
openTimerCancelled = AtalkTimerCancelEvent(&pAdspConn->adspco_OpenTimer,
|
|
NULL);
|
|
|
|
// Connection Denied.
|
|
atalkAdspConnDeQueueConnectList(pAdspAddr, pAdspConn);
|
|
completionRoutine = pAdspConn->adspco_ConnectCompletion;
|
|
completionCtx = pAdspConn->adspco_ConnectCtx;
|
|
error = ATALK_ADSP_SERVER_BUSY;
|
|
}
|
|
|
|
break;
|
|
|
|
case ADSP_OPENCONN_REQANDACK_CODE:
|
|
|
|
// Connection Request Accepted By Remote. If we are disconnecting
|
|
// drop this.
|
|
if ((pAdspConn->adspco_Flags & (ADSPCO_SEEN_REMOTE_OPEN |
|
|
ADSPCO_DISCONNECTING)) == 0)
|
|
{
|
|
ULONG index;
|
|
|
|
// If the connecting connection has not already seen
|
|
// the remote open request, then get all the relevent
|
|
// remote info for the connection.
|
|
pAdspConn->adspco_Flags |= (ADSPCO_SEEN_REMOTE_OPEN |
|
|
ADSPCO_HALF_ACTIVE);
|
|
|
|
atalkAdspConnDeQueueConnectList(pAdspAddr, pAdspConn);
|
|
|
|
pAdspConn->adspco_RemoteConnId = RemoteConnId;
|
|
pAdspConn->adspco_RemoteAddr = *pSrcAddr;
|
|
pAdspConn->adspco_SendSeq = RemoteNextRecvSeq;
|
|
pAdspConn->adspco_FirstRtmtSeq = RemoteNextRecvSeq;
|
|
pAdspConn->adspco_RecvAttnSeq = recvAttnSeq;
|
|
pAdspConn->adspco_SendWindowSeq = RemoteNextRecvSeq +
|
|
RemoteRecvWindow -
|
|
(ULONG)1;
|
|
|
|
// Thread the connection object into addr lookup by session id.
|
|
index = HASH_ID_SRCADDR(RemoteConnId, pSrcAddr);
|
|
|
|
index %= ADSP_CONN_HASH_SIZE;
|
|
|
|
pAdspConn->adspco_pNextActive = pAdspAddr->adspao_pActiveHash[index];
|
|
pAdspAddr->adspao_pActiveHash[index] = pAdspConn;
|
|
}
|
|
else
|
|
{
|
|
// We've already seen the remote request.
|
|
break;
|
|
}
|
|
|
|
case ADSP_OPENCONN_ACK_CODE:
|
|
|
|
// Ensure we are not closing, so we can reference properly. Drop
|
|
// if we are disconnecting.
|
|
if ((pAdspConn->adspco_Flags & ADSPCO_HALF_ACTIVE) &&
|
|
((pAdspConn->adspco_Flags & ( ADSPCO_DISCONNECTING |
|
|
ADSPCO_STOPPING |
|
|
ADSPCO_CLOSING)) == 0))
|
|
{
|
|
// Cancel open timer
|
|
ASSERT(pAdspConn->adspco_Flags & ADSPCO_OPEN_TIMER);
|
|
openTimerCancelled = AtalkTimerCancelEvent(&pAdspConn->adspco_OpenTimer,
|
|
NULL);
|
|
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspHandleOpenControl: OpenTimer %d\n", openTimerCancelled));
|
|
|
|
pAdspConn->adspco_Flags &= ~(ADSPCO_HALF_ACTIVE |
|
|
ADSPCO_CONNECTING |
|
|
ADSPCO_LISTENING);
|
|
|
|
pAdspConn->adspco_Flags |= ADSPCO_ACTIVE;
|
|
|
|
// Prepare to say sends ok
|
|
sendPossibleHandler =
|
|
pAdspConn->adspco_pAssocAddr->adspao_SendPossibleHandler;
|
|
sendPossibleHandlerCtx =
|
|
pAdspConn->adspco_pAssocAddr->adspao_SendPossibleHandlerCtx;
|
|
|
|
// Get the completion routines
|
|
if (pAdspConn->adspco_Flags &
|
|
(ADSPCO_ACCEPT_IRP | ADSPCO_LISTEN_IRP))
|
|
{
|
|
atalkAdspAddrDeQueueOpenReq(pAdspAddr,
|
|
pAdspConn->adspco_RemoteConnId,
|
|
&pAdspConn->adspco_RemoteAddr,
|
|
&pOpenReq);
|
|
|
|
pAdspConn->adspco_Flags &= ~(ADSPCO_ACCEPT_IRP |
|
|
ADSPCO_LISTEN_IRP);
|
|
completionRoutine = pAdspConn->adspco_ListenCompletion;
|
|
completionCtx = pAdspConn->adspco_ListenCtx;
|
|
}
|
|
else
|
|
{
|
|
ASSERT(pAdspConn->adspco_pAssocAddr->adspao_Flags & ADSPAO_CONNECT);
|
|
|
|
completionRoutine = pAdspConn->adspco_ConnectCompletion;
|
|
completionCtx = pAdspConn->adspco_ConnectCtx;
|
|
}
|
|
|
|
// Start the probe and the retransmit timers
|
|
// Set the flags
|
|
pAdspConn->adspco_Flags |= (ADSPCO_CONN_TIMER | ADSPCO_RETRANSMIT_TIMER);
|
|
AtalkAdspConnReferenceByPtrNonInterlock(pAdspConn, 2, &error);
|
|
if (!ATALK_SUCCESS(error))
|
|
{
|
|
KeBugCheck(0);
|
|
}
|
|
AtalkTimerInitialize(&pAdspConn->adspco_ConnTimer,
|
|
atalkAdspConnMaintenanceTimer,
|
|
ADSP_PROBE_INTERVAL);
|
|
AtalkTimerScheduleEvent(&pAdspConn->adspco_ConnTimer);
|
|
|
|
AtalkTimerInitialize(&pAdspConn->adspco_RetransmitTimer,
|
|
atalkAdspRetransmitTimer,
|
|
ADSP_RETRANSMIT_INTERVAL);
|
|
AtalkTimerScheduleEvent(&pAdspConn->adspco_RetransmitTimer);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
KeBugCheck(0);
|
|
break;
|
|
}
|
|
|
|
RELEASE_SPIN_LOCK_DPC(&pAdspConn->adspco_Lock);
|
|
RELEASE_SPIN_LOCK(&pAdspAddr->adspao_Lock, OldIrql);
|
|
relAddrLock = FALSE;
|
|
|
|
// If a open request was dequeue free it now
|
|
if (pOpenReq != NULL)
|
|
{
|
|
AtalkFreeMemory(pOpenReq);
|
|
}
|
|
|
|
// Set last contact time. ConnMaintenanceTimer is in order of seconds.
|
|
pAdspConn->adspco_LastContactTime = AtalkGetCurrentTick();
|
|
|
|
if (controlCode == ADSP_OPENCONN_REQANDACK_CODE)
|
|
{
|
|
// If we received a req&ack
|
|
atalkAdspSendOpenControl(pAdspConn);
|
|
}
|
|
|
|
// Call connect routine
|
|
if (*completionRoutine != NULL)
|
|
{
|
|
(*completionRoutine)(error, completionCtx);
|
|
}
|
|
|
|
// Are sends ok?
|
|
if (*sendPossibleHandler != NULL)
|
|
{
|
|
(*sendPossibleHandler)(sendPossibleHandlerCtx,
|
|
pAdspConn->adspco_ConnCtx,
|
|
atalkAdspMaxSendSize(pAdspConn));
|
|
}
|
|
|
|
if (openTimerCancelled)
|
|
{
|
|
AtalkAdspConnDereference(pAdspConn);
|
|
}
|
|
|
|
AtalkAdspConnDereference(pAdspConn);
|
|
}
|
|
#if DBG
|
|
else
|
|
{
|
|
ASSERT(0);
|
|
}
|
|
#endif
|
|
|
|
if (relAddrLock)
|
|
{
|
|
RELEASE_SPIN_LOCK(&pAdspAddr->adspao_Lock, OldIrql);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
LOCAL VOID
|
|
atalkAdspHandleAttn(
|
|
IN PADSP_CONNOBJ pAdspConn,
|
|
IN PBYTE pPkt,
|
|
IN USHORT PktLen,
|
|
IN PATALK_ADDR pSrcAddr,
|
|
IN ULONG RemoteAttnSendSeq,
|
|
IN ULONG RemoteAttnRecvSeq,
|
|
IN ULONG RemoteRecvWindow,
|
|
IN BYTE Descriptor
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
BYTE controlCode;
|
|
KIRQL OldIrql;
|
|
PIRP exRecvIrp;
|
|
PTDI_IND_RECEIVE_EXPEDITED exRecvHandler;
|
|
PVOID exRecvHandlerCtx;
|
|
ULONG exIndicateFlags;
|
|
NTSTATUS ntStatus;
|
|
PBYTE exReadBuf;
|
|
ULONG bytesTaken;
|
|
USHORT exWriteBufLen;
|
|
PBYTE exWriteChBuf = NULL;
|
|
BOOLEAN freeBuf = FALSE,
|
|
timerCancelled = FALSE;
|
|
PAMDL exWriteBuf = NULL;
|
|
GENERIC_WRITE_COMPLETION exWriteCompletion = NULL;
|
|
PVOID exWriteCtx = NULL;
|
|
|
|
UNREFERENCED_PARAMETER(RemoteRecvWindow);
|
|
|
|
controlCode = (Descriptor & ADSP_CONTROL_MASK);
|
|
|
|
// Skip the adsp header
|
|
pPkt += ADSP_DATA_OFF;
|
|
PktLen -= ADSP_DATA_OFF;
|
|
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspHandleAttn: PktLen %d\n", PktLen));
|
|
|
|
// Drop if we are not active! Pkt must atleast contain
|
|
// attention code if it is not a control packet.
|
|
if (((pAdspConn->adspco_Flags & ADSPCO_ACTIVE) == 0) ||
|
|
(controlCode != 0) ||
|
|
(((Descriptor & ADSP_CONTROL_FLAG) == 0) &&
|
|
(PktLen < ADSP_MIN_ATTEN_PKT_SIZE)))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Allocate if we have some data, ie. we are not just an ack.
|
|
if ((Descriptor & ADSP_CONTROL_FLAG) == 0)
|
|
{
|
|
if ((exReadBuf = AtalkAllocMemory(PktLen)) == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
freeBuf = TRUE;
|
|
|
|
// Copy the attention code from wire-to-host format
|
|
GETSHORT2SHORT((PUSHORT)exReadBuf, pPkt);
|
|
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspHandleAttn: Recd Attn Code %lx\n", *(PUSHORT)exReadBuf));
|
|
|
|
// Copy the rest of the data
|
|
RtlCopyMemory(exReadBuf + sizeof(USHORT),
|
|
pPkt + sizeof(USHORT),
|
|
PktLen - sizeof(USHORT));
|
|
}
|
|
|
|
ACQUIRE_SPIN_LOCK(&pAdspConn->adspco_Lock, &OldIrql);
|
|
|
|
do
|
|
{
|
|
if (RemoteAttnRecvSeq == (pAdspConn->adspco_SendAttnSeq + 1))
|
|
{
|
|
// This implies an ack of our last attention
|
|
pAdspConn->adspco_SendAttnSeq += 1;
|
|
|
|
// Check if we are waiting for an attention ack.
|
|
if (pAdspConn->adspco_Flags & ADSPCO_EXSEND_IN_PROGRESS)
|
|
{
|
|
exWriteCompletion = pAdspConn->adspco_ExWriteCompletion;
|
|
exWriteCtx = pAdspConn->adspco_ExWriteCtx;
|
|
exWriteBuf = pAdspConn->adspco_ExWriteBuf;
|
|
exWriteBufLen = pAdspConn->adspco_ExWriteBufLen;
|
|
exWriteChBuf = pAdspConn->adspco_ExWriteChBuf;
|
|
|
|
timerCancelled = AtalkTimerCancelEvent(&pAdspConn->adspco_ExRetryTimer,
|
|
NULL);
|
|
|
|
pAdspConn->adspco_Flags &= ~ADSPCO_EXSEND_IN_PROGRESS;
|
|
}
|
|
}
|
|
|
|
if (RemoteAttnSendSeq != pAdspConn->adspco_RecvAttnSeq)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (Descriptor & ADSP_CONTROL_FLAG)
|
|
{
|
|
// Ack only, no data to handle
|
|
break;
|
|
}
|
|
|
|
// Get the expedited receive handler.
|
|
exRecvHandler = pAdspConn->adspco_pAssocAddr->adspao_ExpRecvHandler;
|
|
exRecvHandlerCtx = pAdspConn->adspco_pAssocAddr->adspao_ExpRecvHandlerCtx;
|
|
|
|
if (((pAdspConn->adspco_Flags & ADSPCO_ATTN_DATA_RECD) == 0) &&
|
|
(*exRecvHandler != NULL))
|
|
{
|
|
exIndicateFlags = TDI_RECEIVE_EXPEDITED |
|
|
TDI_RECEIVE_PARTIAL;
|
|
|
|
pAdspConn->adspco_Flags |= ADSPCO_ATTN_DATA_RECD;
|
|
if (Descriptor & ADSP_EOM_FLAG)
|
|
{
|
|
exIndicateFlags &= ~TDI_RECEIVE_PARTIAL;
|
|
exIndicateFlags |= TDI_RECEIVE_ENTIRE_MESSAGE;
|
|
pAdspConn->adspco_Flags |= ADSPCO_ATTN_DATA_EOM;
|
|
}
|
|
|
|
pAdspConn->adspco_ExRecdData = exReadBuf;
|
|
pAdspConn->adspco_ExRecdLen = PktLen;
|
|
freeBuf = FALSE;
|
|
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspHandleAttn: Indicating exp data %ld\n", PktLen));
|
|
|
|
RELEASE_SPIN_LOCK(&pAdspConn->adspco_Lock, OldIrql);
|
|
ntStatus = (*exRecvHandler)(exRecvHandlerCtx,
|
|
pAdspConn->adspco_ConnCtx,
|
|
exIndicateFlags,
|
|
PktLen,
|
|
PktLen,
|
|
&bytesTaken,
|
|
pPkt,
|
|
&exRecvIrp);
|
|
|
|
ASSERT((bytesTaken == 0) || (bytesTaken == PktLen));
|
|
if (ntStatus == STATUS_MORE_PROCESSING_REQUIRED)
|
|
{
|
|
if (exRecvIrp != NULL)
|
|
{
|
|
// Post the receive as if it came from the io system
|
|
ntStatus = AtalkDispatchInternalDeviceControl(
|
|
(PDEVICE_OBJECT)AtalkDeviceObject[ATALK_DEV_ADSP],
|
|
exRecvIrp);
|
|
|
|
ASSERT(ntStatus == STATUS_PENDING);
|
|
}
|
|
else
|
|
{
|
|
ASSERTMSG("atalkAdspReadComplete: No receive irp!\n", 0);
|
|
KeBugCheck(0);
|
|
}
|
|
ACQUIRE_SPIN_LOCK(&pAdspConn->adspco_Lock, &OldIrql);
|
|
}
|
|
else if (ntStatus == STATUS_SUCCESS)
|
|
{
|
|
ACQUIRE_SPIN_LOCK(&pAdspConn->adspco_Lock, &OldIrql);
|
|
if (bytesTaken != 0)
|
|
{
|
|
// Assume all of the data was read.
|
|
ASSERT(bytesTaken == PktLen);
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspHandleAttn: All bytes read %lx\n", bytesTaken));
|
|
|
|
// Attention has been accepted, we need to ack it.
|
|
// Since spinlock was released, recheck flag.
|
|
if (pAdspConn->adspco_Flags & ADSPCO_ATTN_DATA_RECD)
|
|
{
|
|
pAdspConn->adspco_Flags &= ~(ADSPCO_ATTN_DATA_RECD |
|
|
ADSPCO_ATTN_DATA_EOM);
|
|
freeBuf = TRUE;
|
|
}
|
|
|
|
// Send ack for the attention
|
|
atalkAdspSendControl(pAdspConn,
|
|
ADSP_CONTROL_FLAG + ADSP_ATTEN_FLAG);
|
|
}
|
|
}
|
|
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_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspHandleAttn: Indication status %lx\n", ntStatus));
|
|
|
|
ACQUIRE_SPIN_LOCK(&pAdspConn->adspco_Lock, &OldIrql);
|
|
}
|
|
}
|
|
|
|
if (pAdspConn->adspco_Flags & ADSPCO_ATTN_DATA_RECD)
|
|
{
|
|
atalkAdspRecvAttn(pAdspConn);
|
|
}
|
|
|
|
} while (FALSE);
|
|
RELEASE_SPIN_LOCK(&pAdspConn->adspco_Lock, OldIrql);
|
|
|
|
if (*exWriteCompletion != NULL)
|
|
{
|
|
if (exWriteChBuf != NULL)
|
|
{
|
|
AtalkFreeMemory(exWriteChBuf);
|
|
}
|
|
|
|
(*exWriteCompletion)(ATALK_NO_ERROR,
|
|
exWriteBuf,
|
|
exWriteBufLen,
|
|
exWriteCtx);
|
|
}
|
|
|
|
if (timerCancelled)
|
|
{
|
|
AtalkAdspConnDereference(pAdspConn);
|
|
}
|
|
|
|
if (freeBuf)
|
|
{
|
|
ASSERT(exReadBuf != NULL);
|
|
AtalkFreeMemory(exReadBuf);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
LOCAL VOID
|
|
atalkAdspHandlePiggyBackAck(
|
|
IN PADSP_CONNOBJ pAdspConn,
|
|
IN ULONG RemoteNextRecvSeq,
|
|
IN ULONG RemoteRecvWindow
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
ULONG newSendWindowSeq, sendSize, windowSize;
|
|
PTDI_IND_SEND_POSSIBLE sendPossibleHandler;
|
|
KIRQL OldIrql;
|
|
PVOID sendPossibleHandlerCtx;
|
|
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspHandlePiggyBackAck: Recd ack %lx - %lx.%lx\n",
|
|
pAdspConn, RemoteNextRecvSeq, RemoteRecvWindow));
|
|
|
|
ACQUIRE_SPIN_LOCK(&pAdspConn->adspco_Lock, &OldIrql);
|
|
if (UNSIGNED_BETWEEN_WITH_WRAP(pAdspConn->adspco_FirstRtmtSeq,
|
|
pAdspConn->adspco_SendSeq,
|
|
RemoteNextRecvSeq))
|
|
{
|
|
ULONG size;
|
|
|
|
// Discard acked data from the send queue
|
|
size = (ULONG)(RemoteNextRecvSeq - pAdspConn->adspco_FirstRtmtSeq);
|
|
pAdspConn->adspco_FirstRtmtSeq = RemoteNextRecvSeq;
|
|
|
|
atalkAdspDiscardFromBufferQueue(&pAdspConn->adspco_SendQueue,
|
|
size,
|
|
&pAdspConn->adspco_NextSendQueue,
|
|
ATALK_NO_ERROR,
|
|
pAdspConn);
|
|
}
|
|
|
|
|
|
// We almost always can use the header values to update the
|
|
// sendwindowseqnum
|
|
newSendWindowSeq = RemoteNextRecvSeq +
|
|
(ULONG)RemoteRecvWindow -
|
|
(ULONG)1;
|
|
|
|
if (UNSIGNED_GREATER_WITH_WRAP(newSendWindowSeq,
|
|
pAdspConn->adspco_SendWindowSeq))
|
|
{
|
|
pAdspConn->adspco_SendWindowSeq = newSendWindowSeq;
|
|
}
|
|
|
|
if (!IsListEmpty(&pAdspConn->adspco_PendedSends))
|
|
{
|
|
AtalkAdspProcessQueuedSend(pAdspConn);
|
|
}
|
|
|
|
sendPossibleHandler =
|
|
pAdspConn->adspco_pAssocAddr->adspao_SendPossibleHandler;
|
|
sendPossibleHandlerCtx =
|
|
pAdspConn->adspco_pAssocAddr->adspao_SendPossibleHandlerCtx;
|
|
|
|
// Call sendok handler for the size of the connection if non-zero
|
|
windowSize = (LONG)(pAdspConn->adspco_SendWindowSeq -
|
|
pAdspConn->adspco_SendSeq +
|
|
(LONG)1);
|
|
|
|
sendSize = MIN(atalkAdspMaxSendSize(pAdspConn), windowSize);
|
|
|
|
if ((sendSize != 0) &&
|
|
IsListEmpty(&pAdspConn->adspco_PendedSends) &&
|
|
(pAdspConn->adspco_Flags & ADSPCO_SEND_WINDOW_CLOSED) &&
|
|
(*sendPossibleHandler != NULL))
|
|
{
|
|
(*sendPossibleHandler)(sendPossibleHandlerCtx,
|
|
pAdspConn->adspco_ConnCtx,
|
|
sendSize);
|
|
|
|
pAdspConn->adspco_Flags &= ~ADSPCO_SEND_WINDOW_CLOSED;
|
|
}
|
|
|
|
RELEASE_SPIN_LOCK(&pAdspConn->adspco_Lock, OldIrql);
|
|
}
|
|
|
|
|
|
|
|
|
|
LOCAL VOID
|
|
atalkAdspHandleControl(
|
|
IN PADSP_CONNOBJ pAdspConn,
|
|
IN PBYTE pPkt,
|
|
IN USHORT PktLen,
|
|
IN PATALK_ADDR pSrcAddr,
|
|
IN ULONG RemoteFirstByteSeq,
|
|
IN ULONG RemoteNextRecvSeq,
|
|
IN ULONG RemoteRecvWindow,
|
|
IN BYTE Descriptor
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
BYTE controlCode;
|
|
KIRQL OldIrql;
|
|
ATALK_ERROR Error;
|
|
|
|
// The ack request flag can be set in any control packet. Send
|
|
// an immediately. We will also send any data if possible.
|
|
if (Descriptor & ADSP_ACK_REQ_FLAG)
|
|
{
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspHandleControl: Recd ackreq for %lx\n", pAdspConn));
|
|
|
|
ACQUIRE_SPIN_LOCK(&pAdspConn->adspco_Lock, &OldIrql);
|
|
atalkAdspSendData(pAdspConn);
|
|
RELEASE_SPIN_LOCK(&pAdspConn->adspco_Lock, OldIrql);
|
|
}
|
|
|
|
controlCode = (Descriptor & ADSP_CONTROL_MASK);
|
|
switch (controlCode)
|
|
{
|
|
case ADSP_PROBE_OR_ACK_CODE:
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspHandleControl: Recd probe for %lx\n", pAdspConn));
|
|
|
|
// A PROBE has its ACKRequest flag set, so we would have handled
|
|
// that above. Also, we've already set the lastContactTime in the
|
|
// packet in routine. So if this is an ack we've handled it.
|
|
// Check to see if some data was acked and if we have data to send.
|
|
ACQUIRE_SPIN_LOCK(&pAdspConn->adspco_Lock, &OldIrql);
|
|
if (!(Descriptor & ADSP_ACK_REQ_FLAG) &&
|
|
(atalkAdspBufferQueueSize(&pAdspConn->adspco_NextSendQueue) != 0) &&
|
|
(pAdspConn->adspco_SendSeq != (pAdspConn->adspco_SendWindowSeq + 1)))
|
|
{
|
|
atalkAdspSendData(pAdspConn);
|
|
}
|
|
RELEASE_SPIN_LOCK(&pAdspConn->adspco_Lock, OldIrql);
|
|
break;
|
|
|
|
case ADSP_CLOSE_CONN_CODE:
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspHandleControl: Recd CLOSE for %lx\n", pAdspConn));
|
|
|
|
AtalkAdspConnReferenceByPtr(pAdspConn, &Error);
|
|
if (ATALK_SUCCESS(Error))
|
|
{
|
|
AtalkTimerScheduleEvent(&pAdspConn->adspco_DisconnectTimer);
|
|
}
|
|
else
|
|
{
|
|
AtalkAdspDisconnect(pAdspConn,
|
|
ATALK_REMOTE_DISCONNECT,
|
|
NULL,
|
|
NULL);
|
|
}
|
|
break;
|
|
|
|
case ADSP_FORWARD_RESET_CODE:
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_ERR,
|
|
("atalkAdspHandleControl: Recd FWDRESET for %lx\n", pAdspConn));
|
|
|
|
pAdspConn->adspco_Flags |= ADSPCO_FORWARD_RESET_RECD;
|
|
AtalkAdspDisconnect(pAdspConn,
|
|
ATALK_LOCAL_DISCONNECT,
|
|
NULL,
|
|
NULL);
|
|
break;
|
|
|
|
case ADSP_FORWARD_RESETACK_CODE:
|
|
// We never send forward resets (interface not exposed), so
|
|
// we should never be getting these. Drop if we do.
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_ERR,
|
|
("atalkAdspHandleControl: Recd ForwardReset ACK!!\n"));
|
|
break;
|
|
|
|
case ADSP_RETRANSMIT_CODE:
|
|
// Any acks should have been processed by now. Back up and
|
|
// do a retransmit by rewinding sequence number.
|
|
ACQUIRE_SPIN_LOCK(&pAdspConn->adspco_Lock, &OldIrql);
|
|
if (UNSIGNED_BETWEEN_WITH_WRAP(pAdspConn->adspco_FirstRtmtSeq,
|
|
pAdspConn->adspco_SendSeq,
|
|
RemoteNextRecvSeq))
|
|
{
|
|
pAdspConn->adspco_SendSeq = pAdspConn->adspco_FirstRtmtSeq;
|
|
pAdspConn->adspco_NextSendQueue = pAdspConn->adspco_SendQueue;
|
|
atalkAdspSendData(pAdspConn);
|
|
}
|
|
RELEASE_SPIN_LOCK(&pAdspConn->adspco_Lock, OldIrql);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
LOCAL VOID
|
|
atalkAdspHandleData(
|
|
IN PADSP_CONNOBJ pAdspConn,
|
|
IN PBYTE pPkt,
|
|
IN USHORT PktLen,
|
|
IN PATALK_ADDR pSrcAddr,
|
|
IN ULONG RemoteFirstByteSeq,
|
|
IN ULONG RemoteNextRecvSeq,
|
|
IN ULONG RemoteRecvWindow,
|
|
IN BYTE Descriptor
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN eom, tdiEom;
|
|
PBUFFER_CHUNK pBufferChunk;
|
|
KIRQL OldIrql;
|
|
ULONG dataSize;
|
|
BOOLEAN freeChunk = FALSE,
|
|
sendAck = (Descriptor & ADSP_ACK_REQ_FLAG);
|
|
|
|
eom = (Descriptor & ADSP_EOM_FLAG) ? TRUE : FALSE;
|
|
dataSize = PktLen - ADSP_DATA_OFF;
|
|
|
|
ACQUIRE_SPIN_LOCK(&pAdspConn->adspco_Lock, &OldIrql);
|
|
|
|
do
|
|
{
|
|
// Drop if we are not active! And if there is no data
|
|
if ((pAdspConn->adspco_Flags & ADSPCO_ACTIVE) == 0)
|
|
{
|
|
sendAck = FALSE;
|
|
break;
|
|
}
|
|
|
|
tdiEom = (eom && (pAdspConn->adspco_pAssocAddr->adspao_Flags & ADSPAO_MESSAGE));
|
|
|
|
// We can only access addr object when active.
|
|
if ((dataSize == 0) && !tdiEom)
|
|
{
|
|
// Increment seqnumbers and we have consumed this packet.
|
|
pAdspConn->adspco_RecvSeq += (ULONG)(BYTECOUNT(eom));
|
|
pAdspConn->adspco_RecvWindow -= (LONG)(BYTECOUNT(eom));
|
|
break;
|
|
}
|
|
|
|
// Preallocate the buffer chunk
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("Recd Data %d Eom %d\n", dataSize, eom));
|
|
|
|
pBufferChunk = atalkAdspAllocCopyChunk(pPkt + ADSP_DATA_OFF,
|
|
(USHORT)dataSize,
|
|
tdiEom,
|
|
TRUE);
|
|
if (pBufferChunk == NULL)
|
|
break;
|
|
|
|
freeChunk = TRUE;
|
|
|
|
if (RemoteFirstByteSeq != pAdspConn->adspco_RecvSeq)
|
|
{
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_WARN,
|
|
("atalkAdspHandleData: Dropping out of sequence adsp packet\n"));
|
|
|
|
if ((pAdspConn->adspco_OutOfSeqCount += 1) >= ADSP_OUT_OF_SEQ_PACKETS_MAX)
|
|
{
|
|
atalkAdspSendControl(pAdspConn,
|
|
ADSP_CONTROL_FLAG + ADSP_RETRANSMIT_CODE);
|
|
|
|
pAdspConn->adspco_OutOfSeqCount = 0;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
// Handle a > receive window packet
|
|
if ((dataSize + BYTECOUNT(eom)) > (ULONG)pAdspConn->adspco_RecvWindow)
|
|
{
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspHandleData: Recd > window data %d.%ld\n",
|
|
dataSize, pAdspConn->adspco_RecvWindow));
|
|
|
|
break;
|
|
}
|
|
|
|
// Accept the data
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspHandleData: accepting data adsp packet %d\n", dataSize));
|
|
|
|
atalkAdspAddToBufferQueue(&pAdspConn->adspco_RecvQueue,
|
|
pBufferChunk,
|
|
NULL);
|
|
|
|
// Put it in the queue successfully
|
|
freeChunk = FALSE;
|
|
|
|
// Update the receive sequence numbers
|
|
pAdspConn->adspco_RecvSeq += (ULONG)(dataSize + BYTECOUNT(eom));
|
|
pAdspConn->adspco_RecvWindow -= (LONG)(dataSize + BYTECOUNT(eom));
|
|
|
|
// The receive windows should never go below zero! If it does, we could have
|
|
// memory overruns.
|
|
ASSERT(pAdspConn->adspco_RecvWindow >= 0);
|
|
if (pAdspConn->adspco_RecvWindow < 0)
|
|
{
|
|
KeBugCheck(0);
|
|
}
|
|
|
|
// Do indications/handle pending receives etc.
|
|
atalkAdspRecvData(pAdspConn);
|
|
|
|
} while (FALSE);
|
|
|
|
// ACK if requested, and send any data at the same time too.
|
|
if (sendAck)
|
|
{
|
|
atalkAdspSendData(pAdspConn);
|
|
}
|
|
|
|
RELEASE_SPIN_LOCK(&pAdspConn->adspco_Lock, OldIrql);
|
|
|
|
if (freeChunk)
|
|
{
|
|
ASSERT(pBufferChunk != NULL);
|
|
AtalkFreeMemory(pBufferChunk);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// ADSP SUPPORT ROUTINES
|
|
//
|
|
|
|
|
|
#define SLS_OPEN_CONN_REF 0x0008
|
|
#define SLS_ACCEPT_IRP 0x0010
|
|
#define SLS_CONN_TIMER_REF 0x0040
|
|
#define SLS_LISTEN_DEQUEUED 0x0080
|
|
|
|
LOCAL VOID
|
|
atalkAdspHandleOpenReq(
|
|
IN PADSP_ADDROBJ pAdspAddr,
|
|
IN PBYTE pPkt,
|
|
IN USHORT PktLen,
|
|
IN PATALK_ADDR pSrcAddr,
|
|
IN USHORT RemoteConnId,
|
|
IN ULONG RemoteFirstByteSeq,
|
|
IN ULONG RemoteNextRecvSeq,
|
|
IN ULONG RemoteRecvWindow,
|
|
IN BYTE Descriptor
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PADSP_CONNOBJ pAdspConn;
|
|
|
|
PTDI_IND_SEND_POSSIBLE sendPossibleHandler;
|
|
PVOID sendPossibleHandlerCtx;
|
|
|
|
USHORT adspVersionStamp, destConnId, localConnId;
|
|
ULONG recvAttnSeq;
|
|
ULONG index;
|
|
|
|
BOOLEAN DerefConn = FALSE;
|
|
PADSP_OPEN_REQ pOpenReq = NULL;
|
|
USHORT openResr = 0;
|
|
KIRQL OldIrql;
|
|
ATALK_ERROR error = ATALK_NO_ERROR;
|
|
|
|
// Are there any listening connections? Or do we have a
|
|
// set handler?
|
|
ACQUIRE_SPIN_LOCK(&pAdspAddr->adspao_Lock, &OldIrql);
|
|
do
|
|
{
|
|
// Get the other information from the adsp header
|
|
GETSHORT2SHORT(&adspVersionStamp, pPkt + ADSP_VERSION_STAMP_OFF);
|
|
|
|
GETSHORT2SHORT(&destConnId, pPkt + ADSP_DEST_CONNID_OFF);
|
|
|
|
GETDWORD2DWORD(&recvAttnSeq, pPkt + ADSP_NEXT_ATTEN_SEQNUM_OFF);
|
|
|
|
// Drop request if version isnt right.
|
|
if (adspVersionStamp != ADSP_VERSION)
|
|
{
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_ERR,
|
|
("atalkAdspPacketIn: Version incorrect\n"));
|
|
|
|
error = ATALK_INVALID_REQUEST;
|
|
break;
|
|
}
|
|
|
|
// Is this a duplicate request - same remote address/id?
|
|
if (atalkAdspIsDuplicateOpenReq(pAdspAddr,
|
|
RemoteConnId,
|
|
pSrcAddr))
|
|
{
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_ERR,
|
|
("atalkAdspPacketIn: Duplicate open req\n"));
|
|
|
|
error = ATALK_INVALID_REQUEST;
|
|
break;
|
|
}
|
|
|
|
// Allocate the open request structure. Do it here to avoid
|
|
// sending in a whole lot of parameters.
|
|
if ((pOpenReq = (PADSP_OPEN_REQ)AtalkAllocMemory(sizeof(ADSP_OPEN_REQ))) == NULL)
|
|
{
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_ERR,
|
|
("atalkAdspPacketIn: Could not alloc\n"));
|
|
|
|
error = ATALK_RESR_MEM;
|
|
RES_LOG_ERROR();
|
|
break;
|
|
}
|
|
|
|
// Initialize the structure. This will be queued into the address
|
|
// object by listenindicate if successful.
|
|
pOpenReq->or_Next = NULL;
|
|
pOpenReq->or_RemoteAddr = *pSrcAddr;
|
|
pOpenReq->or_RemoteConnId = RemoteConnId;
|
|
pOpenReq->or_FirstByteSeq = RemoteFirstByteSeq;
|
|
pOpenReq->or_NextRecvSeq = RemoteNextRecvSeq;
|
|
pOpenReq->or_RecvWindow = RemoteRecvWindow;
|
|
|
|
localConnId = atalkAdspGetNextConnId(pAdspAddr, &error);
|
|
ASSERT(ATALK_SUCCESS(error));
|
|
|
|
if (ATALK_SUCCESS(error))
|
|
{
|
|
atalkAdspListenIndicateNonInterlock(pAdspAddr,
|
|
pOpenReq,
|
|
&pAdspConn,
|
|
&error);
|
|
}
|
|
|
|
} while (FALSE);
|
|
|
|
// If either the indication or listen didnt happen well,
|
|
// break out of the main while loop.
|
|
if (!ATALK_SUCCESS(error))
|
|
{
|
|
RELEASE_SPIN_LOCK(&pAdspAddr->adspao_Lock, OldIrql);
|
|
|
|
if (pOpenReq != NULL)
|
|
{
|
|
AtalkFreeMemory(pOpenReq);
|
|
}
|
|
return;
|
|
}
|
|
|
|
ASSERT(ATALK_SUCCESS(error));
|
|
|
|
// Common for both listen and indicate. The connection object
|
|
// should be referenced.
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspOpenReq: ConnId %lx Rem %lx.%lx.%lx\n",
|
|
pOpenReq->or_RemoteConnId,
|
|
pOpenReq->or_RemoteAddr.ata_Network,
|
|
pOpenReq->or_RemoteAddr.ata_Node,
|
|
pOpenReq->or_RemoteAddr.ata_Socket));
|
|
|
|
// Thread the connection object into addr lookup by session id.
|
|
index = HASH_ID_SRCADDR(pOpenReq->or_RemoteConnId,
|
|
&pOpenReq->or_RemoteAddr);
|
|
|
|
index %= ADSP_CONN_HASH_SIZE;
|
|
|
|
ACQUIRE_SPIN_LOCK_DPC(&pAdspConn->adspco_Lock);
|
|
|
|
pAdspConn->adspco_Flags &= ~ADSPCO_LISTENING;
|
|
pAdspConn->adspco_Flags |= (ADSPCO_HALF_ACTIVE |
|
|
ADSPCO_SEEN_REMOTE_OPEN |
|
|
ADSPCO_OPEN_TIMER);
|
|
|
|
pAdspConn->adspco_ConnectAttempts = ADSP_MAX_OPEN_ATTEMPTS;
|
|
|
|
// Store the information in the connection structure given by
|
|
// the connection object thats passed back in the indication
|
|
// or is part of the listen structure.
|
|
pAdspConn->adspco_RecvWindow=
|
|
pAdspConn->adspco_SendQueueMax =
|
|
pAdspConn->adspco_RecvQueueMax = ADSP_DEF_SEND_RX_WINDOW_SIZE;
|
|
|
|
// Store the remote information
|
|
pAdspConn->adspco_RemoteAddr = pOpenReq->or_RemoteAddr;
|
|
pAdspConn->adspco_RemoteConnId = pOpenReq->or_RemoteConnId;
|
|
pAdspConn->adspco_LocalConnId = localConnId;
|
|
|
|
pAdspConn->adspco_SendSeq = pOpenReq->or_FirstByteSeq;
|
|
pAdspConn->adspco_FirstRtmtSeq = pOpenReq->or_NextRecvSeq;
|
|
pAdspConn->adspco_SendWindowSeq = pOpenReq->or_NextRecvSeq +
|
|
pOpenReq->or_RecvWindow - 1;
|
|
|
|
pAdspConn->adspco_RecvAttnSeq = recvAttnSeq;
|
|
|
|
pAdspConn->adspco_pNextActive = pAdspAddr->adspao_pActiveHash[index];
|
|
pAdspAddr->adspao_pActiveHash[index] = pAdspConn;
|
|
|
|
// Remember the ddp socket.
|
|
pAdspConn->adspco_pDdpAddr = pAdspAddr->adspao_pDdpAddr;
|
|
|
|
// Initialize pended sends list
|
|
InitializeListHead(&pAdspConn->adspco_PendedSends);
|
|
|
|
// Call the send data event handler on the associated address with
|
|
// 0 to turn off selects on writes. We do this before we complete the
|
|
// accept.
|
|
sendPossibleHandler = pAdspAddr->adspao_SendPossibleHandler;
|
|
sendPossibleHandlerCtx = pAdspAddr->adspao_SendPossibleHandlerCtx;
|
|
|
|
// Start open timer. Reference is the reference
|
|
// at the beginning.
|
|
AtalkTimerInitialize(&pAdspConn->adspco_OpenTimer,
|
|
atalkAdspOpenTimer,
|
|
ADSP_OPEN_INTERVAL);
|
|
AtalkTimerScheduleEvent(&pAdspConn->adspco_OpenTimer);
|
|
|
|
RELEASE_SPIN_LOCK_DPC(&pAdspConn->adspco_Lock);
|
|
RELEASE_SPIN_LOCK(&pAdspAddr->adspao_Lock, OldIrql);
|
|
|
|
// Connection is all set up, send ack to remote and wait
|
|
// for its ack before switching state to active.
|
|
if (*sendPossibleHandler != NULL)
|
|
{
|
|
(*sendPossibleHandler)(sendPossibleHandlerCtx,
|
|
pAdspConn->adspco_ConnCtx,
|
|
0);
|
|
}
|
|
|
|
// Send the open control.
|
|
atalkAdspSendOpenControl(pAdspConn);
|
|
|
|
// Remove the reference on the connection added during
|
|
// indicate/listen if we did not start the open timer.
|
|
if (DerefConn)
|
|
{
|
|
AtalkAdspConnDereference(pAdspConn);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
LOCAL VOID
|
|
atalkAdspListenIndicateNonInterlock(
|
|
IN PADSP_ADDROBJ pAdspAddr,
|
|
IN PADSP_OPEN_REQ pOpenReq,
|
|
IN PADSP_CONNOBJ * ppAdspConn,
|
|
IN PATALK_ERROR pError
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
ATALK_ERROR error;
|
|
TA_APPLETALK_ADDRESS tdiAddr;
|
|
PTDI_IND_CONNECT indicationRoutine;
|
|
PVOID indicationCtx;
|
|
NTSTATUS status;
|
|
CONNECTION_CONTEXT ConnCtx;
|
|
PIRP acceptIrp;
|
|
PADSP_CONNOBJ pAdspConn;
|
|
ATALK_ADDR remoteAddr;
|
|
USHORT remoteConnId;
|
|
BOOLEAN indicate = TRUE;
|
|
|
|
// If no listens posted, no handler, drop the request.
|
|
error = ATALK_RESR_MEM;
|
|
|
|
// Queue in the open request to the address. Cant release the
|
|
// addrlock without doing this.
|
|
pOpenReq->or_Next = pAdspAddr->adspao_OpenReq;
|
|
pAdspAddr->adspao_OpenReq = pOpenReq;
|
|
|
|
pAdspConn = pAdspAddr->adspao_pListenConn;
|
|
remoteAddr = pOpenReq->or_RemoteAddr;
|
|
remoteConnId = pOpenReq->or_RemoteConnId;
|
|
if (pAdspConn != NULL)
|
|
{
|
|
ASSERT(VALID_ADSPCO(pAdspConn));
|
|
|
|
indicate = FALSE;
|
|
|
|
ACQUIRE_SPIN_LOCK_DPC(&pAdspConn->adspco_Lock);
|
|
|
|
// Ok, now its possible the connection object is already
|
|
// disconnecting/closing. Check for that, if so,
|
|
// drop this request
|
|
if (pAdspConn->adspco_Flags & ( ADSPCO_CLOSING |
|
|
ADSPCO_STOPPING |
|
|
ADSPCO_DISCONNECTING))
|
|
{
|
|
// dequeue open request, still first in list.
|
|
pAdspAddr->adspao_OpenReq = pAdspAddr->adspao_OpenReq->or_Next;
|
|
|
|
*pError = ATALK_INVALID_CONNECTION;
|
|
RELEASE_SPIN_LOCK_DPC(&pAdspConn->adspco_Lock);
|
|
return;
|
|
}
|
|
|
|
// There a connection with a pending listen. use it.
|
|
pAdspAddr->adspao_pListenConn = pAdspConn->adspco_pNextListen;
|
|
|
|
// Reference the connection object with a listen posted on it.
|
|
AtalkAdspConnReferenceByPtrNonInterlock(pAdspConn, 1, &error);
|
|
if (!ATALK_SUCCESS(error))
|
|
{
|
|
KeBugCheck(0);
|
|
}
|
|
|
|
// The listen request will also be completed when the
|
|
// ack is received.
|
|
pAdspConn->adspco_Flags |= ADSPCO_LISTEN_IRP;
|
|
|
|
RELEASE_SPIN_LOCK_DPC(&pAdspConn->adspco_Lock);
|
|
}
|
|
else if ((indicationRoutine = pAdspAddr->adspao_ConnHandler) != NULL)
|
|
{
|
|
indicationCtx = pAdspAddr->adspao_ConnHandlerCtx;
|
|
|
|
// Convert remote atalk address to tdi address
|
|
ATALKADDR_TO_TDI(&tdiAddr, &pOpenReq->or_RemoteAddr);
|
|
|
|
#if DBG
|
|
(&pAdspAddr->adspao_Lock)->FileLineLock |= 0x80000000;
|
|
#endif
|
|
RELEASE_SPIN_LOCK_DPC(&pAdspAddr->adspao_Lock);
|
|
status = (*indicationRoutine)(indicationCtx,
|
|
sizeof(tdiAddr),
|
|
(PVOID)&tdiAddr,
|
|
0, // User data length
|
|
NULL, // User data
|
|
0, // Option length
|
|
NULL, // Options
|
|
&ConnCtx,
|
|
&acceptIrp);
|
|
|
|
ACQUIRE_SPIN_LOCK_DPC(&pAdspAddr->adspao_Lock);
|
|
#if DBG
|
|
(&pAdspAddr->adspao_Lock)->FileLineLock &= ~0x80000000;
|
|
#endif
|
|
|
|
ASSERT(acceptIrp != NULL);
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspSlsHandler: indicate status %lx\n", status));
|
|
|
|
error = ATALK_RESR_MEM;
|
|
if (status == STATUS_MORE_PROCESSING_REQUIRED)
|
|
{
|
|
// Find the connection and accept the connection using that
|
|
// connection object.
|
|
|
|
AtalkAdspConnReferenceByCtxNonInterlock(pAdspAddr,
|
|
ConnCtx,
|
|
&pAdspConn,
|
|
&error);
|
|
|
|
if (!ATALK_SUCCESS(error))
|
|
{
|
|
// The connection object is closing, or is not found
|
|
// in our list. The accept irp must have had the same
|
|
// connection object. AFD isnt behaving well.
|
|
KeBugCheck(0);
|
|
}
|
|
|
|
if (acceptIrp != NULL)
|
|
{
|
|
// AFD re-uses connection objects. Make sure ths one is in
|
|
// the right state
|
|
pAdspConn->adspco_Flags &= ~(ADSPCO_LISTENING |
|
|
ADSPCO_CONNECTING |
|
|
ADSPCO_ACCEPT_IRP |
|
|
ADSPCO_LISTEN_IRP |
|
|
ADSPCO_ACTIVE |
|
|
ADSPCO_HALF_ACTIVE |
|
|
ADSPCO_SEEN_REMOTE_OPEN |
|
|
ADSPCO_DISCONNECTING |
|
|
ADSPCO_REMOTE_CLOSE |
|
|
ADSPCO_SEND_IN_PROGRESS |
|
|
ADSPCO_SEND_DENY |
|
|
ADSPCO_SEND_OPENACK |
|
|
ADSPCO_SEND_WINDOW_CLOSED |
|
|
ADSPCO_READ_PENDING |
|
|
ADSPCO_EXREAD_PENDING |
|
|
ADSPCO_FORWARD_RESET_RECD |
|
|
ADSPCO_ATTN_DATA_RECD |
|
|
ADSPCO_ATTN_DATA_EOM |
|
|
ADSPCO_EXSEND_IN_PROGRESS |
|
|
ADSPCO_OPEN_TIMER |
|
|
ADSPCO_RETRANSMIT_TIMER |
|
|
ADSPCO_CONN_TIMER);
|
|
|
|
|
|
|
|
pAdspConn->adspco_ListenCompletion = atalkAdspGenericComplete;
|
|
pAdspConn->adspco_ListenCtx = (PVOID)acceptIrp;
|
|
|
|
// This will be completed when we receive an ack
|
|
// for the open from the remote, i.e. both ends of the
|
|
// connection are open.
|
|
pAdspConn->adspco_Flags |= ADSPCO_ACCEPT_IRP;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ATALK_SUCCESS(*pError = error))
|
|
{
|
|
*ppAdspConn = pAdspConn;
|
|
}
|
|
else
|
|
{
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_ERR,
|
|
("atalkAdspListenIndicateNonInterlock: No listen %lx\n", status));
|
|
|
|
if (indicate)
|
|
{
|
|
// Dequeue the open request.
|
|
atalkAdspAddrDeQueueOpenReq(pAdspAddr,
|
|
remoteConnId,
|
|
&remoteAddr,
|
|
&pOpenReq);
|
|
}
|
|
|
|
#if DBG
|
|
(&pAdspAddr->adspao_Lock)->FileLineLock |= 0x80000000;
|
|
#endif
|
|
RELEASE_SPIN_LOCK_DPC(&pAdspAddr->adspao_Lock);
|
|
atalkAdspSendDeny(pAdspAddr,
|
|
&remoteAddr,
|
|
remoteConnId);
|
|
ACQUIRE_SPIN_LOCK_DPC(&pAdspAddr->adspao_Lock);
|
|
#if DBG
|
|
(&pAdspAddr->adspao_Lock)->FileLineLock &= ~0x80000000;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
ATALK_ERROR
|
|
atalkAdspSendExpedited(
|
|
IN PADSP_CONNOBJ pAdspConn,
|
|
IN PAMDL pWriteBuf,
|
|
IN USHORT WriteBufLen,
|
|
IN ULONG SendFlags,
|
|
IN PVOID pWriteCtx,
|
|
IN GENERIC_WRITE_COMPLETION CompletionRoutine
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
The first two bytes of the writebuffer will contain the ushort
|
|
attention code. We need to put this back in the on-the-wire format
|
|
before sending it out.
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
ATALK_ERROR error;
|
|
KIRQL OldIrql;
|
|
PBYTE pExWriteChBuf;
|
|
USHORT attnCode;
|
|
NTSTATUS status;
|
|
ULONG bytesCopied;
|
|
BOOLEAN DerefConn = FALSE;
|
|
|
|
if ((WriteBufLen < ADSP_MIN_ATTEN_PKT_SIZE) ||
|
|
(WriteBufLen > ADSP_MAX_ATTEN_PKT_SIZE))
|
|
{
|
|
return ATALK_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
if ((pExWriteChBuf = AtalkAllocMemory(WriteBufLen)) == NULL)
|
|
{
|
|
return ATALK_RESR_MEM;
|
|
}
|
|
|
|
status = TdiCopyMdlToBuffer((PMDL)pWriteBuf,
|
|
0,
|
|
pExWriteChBuf,
|
|
0,
|
|
WriteBufLen,
|
|
&bytesCopied);
|
|
|
|
ASSERT(!NT_ERROR(status) && (bytesCopied == (ULONG)WriteBufLen));
|
|
|
|
ACQUIRE_SPIN_LOCK(&pAdspConn->adspco_Lock, &OldIrql);
|
|
do
|
|
{
|
|
if (((pAdspConn->adspco_Flags & ADSPCO_ACTIVE) == 0) ||
|
|
((pAdspConn->adspco_Flags & (ADSPCO_CLOSING |
|
|
ADSPCO_STOPPING|
|
|
ADSPCO_DISCONNECTING))))
|
|
{
|
|
error = ATALK_ADSP_CONN_NOT_ACTIVE;
|
|
break;
|
|
}
|
|
|
|
if (pAdspConn->adspco_Flags & ADSPCO_EXSEND_IN_PROGRESS)
|
|
{
|
|
if (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_DEVICE_NOT_READY;
|
|
|
|
error = ATALK_REQUEST_NOT_ACCEPTED;
|
|
}
|
|
else
|
|
{
|
|
error = ATALK_TOO_MANY_COMMANDS;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
// Verify the attention code, this will a ushort in the first
|
|
// two bytes of the buffer, in host format.
|
|
attnCode = *(PUSHORT)pExWriteChBuf;
|
|
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspSendExpedited: attnCode %lx\n", attnCode));
|
|
|
|
if ((attnCode < ADSP_MIN_ATTENCODE) ||
|
|
(attnCode > ADSP_MAX_ATTENCODE))
|
|
{
|
|
error = ATALK_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
// Put it back in machine format
|
|
PUTSHORT2SHORT(pExWriteChBuf, attnCode);
|
|
|
|
// Try to reference for the attention retransmit timer
|
|
AtalkAdspConnReferenceByPtrNonInterlock(pAdspConn, 1, &error);
|
|
if (!ATALK_SUCCESS(error))
|
|
{
|
|
break;
|
|
}
|
|
|
|
DerefConn = TRUE;
|
|
|
|
// Remember all the information in the connection object
|
|
pAdspConn->adspco_ExWriteFlags = SendFlags;
|
|
pAdspConn->adspco_ExWriteBuf = pWriteBuf;
|
|
pAdspConn->adspco_ExWriteBufLen = WriteBufLen;
|
|
pAdspConn->adspco_ExWriteCompletion = CompletionRoutine;
|
|
pAdspConn->adspco_ExWriteCtx = pWriteCtx;
|
|
pAdspConn->adspco_ExWriteChBuf = pExWriteChBuf;
|
|
|
|
pAdspConn->adspco_Flags |= ADSPCO_EXSEND_IN_PROGRESS;
|
|
|
|
// Start the retry timer
|
|
AtalkTimerInitialize(&pAdspConn->adspco_ExRetryTimer,
|
|
atalkAdspAttnRetransmitTimer,
|
|
ADSP_ATTENTION_INTERVAL);
|
|
AtalkTimerScheduleEvent(&pAdspConn->adspco_ExRetryTimer);
|
|
|
|
error = ATALK_PENDING;
|
|
|
|
} while (FALSE);
|
|
RELEASE_SPIN_LOCK(&pAdspConn->adspco_Lock, OldIrql);
|
|
|
|
if (ATALK_SUCCESS(error))
|
|
{
|
|
atalkAdspSendAttn(pAdspConn);
|
|
error = ATALK_PENDING;
|
|
}
|
|
else
|
|
{
|
|
if (DerefConn)
|
|
{
|
|
AtalkAdspConnDereference(pAdspConn);
|
|
}
|
|
|
|
AtalkFreeMemory(pExWriteChBuf);
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
|
|
|
|
LOCAL VOID
|
|
atalkAdspSendOpenControl(
|
|
IN PADSP_CONNOBJ pAdspConn
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
ATALK_ERROR error;
|
|
PBUFFER_DESC pBuffDesc;
|
|
BYTE descriptor;
|
|
KIRQL OldIrql;
|
|
BOOLEAN DerefConn = FALSE;
|
|
USHORT remoteConnId = 0;
|
|
SEND_COMPL_INFO SendInfo;
|
|
|
|
descriptor = ADSP_CONTROL_FLAG;
|
|
if (pAdspConn->adspco_Flags & ADSPCO_SEND_DENY)
|
|
{
|
|
descriptor += ADSP_OPENCONN_DENY_CODE;
|
|
remoteConnId = pAdspConn->adspco_RemoteConnId;
|
|
}
|
|
else if (pAdspConn->adspco_Flags & ADSPCO_ACTIVE)
|
|
{
|
|
descriptor += ADSP_OPENCONN_ACK_CODE;
|
|
remoteConnId = pAdspConn->adspco_RemoteConnId;
|
|
}
|
|
else if (pAdspConn->adspco_Flags & ADSPCO_SEEN_REMOTE_OPEN)
|
|
{
|
|
descriptor += ADSP_OPENCONN_REQANDACK_CODE;
|
|
remoteConnId = pAdspConn->adspco_RemoteConnId;
|
|
}
|
|
else
|
|
{
|
|
descriptor += ADSP_OPENCONN_REQ_CODE;
|
|
}
|
|
|
|
// Allocate the datagram buffer
|
|
pBuffDesc = AtalkAllocBuffDesc(NULL,
|
|
ADSP_NEXT_ATTEN_SEQNUM_OFF + sizeof(ULONG),
|
|
BD_CHAR_BUFFER | BD_FREE_BUFFER);
|
|
|
|
if (pBuffDesc == NULL)
|
|
{
|
|
DBGPRINT(DBG_COMP_RTMP, DBG_LEVEL_ERR,
|
|
("AtalkAdspSendOpenControl: AtalkAllocBuffDesc failed\n"));
|
|
|
|
RES_LOG_ERROR();
|
|
return;
|
|
}
|
|
|
|
ACQUIRE_SPIN_LOCK(&pAdspConn->adspco_Lock, &OldIrql);
|
|
|
|
// Try to reference connection for this call.
|
|
AtalkAdspConnReferenceByPtrNonInterlock(pAdspConn, 1, &error);
|
|
if (ATALK_SUCCESS(error))
|
|
{
|
|
DerefConn = TRUE;
|
|
|
|
PUTSHORT2SHORT(pBuffDesc->bd_CharBuffer + ADSP_SRC_CONNID_OFF,
|
|
pAdspConn->adspco_LocalConnId);
|
|
|
|
PUTDWORD2DWORD(pBuffDesc->bd_CharBuffer + ADSP_FIRST_BYTE_SEQNUM_OFF,
|
|
pAdspConn->adspco_SendSeq);
|
|
|
|
PUTDWORD2DWORD(pBuffDesc->bd_CharBuffer + ADSP_NEXT_RX_BYTESEQNUM_OFF,
|
|
pAdspConn->adspco_RecvSeq);
|
|
|
|
PUTSHORT2SHORT(pBuffDesc->bd_CharBuffer + ADSP_RX_WINDOW_SIZE_OFF,
|
|
pAdspConn->adspco_RecvWindow);
|
|
|
|
// Set the descriptor
|
|
pBuffDesc->bd_CharBuffer[ADSP_DESCRIPTOR_OFF] = descriptor;
|
|
|
|
PUTSHORT2SHORT(pBuffDesc->bd_CharBuffer + ADSP_VERSION_STAMP_OFF,
|
|
ADSP_VERSION);
|
|
|
|
PUTSHORT2SHORT(pBuffDesc->bd_CharBuffer + ADSP_DEST_CONNID_OFF,
|
|
remoteConnId);
|
|
|
|
PUTDWORD2DWORD(pBuffDesc->bd_CharBuffer + ADSP_NEXT_ATTEN_SEQNUM_OFF,
|
|
pAdspConn->adspco_RecvAttnSeq);
|
|
}
|
|
RELEASE_SPIN_LOCK(&pAdspConn->adspco_Lock, OldIrql);
|
|
|
|
if (ATALK_SUCCESS(error))
|
|
{
|
|
// We let the completion routine Deref the conn.
|
|
DerefConn = FALSE;
|
|
|
|
SendInfo.sc_TransmitCompletion = atalkAdspConnSendComplete;
|
|
SendInfo.sc_Ctx1 = pAdspConn;
|
|
SendInfo.sc_Ctx2 = pBuffDesc;
|
|
// SendInfo.sc_Ctx3 = NULL;
|
|
if(!ATALK_SUCCESS(AtalkDdpSend(pAdspConn->adspco_pDdpAddr,
|
|
&pAdspConn->adspco_RemoteAddr,
|
|
DDPPROTO_ADSP,
|
|
FALSE,
|
|
pBuffDesc,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
&SendInfo)))
|
|
{
|
|
atalkAdspConnSendComplete(NDIS_STATUS_FAILURE, &SendInfo);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Free the buffer descriptor
|
|
AtalkFreeBuffDesc(pBuffDesc);
|
|
}
|
|
|
|
if (DerefConn)
|
|
{
|
|
AtalkAdspConnDereference(pAdspConn);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
LOCAL VOID
|
|
atalkAdspSendControl(
|
|
IN PADSP_CONNOBJ pAdspConn,
|
|
IN BYTE Descriptor
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
ATALK_ERROR error;
|
|
PBUFFER_DESC pBuffDesc;
|
|
ULONG sendSeq, recvSeq, recvWindow;
|
|
BOOLEAN DerefConn = FALSE;
|
|
SEND_COMPL_INFO SendInfo;
|
|
|
|
// Try to reference connection for this call.
|
|
AtalkAdspConnReferenceByPtrNonInterlock(pAdspConn, 1, &error);
|
|
if (ATALK_SUCCESS(error))
|
|
{
|
|
DerefConn = TRUE;
|
|
if ((Descriptor & ADSP_ATTEN_FLAG) == 0)
|
|
{
|
|
sendSeq = pAdspConn->adspco_SendSeq;
|
|
recvSeq = pAdspConn->adspco_RecvSeq;
|
|
recvWindow = pAdspConn->adspco_RecvWindow;
|
|
}
|
|
else
|
|
{
|
|
sendSeq = pAdspConn->adspco_SendAttnSeq;
|
|
recvSeq = pAdspConn->adspco_RecvAttnSeq;
|
|
recvWindow = 0;
|
|
}
|
|
|
|
// Allocate the datagram buffer
|
|
if ((pBuffDesc = AtalkAllocBuffDesc(NULL,
|
|
ADSP_DATA_OFF,
|
|
BD_CHAR_BUFFER | BD_FREE_BUFFER)) != NULL)
|
|
{
|
|
PUTSHORT2SHORT(pBuffDesc->bd_CharBuffer + ADSP_SRC_CONNID_OFF,
|
|
pAdspConn->adspco_LocalConnId);
|
|
|
|
PUTDWORD2DWORD(pBuffDesc->bd_CharBuffer + ADSP_FIRST_BYTE_SEQNUM_OFF,
|
|
sendSeq);
|
|
|
|
PUTDWORD2DWORD(pBuffDesc->bd_CharBuffer + ADSP_NEXT_RX_BYTESEQNUM_OFF,
|
|
recvSeq);
|
|
|
|
PUTSHORT2SHORT(pBuffDesc->bd_CharBuffer + ADSP_RX_WINDOW_SIZE_OFF,
|
|
recvWindow);
|
|
|
|
// Set the descriptor
|
|
pBuffDesc->bd_CharBuffer[ADSP_DESCRIPTOR_OFF] = Descriptor;
|
|
}
|
|
else
|
|
{
|
|
error = ATALK_RESR_MEM;
|
|
}
|
|
}
|
|
|
|
#if DBG
|
|
(&pAdspConn->adspco_Lock)->FileLineLock |= 0x80000000;
|
|
#endif
|
|
RELEASE_SPIN_LOCK_DPC(&pAdspConn->adspco_Lock);
|
|
|
|
if (ATALK_SUCCESS(error))
|
|
{
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("AtalkAdspSendControl: %lx.%lx\n", pAdspConn, Descriptor));
|
|
|
|
// We let the completion routine Deref the conn.
|
|
SendInfo.sc_TransmitCompletion = atalkAdspConnSendComplete;
|
|
SendInfo.sc_Ctx1 = pAdspConn;
|
|
SendInfo.sc_Ctx2 = pBuffDesc;
|
|
// SendInfo.sc_Ctx3 = NULL;
|
|
if (!ATALK_SUCCESS(AtalkDdpSend(pAdspConn->adspco_pDdpAddr,
|
|
&pAdspConn->adspco_RemoteAddr,
|
|
DDPPROTO_ADSP,
|
|
FALSE,
|
|
pBuffDesc,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
&SendInfo)))
|
|
{
|
|
atalkAdspConnSendComplete(NDIS_STATUS_FAILURE, &SendInfo);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (DerefConn)
|
|
{
|
|
AtalkAdspConnDereference(pAdspConn);
|
|
}
|
|
}
|
|
|
|
ACQUIRE_SPIN_LOCK_DPC(&pAdspConn->adspco_Lock);
|
|
#if DBG
|
|
(&pAdspConn->adspco_Lock)->FileLineLock &= ~0x80000000;
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
LOCAL VOID
|
|
atalkAdspSendDeny(
|
|
IN PADSP_ADDROBJ pAdspAddr,
|
|
IN PATALK_ADDR pRemoteAddr,
|
|
IN USHORT RemoteConnId
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
ATALK_ERROR error;
|
|
PBUFFER_DESC pBuffDesc;
|
|
SEND_COMPL_INFO SendInfo;
|
|
|
|
// Allocate the datagram buffer
|
|
if ((pBuffDesc = AtalkAllocBuffDesc(NULL,
|
|
ADSP_NEXT_ATTEN_SEQNUM_OFF + sizeof(ULONG),
|
|
BD_CHAR_BUFFER | BD_FREE_BUFFER)) == NULL)
|
|
{
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_ERR,
|
|
("AtalkAdspSendControl: AtalkAllocBuffDesc failed\n"));
|
|
|
|
RES_LOG_ERROR();
|
|
return;
|
|
}
|
|
|
|
// Try to reference address for this call.
|
|
AtalkAdspAddrReference(pAdspAddr, &error);
|
|
if (!ATALK_SUCCESS(error))
|
|
{
|
|
AtalkFreeBuffDesc(pBuffDesc);
|
|
return;
|
|
}
|
|
|
|
PUTSHORT2SHORT(pBuffDesc->bd_CharBuffer + ADSP_SRC_CONNID_OFF, 0);
|
|
|
|
PUTDWORD2DWORD(pBuffDesc->bd_CharBuffer + ADSP_FIRST_BYTE_SEQNUM_OFF, 0);
|
|
|
|
PUTDWORD2DWORD(pBuffDesc->bd_CharBuffer + ADSP_NEXT_RX_BYTESEQNUM_OFF, 0);
|
|
|
|
PUTSHORT2SHORT(pBuffDesc->bd_CharBuffer + ADSP_RX_WINDOW_SIZE_OFF, 0);
|
|
|
|
// Set the descriptor
|
|
pBuffDesc->bd_CharBuffer[ADSP_DESCRIPTOR_OFF] = ADSP_CONTROL_FLAG |
|
|
ADSP_OPENCONN_DENY_CODE;
|
|
|
|
PUTSHORT2SHORT(pBuffDesc->bd_CharBuffer + ADSP_VERSION_STAMP_OFF,
|
|
ADSP_VERSION);
|
|
|
|
PUTSHORT2SHORT(pBuffDesc->bd_CharBuffer + ADSP_DEST_CONNID_OFF,
|
|
RemoteConnId);
|
|
|
|
PUTDWORD2DWORD(pBuffDesc->bd_CharBuffer + ADSP_NEXT_ATTEN_SEQNUM_OFF,
|
|
0);
|
|
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("AtalkAdspSendDeny: %lx.%lx\n", pAdspAddr, pBuffDesc));
|
|
|
|
// We let the completion routine Deref the conn.
|
|
SendInfo.sc_TransmitCompletion = atalkAdspAddrSendComplete;
|
|
SendInfo.sc_Ctx1 = pAdspAddr;
|
|
SendInfo.sc_Ctx2 = pBuffDesc;
|
|
// SendInfo.sc_Ctx3 = NULL;
|
|
if(!ATALK_SUCCESS(AtalkDdpSend(AtalkAdspGetDdpAddress(pAdspAddr),
|
|
pRemoteAddr,
|
|
DDPPROTO_ADSP,
|
|
FALSE,
|
|
pBuffDesc,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
&SendInfo)))
|
|
{
|
|
atalkAdspAddrSendComplete(NDIS_STATUS_FAILURE, &SendInfo);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
LOCAL VOID
|
|
atalkAdspSendAttn(
|
|
IN PADSP_CONNOBJ pAdspConn
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
KIRQL OldIrql;
|
|
PBYTE adspHeader;
|
|
ATALK_ERROR error = ATALK_NO_ERROR;
|
|
PBUFFER_DESC pBuffDesc = NULL;
|
|
SEND_COMPL_INFO SendInfo;
|
|
|
|
do
|
|
{
|
|
pBuffDesc = AtalkAllocBuffDesc(NULL,
|
|
ADSP_DATA_OFF + ADSP_MAX_DATA_SIZE,
|
|
BD_CHAR_BUFFER | BD_FREE_BUFFER);
|
|
|
|
if (pBuffDesc == NULL)
|
|
{
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_ERR,
|
|
("AtalkAdspSendAttn: AtalkAllocBuffDesc failed\n"));
|
|
|
|
RES_LOG_ERROR();
|
|
break;
|
|
}
|
|
|
|
adspHeader = pBuffDesc->bd_CharBuffer;
|
|
|
|
ACQUIRE_SPIN_LOCK(&pAdspConn->adspco_Lock, &OldIrql);
|
|
if (pAdspConn->adspco_Flags & ADSPCO_EXSEND_IN_PROGRESS)
|
|
{
|
|
PUTSHORT2SHORT(adspHeader + ADSP_SRC_CONNID_OFF,
|
|
pAdspConn->adspco_LocalConnId);
|
|
|
|
PUTDWORD2DWORD(adspHeader + ADSP_THIS_ATTEN_SEQNUM_OFF,
|
|
pAdspConn->adspco_SendAttnSeq);
|
|
|
|
PUTDWORD2DWORD(adspHeader + ADSP_NEXT_RX_ATTNSEQNUM_OFF,
|
|
pAdspConn->adspco_RecvAttnSeq);
|
|
|
|
PUTSHORT2SHORT(adspHeader + ADSP_RX_ATTEN_SIZE_OFF, 0);
|
|
|
|
// Set the descriptor
|
|
adspHeader[ADSP_DESCRIPTOR_OFF] = ADSP_ATTEN_FLAG + ADSP_ACK_REQ_FLAG;
|
|
|
|
// Send eom?
|
|
if (((pAdspConn->adspco_ExWriteFlags & TDI_SEND_PARTIAL) == 0) &&
|
|
(pAdspConn->adspco_pAssocAddr->adspao_Flags & ADSPAO_MESSAGE))
|
|
{
|
|
adspHeader[ADSP_DESCRIPTOR_OFF] += ADSP_EOM_FLAG;
|
|
}
|
|
|
|
// Copy the attention data
|
|
RtlCopyMemory(&adspHeader[ADSP_DATA_OFF],
|
|
pAdspConn->adspco_ExWriteChBuf,
|
|
pAdspConn->adspco_ExWriteBufLen);
|
|
|
|
// Set the size in the buffer descriptor
|
|
AtalkSetSizeOfBuffDescData(pBuffDesc,
|
|
ADSP_DATA_OFF +
|
|
pAdspConn->adspco_ExWriteBufLen);
|
|
}
|
|
else
|
|
{
|
|
error = ATALK_FAILURE;
|
|
}
|
|
RELEASE_SPIN_LOCK(&pAdspConn->adspco_Lock, OldIrql);
|
|
|
|
if (ATALK_SUCCESS(error))
|
|
{
|
|
// Send the packet
|
|
SendInfo.sc_TransmitCompletion = atalkAdspSendAttnComplete;
|
|
SendInfo.sc_Ctx1 = pAdspConn;
|
|
SendInfo.sc_Ctx2 = pBuffDesc;
|
|
// SendInfo.sc_Ctx3 = NULL;
|
|
error = AtalkDdpSend(pAdspConn->adspco_pDdpAddr,
|
|
&pAdspConn->adspco_RemoteAddr,
|
|
(BYTE)DDPPROTO_ADSP,
|
|
FALSE,
|
|
pBuffDesc,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
&SendInfo);
|
|
|
|
if (!ATALK_SUCCESS(error))
|
|
{
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_ERR,
|
|
("AtalkAdspSendAttn: DdpSend failed %ld\n", error));
|
|
|
|
atalkAdspSendAttnComplete(NDIS_STATUS_FAILURE, &SendInfo);
|
|
}
|
|
|
|
error = ATALK_PENDING;
|
|
}
|
|
|
|
} while (FALSE);
|
|
|
|
if (!ATALK_SUCCESS(error) && (pBuffDesc != NULL))
|
|
{
|
|
AtalkFreeBuffDesc(pBuffDesc);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
LOCAL VOID
|
|
atalkAdspSendData(
|
|
IN PADSP_CONNOBJ pAdspConn
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
MUST BE ENTERED WITH CONNECTION LOCK HELD !!!
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
ATALK_ERROR error;
|
|
BYTE descriptor;
|
|
ULONG dataSize;
|
|
BOOLEAN eom;
|
|
BYTE adspHeader[ADSP_DATA_OFF];
|
|
LONG windowSize = 0;
|
|
PBUFFER_CHUNK pBufferChunk = NULL;
|
|
PBUFFER_DESC pBuffDesc = NULL;
|
|
SEND_COMPL_INFO SendInfo;
|
|
|
|
|
|
// If there is no data to send or if the remote cannot handle any more
|
|
// data, just send an ack.
|
|
|
|
SendInfo.sc_TransmitCompletion = atalkAdspSendDataComplete;
|
|
SendInfo.sc_Ctx1 = pAdspConn;
|
|
|
|
while (TRUE)
|
|
{
|
|
if ((pAdspConn->adspco_Flags & (ADSPCO_ACTIVE |
|
|
ADSPCO_CLOSING |
|
|
ADSPCO_STOPPING |
|
|
ADSPCO_DISCONNECTING)) != ADSPCO_ACTIVE)
|
|
{
|
|
break;
|
|
}
|
|
|
|
// dataSize includes count of eom if present
|
|
dataSize = atalkAdspBufferQueueSize(&pAdspConn->adspco_NextSendQueue);
|
|
windowSize = (LONG)(pAdspConn->adspco_SendWindowSeq -
|
|
pAdspConn->adspco_SendSeq +
|
|
(LONG)1);
|
|
|
|
ASSERTMSG("WindowSize incorrect!\n",
|
|
((windowSize >= 0) || (dataSize == 0)));
|
|
|
|
if ((dataSize == 0) || (windowSize == 0))
|
|
{
|
|
// Send a ack request to the remote end.
|
|
descriptor = ADSP_CONTROL_FLAG + ADSP_PROBE_OR_ACK_CODE +
|
|
((windowSize == 0) ? ADSP_ACK_REQ_FLAG : 0);
|
|
|
|
atalkAdspSendControl(pAdspConn, descriptor);
|
|
break;
|
|
}
|
|
|
|
ASSERTMSG("WindowSize incorrect!\n", (windowSize >= 0));
|
|
if (windowSize < 0)
|
|
{
|
|
// This should never happen. It can be negative, but only if
|
|
// the datasize is 0.
|
|
}
|
|
|
|
|
|
// We have some data to send
|
|
windowSize = MIN((ULONG)windowSize, dataSize);
|
|
|
|
// compute the amount of data to be sent. This will only get
|
|
// the data in one buffer chunk, i.e. if the current buffer chunk
|
|
// has only one byte to be sent, it will return just that, although
|
|
// the next buffer chunk might still have some data to be sent. It will
|
|
// return a built buffer chunk with the proper amount of data in it.
|
|
// Given checks above there is guaranteed to be dataSize amount of data
|
|
// in queue.
|
|
dataSize = atalkAdspDescribeFromBufferQueue(&pAdspConn->adspco_NextSendQueue,
|
|
&eom,
|
|
windowSize,
|
|
&pBufferChunk,
|
|
&pBuffDesc);
|
|
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspSendData: DataSize %ld\n", dataSize));
|
|
|
|
ASSERT(dataSize <= (ULONG)windowSize);
|
|
|
|
descriptor = (eom ? ADSP_EOM_FLAG : 0);
|
|
if (windowSize == (LONG)(dataSize + BYTECOUNT(eom)))
|
|
{
|
|
descriptor += ADSP_ACK_REQ_FLAG;
|
|
}
|
|
|
|
PUTSHORT2SHORT(adspHeader + ADSP_SRC_CONNID_OFF,
|
|
pAdspConn->adspco_LocalConnId);
|
|
|
|
PUTDWORD2DWORD(adspHeader + ADSP_FIRST_BYTE_SEQNUM_OFF,
|
|
pAdspConn->adspco_SendSeq);
|
|
|
|
PUTDWORD2DWORD(adspHeader + ADSP_NEXT_RX_BYTESEQNUM_OFF,
|
|
pAdspConn->adspco_RecvSeq);
|
|
|
|
PUTSHORT2SHORT(adspHeader + ADSP_RX_WINDOW_SIZE_OFF,
|
|
pAdspConn->adspco_RecvWindow);
|
|
|
|
// Set the descriptor
|
|
adspHeader[ADSP_DESCRIPTOR_OFF] = descriptor;
|
|
|
|
// Move up our seq num. We should do it before we release the lock
|
|
// so that other calls to this routine do not mess it up.
|
|
// !!!NOTE!!! Due to calling describe, dataSize *does not* include
|
|
// eom in its count.
|
|
pAdspConn->adspco_SendSeq += (ULONG)dataSize + BYTECOUNT(eom);
|
|
|
|
windowSize -= dataSize;
|
|
|
|
|
|
#if DBG
|
|
(&pAdspConn->adspco_Lock)->FileLineLock |= 0x80000000;
|
|
#endif
|
|
RELEASE_SPIN_LOCK_DPC(&pAdspConn->adspco_Lock);
|
|
|
|
// Send the packet
|
|
SendInfo.sc_Ctx2 = pBuffDesc;
|
|
SendInfo.sc_Ctx3 = pBufferChunk;
|
|
error = AtalkDdpSend(pAdspConn->adspco_pDdpAddr,
|
|
&pAdspConn->adspco_RemoteAddr,
|
|
(BYTE)DDPPROTO_ADSP,
|
|
FALSE,
|
|
pBuffDesc,
|
|
adspHeader,
|
|
sizeof(adspHeader),
|
|
NULL,
|
|
&SendInfo);
|
|
|
|
if (!ATALK_SUCCESS(error))
|
|
{
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_ERR,
|
|
("AtalkAdspSendData: DdpSend failed %ld\n", error));
|
|
|
|
atalkAdspSendDataComplete(NDIS_STATUS_FAILURE, &SendInfo);
|
|
}
|
|
|
|
ACQUIRE_SPIN_LOCK_DPC(&pAdspConn->adspco_Lock);
|
|
#if DBG
|
|
(&pAdspConn->adspco_Lock)->FileLineLock &= ~0x80000000;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
LOCAL VOID
|
|
atalkAdspRecvData(
|
|
IN PADSP_CONNOBJ pAdspConn
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
MUST HAVE THE CONNECTION LOCK HELD BEFORE ENTERING HERE !!!
|
|
|
|
SHOULD THIS ROUTINE HAVE ITS OWN REFERENCE FOR THE CONNECTION?
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN eom;
|
|
ULONG msgSize, readSize, bytesTaken, bytesRead;
|
|
ULONG lookaheadSize;
|
|
PBYTE lookaheadData;
|
|
ULONG readFlags;
|
|
PAMDL readBuf;
|
|
USHORT readBufLen;
|
|
GENERIC_READ_COMPLETION readCompletion;
|
|
PVOID readCtx;
|
|
PIRP recvIrp;
|
|
PTDI_IND_RECEIVE recvHandler;
|
|
PVOID recvHandlerCtx;
|
|
NTSTATUS ntStatus;
|
|
BOOLEAN callComp = FALSE, fWdwChanged = FALSE;
|
|
ATALK_ERROR ErrorCode;
|
|
|
|
do
|
|
{
|
|
if ((pAdspConn->adspco_Flags &
|
|
(ADSPCO_READ_PENDING | ADSPCO_FORWARD_RESET_RECD)) ==
|
|
(ADSPCO_READ_PENDING | ADSPCO_FORWARD_RESET_RECD))
|
|
{
|
|
readFlags = pAdspConn->adspco_ReadFlags;
|
|
readBuf = pAdspConn->adspco_ReadBuf;
|
|
readBufLen = pAdspConn->adspco_ReadBufLen;
|
|
readCompletion = pAdspConn->adspco_ReadCompletion;
|
|
readCtx = pAdspConn->adspco_ReadCtx;
|
|
|
|
pAdspConn->adspco_Flags &= ~ADSPCO_READ_PENDING;
|
|
|
|
#if DBG
|
|
(&pAdspConn->adspco_Lock)->FileLineLock |= 0x80000000;
|
|
#endif
|
|
RELEASE_SPIN_LOCK_DPC(&pAdspConn->adspco_Lock);
|
|
|
|
if (*readCompletion != NULL)
|
|
{
|
|
(*readCompletion)(ATALK_ADSP_CONN_RESET,
|
|
readBuf,
|
|
readBufLen,
|
|
readFlags,
|
|
readCtx);
|
|
}
|
|
|
|
// Deref connection for the read
|
|
AtalkAdspConnDereference(pAdspConn);
|
|
|
|
ACQUIRE_SPIN_LOCK_DPC(&pAdspConn->adspco_Lock);
|
|
#if DBG
|
|
(&pAdspConn->adspco_Lock)->FileLineLock &= ~0x80000000;
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
// Check for pending attention data
|
|
if (pAdspConn->adspco_Flags & ADSPCO_ATTN_DATA_RECD)
|
|
{
|
|
atalkAdspRecvAttn(pAdspConn);
|
|
}
|
|
|
|
// Get the receive handler.
|
|
recvHandler = pAdspConn->adspco_pAssocAddr->adspao_RecvHandler;
|
|
recvHandlerCtx = pAdspConn->adspco_pAssocAddr->adspao_RecvHandlerCtx;
|
|
|
|
// !!!NOTE!!!
|
|
// Its possible that when we get a disconnect packet before we
|
|
// get previously sent data, we could end up indicating disconnect
|
|
// to afd before indicating the received data. This hits an assertion
|
|
// in afd on a checked build, but afd still behaves as it should.
|
|
msgSize = atalkAdspMessageSize(&pAdspConn->adspco_RecvQueue, &eom);
|
|
bytesRead = 1; // A Non-zero value so we enter the loop
|
|
while (((msgSize > 0) || eom) && (bytesRead > 0))
|
|
{
|
|
bytesRead = 0;
|
|
|
|
// Check for no pending reads, but we have new data to indicate, and the
|
|
// client has read all the previously indicated data.
|
|
if (((pAdspConn->adspco_Flags & ADSPCO_READ_PENDING) == 0) &&
|
|
(*recvHandler != NULL) &&
|
|
(pAdspConn->adspco_PrevIndicatedData == 0))
|
|
{
|
|
pAdspConn->adspco_PrevIndicatedData = msgSize;
|
|
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspRecvData: PrevInd1 %d\n", pAdspConn->adspco_PrevIndicatedData));
|
|
|
|
lookaheadData = atalkAdspGetLookahead(&pAdspConn->adspco_RecvQueue,
|
|
&lookaheadSize);
|
|
|
|
readFlags = ((eom) ?
|
|
(TDI_RECEIVE_NORMAL | TDI_RECEIVE_ENTIRE_MESSAGE) :
|
|
(TDI_RECEIVE_PARTIAL | TDI_RECEIVE_NORMAL));
|
|
|
|
if (*recvHandler != NULL)
|
|
{
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspRecvData: Indicating data %ld.%ld!\n", lookaheadSize, msgSize));
|
|
|
|
#if DBG
|
|
(&pAdspConn->adspco_Lock)->FileLineLock |= 0x80000000;
|
|
#endif
|
|
RELEASE_SPIN_LOCK_DPC(&pAdspConn->adspco_Lock);
|
|
ntStatus = (*recvHandler)(recvHandlerCtx,
|
|
pAdspConn->adspco_ConnCtx,
|
|
readFlags,
|
|
lookaheadSize,
|
|
msgSize,
|
|
&bytesTaken,
|
|
lookaheadData,
|
|
&recvIrp);
|
|
|
|
ASSERT((bytesTaken == 0) || (bytesTaken == msgSize));
|
|
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_ADSP],
|
|
recvIrp);
|
|
|
|
ASSERT(ntStatus == STATUS_PENDING);
|
|
}
|
|
else
|
|
{
|
|
ASSERTMSG("atalkAdspRecvData: No receive irp!\n", 0);
|
|
KeBugCheck(0);
|
|
}
|
|
}
|
|
else if (ntStatus == STATUS_SUCCESS)
|
|
{
|
|
if (bytesTaken != 0)
|
|
{
|
|
// Assume all of the data was read.
|
|
ASSERT(bytesTaken == msgSize);
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspRecvData: All bytes read %lx\n", bytesTaken));
|
|
|
|
// Discard data from queue (msgSize + BYTECOUNT(eom))
|
|
// amount of data).
|
|
}
|
|
}
|
|
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_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspRecvData: Indication status %lx\n", ntStatus));
|
|
}
|
|
ACQUIRE_SPIN_LOCK_DPC(&pAdspConn->adspco_Lock);
|
|
#if DBG
|
|
(&pAdspConn->adspco_Lock)->FileLineLock &= ~0x80000000;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// Check for any posted receives, this may have happened during
|
|
// the receive indication.
|
|
if (pAdspConn->adspco_Flags & ADSPCO_READ_PENDING)
|
|
{
|
|
readFlags = pAdspConn->adspco_ReadFlags;
|
|
readBuf = pAdspConn->adspco_ReadBuf;
|
|
readBufLen = pAdspConn->adspco_ReadBufLen;
|
|
readCompletion = pAdspConn->adspco_ReadCompletion;
|
|
readCtx = pAdspConn->adspco_ReadCtx;
|
|
|
|
// For a message-based socket, we do not complete
|
|
// a read until eom, or the buffer fills up.
|
|
if ((pAdspConn->adspco_pAssocAddr->adspao_Flags & ADSPAO_MESSAGE) &&
|
|
(!eom && (msgSize < readBufLen)))
|
|
{
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspRecv: MsgSize < readLen %lx.%lx\n", msgSize, readBufLen));
|
|
|
|
// If we are disconnected and this data is just the last
|
|
// remnant from remote, we just copy what we got and leave.
|
|
// There may not have been an EOM from the remote.
|
|
// Also, if the msg is bigger than what transport can hold (8K),
|
|
// give whatever we have so far to the app so that our recv window
|
|
// can open up. That is, break out of the loop only if recv window
|
|
// has room to accept more data
|
|
if ( (pAdspConn->adspco_Flags & ADSPCO_ACTIVE) &&
|
|
(pAdspConn->adspco_RecvWindow > 1))
|
|
{
|
|
break;
|
|
}
|
|
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_WARN,
|
|
("AtalkAdspRead: READ AFTER DISC %lx Flg %lx\n",
|
|
pAdspConn, pAdspConn->adspco_Flags));
|
|
}
|
|
|
|
|
|
// This will return the data in the mdl from the
|
|
// receive queue.
|
|
readSize = atalkAdspReadFromBufferQueue(&pAdspConn->adspco_RecvQueue,
|
|
readFlags,
|
|
readBuf,
|
|
&readBufLen,
|
|
&eom);
|
|
|
|
if ((readSize == 0) && !eom)
|
|
{
|
|
pAdspConn->adspco_PrevIndicatedData = 0;
|
|
break;
|
|
}
|
|
|
|
bytesRead += (readSize + BYTECOUNT(eom));
|
|
pAdspConn->adspco_Flags &= ~ADSPCO_READ_PENDING;
|
|
|
|
// If this is not a PEEK receive, the data will be
|
|
// discarded from the queue. If so, increase our window size, do a
|
|
// senddata to let remote know of the change.
|
|
if ((readFlags & TDI_RECEIVE_PEEK) == 0)
|
|
{
|
|
pAdspConn->adspco_RecvWindow += (readSize + BYTECOUNT(eom));
|
|
|
|
ASSERT(pAdspConn->adspco_RecvWindow <=
|
|
pAdspConn->adspco_RecvQueueMax);
|
|
|
|
fWdwChanged = TRUE;
|
|
}
|
|
|
|
#if DBG
|
|
(&pAdspConn->adspco_Lock)->FileLineLock |= 0x80000000;
|
|
#endif
|
|
RELEASE_SPIN_LOCK_DPC(&pAdspConn->adspco_Lock);
|
|
if (*readCompletion != NULL)
|
|
{
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspRecvData: Read for %d, %x\n", readBufLen, readFlags));
|
|
|
|
ErrorCode = ATALK_NO_ERROR;
|
|
|
|
if ((pAdspConn->adspco_pAssocAddr->adspao_Flags & ADSPAO_MESSAGE) && !eom)
|
|
{
|
|
ErrorCode = ATALK_ADSP_PARTIAL_RECEIVE;
|
|
}
|
|
(*readCompletion)(ErrorCode,
|
|
readBuf,
|
|
readBufLen,
|
|
readFlags,
|
|
readCtx);
|
|
}
|
|
|
|
// Deref connection for the read
|
|
AtalkAdspConnDereference(pAdspConn);
|
|
|
|
ACQUIRE_SPIN_LOCK_DPC(&pAdspConn->adspco_Lock);
|
|
#if DBG
|
|
(&pAdspConn->adspco_Lock)->FileLineLock &= ~0x80000000;
|
|
#endif
|
|
|
|
// Now change our prev indicated field. Until we
|
|
// complete the read, we musn't indicate new data.
|
|
// If the read was PEEK, then we don't want to do
|
|
// any more indications until a *real* read happens.
|
|
if ((readFlags & TDI_RECEIVE_PEEK) == 0)
|
|
{
|
|
pAdspConn->adspco_PrevIndicatedData -=
|
|
MIN(readSize, pAdspConn->adspco_PrevIndicatedData);
|
|
}
|
|
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspRecvData: PrevInd2 %d\n",
|
|
pAdspConn->adspco_PrevIndicatedData));
|
|
}
|
|
|
|
msgSize = atalkAdspMessageSize(&pAdspConn->adspco_RecvQueue, &eom);
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("Second msg %d.%d\n", msgSize, eom));
|
|
}
|
|
|
|
} while (FALSE);
|
|
|
|
if (fWdwChanged &&
|
|
(pAdspConn->adspco_PrevIndicatedData == 0))
|
|
{
|
|
atalkAdspSendData(pAdspConn);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
LOCAL VOID
|
|
atalkAdspRecvAttn(
|
|
IN PADSP_CONNOBJ pAdspConn
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
!!!THIS ROUTINE MUST PRESERVE THE STATE OF THE CONNECTION LOCK!!!
|
|
|
|
SHOULD THIS ROUTINE HAVE ITS OWN REFERENCE FOR THE CONNECTION?
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
ATALK_ERROR error;
|
|
PAMDL readBuf;
|
|
USHORT readBufLen;
|
|
ULONG readFlags;
|
|
GENERIC_READ_COMPLETION readCompletion;
|
|
PVOID readCtx;
|
|
PBYTE attnData;
|
|
USHORT attnDataSize;
|
|
ULONG bytesRead;
|
|
NTSTATUS status;
|
|
|
|
do
|
|
{
|
|
if ((pAdspConn->adspco_Flags & ADSPCO_ATTN_DATA_RECD) == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (pAdspConn->adspco_Flags & ADSPCO_EXREAD_PENDING)
|
|
{
|
|
// Use the expedited receive posted
|
|
readFlags = pAdspConn->adspco_ExReadFlags;
|
|
readBuf = pAdspConn->adspco_ExReadBuf;
|
|
readBufLen = pAdspConn->adspco_ExReadBufLen;
|
|
readCompletion = pAdspConn->adspco_ExReadCompletion;
|
|
readCtx = pAdspConn->adspco_ExReadCtx;
|
|
|
|
pAdspConn->adspco_Flags &= ~ADSPCO_EXREAD_PENDING;
|
|
}
|
|
else if ((pAdspConn->adspco_Flags & ADSPCO_READ_PENDING) &&
|
|
(pAdspConn->adspco_ReadFlags & TDI_RECEIVE_EXPEDITED))
|
|
{
|
|
// Use the normal receive
|
|
readFlags = pAdspConn->adspco_ReadFlags;
|
|
readBuf = pAdspConn->adspco_ReadBuf;
|
|
readBufLen = pAdspConn->adspco_ReadBufLen;
|
|
readCompletion = pAdspConn->adspco_ReadCompletion;
|
|
readCtx = pAdspConn->adspco_ReadCtx;
|
|
|
|
pAdspConn->adspco_Flags &= ~ADSPCO_READ_PENDING;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
|
|
attnData = pAdspConn->adspco_ExRecdData;
|
|
attnDataSize = pAdspConn->adspco_ExRecdLen;
|
|
|
|
// Copy received attention data into the read buffer
|
|
error = ATALK_ADSP_PAREXPED_RECEIVE;
|
|
if (pAdspConn->adspco_Flags & ADSPCO_ATTN_DATA_EOM)
|
|
{
|
|
error = ATALK_ADSP_EXPED_RECEIVE;
|
|
}
|
|
|
|
if (attnDataSize > readBufLen)
|
|
{
|
|
attnDataSize = readBufLen;
|
|
}
|
|
|
|
status = TdiCopyBufferToMdl(attnData,
|
|
0,
|
|
attnDataSize,
|
|
readBuf,
|
|
0,
|
|
&bytesRead);
|
|
|
|
ASSERT(NT_SUCCESS(status) && (attnDataSize == bytesRead));
|
|
|
|
// Update sequence number etc., only if this was not a peek.
|
|
if ((readFlags & TDI_RECEIVE_PEEK) == 0)
|
|
{
|
|
pAdspConn->adspco_ExRecdData = NULL;
|
|
|
|
// Advance our receive attention sequence number
|
|
pAdspConn->adspco_RecvAttnSeq += 1;
|
|
|
|
pAdspConn->adspco_Flags &= ~(ADSPCO_ATTN_DATA_RECD |
|
|
ADSPCO_ATTN_DATA_EOM);
|
|
}
|
|
|
|
#if DBG
|
|
(&pAdspConn->adspco_Lock)->FileLineLock |= 0x80000000;
|
|
#endif
|
|
RELEASE_SPIN_LOCK_DPC(&pAdspConn->adspco_Lock);
|
|
|
|
// Complete receive
|
|
ASSERT(*readCompletion != NULL);
|
|
(*readCompletion)(error,
|
|
readBuf,
|
|
attnDataSize,
|
|
TDI_RECEIVE_EXPEDITED,
|
|
readCtx);
|
|
|
|
// Free the allocated buffer if this was not a peek
|
|
if ((readFlags & TDI_RECEIVE_PEEK) == 0)
|
|
{
|
|
AtalkFreeMemory(attnData);
|
|
}
|
|
|
|
// Deref connection for the read
|
|
AtalkAdspConnDereference(pAdspConn);
|
|
|
|
ACQUIRE_SPIN_LOCK_DPC(&pAdspConn->adspco_Lock);
|
|
#if DBG
|
|
(&pAdspConn->adspco_Lock)->FileLineLock &= ~0x80000000;
|
|
#endif
|
|
|
|
// Send ack for the attention only if this was not a peek
|
|
if ((readFlags & TDI_RECEIVE_PEEK) == 0)
|
|
{
|
|
atalkAdspSendControl(pAdspConn,
|
|
ADSP_CONTROL_FLAG + ADSP_ATTEN_FLAG);
|
|
}
|
|
|
|
} while (FALSE);
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID FASTCALL
|
|
atalkAdspConnSendComplete(
|
|
IN NDIS_STATUS Status,
|
|
IN PSEND_COMPL_INFO pSendInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
if (pSendInfo->sc_Ctx2 != NULL)
|
|
{
|
|
AtalkFreeBuffDesc((PBUFFER_DESC)(pSendInfo->sc_Ctx2));
|
|
}
|
|
|
|
AtalkAdspConnDereference((PADSP_CONNOBJ)(pSendInfo->sc_Ctx1));
|
|
}
|
|
|
|
|
|
|
|
VOID FASTCALL
|
|
atalkAdspAddrSendComplete(
|
|
IN NDIS_STATUS Status,
|
|
IN PSEND_COMPL_INFO pSendInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
if (pSendInfo->sc_Ctx2 != NULL)
|
|
{
|
|
AtalkFreeBuffDesc((PBUFFER_DESC)(pSendInfo->sc_Ctx2));
|
|
}
|
|
|
|
AtalkAdspAddrDereference((PADSP_ADDROBJ)(pSendInfo->sc_Ctx1));
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID FASTCALL
|
|
atalkAdspSendAttnComplete(
|
|
IN NDIS_STATUS Status,
|
|
IN PSEND_COMPL_INFO pSendInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
if (pSendInfo->sc_Ctx2 != NULL)
|
|
{
|
|
AtalkFreeBuffDesc((PBUFFER_DESC)(pSendInfo->sc_Ctx2));
|
|
}
|
|
}
|
|
|
|
|
|
|
|
VOID FASTCALL
|
|
atalkAdspSendDataComplete(
|
|
IN NDIS_STATUS Status,
|
|
IN PSEND_COMPL_INFO pSendInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
if (pSendInfo->sc_Ctx2 != NULL)
|
|
{
|
|
AtalkFreeBuffDesc((PBUFFER_DESC)(pSendInfo->sc_Ctx2));
|
|
}
|
|
|
|
if (pSendInfo->sc_Ctx3 != NULL)
|
|
{
|
|
atalkAdspBufferChunkDereference((PBUFFER_CHUNK)(pSendInfo->sc_Ctx3),
|
|
FALSE,
|
|
NULL);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// ADSP TIMER ROUTINES
|
|
//
|
|
|
|
LOCAL LONG FASTCALL
|
|
atalkAdspConnMaintenanceTimer(
|
|
IN PTIMERLIST pTimer,
|
|
IN BOOLEAN TimerShuttingDown
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PADSP_CONNOBJ pAdspConn;
|
|
LONG now;
|
|
BOOLEAN done = FALSE;
|
|
|
|
pAdspConn = (PADSP_CONNOBJ)CONTAINING_RECORD(pTimer, ADSP_CONNOBJ, adspco_ConnTimer);
|
|
|
|
ASSERT(VALID_ADSPCO(pAdspConn));
|
|
|
|
if (TimerShuttingDown)
|
|
{
|
|
done = TRUE;
|
|
}
|
|
else
|
|
{
|
|
ASSERT(VALID_ADSPCO(pAdspConn));
|
|
ACQUIRE_SPIN_LOCK_DPC(&pAdspConn->adspco_Lock);
|
|
if (pAdspConn->adspco_Flags & ( ADSPCO_CLOSING |
|
|
ADSPCO_STOPPING |
|
|
ADSPCO_DISCONNECTING))
|
|
{
|
|
done = TRUE;
|
|
}
|
|
RELEASE_SPIN_LOCK_DPC(&pAdspConn->adspco_Lock);
|
|
}
|
|
|
|
if (done)
|
|
{
|
|
// Dereference connection for the timer.
|
|
AtalkAdspConnDereference(pAdspConn);
|
|
return ATALK_TIMER_NO_REQUEUE;
|
|
}
|
|
|
|
now = AtalkGetCurrentTick();
|
|
if ((now - pAdspConn->adspco_LastContactTime) > ADSP_CONNECTION_INTERVAL)
|
|
{
|
|
// Connection has expired.
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_ERR,
|
|
("atalkAdspConnMaintenanceTimer: Connection %lx.%lx expired\n",
|
|
pAdspConn, pAdspConn->adspco_LocalConnId));
|
|
|
|
AtalkAdspDisconnect(pAdspConn,
|
|
ATALK_TIMER_DISCONNECT,
|
|
NULL,
|
|
NULL);
|
|
|
|
// Dereference connection for the timer.
|
|
AtalkAdspConnDereference(pAdspConn);
|
|
return ATALK_TIMER_NO_REQUEUE;
|
|
}
|
|
|
|
// If we have not heard from the other side recently, send out a
|
|
// probe.
|
|
if ((now - pAdspConn->adspco_LastContactTime) > (ADSP_PROBE_INTERVAL/ATALK_TIMER_FACTOR))
|
|
{
|
|
KIRQL OldIrql;
|
|
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_WARN,
|
|
("atalkAdspConnMaintenanceTimer: Connection %lx.%lx sending probe\n",
|
|
pAdspConn, pAdspConn->adspco_LocalConnId));
|
|
|
|
ACQUIRE_SPIN_LOCK(&pAdspConn->adspco_Lock, &OldIrql);
|
|
atalkAdspSendControl(pAdspConn,
|
|
ADSP_CONTROL_FLAG + ADSP_ACK_REQ_FLAG + ADSP_PROBE_OR_ACK_CODE);
|
|
RELEASE_SPIN_LOCK(&pAdspConn->adspco_Lock, OldIrql);
|
|
}
|
|
|
|
return ATALK_TIMER_REQUEUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
LOCAL LONG FASTCALL
|
|
atalkAdspRetransmitTimer(
|
|
IN PTIMERLIST pTimer,
|
|
IN BOOLEAN TimerShuttingDown
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PADSP_CONNOBJ pAdspConn;
|
|
BOOLEAN done = FALSE;
|
|
KIRQL OldIrql;
|
|
|
|
pAdspConn = (PADSP_CONNOBJ)CONTAINING_RECORD(pTimer, ADSP_CONNOBJ, adspco_RetransmitTimer);
|
|
|
|
ASSERT(VALID_ADSPCO(pAdspConn));
|
|
|
|
// BUG #19777: Since this routine could end up calling SendData which
|
|
// releases/acquires lock and assumes lock was acquired using the normal
|
|
// acquire spin lock, we can't use ACQUIRE_SPIN_LOCK_DPC here. Not a big
|
|
// deal as this is the retransmit case.
|
|
// ACQUIRE_SPIN_LOCK_DPC(&pAdspConn->adspco_Lock);
|
|
|
|
ACQUIRE_SPIN_LOCK(&pAdspConn->adspco_Lock, &OldIrql);
|
|
if (TimerShuttingDown)
|
|
{
|
|
done = TRUE;
|
|
}
|
|
else
|
|
{
|
|
ASSERT(VALID_ADSPCO(pAdspConn));
|
|
if (pAdspConn->adspco_Flags & ( ADSPCO_CLOSING |
|
|
ADSPCO_STOPPING |
|
|
ADSPCO_DISCONNECTING))
|
|
{
|
|
done = TRUE;
|
|
}
|
|
}
|
|
|
|
if (done)
|
|
{
|
|
RELEASE_SPIN_LOCK(&pAdspConn->adspco_Lock, OldIrql);
|
|
|
|
// Dereference connection for the timer.
|
|
AtalkAdspConnDereference(pAdspConn);
|
|
return ATALK_TIMER_NO_REQUEUE;
|
|
}
|
|
|
|
// We only send data if the remote has not accepted any data from the last
|
|
// time we fired. AND we have previously sent but still unacked data pending.
|
|
if ((pAdspConn->adspco_FirstRtmtSeq == pAdspConn->adspco_LastTimerRtmtSeq) &&
|
|
(atalkAdspBufferQueueSize(&pAdspConn->adspco_SendQueue) >
|
|
atalkAdspBufferQueueSize(&pAdspConn->adspco_NextSendQueue)))
|
|
{
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspConnRetransmitTimer: Conn %lx Sending Data from %lx\n",
|
|
pAdspConn, pAdspConn->adspco_FirstRtmtSeq));
|
|
|
|
// Rewind sequence number and resend
|
|
pAdspConn->adspco_SendSeq = pAdspConn->adspco_FirstRtmtSeq;
|
|
pAdspConn->adspco_NextSendQueue = pAdspConn->adspco_SendQueue;
|
|
atalkAdspSendData(pAdspConn);
|
|
}
|
|
else
|
|
{
|
|
pAdspConn->adspco_LastTimerRtmtSeq = pAdspConn->adspco_FirstRtmtSeq;
|
|
}
|
|
RELEASE_SPIN_LOCK(&pAdspConn->adspco_Lock, OldIrql);
|
|
|
|
return ATALK_TIMER_REQUEUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
LOCAL LONG FASTCALL
|
|
atalkAdspAttnRetransmitTimer(
|
|
IN PTIMERLIST pTimer,
|
|
IN BOOLEAN TimerShuttingDown
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PADSP_CONNOBJ pAdspConn;
|
|
|
|
pAdspConn = (PADSP_CONNOBJ)CONTAINING_RECORD(pTimer, ADSP_CONNOBJ, adspco_ExRetryTimer);
|
|
|
|
ASSERT(VALID_ADSPCO(pAdspConn));
|
|
|
|
if (TimerShuttingDown)
|
|
{
|
|
return ATALK_TIMER_NO_REQUEUE;
|
|
}
|
|
|
|
atalkAdspSendAttn(pAdspConn);
|
|
|
|
return ATALK_TIMER_REQUEUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
LOCAL LONG FASTCALL
|
|
atalkAdspOpenTimer(
|
|
IN PTIMERLIST pTimer,
|
|
IN BOOLEAN TimerShuttingDown
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PADSP_CONNOBJ pAdspConn;
|
|
ATALK_ERROR error;
|
|
BOOLEAN done = FALSE;
|
|
|
|
pAdspConn = (PADSP_CONNOBJ)CONTAINING_RECORD(pTimer, ADSP_CONNOBJ, adspco_OpenTimer);
|
|
|
|
ASSERT(VALID_ADSPCO(pAdspConn));
|
|
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspOpenTimer: Entered \n"));
|
|
|
|
|
|
ACQUIRE_SPIN_LOCK_DPC(&pAdspConn->adspco_Lock);
|
|
// If the timer is shutting down, or if we have gone active, return
|
|
if ((TimerShuttingDown) ||
|
|
(pAdspConn->adspco_Flags & ADSPCO_ACTIVE) ||
|
|
((pAdspConn->adspco_Flags & ADSPCO_OPEN_TIMER) == 0))
|
|
{
|
|
pAdspConn->adspco_Flags &= ~ADSPCO_OPEN_TIMER;
|
|
RELEASE_SPIN_LOCK_DPC(&pAdspConn->adspco_Lock);
|
|
|
|
AtalkAdspConnDereference(pAdspConn);
|
|
return ATALK_TIMER_NO_REQUEUE;
|
|
}
|
|
|
|
if ((pAdspConn->adspco_Flags & (ADSPCO_CLOSING |
|
|
ADSPCO_STOPPING |
|
|
ADSPCO_DISCONNECTING)) ||
|
|
|
|
(pAdspConn->adspco_ConnectAttempts == 0))
|
|
{
|
|
done = TRUE;
|
|
}
|
|
else
|
|
{
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspOpenTimer: Connect attempt %d\n", pAdspConn->adspco_ConnectAttempts));
|
|
|
|
ASSERT(pAdspConn->adspco_ConnectAttempts > 0);
|
|
pAdspConn->adspco_ConnectAttempts--;
|
|
}
|
|
RELEASE_SPIN_LOCK_DPC(&pAdspConn->adspco_Lock);
|
|
|
|
if (!done)
|
|
{
|
|
// Resend the open request.
|
|
atalkAdspSendOpenControl(pAdspConn);
|
|
}
|
|
else
|
|
{
|
|
error = AtalkAdspDisconnect(pAdspConn,
|
|
ATALK_TIMER_DISCONNECT,
|
|
NULL,
|
|
NULL);
|
|
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_ERR,
|
|
("atalkAdspOpenTimer: Disconnect %lx\n", error));
|
|
|
|
AtalkAdspConnDereference(pAdspConn);
|
|
}
|
|
|
|
return (done ? ATALK_TIMER_NO_REQUEUE : ATALK_TIMER_REQUEUE);
|
|
}
|
|
|
|
|
|
|
|
LOCAL LONG FASTCALL
|
|
atalkAdspDisconnectTimer(
|
|
IN PTIMERLIST pTimer,
|
|
IN BOOLEAN TimerShuttingDown
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PADSP_CONNOBJ pAdspConn;
|
|
|
|
pAdspConn = (PADSP_CONNOBJ)CONTAINING_RECORD(pTimer, ADSP_CONNOBJ, adspco_DisconnectTimer);
|
|
|
|
ASSERT(VALID_ADSPCO(pAdspConn));
|
|
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspDisconnectTimer: Entered \n"));
|
|
|
|
AtalkAdspDisconnect(pAdspConn,
|
|
ATALK_REMOTE_DISCONNECT,
|
|
NULL,
|
|
NULL);
|
|
AtalkAdspConnDereference(pAdspConn);
|
|
|
|
return ATALK_TIMER_NO_REQUEUE;
|
|
}
|
|
|
|
|
|
//
|
|
// ADSP REFERENCE/DerefERENCE ROUTINES
|
|
//
|
|
|
|
VOID
|
|
atalkAdspAddrRefNonInterlock(
|
|
IN PADSP_ADDROBJ pAdspAddr,
|
|
OUT PATALK_ERROR pError
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
*pError = ATALK_NO_ERROR;
|
|
|
|
if (pAdspAddr == NULL)
|
|
{
|
|
*pError = ATALK_INVALID_ADDRESS;
|
|
return;
|
|
}
|
|
|
|
if ((pAdspAddr->adspao_Flags & ADSPAO_CLOSING) == 0)
|
|
{
|
|
ASSERT(pAdspAddr->adspao_RefCount >= 1);
|
|
pAdspAddr->adspao_RefCount++;
|
|
}
|
|
else
|
|
{
|
|
*pError = ATALK_ADSP_ADDR_CLOSING;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
atalkAdspAddrDeref(
|
|
IN PADSP_ADDROBJ pAdspAddr
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN done = FALSE;
|
|
KIRQL OldIrql;
|
|
|
|
ACQUIRE_SPIN_LOCK(&pAdspAddr->adspao_Lock, &OldIrql);
|
|
ASSERT(pAdspAddr->adspao_RefCount > 0);
|
|
if (--pAdspAddr->adspao_RefCount == 0)
|
|
{
|
|
done = TRUE;
|
|
ASSERT(pAdspAddr->adspao_Flags & ADSPAO_CLOSING);
|
|
}
|
|
|
|
RELEASE_SPIN_LOCK(&pAdspAddr->adspao_Lock, OldIrql);
|
|
|
|
if (done)
|
|
{
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspAddrDeref: Addr %lx done with.\n", pAdspAddr));
|
|
|
|
// Close the DDP Address Object. This should only be done after
|
|
// all the connections are gone.
|
|
AtalkDdpCloseAddress(pAdspAddr->adspao_pDdpAddr, NULL, NULL);
|
|
|
|
if (*pAdspAddr->adspao_CloseComp != NULL)
|
|
{
|
|
(*pAdspAddr->adspao_CloseComp)(ATALK_NO_ERROR,
|
|
pAdspAddr->adspao_CloseCtx);
|
|
}
|
|
|
|
// Remove from the global list.
|
|
atalkAdspAddrDeQueueGlobalList(pAdspAddr);
|
|
|
|
AtalkFreeMemory(pAdspAddr);
|
|
|
|
AtalkUnlockAdspIfNecessary();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
atalkAdspConnRefByPtrNonInterlock(
|
|
IN PADSP_CONNOBJ pAdspConn,
|
|
IN ULONG NumCount,
|
|
OUT PATALK_ERROR pError
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
*pError = ATALK_NO_ERROR;
|
|
ASSERT(VALID_ADSPCO(pAdspConn));
|
|
|
|
if (pAdspConn == NULL)
|
|
{
|
|
*pError = ATALK_INVALID_CONNECTION;
|
|
return;
|
|
}
|
|
|
|
if ((pAdspConn->adspco_Flags & ADSPCO_CLOSING) == 0)
|
|
{
|
|
ASSERT(pAdspConn->adspco_RefCount >= 1);
|
|
ASSERT(NumCount > 0);
|
|
|
|
pAdspConn->adspco_RefCount += NumCount;
|
|
}
|
|
else
|
|
{
|
|
*pError = ATALK_ADSP_CONN_CLOSING;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
atalkAdspConnRefByCtxNonInterlock(
|
|
IN PADSP_ADDROBJ pAdspAddr,
|
|
IN CONNECTION_CONTEXT Ctx,
|
|
OUT PADSP_CONNOBJ * pAdspConn,
|
|
OUT PATALK_ERROR pError
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
!!!MUST BE CALLED WITH THE ADDRESS LOCK HELD!!!
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PADSP_CONNOBJ pAdspChkConn;
|
|
|
|
*pError = ATALK_ADSP_CONN_NOT_FOUND;
|
|
|
|
for (pAdspChkConn = pAdspAddr->adspao_pAssocConn;
|
|
pAdspChkConn != NULL;
|
|
pAdspChkConn = pAdspChkConn->adspco_pNextAssoc)
|
|
{
|
|
if (pAdspChkConn->adspco_ConnCtx == Ctx)
|
|
{
|
|
AtalkAdspConnReferenceByPtr(pAdspChkConn, pError);
|
|
if (ATALK_SUCCESS(*pError))
|
|
{
|
|
*pAdspConn = pAdspChkConn;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
atalkAdspConnRefBySrcAddr(
|
|
IN PADSP_ADDROBJ pAdspAddr,
|
|
IN PATALK_ADDR pRemoteAddr,
|
|
IN USHORT RemoteConnId,
|
|
OUT PADSP_CONNOBJ * ppAdspConn,
|
|
OUT PATALK_ERROR pError
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
!!!MUST BE CALLED WITH THE ADDRESS LOCK HELD!!!
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
ULONG index;
|
|
PADSP_CONNOBJ pAdspConn;
|
|
|
|
// Thread the connection object into addr lookup by session id.
|
|
index = HASH_ID_SRCADDR(RemoteConnId, pRemoteAddr);
|
|
|
|
index %= ADSP_CONN_HASH_SIZE;
|
|
|
|
for (pAdspConn = pAdspAddr->adspao_pActiveHash[index];
|
|
pAdspConn != NULL;
|
|
pAdspConn = pAdspConn->adspco_pNextActive)
|
|
{
|
|
if ((pAdspConn->adspco_RemoteConnId == RemoteConnId) &&
|
|
(ATALK_ADDRS_EQUAL(&pAdspConn->adspco_RemoteAddr, pRemoteAddr)))
|
|
{
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspConnRefBySrcAddr: Found %lx\n", pAdspConn));
|
|
break;
|
|
}
|
|
}
|
|
|
|
*pError = ATALK_INVALID_CONNECTION;
|
|
if (pAdspConn != NULL)
|
|
{
|
|
KIRQL OldIrql;
|
|
|
|
// Check state to make sure we are not disconnecting/stopping/closing.
|
|
ACQUIRE_SPIN_LOCK(&pAdspConn->adspco_Lock, &OldIrql);
|
|
if ((pAdspConn->adspco_Flags & (ADSPCO_ACTIVE | ADSPCO_HALF_ACTIVE)) &&
|
|
((pAdspConn->adspco_Flags & (ADSPCO_CLOSING |
|
|
ADSPCO_STOPPING|
|
|
ADSPCO_DISCONNECTING)) == 0))
|
|
{
|
|
pAdspConn->adspco_RefCount++;
|
|
*pError = ATALK_NO_ERROR;
|
|
*ppAdspConn = pAdspConn;
|
|
}
|
|
RELEASE_SPIN_LOCK(&pAdspConn->adspco_Lock, OldIrql);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
atalkAdspConnRefNextNc(
|
|
IN PADSP_CONNOBJ pAdspConn,
|
|
IN PADSP_CONNOBJ * ppAdspConnNext,
|
|
OUT PATALK_ERROR pError
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
MUST BE CALLED WITH THE ASSOCIATED ADDRESS LOCK HELD!
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PADSP_CONNOBJ pNextConn = NULL;
|
|
|
|
*pError = ATALK_FAILURE;
|
|
|
|
ASSERT(VALID_ADSPCO(pAdspConn));
|
|
|
|
for (; pAdspConn != NULL; pAdspConn = pAdspConn->adspco_pNextActive)
|
|
{
|
|
AtalkAdspConnReferenceByPtr(pAdspConn, pError);
|
|
if (ATALK_SUCCESS(*pError))
|
|
{
|
|
// Ok, this connection is referenced!
|
|
*ppAdspConnNext = pAdspConn;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
atalkAdspConnDeref(
|
|
IN PADSP_CONNOBJ pAdspConn
|
|
)
|
|
/*++
|
|
|
|
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.
|
|
|
|
Creation reference is never removed until cleanup completes.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN fEndProcessing = FALSE;
|
|
KIRQL OldIrql;
|
|
|
|
ASSERT(VALID_ADSPCO(pAdspConn));
|
|
ACQUIRE_SPIN_LOCK(&pAdspConn->adspco_Lock, &OldIrql);
|
|
|
|
ASSERT(pAdspConn->adspco_RefCount > 0);
|
|
--pAdspConn->adspco_RefCount;
|
|
|
|
if (pAdspConn->adspco_RefCount > 1)
|
|
{
|
|
fEndProcessing = TRUE;
|
|
}
|
|
RELEASE_SPIN_LOCK(&pAdspConn->adspco_Lock, OldIrql);
|
|
|
|
if (fEndProcessing)
|
|
{
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
ATALK_ERROR disconnectStatus;
|
|
PADSP_ADDROBJ pAdspAddr = pAdspConn->adspco_pAssocAddr;
|
|
BOOLEAN done = FALSE;
|
|
BOOLEAN disconnDone = FALSE;
|
|
BOOLEAN pendingRead = FALSE;
|
|
BOOLEAN pendingWrite= FALSE;
|
|
BOOLEAN stopping = FALSE;
|
|
GENERIC_COMPLETION disconnectInform = NULL;
|
|
PVOID disconnectInformCtx = NULL;
|
|
GENERIC_COMPLETION disconnectCompletion = NULL;
|
|
PVOID disconnectCtx = NULL;
|
|
PVOID cleanupCtx = NULL;
|
|
GENERIC_COMPLETION cleanupCompletion = NULL;
|
|
|
|
// 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(&pAdspConn->adspco_Lock, &OldIrql);
|
|
stopping = (pAdspConn->adspco_Flags & ADSPCO_STOPPING) ? TRUE : FALSE;
|
|
if (pAdspConn->adspco_Flags & ADSPCO_DISCONNECTING)
|
|
{
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspConnDeref: Disconnect set for %lx\n", pAdspConn));
|
|
|
|
// Are we done disconnecting? Since cleanup wont complete until disc
|
|
// does, we don't have to worry about the creation ref having gone
|
|
// away.
|
|
if (pAdspConn->adspco_RefCount == 1)
|
|
{
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspConnDeref: Disconnect done (1) %lx\n", pAdspConn));
|
|
|
|
// Avoid multiple disconnect completions/close atp addresses
|
|
// Remember all the disconnect info before we release the lock
|
|
disconnectInform = pAdspConn->adspco_DisconnectInform;
|
|
disconnectInformCtx = pAdspConn->adspco_DisconnectInformCtx;
|
|
disconnectStatus = pAdspConn->adspco_DisconnectStatus;
|
|
disconnectCompletion = pAdspConn->adspco_DisconnectCompletion;
|
|
disconnectCtx = pAdspConn->adspco_DisconnectCtx;
|
|
|
|
// Reset all the be null, so next request doesnt get any
|
|
pAdspConn->adspco_DisconnectInform = NULL;
|
|
pAdspConn->adspco_DisconnectInformCtx = NULL;
|
|
pAdspConn->adspco_DisconnectCompletion = NULL;
|
|
pAdspConn->adspco_DisconnectCtx = NULL;
|
|
|
|
disconnDone = TRUE;
|
|
stopping = (pAdspConn->adspco_Flags & ADSPCO_STOPPING) ? TRUE : FALSE;
|
|
}
|
|
else
|
|
{
|
|
// Set stopping to false as disconnect is not done yet.
|
|
stopping = FALSE;
|
|
}
|
|
}
|
|
|
|
if (pAdspConn->adspco_RefCount == 0)
|
|
{
|
|
done = TRUE;
|
|
ASSERT(pAdspConn->adspco_Flags & ADSPCO_CLOSING);
|
|
}
|
|
RELEASE_SPIN_LOCK(&pAdspConn->adspco_Lock, OldIrql);
|
|
|
|
if (disconnDone)
|
|
{
|
|
// Remove from the active queue.
|
|
// Reset all relevent flags.
|
|
ACQUIRE_SPIN_LOCK(&pAdspAddr->adspao_Lock, &OldIrql);
|
|
ACQUIRE_SPIN_LOCK_DPC(&pAdspConn->adspco_Lock);
|
|
|
|
pAdspConn->adspco_Flags &= ~(ADSPCO_LISTENING |
|
|
ADSPCO_CONNECTING |
|
|
ADSPCO_HALF_ACTIVE|
|
|
ADSPCO_ACTIVE |
|
|
ADSPCO_DISCONNECTING);
|
|
|
|
atalkAdspConnDeQueueActiveList(pAdspAddr, pAdspConn);
|
|
|
|
// if the address has been disassociated, time to unlink it.
|
|
if (!(pAdspConn->adspco_Flags & ADSPCO_ASSOCIATED))
|
|
{
|
|
pAdspConn->adspco_pAssocAddr = NULL;
|
|
}
|
|
|
|
RELEASE_SPIN_LOCK_DPC(&pAdspConn->adspco_Lock);
|
|
RELEASE_SPIN_LOCK(&pAdspAddr->adspao_Lock, OldIrql);
|
|
|
|
// Call the disconnect completion routines.
|
|
if (*disconnectInform != NULL)
|
|
{
|
|
(*disconnectInform)(disconnectStatus, disconnectInformCtx);
|
|
}
|
|
|
|
if (*disconnectCompletion != NULL)
|
|
{
|
|
(*disconnectCompletion)(disconnectStatus, disconnectCtx);
|
|
}
|
|
}
|
|
|
|
if (stopping)
|
|
{
|
|
ACQUIRE_SPIN_LOCK(&pAdspConn->adspco_Lock, &OldIrql);
|
|
if ((pAdspConn->adspco_Flags & ADSPCO_STOPPING) != 0)
|
|
{
|
|
BOOLEAN fDisassoc = FALSE;
|
|
|
|
// See if we do the cleanup irp completion.
|
|
if (pAdspConn->adspco_RefCount == 1)
|
|
{
|
|
cleanupCtx = pAdspConn->adspco_CleanupCtx;
|
|
cleanupCompletion = pAdspConn->adspco_CleanupComp;
|
|
pAdspConn->adspco_CleanupComp = NULL;
|
|
pAdspConn->adspco_Flags &= ~ADSPCO_STOPPING;
|
|
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspConnDeref: Cleanup on %lx.%lx\n", pAdspConn, cleanupCtx));
|
|
|
|
if ((pAdspConn->adspco_Flags & (ADSPCO_LISTENING |
|
|
ADSPCO_CONNECTING |
|
|
ADSPCO_ACTIVE)) == 0)
|
|
{
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspConnDeref: Stopping - do disassoc for %lx\n", pAdspConn));
|
|
|
|
fDisassoc = (pAdspConn->adspco_Flags & ADSPCO_ASSOCIATED) ? TRUE: FALSE;
|
|
}
|
|
}
|
|
RELEASE_SPIN_LOCK(&pAdspConn->adspco_Lock, OldIrql);
|
|
|
|
if (fDisassoc)
|
|
{
|
|
// Call the disassociate routine. This should just fail, if the
|
|
// connection is still active or any other state than just
|
|
// plain associated.
|
|
AtalkAdspDissociateAddress(pAdspConn);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RELEASE_SPIN_LOCK(&pAdspConn->adspco_Lock, OldIrql);
|
|
}
|
|
}
|
|
|
|
if (done)
|
|
{
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspConnDeref: Close done for %lx\n", pAdspConn));
|
|
|
|
// Call the close completion routines
|
|
ASSERT(*pAdspConn->adspco_CloseComp != NULL);
|
|
if (*pAdspConn->adspco_CloseComp != NULL)
|
|
{
|
|
(*pAdspConn->adspco_CloseComp )(ATALK_NO_ERROR,
|
|
pAdspConn->adspco_CloseCtx);
|
|
}
|
|
|
|
// Remove from the global list.
|
|
atalkAdspConnDeQueueGlobalList(pAdspConn);
|
|
|
|
// Free up the connection memory.
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspConnDeref: Freeing up connection %lx\n", pAdspConn));
|
|
|
|
AtalkUnlockAdspIfNecessary();
|
|
AtalkFreeMemory(pAdspConn);
|
|
}
|
|
|
|
if (*cleanupCompletion != NULL)
|
|
{
|
|
(*cleanupCompletion)(ATALK_NO_ERROR, cleanupCtx);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// ADSP BUFFER QUEUE MANAGEMENT ROUTINES
|
|
//
|
|
|
|
ULONG
|
|
atalkAdspMaxSendSize(
|
|
IN PADSP_CONNOBJ pAdspConn
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The answer is the remaining available (to fill) space in the retransmit
|
|
queue -- this includes data we're saving for possible retransmit as well
|
|
as data we haven't sent yet. Actually, this could go negative because
|
|
BufferQueueSize counts EOMs and sendQueueMax doesn't -- answer with zero
|
|
if this happens.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
LONG sendSize;
|
|
|
|
sendSize = pAdspConn->adspco_SendQueueMax -
|
|
atalkAdspBufferQueueSize(&pAdspConn->adspco_SendQueue);
|
|
|
|
if (sendSize < 0)
|
|
{
|
|
sendSize = 0;
|
|
}
|
|
|
|
return ((ULONG)sendSize);
|
|
}
|
|
|
|
|
|
|
|
|
|
ULONG
|
|
atalkAdspMaxNextReadSize(
|
|
IN PBUFFER_QUEUE pQueue,
|
|
OUT PBOOLEAN pEom,
|
|
OUT PBUFFER_CHUNK * pBufferChunk
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Return the size of data in a buffer queue; upto the end of the
|
|
current chunk, or to the eom.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PBUFFER_CHUNK pCurrentChunk;
|
|
ULONG nextReadSize;
|
|
ULONG startIndex = pQueue->bq_StartIndex;
|
|
|
|
ASSERT(((pQueue->bq_Head == NULL) && (pQueue->bq_Tail == NULL)) ||
|
|
((pQueue->bq_Head != NULL) && (pQueue->bq_Tail != NULL)));
|
|
|
|
*pEom = FALSE;
|
|
|
|
// Walk the queue.
|
|
for (pCurrentChunk = pQueue->bq_Head;
|
|
pCurrentChunk != NULL;
|
|
pCurrentChunk = pCurrentChunk->bc_Next)
|
|
{
|
|
// Check for nothing in the current chunk
|
|
if (startIndex == (ULONG)(pCurrentChunk->bc_DataSize +
|
|
BYTECOUNT(pCurrentChunk->bc_Flags & BC_EOM)))
|
|
{
|
|
startIndex = 0;
|
|
continue;
|
|
}
|
|
|
|
nextReadSize = pCurrentChunk->bc_DataSize - startIndex;
|
|
if (pCurrentChunk->bc_Flags & BC_EOM)
|
|
{
|
|
*pEom = TRUE;
|
|
}
|
|
|
|
*pBufferChunk = pCurrentChunk;
|
|
break;
|
|
}
|
|
|
|
// Return the size.
|
|
return nextReadSize;
|
|
}
|
|
|
|
|
|
|
|
|
|
ULONG
|
|
atalkAdspDescribeFromBufferQueue(
|
|
IN PBUFFER_QUEUE pQueue,
|
|
OUT PBOOLEAN pEom,
|
|
IN ULONG WindowSize,
|
|
OUT PBUFFER_CHUNK * ppBufferChunk,
|
|
OUT PBUFFER_DESC * ppBuffDesc
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
In order to avoid pQueue (nextSendQueue) to go to null when all the data available
|
|
is being sent, we make it logically be at the end while still pointing to the
|
|
buffer chunk. This is the reason, we have all the datasize == (startindex + eom)
|
|
checks. This is where such a condition will be created.
|
|
|
|
NO! We let pQueue go to null when all the data is done, otherwise we will have
|
|
pointers to a buffer chunk that will be freed during discard, and we dont want to
|
|
make discard dependent upon the auxqueue.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PBUFFER_CHUNK pCurrentChunk;
|
|
PBUFFER_DESC pBuffDesc;
|
|
ULONG nextReadSize = 0;
|
|
ULONG startIndex = pQueue->bq_StartIndex;
|
|
|
|
*pEom = FALSE;
|
|
*ppBufferChunk = NULL;
|
|
*ppBuffDesc = NULL;
|
|
|
|
ASSERT(((pQueue->bq_Head == NULL) && (pQueue->bq_Tail == NULL)) ||
|
|
((pQueue->bq_Head != NULL) && (pQueue->bq_Tail != NULL)));
|
|
|
|
// Walk the queue.
|
|
for (pCurrentChunk = pQueue->bq_Head;
|
|
pCurrentChunk != NULL;
|
|
pCurrentChunk = pCurrentChunk->bc_Next)
|
|
{
|
|
// Check for nothing in the current chunk
|
|
if (startIndex == (ULONG)(pCurrentChunk->bc_DataSize +
|
|
BYTECOUNT(pCurrentChunk->bc_Flags & BC_EOM)))
|
|
{
|
|
ASSERT(0);
|
|
startIndex = 0;
|
|
continue;
|
|
}
|
|
|
|
nextReadSize = pCurrentChunk->bc_DataSize - startIndex;
|
|
|
|
// Look at eom only if chunk is consumed.
|
|
*pEom = FALSE;
|
|
ASSERT(nextReadSize <= pCurrentChunk->bc_DataSize);
|
|
|
|
// Make sure dataSize is within bounds
|
|
if (nextReadSize > ADSP_MAX_DATA_SIZE)
|
|
{
|
|
nextReadSize = ADSP_MAX_DATA_SIZE;
|
|
}
|
|
|
|
if (nextReadSize > (ULONG)WindowSize)
|
|
{
|
|
nextReadSize = (ULONG)WindowSize;
|
|
}
|
|
|
|
if (nextReadSize > 0)
|
|
{
|
|
// First try to reference the buffer chunk. This should always succeed.
|
|
atalkAdspBufferChunkReference(pCurrentChunk);
|
|
|
|
// Create a descriptor for the data. The above reference goes away in a send
|
|
// complete.
|
|
pBuffDesc = AtalkDescribeBuffDesc((PBYTE)pCurrentChunk + sizeof(BUFFER_CHUNK) + startIndex,
|
|
NULL,
|
|
(USHORT)nextReadSize,
|
|
BD_CHAR_BUFFER);
|
|
|
|
*ppBufferChunk = pCurrentChunk;
|
|
*ppBuffDesc = pBuffDesc;
|
|
}
|
|
|
|
// Also update the queue for this data. Either we have consumed
|
|
// this chunk or we have just used a portion of it.
|
|
if ((nextReadSize + startIndex) == pCurrentChunk->bc_DataSize)
|
|
{
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspDescribeFromBufferQueue: Chunk consumed %d\n",
|
|
pCurrentChunk->bc_DataSize));
|
|
|
|
ASSERT(pQueue->bq_Head != NULL);
|
|
|
|
// Set EOM if chunk had one.
|
|
if (pCurrentChunk->bc_Flags & BC_EOM)
|
|
{
|
|
*pEom = TRUE;
|
|
}
|
|
|
|
if (pQueue->bq_Head == pQueue->bq_Tail)
|
|
{
|
|
ASSERT(pQueue->bq_Head->bc_Next == NULL);
|
|
pQueue->bq_Tail = pQueue->bq_Head->bc_Next;
|
|
ASSERT(pQueue->bq_Tail == NULL);
|
|
}
|
|
|
|
pQueue->bq_Head = pQueue->bq_Head->bc_Next;
|
|
pQueue->bq_StartIndex = (ULONG)0;
|
|
}
|
|
else
|
|
{
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspDescribeFromBufferQueue: Chunk not consumed %d.%d\n",
|
|
pCurrentChunk->bc_DataSize, nextReadSize+startIndex));
|
|
|
|
// Just set the start index
|
|
pQueue->bq_StartIndex += (ULONG)nextReadSize;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
// Return the size.
|
|
return nextReadSize;
|
|
}
|
|
|
|
|
|
|
|
ULONG
|
|
atalkAdspBufferQueueSize(
|
|
IN PBUFFER_QUEUE pQueue
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Return the total size of a buffer queue; each EOM counts as a single
|
|
byte.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PBUFFER_CHUNK pCurrentChunk;
|
|
ULONG startIndex;
|
|
ULONG queueSize;
|
|
|
|
ASSERT(((pQueue->bq_Head == NULL) && (pQueue->bq_Tail == NULL)) ||
|
|
((pQueue->bq_Head != NULL) && (pQueue->bq_Tail != NULL)));
|
|
|
|
// Walk the queue.
|
|
for (queueSize = 0, startIndex = pQueue->bq_StartIndex, pCurrentChunk = pQueue->bq_Head;
|
|
pCurrentChunk != NULL;
|
|
pCurrentChunk = pCurrentChunk->bc_Next)
|
|
{
|
|
// Check for nothing in the current chunk.
|
|
if (startIndex == (ULONG)(pCurrentChunk->bc_DataSize +
|
|
BYTECOUNT(pCurrentChunk->bc_Flags & BC_EOM)))
|
|
{
|
|
startIndex = 0;
|
|
continue;
|
|
}
|
|
|
|
queueSize += ( pCurrentChunk->bc_DataSize -
|
|
startIndex +
|
|
BYTECOUNT(pCurrentChunk->bc_Flags & BC_EOM));
|
|
|
|
// StartIndex only counts in first chunk
|
|
startIndex = 0;
|
|
}
|
|
|
|
// Return the size.
|
|
return queueSize;
|
|
}
|
|
|
|
|
|
|
|
|
|
ULONG
|
|
atalkAdspMessageSize(
|
|
IN PBUFFER_QUEUE pQueue,
|
|
OUT PBOOLEAN pEom
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Return the total size of the data in the buffer queue, stopping at eom
|
|
or end of data. EOM is not part of the count.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PBUFFER_CHUNK pCurrentChunk;
|
|
ULONG msgSize = 0;
|
|
ULONG startIndex = pQueue->bq_StartIndex;
|
|
|
|
ASSERT(((pQueue->bq_Head == NULL) && (pQueue->bq_Tail == NULL)) ||
|
|
((pQueue->bq_Head != NULL) && (pQueue->bq_Tail != NULL)));
|
|
|
|
*pEom = FALSE;
|
|
|
|
// Walk the queue.
|
|
for (pCurrentChunk = pQueue->bq_Head;
|
|
pCurrentChunk != NULL;
|
|
pCurrentChunk = pCurrentChunk->bc_Next)
|
|
{
|
|
// Check for nothing in the current chunk.
|
|
if (startIndex == (ULONG)(pCurrentChunk->bc_DataSize +
|
|
BYTECOUNT(pCurrentChunk->bc_Flags & BC_EOM)))
|
|
{
|
|
startIndex = 0;
|
|
continue;
|
|
}
|
|
|
|
msgSize += (pCurrentChunk->bc_DataSize - startIndex);
|
|
if (pCurrentChunk->bc_Flags & BC_EOM)
|
|
{
|
|
*pEom = TRUE;
|
|
break;
|
|
}
|
|
|
|
// StartIndex only counts in first chunk
|
|
startIndex = 0;
|
|
}
|
|
|
|
// Return the size.
|
|
return msgSize;
|
|
}
|
|
|
|
|
|
|
|
|
|
PBUFFER_CHUNK
|
|
atalkAdspAllocCopyChunk(
|
|
IN PVOID pWriteBuf,
|
|
IN USHORT WriteBufLen,
|
|
IN BOOLEAN Eom,
|
|
IN BOOLEAN IsCharBuffer
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PBUFFER_CHUNK pChunk;
|
|
PBYTE pData;
|
|
NTSTATUS status;
|
|
ULONG bytesCopied;
|
|
|
|
if ((pChunk = (PBUFFER_CHUNK)AtalkAllocMemory(sizeof(BUFFER_CHUNK) + WriteBufLen)) != NULL)
|
|
{
|
|
|
|
pChunk->bc_DataSize = WriteBufLen;
|
|
pChunk->bc_Flags = (Eom ? BC_EOM : 0);
|
|
pChunk->bc_Next = NULL;
|
|
pChunk->bc_RefCount = 1; // Creation ref count
|
|
|
|
INITIALIZE_SPIN_LOCK(&pChunk->bc_Lock);
|
|
|
|
// Copy the data over if its greater than zero
|
|
if (WriteBufLen > 0)
|
|
{
|
|
pData = (PBYTE)pChunk + sizeof(BUFFER_CHUNK);
|
|
if (IsCharBuffer)
|
|
{
|
|
RtlCopyMemory(pData,
|
|
(PBYTE)pWriteBuf,
|
|
WriteBufLen);
|
|
}
|
|
else
|
|
{
|
|
status = TdiCopyMdlToBuffer((PMDL)pWriteBuf,
|
|
0,
|
|
pData,
|
|
0,
|
|
WriteBufLen,
|
|
&bytesCopied);
|
|
|
|
ASSERT(!NT_ERROR(status) && (bytesCopied == (ULONG)WriteBufLen));
|
|
}
|
|
}
|
|
}
|
|
|
|
return pChunk;
|
|
}
|
|
|
|
|
|
|
|
|
|
PBYTE
|
|
atalkAdspGetLookahead(
|
|
IN PBUFFER_QUEUE pQueue,
|
|
OUT PULONG pLookaheadSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PBUFFER_CHUNK pCurrentChunk;
|
|
ULONG startIndex = pQueue->bq_StartIndex;
|
|
|
|
ASSERT(((pQueue->bq_Head == NULL) && (pQueue->bq_Tail == NULL)) ||
|
|
((pQueue->bq_Head != NULL) && (pQueue->bq_Tail != NULL)));
|
|
|
|
pCurrentChunk = pQueue->bq_Head;
|
|
if (pCurrentChunk != NULL)
|
|
{
|
|
// Do we need to go past the current chunk?
|
|
if (startIndex == (ULONG)(pCurrentChunk->bc_DataSize +
|
|
BYTECOUNT(pCurrentChunk->bc_Flags & BC_EOM)))
|
|
{
|
|
pCurrentChunk = pCurrentChunk->bc_Next;
|
|
startIndex = 0;
|
|
}
|
|
}
|
|
|
|
ASSERT(pCurrentChunk != NULL);
|
|
if (pCurrentChunk == NULL)
|
|
{
|
|
KeBugCheck(0);
|
|
}
|
|
|
|
*pLookaheadSize = pCurrentChunk->bc_DataSize - startIndex;
|
|
return((*pLookaheadSize == 0) ?
|
|
NULL :
|
|
(PBYTE)pCurrentChunk + sizeof(BUFFER_CHUNK) + startIndex);
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
atalkAdspAddToBufferQueue(
|
|
IN OUT PBUFFER_QUEUE pQueue,
|
|
IN PBUFFER_CHUNK pChunk,
|
|
IN OUT PBUFFER_QUEUE pAuxQueue OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
!!!MUST BE CALLED WITH THE CONNECTION LOCK HELD!!!
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
ASSERT(((pQueue->bq_Head == NULL) && (pQueue->bq_Tail == NULL)) ||
|
|
((pQueue->bq_Head != NULL) && (pQueue->bq_Tail != NULL)));
|
|
|
|
if (pQueue->bq_Head != NULL)
|
|
{
|
|
// Add the chunk to the end of the queue
|
|
ASSERT(pQueue->bq_Tail != NULL);
|
|
pQueue->bq_Tail->bc_Next = pChunk;
|
|
pQueue->bq_Tail = pChunk;
|
|
|
|
ASSERT(pChunk->bc_Next == NULL);
|
|
|
|
// The auxiliary queue is the nextsend queue, which can go to null
|
|
// if we have sent all the data. If that is the case, we need to
|
|
// reset the head also.
|
|
if (ARGUMENT_PRESENT(pAuxQueue))
|
|
{
|
|
if (pAuxQueue->bq_Head == NULL)
|
|
{
|
|
pAuxQueue->bq_Head = pChunk;
|
|
}
|
|
|
|
pAuxQueue->bq_Tail = pChunk;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pQueue->bq_Head = pQueue->bq_Tail = pChunk;
|
|
pQueue->bq_StartIndex = (ULONG)0;
|
|
if (ARGUMENT_PRESENT(pAuxQueue))
|
|
{
|
|
// Initialize the next send queue only if this is a send queue
|
|
pAuxQueue->bq_Head = pAuxQueue->bq_Tail = pChunk;
|
|
pAuxQueue->bq_StartIndex= (ULONG)0;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
ULONG
|
|
atalkAdspReadFromBufferQueue(
|
|
IN PBUFFER_QUEUE pQueue,
|
|
IN ULONG ReadFlags,
|
|
OUT PAMDL pReadBuf,
|
|
IN OUT PUSHORT pReadLen,
|
|
OUT PBOOLEAN pEom
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PBUFFER_CHUNK pCurrentChunk;
|
|
ULONG bytesRead, copySize, dataIndex, dataSize, lastReadIndex;
|
|
NTSTATUS status;
|
|
LONG startIndex = pQueue->bq_StartIndex;
|
|
ATALK_ERROR error = ATALK_NO_ERROR;
|
|
ULONG readSize = 0; // size counting eom
|
|
|
|
ASSERT(((pQueue->bq_Head == NULL) && (pQueue->bq_Tail == NULL)) ||
|
|
((pQueue->bq_Head != NULL) && (pQueue->bq_Tail != NULL)));
|
|
|
|
*pEom = FALSE;
|
|
readSize = 0;
|
|
pCurrentChunk = pQueue->bq_Head;
|
|
if ((pCurrentChunk == NULL) ||
|
|
((pCurrentChunk->bc_Next == NULL) &&
|
|
((ULONG)startIndex == pCurrentChunk->bc_DataSize +
|
|
BYTECOUNT(pCurrentChunk->bc_Flags & BC_EOM))))
|
|
{
|
|
*pReadLen = 0;
|
|
return 0;
|
|
}
|
|
|
|
dataIndex = 0;
|
|
dataSize = *pReadLen;
|
|
|
|
// Copy data until we exhaust src/dest buffers or hit an eom
|
|
for (;
|
|
pCurrentChunk != NULL;
|
|
pCurrentChunk = pCurrentChunk->bc_Next)
|
|
{
|
|
if ((ULONG)startIndex == pCurrentChunk->bc_DataSize +
|
|
BYTECOUNT(pCurrentChunk->bc_Flags & BC_EOM))
|
|
{
|
|
ASSERT(0);
|
|
startIndex = 0;
|
|
continue;
|
|
}
|
|
|
|
copySize = MIN((ULONG)(pCurrentChunk->bc_DataSize - startIndex), dataSize);
|
|
if (copySize > 0)
|
|
{
|
|
status = TdiCopyBufferToMdl((PBYTE)pCurrentChunk +
|
|
sizeof(BUFFER_CHUNK) +
|
|
startIndex,
|
|
0,
|
|
copySize,
|
|
pReadBuf,
|
|
dataIndex,
|
|
&bytesRead);
|
|
|
|
ASSERT(NT_SUCCESS(status) && (copySize == bytesRead));
|
|
}
|
|
|
|
dataIndex += copySize;
|
|
readSize += copySize;
|
|
dataSize -= copySize;
|
|
lastReadIndex = startIndex + copySize;
|
|
|
|
// Check for terminating conditions
|
|
startIndex = 0;
|
|
|
|
// Check EOM only if chunk consumed.
|
|
if ((lastReadIndex == pCurrentChunk->bc_DataSize) &&
|
|
(pCurrentChunk->bc_Flags & BC_EOM))
|
|
{
|
|
readSize += 1;
|
|
*pEom = TRUE;
|
|
break;
|
|
}
|
|
|
|
if (dataSize == 0) // Is the user buffer full?
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
*pReadLen = (USHORT)dataIndex;
|
|
|
|
// Free any chunks that we are done with, only if this was not a peek request.
|
|
if ((ReadFlags & TDI_RECEIVE_PEEK) == 0)
|
|
{
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspReadFromBufferQueue: Discarding data %lx\n", dataIndex));
|
|
|
|
atalkAdspDiscardFromBufferQueue(pQueue,
|
|
readSize,
|
|
NULL,
|
|
ATALK_NO_ERROR,
|
|
NULL);
|
|
}
|
|
|
|
return dataIndex;
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOLEAN
|
|
atalkAdspDiscardFromBufferQueue(
|
|
IN PBUFFER_QUEUE pQueue,
|
|
IN ULONG DataSize,
|
|
OUT PBUFFER_QUEUE pAuxQueue,
|
|
IN ATALK_ERROR Error,
|
|
IN PADSP_CONNOBJ pAdspConn OPTIONAL // Required for send queue
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PBUFFER_CHUNK pCurrentChunk, pNextChunk;
|
|
ULONG chunkSize, startIndex = pQueue->bq_StartIndex;
|
|
|
|
// BUBBUG: error checks
|
|
|
|
// Walk along the queue discarding the data we have already read
|
|
for (pCurrentChunk = pQueue->bq_Head, pNextChunk = NULL;
|
|
pCurrentChunk != NULL;
|
|
pCurrentChunk = pNextChunk)
|
|
{
|
|
pNextChunk = pCurrentChunk->bc_Next;
|
|
|
|
chunkSize = pCurrentChunk->bc_DataSize -
|
|
startIndex + BYTECOUNT(pCurrentChunk->bc_Flags & BC_EOM);
|
|
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspDiscardFromBufferQueue: Discarding %ld.%ld\n", DataSize, chunkSize));
|
|
|
|
// If we finished discarding but there is still some data left in the
|
|
// current chunk, just reset the start index.
|
|
if (DataSize < chunkSize)
|
|
{
|
|
// Already done: pQueue->bq_Head = pCurrentChunk;
|
|
pQueue->bq_StartIndex = startIndex + DataSize;
|
|
|
|
ASSERT((pQueue->bq_Head != pQueue->bq_Tail) ||
|
|
(pCurrentChunk->bc_Next == NULL));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// Otherwise, we have discarded a whole chunk
|
|
if ((pAuxQueue != NULL) &&
|
|
(pAuxQueue->bq_Head == pCurrentChunk) &&
|
|
((pAuxQueue->bq_Head->bc_Next != NULL) ||
|
|
(pAuxQueue->bq_StartIndex <
|
|
(pAuxQueue->bq_Head->bc_DataSize +
|
|
(ULONG)BYTECOUNT(pAuxQueue->bq_Head->bc_Flags & BC_EOM)))))
|
|
{
|
|
ASSERT(0);
|
|
pAuxQueue->bq_Head = pAuxQueue->bq_Tail = NULL;
|
|
pAuxQueue->bq_StartIndex = (ULONG)0;
|
|
}
|
|
|
|
// If SEND chunk, set error for the send to be success
|
|
if (pCurrentChunk->bc_Flags & BC_SEND)
|
|
{
|
|
pCurrentChunk->bc_WriteError = Error;
|
|
ASSERT(pAdspConn != NULL);
|
|
}
|
|
|
|
//
|
|
// make our head point to the next guy since this chunk is going away.
|
|
//
|
|
pQueue->bq_Head = pNextChunk;
|
|
pQueue->bq_StartIndex = 0;
|
|
|
|
if (pQueue->bq_Tail == pCurrentChunk)
|
|
{
|
|
pQueue->bq_Tail = NULL;
|
|
}
|
|
|
|
// Deref for creation.
|
|
atalkAdspBufferChunkDereference(pCurrentChunk,
|
|
TRUE,
|
|
pAdspConn);
|
|
|
|
// Move on to the next chunk
|
|
DataSize -= chunkSize;
|
|
startIndex = 0;
|
|
}
|
|
|
|
// If we are here, then the whole queue has been discarded, mark
|
|
// it as empty
|
|
ASSERT(DataSize == 0);
|
|
|
|
//pQueue->bq_Head = pQueue->bq_Tail = NULL;
|
|
//pQueue->bq_StartIndex = 0;
|
|
|
|
//
|
|
// if the last chunk gets freed above, we release the spinlock to complete the
|
|
// irp associated with the chunk and then grab it again. It's possible to get
|
|
// a new send in that window, so bq_head may not necessarily be NULL at this
|
|
// point (in fact, bug #16660 turned out to be exactly this!!)
|
|
//
|
|
if (pQueue->bq_Head == NULL)
|
|
{
|
|
ASSERT(pQueue->bq_Tail == NULL);
|
|
|
|
if (pAuxQueue != NULL)
|
|
{
|
|
pAuxQueue->bq_Head = pAuxQueue->bq_Tail = NULL;
|
|
pAuxQueue->bq_StartIndex = (LONG)0;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
atalkAdspBufferChunkReference(
|
|
IN PBUFFER_CHUNK pBufferChunk
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
KIRQL OldIrql;
|
|
|
|
ACQUIRE_SPIN_LOCK(&pBufferChunk->bc_Lock, &OldIrql);
|
|
if ((pBufferChunk->bc_Flags & BC_CLOSING) == 0)
|
|
{
|
|
pBufferChunk->bc_RefCount++;
|
|
}
|
|
else
|
|
{
|
|
// Should never be trying to reference this when closing. The retransmit
|
|
// timer should have been cancelled.
|
|
KeBugCheck(0);
|
|
}
|
|
RELEASE_SPIN_LOCK(&pBufferChunk->bc_Lock, OldIrql);
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
atalkAdspBufferChunkDereference(
|
|
IN PBUFFER_CHUNK pBufferChunk,
|
|
IN BOOLEAN CreationDeref,
|
|
IN PADSP_CONNOBJ pAdspConn OPTIONAL // Required for send chunk
|
|
// If spinlock held
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN done = FALSE;
|
|
BOOLEAN sendChunk = FALSE;
|
|
KIRQL OldIrql;
|
|
|
|
|
|
ACQUIRE_SPIN_LOCK(&pBufferChunk->bc_Lock, &OldIrql);
|
|
if (!CreationDeref ||
|
|
((pBufferChunk->bc_Flags & BC_CLOSING) == 0))
|
|
{
|
|
if (CreationDeref)
|
|
{
|
|
pBufferChunk->bc_Flags |= BC_CLOSING;
|
|
}
|
|
|
|
if (--pBufferChunk->bc_RefCount == 0)
|
|
{
|
|
ASSERT(pBufferChunk->bc_Flags & BC_CLOSING);
|
|
done = TRUE;
|
|
sendChunk = (pBufferChunk->bc_Flags & BC_SEND) ? TRUE : FALSE;
|
|
}
|
|
}
|
|
RELEASE_SPIN_LOCK(&pBufferChunk->bc_Lock, OldIrql);
|
|
|
|
if (done)
|
|
{
|
|
// Call send completion if this is a send buffer chunk
|
|
if (sendChunk)
|
|
{
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkChunkDereference: Completing send %lx. %lx - %d.%d\n",
|
|
pAdspConn, pBufferChunk->bc_WriteCtx,
|
|
pBufferChunk->bc_DataSize, pBufferChunk->bc_WriteError));
|
|
|
|
if (pAdspConn != NULL)
|
|
{
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkChunkDereference: Completing send %lx.%lx\n",
|
|
pAdspConn, pBufferChunk->bc_WriteCtx));
|
|
|
|
// Release connection lock
|
|
RELEASE_SPIN_LOCK(&pAdspConn->adspco_Lock, OldIrql);
|
|
}
|
|
|
|
// Call the completion routine. We complete with no error, but
|
|
// need to return pending.
|
|
ASSERT((*pBufferChunk->bc_WriteCompletion) != NULL);
|
|
(*pBufferChunk->bc_WriteCompletion)(pBufferChunk->bc_WriteError,
|
|
pBufferChunk->bc_WriteBuf,
|
|
pBufferChunk->bc_DataSize,
|
|
pBufferChunk->bc_WriteCtx);
|
|
|
|
if (pAdspConn != NULL)
|
|
{
|
|
ACQUIRE_SPIN_LOCK(&pAdspConn->adspco_Lock, &OldIrql);
|
|
}
|
|
}
|
|
|
|
|
|
// This better not be part of the queues at this point, we should
|
|
// just be able to free it up. The idea is that if a particular
|
|
// buffer descriptor has its creation reference removed, its only
|
|
// because the data is being discarded or the connection is shutting
|
|
// down, in both cases, the data previous to this must be also being
|
|
// discarded and the buffer queue pointers will be set to the chunks
|
|
// following the ones being discarded. If this wont be true, walk the
|
|
// list (need more info coming in) and unlink this chunk before freeing
|
|
// it.
|
|
AtalkFreeMemory(pBufferChunk);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// ADSP UTILITY ROUTINES
|
|
//
|
|
|
|
|
|
VOID
|
|
atalkAdspDecodeHeader(
|
|
IN PBYTE Datagram,
|
|
OUT PUSHORT RemoteConnId,
|
|
OUT PULONG FirstByteSeq,
|
|
OUT PULONG NextRecvSeq,
|
|
OUT PLONG Window,
|
|
OUT PBYTE Descriptor
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
GETSHORT2SHORT(RemoteConnId, Datagram + ADSP_SRC_CONNID_OFF);
|
|
|
|
GETDWORD2DWORD(FirstByteSeq, Datagram + ADSP_FIRST_BYTE_SEQNUM_OFF);
|
|
|
|
GETDWORD2DWORD(NextRecvSeq, Datagram + ADSP_NEXT_RX_BYTESEQNUM_OFF);
|
|
|
|
GETSHORT2DWORD(Window, Datagram + ADSP_RX_WINDOW_SIZE_OFF);
|
|
|
|
// Set the descriptor
|
|
*Descriptor = Datagram[ADSP_DESCRIPTOR_OFF];
|
|
}
|
|
|
|
|
|
|
|
|
|
LOCAL USHORT
|
|
atalkAdspGetNextConnId(
|
|
IN PADSP_ADDROBJ pAdspAddr,
|
|
OUT PATALK_ERROR pError
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
CALLED WITH THE ADDRESS SPIN LOCK HELD!
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PADSP_CONNOBJ pAdspConn;
|
|
USHORT i;
|
|
USHORT startConnId, connId;
|
|
ATALK_ERROR error = ATALK_NO_ERROR;
|
|
|
|
startConnId = connId = ++pAdspAddr->adspao_NextConnId;
|
|
while (TRUE)
|
|
{
|
|
for (i = 0; i < ADSP_CONN_HASH_SIZE; i++)
|
|
{
|
|
for (pAdspConn = pAdspAddr->adspao_pActiveHash[i];
|
|
((pAdspConn != NULL) && (pAdspConn->adspco_LocalConnId != connId));
|
|
pAdspConn = pAdspConn->adspco_pNextActive);
|
|
|
|
if (pAdspConn != NULL)
|
|
break;
|
|
}
|
|
|
|
if (pAdspConn == NULL)
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if (connId == (startConnId - 1))
|
|
{
|
|
ASSERT(0);
|
|
|
|
// We wrapped around and there are no more conn ids.
|
|
error = ATALK_RESR_MEM;
|
|
break;
|
|
}
|
|
connId++;
|
|
}
|
|
}
|
|
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspGetNextConnId: ConnId %lx for %lx\n", connId, pAdspAddr));
|
|
|
|
*pError = error;
|
|
return(ATALK_SUCCESS(error) ? connId : 0);
|
|
}
|
|
|
|
|
|
|
|
|
|
LOCAL BOOLEAN
|
|
atalkAdspConnDeQueueAssocList(
|
|
IN PADSP_ADDROBJ pAdspAddr,
|
|
IN PADSP_CONNOBJ pAdspConn
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PADSP_CONNOBJ pAdspRemConn, *ppAdspRemConn;
|
|
BOOLEAN removed = FALSE;
|
|
|
|
for (ppAdspRemConn = &pAdspAddr->adspao_pAssocConn;
|
|
((pAdspRemConn = *ppAdspRemConn) != NULL); )
|
|
{
|
|
if (pAdspRemConn == pAdspConn)
|
|
{
|
|
removed = TRUE;
|
|
*ppAdspRemConn = pAdspRemConn->adspco_pNextAssoc;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
ppAdspRemConn = &pAdspRemConn->adspco_pNextAssoc;
|
|
}
|
|
}
|
|
|
|
return removed;
|
|
}
|
|
|
|
|
|
|
|
|
|
LOCAL BOOLEAN
|
|
atalkAdspConnDeQueueConnectList(
|
|
IN PADSP_ADDROBJ pAdspAddr,
|
|
IN PADSP_CONNOBJ pAdspConn
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PADSP_CONNOBJ pAdspRemConn, *ppAdspRemConn;
|
|
BOOLEAN removed = FALSE;
|
|
|
|
ASSERT(pAdspAddr->adspao_Flags & ADSPAO_CONNECT);
|
|
|
|
for (ppAdspRemConn = &pAdspAddr->adspao_pConnectConn;
|
|
((pAdspRemConn = *ppAdspRemConn) != NULL); )
|
|
{
|
|
if (pAdspRemConn == pAdspConn)
|
|
{
|
|
removed = TRUE;
|
|
*ppAdspRemConn = pAdspRemConn->adspco_pNextConnect;
|
|
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspConnDeQueueConnectList: Removed connect conn %lx\n", pAdspConn));
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
ppAdspRemConn = &pAdspRemConn->adspco_pNextConnect;
|
|
}
|
|
}
|
|
|
|
return removed;
|
|
}
|
|
|
|
|
|
|
|
|
|
LOCAL BOOLEAN
|
|
atalkAdspConnDeQueueListenList(
|
|
IN PADSP_ADDROBJ pAdspAddr,
|
|
IN PADSP_CONNOBJ pAdspConn
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PADSP_CONNOBJ pAdspRemConn, *ppAdspRemConn;
|
|
BOOLEAN removed = FALSE;
|
|
|
|
ASSERT(pAdspAddr->adspao_Flags & ADSPAO_LISTENER);
|
|
|
|
for (ppAdspRemConn = &pAdspAddr->adspao_pListenConn;
|
|
((pAdspRemConn = *ppAdspRemConn) != NULL); )
|
|
{
|
|
if (pAdspRemConn == pAdspConn)
|
|
{
|
|
removed = TRUE;
|
|
*ppAdspRemConn = pAdspRemConn->adspco_pNextListen;
|
|
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspConnDeQueueListenList: Removed listen conn %lx\n", pAdspConn));
|
|
}
|
|
else
|
|
{
|
|
ppAdspRemConn = &pAdspRemConn->adspco_pNextListen;
|
|
}
|
|
}
|
|
|
|
return removed;
|
|
}
|
|
|
|
|
|
|
|
|
|
LOCAL BOOLEAN
|
|
atalkAdspConnDeQueueActiveList(
|
|
IN PADSP_ADDROBJ pAdspAddr,
|
|
IN PADSP_CONNOBJ pAdspConn
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PADSP_CONNOBJ pAdspRemConn, *ppAdspRemConn;
|
|
ULONG index;
|
|
BOOLEAN removed = FALSE;
|
|
|
|
index = HASH_ID_SRCADDR(
|
|
pAdspConn->adspco_RemoteConnId,
|
|
&pAdspConn->adspco_RemoteAddr);
|
|
|
|
index %= ADSP_CONN_HASH_SIZE;
|
|
|
|
for (ppAdspRemConn = &pAdspAddr->adspao_pActiveHash[index];
|
|
((pAdspRemConn = *ppAdspRemConn) != NULL); )
|
|
{
|
|
if (pAdspRemConn == pAdspConn)
|
|
{
|
|
removed = TRUE;
|
|
*ppAdspRemConn = pAdspRemConn->adspco_pNextActive;
|
|
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspConnDeQueueActiveList: Removed active conn %lx\n", pAdspConn));
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
ppAdspRemConn = &pAdspRemConn->adspco_pNextActive;
|
|
}
|
|
}
|
|
|
|
return removed;
|
|
}
|
|
|
|
|
|
|
|
|
|
LOCAL VOID
|
|
atalkAdspAddrQueueGlobalList(
|
|
IN PADSP_ADDROBJ pAdspAddr
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
KIRQL OldIrql;
|
|
|
|
ACQUIRE_SPIN_LOCK(&atalkAdspLock, &OldIrql);
|
|
pAdspAddr->adspao_pNextGlobal = atalkAdspAddrList;
|
|
atalkAdspAddrList = pAdspAddr;
|
|
RELEASE_SPIN_LOCK(&atalkAdspLock, OldIrql);
|
|
}
|
|
|
|
|
|
LOCAL VOID
|
|
atalkAdspAddrDeQueueGlobalList(
|
|
IN PADSP_ADDROBJ pAdspAddr
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
KIRQL OldIrql;
|
|
PADSP_ADDROBJ pAdspRemAddr, *ppAdspRemAddr;
|
|
|
|
ACQUIRE_SPIN_LOCK(&atalkAdspLock, &OldIrql);
|
|
for (ppAdspRemAddr = &atalkAdspAddrList;
|
|
((pAdspRemAddr = *ppAdspRemAddr) != NULL); )
|
|
{
|
|
if (pAdspRemAddr == pAdspAddr)
|
|
{
|
|
*ppAdspRemAddr = pAdspRemAddr->adspao_pNextGlobal;
|
|
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspAddrDeQueueGlobalList: Removed global conn %lx\n",pAdspAddr));
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
ppAdspRemAddr = &pAdspRemAddr->adspao_pNextGlobal;
|
|
}
|
|
}
|
|
RELEASE_SPIN_LOCK(&atalkAdspLock, OldIrql);
|
|
}
|
|
|
|
|
|
|
|
|
|
LOCAL VOID
|
|
atalkAdspConnDeQueueGlobalList(
|
|
IN PADSP_CONNOBJ pAdspConn
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
KIRQL OldIrql;
|
|
PADSP_CONNOBJ pAdspRemConn, *ppAdspRemConn;
|
|
|
|
ACQUIRE_SPIN_LOCK(&atalkAdspLock, &OldIrql);
|
|
for (ppAdspRemConn = &atalkAdspConnList;
|
|
((pAdspRemConn = *ppAdspRemConn) != NULL); )
|
|
{
|
|
if (pAdspRemConn == pAdspConn)
|
|
{
|
|
*ppAdspRemConn = pAdspRemConn->adspco_pNextGlobal;
|
|
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspConnDeQueueGlobalList: Removed global conn %lx\n", pAdspConn));
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
ppAdspRemConn = &pAdspRemConn->adspco_pNextGlobal;
|
|
}
|
|
}
|
|
RELEASE_SPIN_LOCK(&atalkAdspLock, OldIrql);
|
|
}
|
|
|
|
|
|
|
|
|
|
LOCAL BOOLEAN
|
|
atalkAdspAddrDeQueueOpenReq(
|
|
IN PADSP_ADDROBJ pAdspAddr,
|
|
IN USHORT RemoteConnId,
|
|
IN PATALK_ADDR pSrcAddr,
|
|
OUT PADSP_OPEN_REQ *ppAdspOpenReq
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PADSP_OPEN_REQ pOpenReq, *ppOpenReq;
|
|
BOOLEAN removed = FALSE;
|
|
|
|
for (ppOpenReq = &pAdspAddr->adspao_OpenReq;
|
|
((pOpenReq = *ppOpenReq) != NULL); )
|
|
{
|
|
if ((pOpenReq->or_RemoteConnId == RemoteConnId) &&
|
|
(ATALK_ADDRS_EQUAL(&pOpenReq->or_RemoteAddr, pSrcAddr)))
|
|
{
|
|
removed = TRUE;
|
|
*ppOpenReq = pOpenReq->or_Next;
|
|
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspAddrDeQueueOpenReq: Removed OpenReq %lx\n", pOpenReq));
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
ppOpenReq = &pOpenReq->or_Next;
|
|
}
|
|
}
|
|
|
|
*ppAdspOpenReq = NULL;
|
|
if (removed)
|
|
{
|
|
*ppAdspOpenReq = pOpenReq;
|
|
}
|
|
|
|
return removed;
|
|
}
|
|
|
|
|
|
|
|
|
|
LOCAL BOOLEAN
|
|
atalkAdspIsDuplicateOpenReq(
|
|
IN PADSP_ADDROBJ pAdspAddr,
|
|
IN USHORT RemoteConnId,
|
|
IN PATALK_ADDR pSrcAddr
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
!!!MUST BE CALLED WITH THE ADDRESS LOCK HELD!!!
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PADSP_OPEN_REQ pOpenReqChk;
|
|
BOOLEAN found = FALSE;
|
|
|
|
for (pOpenReqChk = pAdspAddr->adspao_OpenReq;
|
|
pOpenReqChk != NULL;
|
|
pOpenReqChk = pOpenReqChk->or_Next)
|
|
{
|
|
if ((pOpenReqChk->or_RemoteConnId == RemoteConnId) &&
|
|
(ATALK_ADDRS_EQUAL(&pOpenReqChk->or_RemoteAddr, pSrcAddr)))
|
|
{
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspIsDuplicateOpenReq: Found\n"));
|
|
found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return found;
|
|
}
|
|
|
|
|
|
|
|
|
|
LOCAL VOID
|
|
atalkAdspGenericComplete(
|
|
IN ATALK_ERROR ErrorCode,
|
|
IN PIRP pIrp
|
|
)
|
|
{
|
|
DBGPRINT(DBG_COMP_TDI, DBG_LEVEL_INFO,
|
|
("atalkTdiGenericComplete: Completing %lx with %lx\n",
|
|
pIrp, AtalkErrorToNtStatus(ErrorCode)));
|
|
|
|
ASSERT (ErrorCode != ATALK_PENDING);
|
|
TdiCompleteRequest(pIrp, AtalkErrorToNtStatus(ErrorCode));
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
atalkAdspConnFindInConnect(
|
|
IN PADSP_ADDROBJ pAdspAddr,
|
|
IN USHORT DestConnId,
|
|
IN PATALK_ADDR pRemoteAddr,
|
|
OUT PADSP_CONNOBJ * ppAdspConn,
|
|
IN PATALK_ERROR pError
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The MAC could respond with a REQ&ACK from a different socket than
|
|
the one we sent the REQ to. But the network/node id must be the
|
|
same. We don't check for that though, and only use the destination
|
|
connection id.
|
|
|
|
This routine will replace the remote address with the new remote
|
|
address passed in.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PADSP_CONNOBJ pAdspRemConn;
|
|
|
|
ASSERT(pAdspAddr->adspao_Flags & ADSPAO_CONNECT);
|
|
|
|
*pError = ATALK_INVALID_CONNECTION;
|
|
for (pAdspRemConn = pAdspAddr->adspao_pConnectConn;
|
|
pAdspRemConn != NULL; )
|
|
{
|
|
if (pAdspRemConn->adspco_LocalConnId == DestConnId)
|
|
{
|
|
DBGPRINT(DBG_COMP_ADSP, DBG_LEVEL_INFO,
|
|
("atalkAdspFindInConnectList: connect conn %lx\n",
|
|
pAdspRemConn));
|
|
|
|
// Try to reference this.
|
|
AtalkAdspConnReferenceByPtr(pAdspRemConn, pError);
|
|
if (ATALK_SUCCESS(*pError))
|
|
{
|
|
// Change remote address to be the passed in address
|
|
pAdspRemConn->adspco_RemoteAddr = *pRemoteAddr;
|
|
*ppAdspConn = pAdspRemConn;
|
|
}
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
pAdspRemConn = pAdspRemConn->adspco_pNextConnect;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|