/*++ 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 --*/ #include #pragma hdrstop #define FILENUM ASPC #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 = AtalkGetAddressFromMdlSafe( pReadBuf, NormalPagePriority); if (AttnBuf == NULL) { error = ATALK_FAILURE; break; } 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; } BOOLEAN AtalkAspCConnectionIsValid( IN PASPC_CONNOBJ pAspConn ) /*++ Routine Description: Arguments: Return Value: --*/ { KIRQL OldIrql; PASPC_CONNOBJ pTmpConn; BOOLEAN fConnIsValid=FALSE; ACQUIRE_SPIN_LOCK(&atalkAspCLock, &OldIrql); pTmpConn = atalkAspCConnList; while (pTmpConn) { if (pTmpConn == pAspConn) { fConnIsValid = TRUE; break; } pTmpConn = pTmpConn->aspcco_Next; } RELEASE_SPIN_LOCK(&atalkAspCLock, OldIrql); return(fConnIsValid); } 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); atalkAspCDeQueueConnGlobalList(pAspConn); // 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); }