/*++

Copyright (c) 1992  Microsoft Corporation

Module Name:

	aspc.c

Abstract:

	This module implements the ASP client protocol.

Author:

	Jameel Hyder (jameelh@microsoft.com)
	Nikhil Kamkolkar (nikhilk@microsoft.com)

Revision History:
	30 Mar 1993		Initial Version

Notes:	Tab stop: 4
--*/

#define	ASPC_LOCALS
#define	FILENUM		ASPC
#include <atalk.h>
#pragma hdrstop

#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, AtalkInitAspCInitialize)
#pragma alloc_text(PAGE, AtalkAspCCreateAddress)
#pragma alloc_text(PAGEASPC, AtalkAspCCleanupAddress)
#pragma alloc_text(PAGEASPC, AtalkAspCCloseAddress)
#pragma alloc_text(PAGEASPC, AtalkAspCCreateConnection)
#pragma alloc_text(PAGEASPC, AtalkAspCCleanupConnection)
#pragma alloc_text(PAGEASPC, AtalkAspCCloseConnection)
#pragma alloc_text(PAGEASPC, AtalkAspCAssociateAddress)
#pragma alloc_text(PAGEASPC, AtalkAspCDissociateAddress)
#pragma alloc_text(PAGEASPC, AtalkAspCPostConnect)
#pragma alloc_text(PAGEASPC, AtalkAspCDisconnect)
#pragma alloc_text(PAGEASPC, AtalkAspCGetStatus)
#pragma alloc_text(PAGEASPC, AtalkAspCGetAttn)
#pragma alloc_text(PAGEASPC, AtalkAspCCmdOrWrite)
#pragma alloc_text(PAGEASPC, atalkAspCIncomingOpenReply)
#pragma alloc_text(PAGEASPC, atalkAspCIncomingStatus)
#pragma alloc_text(PAGEASPC, atalkAspCIncomingCmdReply)
#pragma alloc_text(PAGEASPC, atalkAspCHandler)
#pragma alloc_text(PAGEASPC, AtalkAspCAddrDereference)
#pragma alloc_text(PAGEASPC, AtalkAspCConnDereference)
#pragma alloc_text(PAGEASPC, atalkAspCSessionMaintenanceTimer)
#pragma alloc_text(PAGEASPC, atalkAspCQueueAddrGlobalList)
#pragma alloc_text(PAGEASPC, atalkAspCDeQueueAddrGlobalList)
#pragma alloc_text(PAGEASPC, atalkAspCQueueConnGlobalList)
#pragma alloc_text(PAGEASPC, atalkAspCDeQueueConnGlobalList)
#endif

VOID
AtalkInitAspCInitialize(
	VOID
	)
/*++

Routine Description:


Arguments:


Return Value:


--*/
{
	AtalkTimerInitialize(&atalkAspCConnMaint.ascm_SMTTimer,
						 atalkAspCSessionMaintenanceTimer,
						 ASP_SESSION_MAINTENANCE_TIMER);
	INITIALIZE_SPIN_LOCK(&atalkAspCLock);
}


ATALK_ERROR
AtalkAspCCreateAddress(
	IN	PATALK_DEV_CTX		pDevCtx	OPTIONAL,
	OUT	PASPC_ADDROBJ	*	ppAspAddr
	)
/*++

Routine Description:

 	Create an ASP address object.

Arguments:


Return Value:


--*/
{
	ATALK_ERROR			Status;
	PASPC_ADDROBJ		pAspAddr;
	int					i;

	DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_INFO,
			("AtalkAspCCreateAddr: Entered\n"));

	// Allocate memory for the Asp address object
	*ppAspAddr = NULL;
	if ((pAspAddr = AtalkAllocZeroedMemory(sizeof(ASPC_ADDROBJ))) == NULL)
	{
		return ATALK_RESR_MEM;
	}

	// Create an Atp Socket on the port for the Sls
	Status = AtalkAtpOpenAddress(AtalkDefaultPort,
								 0,
								 NULL,
								 ATP_DEF_MAX_SINGLE_PKT_SIZE,
								 ATP_DEF_SEND_USER_BYTES_ALL,
								 NULL,
								 FALSE,
								 &pAspAddr->aspcao_pAtpAddr);

	if (!ATALK_SUCCESS(Status))
	{
		DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
				("AtalkAspCCreateAddress: AtalkAtpOpenAddress %ld\n", Status));
		AtalkFreeMemory(pAspAddr);
		return Status;
	}

	// Initialize the Asp address object
#if	DBG
	pAspAddr->aspcao_Signature = ASPCAO_SIGNATURE;
#endif
	INITIALIZE_SPIN_LOCK(&pAspAddr->aspcao_Lock);

	atalkAspCQueueAddrGlobalList(pAspAddr);

	// Refcount for creation and atp address. This goes away when atp address is closed
    // pAspAddr->aspcao_Flags = 0;
	pAspAddr->aspcao_RefCount = 1 + 1;
	*ppAspAddr = pAspAddr;

	return ATALK_NO_ERROR;
}


ATALK_ERROR
AtalkAspCCleanupAddress(
	IN	PASPC_ADDROBJ			pAspAddr
	)
/*++

Routine Description:


Arguments:


Return Value:


--*/
{
	return(ATALK_NO_ERROR);
}


ATALK_ERROR
AtalkAspCCloseAddress(
	IN	PASPC_ADDROBJ			pAspAddr,
	IN	GENERIC_COMPLETION		CompletionRoutine,
	IN	PVOID					CloseContext
	)
/*++

Routine Description:


Arguments:


Return Value:


--*/
{
	return(ATALK_NO_ERROR);
}


ATALK_ERROR
AtalkAspCCreateConnection(
	IN	PVOID					pConnCtx,	// Context to associate with the session
	IN	PATALK_DEV_CTX			pDevCtx		OPTIONAL,
	OUT	PASPC_CONNOBJ 	*		ppAspConn
	)
/*++

Routine Description:

 	Create an ASP 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:


--*/
{
	PASPC_CONNOBJ		pAspConn;

	// Allocate memory for a connection object
	if ((pAspConn = AtalkAllocZeroedMemory(sizeof(ASPC_CONNOBJ))) == NULL)
	{
		return ATALK_RESR_MEM;
	}

#if	DBG
	pAspConn->aspcco_Signature = ASPCCO_SIGNATURE;
#endif

	INITIALIZE_SPIN_LOCK(&pAspConn->aspcco_Lock);
	pAspConn->aspcco_ConnCtx 	= pConnCtx;
	// pAspConn->aspcco_Flags 		= 0;
	pAspConn->aspcco_RefCount 	= 1;			// Creation reference
	pAspConn->aspcco_NextSeqNum = 1;			// Set to 1, not 0.
	AtalkInitializeRT(&pAspConn->aspcco_RT,
					  ASP_INIT_REQ_INTERVAL,
                      ASP_MIN_REQ_INTERVAL,
                      ASP_MAX_REQ_INTERVAL);

	*ppAspConn = pAspConn;

	//	Insert into the global connection list.
	atalkAspCQueueConnGlobalList(pAspConn);

	DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_INFO,
				("AtalkAspCreateConnection: %lx\n", pAspConn));

	return ATALK_NO_ERROR;
}


ATALK_ERROR
AtalkAspCCleanupConnection(
	IN	PASPC_CONNOBJ			pAspConn
	)
/*++

Routine Description:


Arguments:


Return Value:


--*/
{
	return ATALK_NO_ERROR;
}


ATALK_ERROR
AtalkAspCCloseConnection(
	IN	PASPC_CONNOBJ			pAspConn,
	IN	GENERIC_COMPLETION		CompletionRoutine,
	IN	PVOID					CloseContext
	)
/*++

Routine Description:


Arguments:


Return Value:


--*/
{
	return ATALK_NO_ERROR;
}


ATALK_ERROR
AtalkAspCAssociateAddress(
	IN	PASPC_ADDROBJ			pAspAddr,
	IN	PASPC_CONNOBJ			pAspConn
	)
/*++

Routine Description:


Arguments:


Return Value:


--*/
{
	ATALK_ERROR		error;
	KIRQL			OldIrql;

	ASSERT(VALID_ASPCAO(pAspAddr));
	ASSERT(VALID_ASPCCO(pAspConn));

	ACQUIRE_SPIN_LOCK(&pAspConn->aspcco_Lock, &OldIrql);

	error = ATALK_ALREADY_ASSOCIATED;
	if ((pAspConn->aspcco_Flags & ASPCCO_ASSOCIATED) == 0)
	{
		error = ATALK_NO_ERROR;

		pAspConn->aspcco_Flags 	   |= ASPCCO_ASSOCIATED;
		pAspConn->aspcco_pAspCAddr	= pAspAddr;
	}

	RELEASE_SPIN_LOCK(&pAspConn->aspcco_Lock, OldIrql);

	return error;
}


ATALK_ERROR
AtalkAspCDissociateAddress(
	IN	PASPC_CONNOBJ			pAspConn
	)
/*++

Routine Description:


Arguments:


Return Value:


--*/
{
	PASPC_ADDROBJ	pAspAddr;
	KIRQL			OldIrql;
	ATALK_ERROR		error = ATALK_NO_ERROR;

	ASSERT(VALID_ASPCCO(pAspConn));

	ACQUIRE_SPIN_LOCK(&pAspConn->aspcco_Lock, &OldIrql);
	if ((pAspConn->aspcco_Flags & (ASPCCO_CONNECTING	|
								   ASPCCO_ACTIVE 		|
								   ASPCCO_ASSOCIATED)) != ASPCCO_ASSOCIATED)
	{
		error = ATALK_INVALID_CONNECTION;
	}
	else
	{
		pAspAddr = pAspConn->aspcco_pAspCAddr ;
		ASSERT(VALID_ASPCAO(pAspAddr));

		//	Clear associated flag.
		pAspConn->aspcco_Flags 	   &= ~ASPCCO_ASSOCIATED;
		pAspConn->aspcco_pAspCAddr	= NULL;
	}
	RELEASE_SPIN_LOCK(&pAspConn->aspcco_Lock, OldIrql);

	return error;
}


ATALK_ERROR
AtalkAspCPostConnect(
	IN	PASPC_CONNOBJ			pAspConn,
	IN	PATALK_ADDR				pRemoteAddr,
	IN	PVOID					pConnectCtx,
	IN	GENERIC_COMPLETION		CompletionRoutine
	)
/*++

Routine Description:


Arguments:


Return Value:


--*/
{
	ATALK_ERROR		error	= ATALK_NO_ERROR;
	BOOLEAN			DerefConn = FALSE;
	KIRQL			OldIrql;
	BYTE			UserBytes[ATP_USERBYTES_SIZE];
	PBYTE			pOpenPkt = NULL, pRespPkt = NULL;
	PAMDL			pOpenAmdl = NULL, pRespAmdl = NULL;
	PASPC_ADDROBJ	pAspAddr = pAspConn->aspcco_pAspCAddr;

	ASSERT(VALID_ASPCAO(pAspAddr));
	ASSERT(VALID_ASPCCO(pAspConn));

	ACQUIRE_SPIN_LOCK(&pAspConn->aspcco_Lock, &OldIrql);

	do
	{
		if ((pAspConn->aspcco_Flags & (ASPCCO_CONNECTING	|
									   ASPCCO_ACTIVE 		|
									   ASPCCO_ASSOCIATED)) != ASPCCO_ASSOCIATED)
		{
			error = ATALK_INVALID_CONNECTION;
			break;
		}

		//	Reference the connection for the request we will be posting
		AtalkAspCConnReferenceByPtrNonInterlock(pAspConn, &error);
		if (ATALK_SUCCESS(error))
		{
			DerefConn = TRUE;

			//	Make sure flags are clean.
			pAspConn->aspcco_Flags 			   |= ASPCCO_CONNECTING;
			pAspConn->aspcco_ConnectCtx 		= pConnectCtx;
			pAspConn->aspcco_ConnectCompletion 	= CompletionRoutine;
			pAspConn->aspcco_ServerSlsAddr		= *pRemoteAddr;

			//	Copy the atp address object for efficiency
			pAspConn->aspcco_pAtpAddr			= pAspAddr->aspcao_pAtpAddr;
		}
		else
		{
			ASSERTMSG("AtalkAspCPostConnect: Connection ref failed\n", 0);
		}
	} while (FALSE);

	RELEASE_SPIN_LOCK(&pAspConn->aspcco_Lock, OldIrql);

	if (ATALK_SUCCESS(error))
	{
		UserBytes[ASP_CMD_OFF]	= ASP_OPEN_SESSION;
		UserBytes[ASP_WSS_OFF]	= pAspAddr->aspcao_pAtpAddr->atpao_DdpAddr->ddpao_Addr.ata_Socket;
        UserBytes[ASP_VERSION_OFF] = ASP_VERSION[0];
        UserBytes[ASP_VERSION_OFF+1] = ASP_VERSION[1];
	
		//	Post the open session request.
		error = AtalkAtpPostReq(pAspConn->aspcco_pAtpAddr,
								&pAspConn->aspcco_ServerSlsAddr,
								&pAspConn->aspcco_OpenSessTid,
								ATP_REQ_EXACTLY_ONCE,				// ExactlyOnce request
								NULL,
								0,
								UserBytes,
								NULL,
								0,
								ATP_RETRIES_FOR_ASP,
								ATP_MAX_INTERVAL_FOR_ASP,
								THIRTY_SEC_TIMER,
								atalkAspCIncomingOpenReply,
								pAspConn);

		if (ATALK_SUCCESS(error))
		{
			error = ATALK_PENDING;
			DerefConn = FALSE;
		}
		else
		{
			DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
					("AtalkAspCPostConnect: AtalkAtpPostReq: failed %ld\n", error));

			//	Remove connection from the connect list and reset states.
			ACQUIRE_SPIN_LOCK(&pAspConn->aspcco_Lock, &OldIrql);

			pAspConn->aspcco_Flags 			   &= ~ASPCCO_CONNECTING;
			pAspConn->aspcco_ConnectCtx 		= NULL;
			pAspConn->aspcco_ConnectCompletion 	= NULL;
			pAspConn->aspcco_pAtpAddr			= NULL;

			RELEASE_SPIN_LOCK(&pAspConn->aspcco_Lock, OldIrql);

			DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
					("AtalkAspCPostConnect: failed %ld\n", error));
		}
	}

	if (DerefConn)
	{
		AtalkAspCConnDereference(pAspConn);
	}

	return error;
}


ATALK_ERROR
AtalkAspCDisconnect(
	IN	PASPC_CONNOBJ				pAspConn,
	IN	ATALK_DISCONNECT_TYPE		DisconnectType,
	IN	PVOID						pDisconnectCtx,
	IN	GENERIC_COMPLETION			CompletionRoutine
	)
/*++

Routine Description:


Arguments:


Return Value:


--*/
{
	PASPC_REQUEST	pAspReq, pAspReqNext;
	KIRQL			OldIrql;
	ATALK_ERROR		Error;

	// Abort all pending requests.
	ACQUIRE_SPIN_LOCK(&pAspConn->aspcco_Lock, &OldIrql);

	pAspConn->aspcco_Flags |= ASPCCO_DISCONNECTING;
	for (pAspReq = pAspConn->aspcco_pActiveReqs;
		 pAspReq = pAspReq->aspcrq_Next;
		 pAspReq = pAspReqNext)
	{
		pAspReqNext = pAspReq->aspcrq_Next;
	}

	RELEASE_SPIN_LOCK(&pAspConn->aspcco_Lock, OldIrql);

	// Send a close session request to the other end
	// Error = AtalKAtpPostReq(pAspConn->aspcco_ServerSlsAddr);

	return ATALK_NO_ERROR;
}


ATALK_ERROR
AtalkAspCGetStatus(
	IN	PASPC_ADDROBJ				pAspAddr,
	IN	PATALK_ADDR					pRemoteAddr,
	IN	PAMDL						pStatusAmdl,
	IN	USHORT						AmdlSize,
	IN	PACTREQ						pActReq
	)
/*++

Routine Description:


Arguments:


Return Value:


--*/
{
	ATALK_ERROR	error;
	BYTE		UserBytes[ATP_USERBYTES_SIZE];
	USHORT		tid;

	if ((pRemoteAddr->ata_Network == 0) ||
        (pRemoteAddr->ata_Node == 0)	||
        (pRemoteAddr->ata_Socket == 0))
	{
		return ATALK_SOCKET_INVALID;
	}

	*(DWORD *)UserBytes = 0;
	UserBytes[ASP_CMD_OFF]	= ASP_GET_STATUS;

	error = AtalkAtpPostReq(pAspAddr->aspcao_pAtpAddr,
							pRemoteAddr,
							&tid,
							0,							// ExactlyOnce request
							NULL,
							0,
							UserBytes,
							pStatusAmdl,
							AmdlSize,
							ATP_RETRIES_FOR_ASP,
							ATP_MAX_INTERVAL_FOR_ASP,
							THIRTY_SEC_TIMER,
							atalkAspCIncomingStatus,
							(PVOID)pActReq);

	if (ATALK_SUCCESS(error))
	{
		error = ATALK_PENDING;
	}

	return error;
}


ATALK_ERROR
AtalkAspCGetAttn(
	IN	PASPC_CONNOBJ			pAspConn,
	IN	PAMDL					pReadBuf,
	IN	USHORT					ReadBufLen,
	IN	ULONG					ReadFlags,
	IN	PVOID					pReadCtx,
	IN	GENERIC_READ_COMPLETION	CompletionRoutine
	)
/*++

Routine Description:


Arguments:


Return Value:


--*/
{
	ATALK_ERROR	error = ATALK_NO_ERROR;
	KIRQL			OldIrql;

	ASSERT(VALID_ASPCCO(pAspConn));
	ASSERT(*CompletionRoutine != NULL);

	ACQUIRE_SPIN_LOCK(&pAspConn->aspcco_Lock, &OldIrql);

	do
	{
		if ((pAspConn->aspcco_Flags & ASPCCO_ACTIVE) == 0)
		{
			error = ATALK_ASPC_CONN_NOT_ACTIVE;
			break;
		}

		if ((ReadFlags & TDI_RECEIVE_EXPEDITED) == 0)
		{
			error = ATALK_INVALID_PARAMETER;
			break;
		}

		if (pAspConn->aspcco_Flags & ASPCCO_ATTN_PENDING)
		{
			error = ATALK_ASPC_TOO_MANY_READS;
			break;
		}

		//	PEEK not supported for ASPC
		if (ReadFlags & TDI_RECEIVE_PEEK)
		{
			error = ATALK_INVALID_REQUEST;
			break;
		}

		// We should have space for atleast one attention word
		if (ReadBufLen < sizeof(USHORT))
		{
			error = ATALK_BUFFER_TOO_SMALL;
			break;
		}

		// Check if we have any outstanding attention words
		if (pAspConn->aspcco_AttnOutPtr < pAspConn->aspcco_AttnInPtr)
		{
			PUSHORT	AttnBuf;
			USHORT	BufSize = 0;

			AttnBuf = AtalkGetAddressFromMdl(pReadBuf);
			while (pAspConn->aspcco_AttnOutPtr < pAspConn->aspcco_AttnInPtr)
			{
				*AttnBuf++ = pAspConn->aspcco_AttnBuf[pAspConn->aspcco_AttnOutPtr % MAX_ASPC_ATTNS];
                pAspConn->aspcco_AttnOutPtr++;
				BufSize += sizeof(USHORT);
			}
			(*CompletionRoutine)(error,
								pReadBuf,
								BufSize,
								ReadFlags,
								pReadCtx);

			error = ATALK_PENDING;
			break;
		}
		error = ATALK_INVALID_CONNECTION;
		if ((pAspConn->aspcco_Flags & (ASPCCO_CLOSING | ASPCCO_DISCONNECTING)) == 0)
		{
			AtalkAspCConnReferenceByPtrNonInterlock(pAspConn, &error);
		}

		if (!ATALK_SUCCESS(error))
		{
			break;
		}

		pAspConn->aspcco_Flags 	|= ASPCCO_ATTN_PENDING;

		//	Remember read information in the connection object.
		pAspConn->aspcco_ReadCompletion	= CompletionRoutine;
		pAspConn->aspcco_ReadCtx		= pReadCtx;

	} while (FALSE);

	RELEASE_SPIN_LOCK(&pAspConn->aspcco_Lock, OldIrql);

	return error;
}


ATALK_ERROR
AtalkAspCCmdOrWrite(
	IN	PASPC_CONNOBJ				pAspConn,
	IN	PAMDL						pCmdMdl,
	IN	USHORT						CmdSize,
	IN	PAMDL						pReplyMdl,
	IN	USHORT						ReplySize,
	IN	BOOLEAN						fWrite,		// If TRUE, its a write else command
	IN	PACTREQ						pActReq
	)
/*++

Routine Description:


Arguments:
	Reply and Write buffers are overlaid.

Return Value:
                                
--*/
{
	ATALK_ERROR		Error;
	KIRQL			OldIrql;
	PASPC_REQUEST	pAspReq;
	BYTE			UserBytes[ATP_USERBYTES_SIZE];

	do
	{
		if ((pAspConn->aspcco_Flags & (ASPCCO_ACTIVE		|
									   ASPCCO_CONNECTING 	|
									   ASPCCO_LOCAL_CLOSE	|
									   ASPCCO_REMOTE_CLOSE	|
									   ASPCCO_CLOSING) != ASPCCO_ACTIVE))
		{
			Error = ATALK_INVALID_REQUEST;
			break;
		}

		AtalkAspCConnReference(pAspConn, &Error);
		if (!ATALK_SUCCESS(Error))
		{
			break;
		}

		if ((pAspReq = (PASPC_REQUEST)AtalkAllocZeroedMemory(sizeof(ASPC_REQUEST))) == NULL)
		{
			Error = ATALK_RESR_MEM;
			break;
		}
#if DBG
		pAspReq->aspcrq_Signature = ASPCRQ_SIGNATURE;
#endif
		pAspReq->aspcrq_Flags = fWrite ? ASPCRQ_WRITE : ASPCRQ_COMMAND;
		pAspReq->aspcrq_pReplyMdl = pReplyMdl;
		pAspReq->aspcrq_ReplySize = ReplySize;
		pAspReq->aspcrq_RefCount = 2;	// Creation+incoming reply handler

		ACQUIRE_SPIN_LOCK(&pAspConn->aspcco_Lock, &OldIrql);

		pAspReq->aspcrq_SeqNum = pAspConn->aspcco_NextSeqNum ++;
		pAspReq->aspcrq_Next = pAspConn->aspcco_pActiveReqs;
		pAspConn->aspcco_pActiveReqs = pAspReq;
		pAspReq->aspcrq_pAspConn = pAspConn;

		RELEASE_SPIN_LOCK(&pAspConn->aspcco_Lock, OldIrql);

		// Build user bytes and send our request over
		UserBytes[ASP_CMD_OFF] = fWrite ? ASP_CMD : ASP_WRITE;
		UserBytes[ASP_SESSIONID_OFF] = pAspConn->aspcco_SessionId;
		PUTSHORT2SHORT(&UserBytes[ASP_SEQUENCE_NUM_OFF], pAspReq->aspcrq_SeqNum);

		Error = AtalkAtpPostReq(pAspConn->aspcco_pAtpAddr,
								&pAspConn->aspcco_ServerSssAddr,
								&pAspReq->aspcrq_ReqXactId,
								ATP_REQ_EXACTLY_ONCE,		// XO request
								pCmdMdl,
								CmdSize,
								UserBytes,
								pReplyMdl,
								ReplySize,
								ATP_RETRIES_FOR_ASP,		// Retry count
								pAspConn->aspcco_RT.rt_Base,// Retry interval
								THIRTY_SEC_TIMER,
								atalkAspCIncomingCmdReply,
								pAspReq);

		if (!ATALK_SUCCESS(Error))
		{
			DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
					("AtalkAspCCmdOrWrite: AtalkAtpPostReq failed %lx\n", Error));
			atalkAspCIncomingCmdReply(Error,
									  pAspReq,
									  pCmdMdl,
									  pReplyMdl,
									  ReplySize,
									  UserBytes);
		}

	} while (FALSE);

	return Error;
}


LOCAL VOID
atalkAspCCloseSession(
	IN	PASPC_CONNOBJ				pAspConn
	)
/*++

Routine Description:


Arguments:


Return Value:


--*/
{
	
}


LOCAL VOID
atalkAspCIncomingOpenReply(
	IN	ATALK_ERROR					ErrorCode,
	IN	PASPC_CONNOBJ				pAspConn,		// Our context
	IN	PAMDL						pReqAmdl,
	IN	PAMDL						pReadAmdl,
	IN	USHORT						ReadLen,
	IN	PBYTE						ReadUserBytes
	)
/*++

Routine Description:


Arguments:


Return Value:


--*/
{
	ATALK_ERROR				error;
	USHORT					OpenStatus;
	BYTE					UserBytes[ATP_USERBYTES_SIZE];
	BOOLEAN					DerefConn = FALSE;
	PASPC_ADDROBJ			pAspAddr = pAspConn->aspcco_pAspCAddr;

	ASSERT(VALID_ASPCCO(pAspConn));

	if (ATALK_SUCCESS(ErrorCode))
	do
	{
		//	Check for open reply code in packet.
		GETSHORT2SHORT(&OpenStatus, &ReadUserBytes[ASP_ERRORCODE_OFF]);
		if (OpenStatus != 0)
		{
			DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
					("atalkAspCIncomingOpenReply: Failed %ld, %lx\n", OpenStatus, pAspConn));

			DerefConn = TRUE;	// Since we are not queuing a request handler
			ErrorCode = ATALK_REMOTE_CLOSE;
			break;
		}

		ACQUIRE_SPIN_LOCK_DPC(&pAspConn->aspcco_Lock);

		//	Save the socket the server's SSS
		pAspConn->aspcco_ServerSssAddr = pAspConn->aspcco_ServerSlsAddr;
		pAspConn->aspcco_ServerSssAddr.ata_Socket = ReadUserBytes[ASP_SSS_OFF];
		pAspConn->aspcco_SessionId = ReadUserBytes[ASP_SESSIONID_OFF];
		pAspConn->aspcco_Flags &= ~ASPCCO_CONNECTING;
		pAspConn->aspcco_Flags |= ASPCCO_ACTIVE;

		pAspConn->aspcco_LastContactTime = AtalkGetCurrentTick();

		//	Reference for the request handler
		AtalkAspCConnReferenceByPtrNonInterlock(pAspConn, &error);

		//	Build up userBytes to start tickling the other end.
		UserBytes[ASP_CMD_OFF]	= ASP_TICKLE;
		UserBytes[ASP_SESSIONID_OFF] = pAspConn->aspcco_SessionId;
		PUTSHORT2SHORT(UserBytes + ASP_ERRORCODE_OFF, 0);

		RELEASE_SPIN_LOCK_DPC(&pAspConn->aspcco_Lock);

		//	Set the request handler on this connection.
		//	It will handle tickle's, close's and write-continue
		AtalkAtpSetReqHandler(pAspAddr->aspcao_pAtpAddr,
							  atalkAspCHandler,
							  pAspConn);

		error = AtalkAtpPostReq(pAspConn->aspcco_pAtpAddr,
								&pAspConn->aspcco_ServerSlsAddr,
								&pAspConn->aspcco_TickleTid,
								0,						// ALO transaction
								NULL,
								0,
								UserBytes,
								NULL,
								0,
								ATP_INFINITE_RETRIES,
								ASP_TICKLE_INTERVAL,
								THIRTY_SEC_TIMER,
								NULL,
								NULL);

		if (ATALK_SUCCESS(error))
		{
			ACQUIRE_SPIN_LOCK_DPC(&pAspConn->aspcco_Lock);
			pAspConn->aspcco_Flags |= ASPCCO_TICKLING;
			RELEASE_SPIN_LOCK_DPC(&pAspConn->aspcco_Lock);
		}
		else
		{
			DerefConn = TRUE;	// Since we are not queuing a request handler
		}
	} while (FALSE);

	if (!ATALK_SUCCESS(ErrorCode))
	{
		DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
				("atalkAspCIncomingOpenReply: Incoming connect fail %lx\n", ErrorCode));

		AtalkAspCConnDereference(pAspConn);	

		//	Mark it as inactive
		ACQUIRE_SPIN_LOCK_DPC(&pAspConn->aspcco_Lock);
		pAspConn->aspcco_Flags &= ~ASPCCO_ACTIVE;
		RELEASE_SPIN_LOCK_DPC(&pAspConn->aspcco_Lock);
	}

	//	Call the completion routine.
	(*pAspConn->aspcco_ConnectCompletion)(ErrorCode, pAspConn->aspcco_ConnectCtx);
}


LOCAL VOID
atalkAspCIncomingStatus(
	IN	ATALK_ERROR					ErrorCode,
	IN	PACTREQ						pActReq,		// Our Ctx
	IN	PAMDL						pReqAmdl,
	IN	PAMDL						pStatusAmdl,
	IN	USHORT						StatusLen,
	IN	PBYTE						ReadUserBytes
	)
/*++

Routine Description:


Arguments:


Return Value:


--*/
{
	//	Call the action completion routine
	(*pActReq->ar_Completion)(ErrorCode, pActReq);
}




LOCAL VOID
atalkAspCIncomingCmdReply(
	IN	ATALK_ERROR				Error,
	IN	PASPC_REQUEST			pAspReq,
	IN	PAMDL					pReqAMdl,
	IN	PAMDL					pRespAMdl,
	IN	USHORT					RespSize,
	IN	PBYTE					RespUserBytes
	)
/*++

Routine Description:


Arguments:


Return Value:


--*/
{
	PASPC_CONNOBJ	pAspConn;
	PASPC_REQUEST *	ppAspReq;
	KIRQL			OldIrql;

	pAspConn = pAspReq->aspcrq_pAspConn;

	ACQUIRE_SPIN_LOCK(&pAspConn->aspcco_Lock, &OldIrql);
	// Unlink the request from the active list
	for (ppAspReq = &pAspConn->aspcco_pActiveReqs;
		 *ppAspReq != NULL;
		 ppAspReq = &((*ppAspReq)->aspcrq_Next))
	{
		if (pAspReq == *ppAspReq)
		{
			*ppAspReq = pAspReq->aspcrq_Next;
			break;
		}
	}

	ASSERT(*ppAspReq == pAspReq->aspcrq_Next);

	RELEASE_SPIN_LOCK(&pAspConn->aspcco_Lock, OldIrql);

	// Complete the request
	(*pAspReq->aspcrq_pActReq->ar_Completion)(Error, pAspReq->aspcrq_pActReq);

	//  and dereference the connection
	AtalkAspCConnDereference(pAspConn);

	// and finally free the request
	AtalkFreeMemory(pAspReq);
}


LOCAL VOID
atalkAspCHandler(
	IN	ATALK_ERROR					ErrorCode,
	IN	PASPC_CONNOBJ				pAspConn,
	IN	PATP_RESP					pAtpResp,		// Used by PostResp/CancelResp
	IN	PATALK_ADDR					pSrcAddr,		// Address of requestor
	IN	USHORT						PktLen,
	IN	PBYTE						pPkt,
	IN	PBYTE						pUserBytes
	)
/*++

Routine Description:
	Handle tickle, write-continue requests, attentions and close from the server.

Arguments:


Return Value:


--*/
{
	USHORT						SequenceNum;	// From the incoming packet
	BYTE						SessionId;		// -- ditto --
	BYTE						RequestType;	// -- ditto --
	BOOLEAN						CancelTickle, ReleaseLock = TRUE, CancelResp = FALSE, Deref = FALSE;
	PIRP						exRecvIrp;
	PTDI_IND_RECEIVE_EXPEDITED	exRecvHandler;
	PVOID						exRecvHandlerCtx;
	ULONG						exIndicateFlags;
	PASPC_REQUEST				pAspReq;
	ATALK_ERROR					Error;

	do
	{
		if (!ATALK_SUCCESS(ErrorCode))
		{
			DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
					("atalkAspCHandler: Error %ld\n", ErrorCode));
			// Take away the reference on the Conn now that the atp address is closing
			if (ErrorCode == ATALK_ATP_CLOSING)
				AtalkAspCConnDereference(pAspConn);
			break;
		}

		ASSERT(VALID_ASPCCO(pAspConn));

		ACQUIRE_SPIN_LOCK_DPC(&pAspConn->aspcco_Lock);

		SessionId = pUserBytes[ASP_SESSIONID_OFF];
		RequestType = pUserBytes[ASP_CMD_OFF];
		GETSHORT2SHORT(&SequenceNum, pUserBytes+ASP_SEQUENCE_NUM_OFF);

		AtalkAspCConnReferenceByPtrNonInterlock(pAspConn, &Error);
		if (ATALK_SUCCESS(Error) && (pAspConn->aspcco_SessionId == SessionId))
		{
			pAspConn->aspcco_LastContactTime = AtalkGetCurrentTick();
		
			switch (RequestType)
			{
			  case ASP_CLOSE_SESSION:
				// Cancel all outstanding requests (and any posted replies to write continue)
				// and shut down the session. Start off by sending a close response.
				CancelTickle = ((pAspConn->aspcco_Flags &ASPCO_TICKLING) != 0);
				pAspConn->aspcco_Flags &= ~(ASPCCO_ACTIVE | ASPCCO_TICKLING);
				pAspConn->aspcco_Flags |= ASPCO_REMOTE_CLOSE;
				RELEASE_SPIN_LOCK_DPC(&pAspConn->aspcco_Lock);
				ReleaseLock = FALSE;
		
				// Send a CloseSession reply and close the session
				Error = AtalkAtpPostResp(pAtpResp,
										 pSrcAddr,
										 NULL,
										 0,
										 NULL,
										 AtalkAtpGenericRespComplete,
										 pAtpResp);
				if (!ATALK_SUCCESS(Error))
				{
					AtalkAtpGenericRespComplete(Error, pAtpResp);
					DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
							("atalkAspSssXHandler: AtalkAtpPostResp failed %ld\n", Error));
				}
		
				// Cancel the tickle requests for this session
				if (CancelTickle)
				{
					Error = AtalkAtpCancelReq(pAspConn->aspcco_pAtpAddr,
											  pAspConn->aspcco_TickleXactId,
											  &pAspConn->aspcco_ServerSssAddr);
			
					if (!ATALK_SUCCESS(Error))
					{
						DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
								("atalkAspSssXHandler: AtalkAtpCancelReq %ld\n", Error));
					}
				}
		
				// Shut down this session, well almost ... Note that we have a reference
				// to this connection which will be Dereferenced by atalkAspSessionClose.
				atalkAspCCloseSession(pAspConn);
				break;

			  case ASP_ATTENTION:
				// Server is sending us an attention. If we already have a getattn posted
				// complete that. If not, just save the attention word and indicate to AFD
				// that we have recvd. expedited data
				if ((pAspConn->aspcco_AttnInPtr - pAspConn->aspcco_AttnOutPtr) < MAX_ASPC_ATTNS)
				{
					pAspConn->aspcco_AttnBuf[pAspConn->aspcco_AttnInPtr % MAX_ASPC_ATTNS] = SequenceNum;
					pAspConn->aspcco_AttnInPtr++;

					RELEASE_SPIN_LOCK_DPC(&pAspConn->aspcco_Lock);
					ReleaseLock = FALSE;
				}
				break;

			  case ASP_WRITE_DATA:
				// We need to find the request for which we sent a Write command. The
				// server now needs the data. Post a response for this.
				for (pAspReq = pAspConn->aspcco_pActiveReqs;
					 pAspReq != NULL;
					 pAspReq = pAspReq->aspcrq_Next)
				{
					if (pAspReq->aspcrq_SeqNum == SequenceNum)
					{
						RELEASE_SPIN_LOCK_DPC(&pAspConn->aspcco_Lock);
						ReleaseLock = FALSE;
						Error = AtalkAtpPostResp(pAtpResp,
												 pSrcAddr,
												 pAspReq->aspcrq_pWriteMdl,
												 pAspReq->aspcrq_WriteSize,
												 NULL,
												 AtalkAtpGenericRespComplete,
												 pAtpResp);
						Deref = TRUE;
						break;
					}
				}
				break;

			  case ASP_TICKLE:
				DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_INFO,
						("atalkAspCHandler: Received tickle from %x.%x Session %d\n",
						pSrcAddr->ata_Network, pSrcAddr->ata_Node, SessionId));
				CancelResp = TRUE;
				Deref = TRUE;
				break;
			
			  default:
				DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_INFO,
						("atalkAspCHandler: Invalid commnd %d from %x.%x Session %d\n",
						RequestType, pSrcAddr->ata_Network, pSrcAddr->ata_Node, SessionId));
				CancelResp = TRUE;
				Deref = TRUE;
				break;
			}
		}
		else
		{
			DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
					("atalkAspCHandler: Mismatched session id from %d.%d, expected %d, recvd. %d\n",
					pSrcAddr->ata_Network, pSrcAddr->ata_Node,
					pAspConn->aspcco_SessionId, SessionId));
		}

		if (ReleaseLock)
		{
			RELEASE_SPIN_LOCK_DPC(&pAspConn->aspcco_Lock);
		}
		if (CancelResp)
		{
			AtalkAtpCancelResp(pAtpResp);
		}
		if (Deref)
		{
			AtalkAspCConnDereference(pAspConn);
		}
	} while (FALSE);
}


LOCAL LONG FASTCALL
atalkAspCSessionMaintenanceTimer(
	IN	PTIMERLIST				pTimer,
	IN	BOOLEAN					TimerShuttingDown
	)
/*++

Routine Description:


Arguments:


Return Value:


--*/
{
	
	return ATALK_TIMER_REQUEUE;
}


VOID FASTCALL
AtalkAspCAddrDereference(
	IN	PASPC_ADDROBJ			pAspAddr
	)
/*++

Routine Description:


Arguments:


Return Value:


--*/
{
	BOOLEAN	Close = FALSE;
	KIRQL	OldIrql;

	DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_REFASPADDR,
			("AtalkAspCAddrDereference: %lx, %d\n",
			pAspAddr, pAspAddr->aspcao_RefCount-1));

	ASSERT (VALID_ASPCAO(pAspAddr));

	ACQUIRE_SPIN_LOCK(&pAspAddr->aspcao_Lock, &OldIrql);
	if (--(pAspAddr->aspcao_RefCount) == 0)
	{
		ASSERT(pAspAddr->aspcao_Flags & ASPCAO_CLOSING);
		Close = TRUE;
	}
	RELEASE_SPIN_LOCK(&pAspAddr->aspcao_Lock, OldIrql);

	if (Close)
	{
		if (pAspAddr->aspcao_CloseCompletion != NULL)
			(*pAspAddr->aspcao_CloseCompletion)(ATALK_NO_ERROR,
											    pAspAddr->aspcao_CloseContext);
		// Finally free the memory
		AtalkFreeMemory(pAspAddr);

		AtalkUnlockAspCIfNecessary();
	}
}


VOID FASTCALL
AtalkAspCConnDereference(
	IN	PASPC_CONNOBJ			pAspConn
	)
/*++

Routine Description:


Arguments:


Return Value:


--*/
{
	BOOLEAN	Close = FALSE;
	KIRQL	OldIrql;

	ASSERT (VALID_ASPCCO(pAspConn));

	DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_REFASPADDR,
			("AtalkAspCConnDereference: %lx, %d\n",
			pAspConn, pAspConn->aspcco_RefCount-1));

	ACQUIRE_SPIN_LOCK(&pAspConn->aspcco_Lock, &OldIrql);
	if (--(pAspConn->aspcco_RefCount) == 0)
	{
		ASSERT(pAspConn->aspcco_Flags & ASPCCO_CLOSING);
		Close = TRUE;
	}
	RELEASE_SPIN_LOCK(&pAspConn->aspcco_Lock, OldIrql);

	if (Close)
	{
		if (pAspConn->aspcco_CloseComp != NULL)
			(*pAspConn->aspcco_CloseComp)(ATALK_NO_ERROR,
											    pAspConn->aspcco_CloseCtx);
		// Finally free the memory
		AtalkFreeMemory(pAspConn);

		AtalkUnlockAspCIfNecessary();
	}
}


LOCAL	VOID
atalkAspCQueueAddrGlobalList(
	IN	PASPC_ADDROBJ	pAspAddr
	)
/*++

Routine Description:


Arguments:


Return Value:


--*/
{
	KIRQL	OldIrql;

	ACQUIRE_SPIN_LOCK(&atalkAspCLock, &OldIrql);
	AtalkLinkDoubleAtHead(atalkAspCAddrList, pAspAddr, aspcao_Next, aspcao_Prev);
	RELEASE_SPIN_LOCK(&atalkAspCLock, OldIrql);
}


LOCAL	VOID
atalkAspCDeQueueAddrGlobalList(
	IN	PASPC_ADDROBJ	pAspAddr
	)
/*++

Routine Description:


Arguments:


Return Value:


--*/
{
	KIRQL	OldIrql;

	ACQUIRE_SPIN_LOCK(&atalkAspCLock, &OldIrql);
	AtalkUnlinkDouble(pAspAddr, aspcao_Next, aspcao_Prev);
	RELEASE_SPIN_LOCK(&atalkAspCLock, OldIrql);
}


LOCAL	VOID
atalkAspCQueueConnGlobalList(
	IN	PASPC_CONNOBJ	pAspConn
	)
/*++

Routine Description:


Arguments:


Return Value:


--*/
{
	KIRQL	OldIrql;

	ACQUIRE_SPIN_LOCK(&atalkAspCLock, &OldIrql);
	AtalkLinkDoubleAtHead(atalkAspCConnList, pAspConn, aspcco_Next, aspcco_Prev);
	RELEASE_SPIN_LOCK(&atalkAspCLock, OldIrql);
}


LOCAL	VOID
atalkAspCDeQueueConnGlobalList(
	IN	PASPC_CONNOBJ	pAspCConn
	)
/*++

Routine Description:


Arguments:


Return Value:


--*/
{
	KIRQL	OldIrql;

	ACQUIRE_SPIN_LOCK(&atalkAspCLock, &OldIrql);
	AtalkUnlinkDouble(pAspCConn, aspcco_Next, aspcco_Prev);
	RELEASE_SPIN_LOCK(&atalkAspCLock, OldIrql);
}
