/*++ Copyright (c) 1989-1993 Microsoft Corporation Module Name: spxsend.c Abstract: This module contains code that implements the send engine for the SPX transport provider. Author: Nikhil Kamkolkar (nikhilk) 11-November-1993 Environment: Kernel mode Revision History: --*/ #include "precomp.h" #pragma hdrstop // Define module number for event logging entries #define FILENUM SPXSEND VOID SpxSendComplete( IN PNDIS_PACKET pNdisPkt, IN NDIS_STATUS NdisStatus ) /*++ Routine Description: This routine is called by the I/O system to indicate that a connection- oriented packet has been shipped and is no longer needed by the Physical Provider. Arguments: ProtocolBindingContext - The ADAPTER structure for this binding. NdisPacket/RequestHandle - A pointer to the NDIS_PACKET that we sent. NdisStatus - the completion status of the send. Return Value: none. --*/ { PSPX_CONN_FILE pSpxConnFile; PSPX_SEND_RESD pSendResd; PNDIS_BUFFER pNdisBuffer; CTELockHandle lockHandle; UINT bufCount; PREQUEST pRequest = NULL; BOOLEAN completeReq = FALSE, freePkt = FALSE, orphaned = FALSE, lockHeld = FALSE; pSendResd = (PSPX_SEND_RESD)(pNdisPkt->ProtocolReserved); #if DBG if (NdisStatus != NDIS_STATUS_SUCCESS) { DBGPRINT(SEND, DBG, ("SpxSendComplete: For %lx with status **%lx**\n", pNdisPkt, NdisStatus)); } #endif // IPX changes the length set for the first ndis buffer descriptor. // Change it back to its original value here. NdisQueryPacket(pNdisPkt, NULL, &bufCount, &pNdisBuffer, NULL); NdisAdjustBufferLength(pNdisBuffer, IpxMacHdrNeeded + MIN_IPXSPX2_HDRSIZE); do { pSpxConnFile = pSendResd->sr_ConnFile; CTEGetLock(&pSpxConnFile->scf_Lock, &lockHandle); lockHeld = TRUE; #if defined(__PNP) // // if IPX gave us a new LocalTarget, use for our next send. // // But if we are sending connect requests by iterating over NicIds, // dont update the local target bcoz that will screw up our iteration // logic. // if ( DEVICE_NETWORK_PATH_NOT_FOUND == NdisStatus && !( SPX_CONN_CONNECTING(pSpxConnFile) && (SPX_CONNECT_STATE(pSpxConnFile) == SPX_CONNECT_SENTREQ) && (*((UNALIGNED ULONG *)(pSpxConnFile->scf_RemAddr)) == 0) ) ) { pSpxConnFile->scf_LocalTarget = pSendResd->LocalTarget; // // Renegotiate the max packet size if we have an active SPX2 // session going on and we negotiated the max size originally. // if ( SPX_MAIN_STATE(pSpxConnFile) == SPX_CONNFILE_ACTIVE && SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_SPX2) && SPX_CONN_FLAG(pSpxConnFile, SPX_CONNFILE_NEG) ) { // // this call will get the local max size on this new local target // from IPX. // SPX_MAX_PKT_SIZE(pSpxConnFile, TRUE, TRUE, *((UNALIGNED ULONG *)pSpxConnFile->scf_RemAddr ); SPX_SEND_SETSTATE(pSpxConnFile, SPX_SEND_RENEG); DBGPRINT(SEND, DBG3, ("SpxConnProcessAck: %lx CONNECTION ENTERING RENEG\n", pSpxConnFile)); } } #endif __PNP CTEAssert((pSendResd->sr_State & SPX_SENDPKT_IPXOWNS) != 0); // IPX dont own this packet nomore. pSendResd->sr_State &= ~SPX_SENDPKT_IPXOWNS; // If a send packet has been aborted, then we need to call // abort send to go ahead and free up this packet, and deref associated // request, if there is one, potentially completing it. if ((pSendResd->sr_State & SPX_SENDPKT_ABORT) != 0) { spxConnAbortSendPkt( pSpxConnFile, pSendResd, SPX_CALL_TDILEVEL, lockHandle); lockHeld = FALSE; break; } // If there is an associated request, remove reference on it. BUT for a // sequenced packet only if it has been acked and is waiting for the request // to be dereferenced. It is already dequeued from queue, just free it up. if ((((pSendResd->sr_State & SPX_SENDPKT_REQ) != 0) && ((pSendResd->sr_State & SPX_SENDPKT_SEQ) == 0)) || ((pSendResd->sr_State & SPX_SENDPKT_ACKEDPKT) != 0)) { freePkt = (BOOLEAN)((pSendResd->sr_State & SPX_SENDPKT_ACKEDPKT) != 0); pRequest = pSendResd->sr_Request; CTEAssert(pRequest != NULL); DBGPRINT(SEND, DBG, ("IpxSendComplete: ReqRef before dec %lx.%lx\n", pRequest, REQUEST_INFORMATION(pRequest))); // Deref the request and see if we complete it now. We always have our // own reference on the request. // !!! Status should already have been set in request...!!! if (--(REQUEST_INFORMATION(pRequest)) == 0) { CTEAssert(REQUEST_STATUS(pRequest) != STATUS_PENDING); completeReq = TRUE; // If this is acked already, request is not on list. // BUG #11626 if ((pSendResd->sr_State & SPX_SENDPKT_ACKEDPKT) == 0) { RemoveEntryList(REQUEST_LINKAGE(pRequest)); } } } // Do we destroy this packet? if ((pSendResd->sr_State & SPX_SENDPKT_DESTROY) != 0) { // Remove this packet from the send list in the connection. DBGPRINT(SEND, INFO, ("IpxSendComplete: destroy packet...\n")); SpxConnDequeueSendPktLock(pSpxConnFile, pNdisPkt); freePkt = TRUE; } } while (FALSE); if (lockHeld) { CTEFreeLock(&pSpxConnFile->scf_Lock, lockHandle); } if (freePkt) { DBGPRINT(SEND, INFO, ("IpxSendComplete: free packet...\n")); SpxPktSendRelease(pNdisPkt); } if (completeReq) { // If this is a send request, set info to data sent, else it will be // zero. if (REQUEST_MINOR_FUNCTION(pRequest) == TDI_SEND) { PTDI_REQUEST_KERNEL_SEND pParam; pParam = (PTDI_REQUEST_KERNEL_SEND) REQUEST_PARAMETERS(pRequest); REQUEST_INFORMATION(pRequest) = pParam->SendLength; DBGPRINT(SEND, DBG, ("IpxSendComplete: complete req %lx.%lx...\n", REQUEST_STATUS(pRequest), REQUEST_INFORMATION(pRequest))); CTEAssert(pRequest != NULL); CTEAssert(REQUEST_STATUS(pRequest) != STATUS_PENDING); SpxCompleteRequest(pRequest); } else { DBGPRINT(SEND, DBG, ("SpxSendComplete: %lx DISC Request %lx with %lx.%lx\n", pSpxConnFile, pRequest, REQUEST_STATUS(pRequest), REQUEST_INFORMATION(pRequest))); DBGPRINT(SEND, DBG, ("SpxSendComplete: %lx.%lx.%lx\n", pSpxConnFile->scf_RefCount, pSpxConnFile->scf_Flags, pSpxConnFile->scf_Flags2)); // Set the request in the connection, and deref for it. InsertTailList( &pSpxConnFile->scf_DiscLinkage, REQUEST_LINKAGE(pRequest)); } SpxConnFileDereference(pSpxConnFile, CFREF_VERIFY); } return; } // SpxSendComplete