windows-nt/Source/XPSP1/NT/net/sfm/atalk/sys/adsp.c

7151 lines
164 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1992 Microsoft Corporation
Module Name:
adsp.c
Abstract:
This module implements the ADSP protocol.
Author:
Jameel Hyder (jameelh@microsoft.com)
Nikhil Kamkolkar (nikhilk@microsoft.com)
Revision History:
30 Mar 1993 Initial Version
Notes: Tab stop: 4
--*/
#include <atalk.h>
#pragma hdrstop
#define FILENUM 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;
}
}
}