windows-nt/Source/XPSP1/NT/multimedia/directx/dplay/dnet/protocol/command.cpp
2020-09-26 16:20:57 +08:00

1132 lines
36 KiB
C++

/*==========================================================================
*
* Copyright (C) 1999 Microsoft Corporation. All Rights Reserved.
*
* File: Command.cpp
* Content: This file contains code which implements assorted APIs for the
* DirectPlay protocol.
*
* History:
* Date By Reason
* ==== == ======
* 11/06/98 ejs Created
* 07/01/2000 masonb Assumed Ownership
*
****************************************************************************/
#include "dnproti.h"
VOID AbortDatagramSend(PMSD, HRESULT);
/*
** Cancel Command
**
** This procedure is passed a HANDLE returned from a previous asynchronous
** DPLAY command. At the moment, the handle is a pointer to an internal data
** structure. Problem with this is that due to FPM's (fixed pool manager) design
** they will get recycled very quickly and frequently. We might want to map them
** into an external handle table which will force them to recycle much more slowly.
** Perhaps, I will let the upper DN layer do this mapping...
**
** Anyway, the only check I can do right now is that the HANDLE is currently
** allocated to something.
**
** We do not expect cancels to happen very often. Therefore, I do not feel
** bad about walking the global command list to find the Handle. Of course, if
** we do go to a handle mapped system then we should not need to do this walk.
**
** I THINK - That any cancellable command will be on either MessageList or TimeoutList!
**
** Things we can cancel and their possible states:
**
** SEND Datagram
** On SPD Send Queue
** On EPD Send Queue
** In SP call
**
** SEND Reliable
** We can only cancel if it has not started transmitting. Once its started, the
** user program must Abort the link to cancel the send.
**
** CONNECT
** In SP call
** On PD list
**
** LISTEN
** In SP call
** On PD list
**
** Remember, if we cancel a command in SP then the CommandComplete is supposed to
** occur. This means that we should not have to explicitly free the MSD, etc in these
** cases.
*/
#undef DPF_MODNAME
#define DPF_MODNAME "DNPCancelCommand"
HRESULT
DNPCancelCommand(PProtocolData pPData, HANDLE hCommand)
{
PMSD pMSD = (PMSD) hCommand;
HRESULT hr;
DPFX(DPFPREP,DPF_CALLIN_LVL, "Parameters: pPData[%p], hCommand[%x]", pPData, hCommand);
if(pMSD->Sign != MSD_SIGN)
{
DPFX(DPFPREP,0, "Cancel called with invalid handle");
return DPNERR_INVALIDHANDLE;
}
Lock(&pMSD->CommandLock); // Take this early to freeze state of command
// validate instance of MSD
if(pMSD->lRefCnt == -1)
{
DPFX(DPFPREP,0, "Cancel called with invalid handle");
Unlock(&pMSD->CommandLock);
return DPNERR_INVALIDHANDLE;
}
hr = DoCancel(pMSD, DPNERR_USERCANCEL); // Releases CommandLock
return hr;
}
/*
** Do Cancel
**
** This function implements the meat of the cancel asynch operation. It gets called from
** two places. Either from the User cancel API right above, or from the global timeout handler.
**
** ***This code requires the MSD->CommandLock to be help upon entry, unlocks upon return
*/
#undef DPF_MODNAME
#define DPF_MODNAME "DoCancel"
HRESULT
DoCancel(PMSD pMSD, HRESULT CompletionCode)
{
PEPD pEPD;
HRESULT hr = DPN_OK;
DPFX(DPFPREP,7, "Cancelling pMSD=%p", pMSD);
AssertCriticalSectionIsTakenByThisThread(&pMSD->CommandLock, TRUE);
if (!(pMSD->ulMsgFlags1 & MFLAGS_ONE_IN_USE))
{
DPFX(DPFPREP,0, "(%p) MSD is in Pool, returning DPNERR_CANNOTCANCEL, pMSD[%p]", pMSD->pEPD, pMSD);
ASSERT(0);
Unlock(&pMSD->CommandLock);
return DPNERR_CANNOTCANCEL;
}
if(pMSD->ulMsgFlags1 & (MFLAGS_ONE_CANCELLED | MFLAGS_ONE_COMPLETE))
{
DPFX(DPFPREP,7, "(%p) MSD is Cancelled or Complete, returning DPNERR_CANNOTCANCEL, pMSD[%p]", pMSD->pEPD, pMSD);
Unlock(&pMSD->CommandLock);
return DPNERR_CANNOTCANCEL;
}
pMSD->ulMsgFlags1 |= MFLAGS_ONE_CANCELLED;
switch(pMSD->CommandID)
{
case COMMAND_ID_SEND_DATAGRAM:
pEPD = pMSD->pEPD;
ASSERT_EPD(pEPD);
Lock(&pEPD->EPLock);
if(pMSD->ulMsgFlags2 & (MFLAGS_TWO_ABORT | MFLAGS_TWO_TRANSMITTING | MFLAGS_TWO_SEND_COMPLETE))
{
DPFX(DPFPREP,7, "(%p) MSD is Aborted, Transmitting, or Complete, returning DPNERR_CANNOTCANCEL, pMSD[%p]", pEPD, pMSD);
Unlock(&pEPD->EPLock); // Link is dropping or DNET is terminating
hr = DPNERR_CANNOTCANCEL; // To cancel an xmitting reliable send you
break; // must Abort the connection.
}
pMSD->blQLinkage.RemoveFromList(); // Remove from SendQueue (whichever one)
ASSERT(pEPD->uiQueuedMessageCount > 0);
--pEPD->uiQueuedMessageCount; // keep count of MSDs on all send queues
// Clear data-ready flag if everything is sent
if((pEPD->uiQueuedMessageCount == 0) && (pEPD->pCurrentSend == NULL))
{
pEPD->ulEPFlags &= ~(EPFLAGS_SDATA_READY);
}
#ifdef DEBUG
ASSERT(pMSD->ulMsgFlags2 & MFLAGS_TWO_ENQUEUED);
pMSD->ulMsgFlags2 &= ~(MFLAGS_TWO_ENQUEUED);
#endif
Unlock(&pEPD->EPLock); // release lock on queue
DPFX(DPFPREP,7, "(%p) Aborting Datagram send, pMSD[%p]", pEPD, pMSD);
AbortDatagramSend(pMSD, CompletionCode); // Releases CommandLock
return hr;
case COMMAND_ID_SEND_RELIABLE:
pEPD = pMSD->pEPD;
ASSERT_EPD(pEPD);
Lock(&pEPD->EPLock);
if(pMSD->ulMsgFlags2 & (MFLAGS_TWO_ABORT | MFLAGS_TWO_TRANSMITTING | MFLAGS_TWO_SEND_COMPLETE))
{
DPFX(DPFPREP,7, "(%p) MSD is Aborted, Transmitting, or Complete, returning DPNERR_CANNOTCANCEL, pMSD[%p]", pEPD, pMSD);
Unlock(&pEPD->EPLock); // Link is dropping or DNET is terminating
hr = DPNERR_CANNOTCANCEL; // To cancel an xmitting reliable send you
break; // must Abort the connection.
}
if(pMSD->TimeoutTimer != NULL)
{
DPFX(DPFPREP,7, "(%p) Cancelling Timeout Timer", pEPD);
if(CancelMyTimer(pMSD->TimeoutTimer, pMSD->TimeoutTimerUnique) == DPN_OK)
{
DECREMENT_MSD(pMSD, "Send Timeout Timer");
}
else
{
DPFX(DPFPREP,7, "(%p) Cancelling Timeout Timer Failed", pEPD);
}
pMSD->TimeoutTimer = NULL;
}
pMSD->blQLinkage.RemoveFromList(); // Remove cmd from queue
ASSERT(pEPD->uiQueuedMessageCount > 0);
--pEPD->uiQueuedMessageCount; // keep count of MSDs on all send queues
// Clear data-ready flag if everything is sent
if((pEPD->uiQueuedMessageCount == 0) && (pEPD->pCurrentSend == NULL))
{
pEPD->ulEPFlags &= ~(EPFLAGS_SDATA_READY);
}
#ifdef DEBUG
ASSERT(pMSD->ulMsgFlags2 & MFLAGS_TWO_ENQUEUED);
pMSD->ulMsgFlags2 &= ~(MFLAGS_TWO_ENQUEUED);
#endif
// This only gets complex if the cancelled send was the "on deck" send for the endpoint.
//
// New Logic! With advent of priority sending, we can no longer prepare the On Deck send before we are
// ready to transmit since the arrival of a higher priority send should be checked for before starting to
// send a new message. This means that pCurrentSend != pMSD unless the MFLAGS_TRANSMITTING flag has also
// been set, rendering the message impossible to cancel.
ASSERT(pEPD->pCurrentSend != pMSD);
pMSD->uiFrameCount = 0;
DPFX(DPFPREP, DPF_FRAMECNT_LVL, "Send cancelled before sending, pMSD[%p], framecount[%u]", pMSD, pMSD->uiFrameCount);
Unlock(&pEPD->EPLock);
DPFX(DPFPREP,7, "(%p) Completing Reliable Send", pEPD);
CompleteReliableSend(pMSD->pSPD, pMSD, CompletionCode);
return hr;
case COMMAND_ID_CONNECT:
if(pMSD->ulMsgFlags1 & MFLAGS_ONE_IN_SERVICE_PROVIDER)
{
// SP owns the command - issue a cancel and let CompletionEvent clean up command
Unlock(&pMSD->CommandLock); // We could deadlock if we cancel with lock held
DPFX(DPFPREP,DPF_CALLOUT_LVL, "Calling SP->CancelCommand on Connect, pMSD[%p], hCommand[%x], pSPD[%p]", pMSD, pMSD->hCommand, pMSD->pSPD);
(void) IDP8ServiceProvider_CancelCommand(pMSD->pSPD->IISPIntf, pMSD->hCommand, pMSD->dwCommandDesc);
// If the SP Cancel fails it should not matter. It would usually mean we are
// in a race with the command completing, in which case the cancel flag will
// nip it in the bud.
return DPN_OK;
}
// We will only get here once because the entry to this function checks CANCEL and COMPLETE and sets
// CANCEL. CompleteConnect will set COMPLETE as well.
pEPD = pMSD->pEPD;
ASSERT_EPD(pEPD);
Lock(&pEPD->EPLock);
// Unlink the MSD from the EPD
ASSERT(pEPD->pCommand == pMSD);
pEPD->pCommand = NULL;
DECREMENT_MSD(pMSD, "EPD Ref");
DropLink(pEPD); // This unlocks the EPLock
// MSD lock still held
DPFX(DPFPREP,5, "(%p) Connect cancelled, completing Connect, pMSD[%p]", pEPD, pMSD);
CompleteConnect(pMSD, pMSD->pSPD, NULL, DPNERR_USERCANCEL); // releases command lock
return DPN_OK;
case COMMAND_ID_LISTEN:
/*
** Cancel Listen
**
** SP will own parts of the MSD until the SPCommandComplete function is called. We will
** defer much of our cancel processing to this handler.
*/
// Stop listening in SP -- This will prevent new connections from popping up while we are
// closing down any left in progress. Only problem is we need to release command lock to
// do it.
Unlock(&pMSD->CommandLock); // We can deadlock if we hold across this call
DPFX(DPFPREP,DPF_CALLOUT_LVL, "Calling SP->CancelCommand on Listen, pMSD[%p], hCommand[%x], pSPD[%p]", pMSD, pMSD->hCommand, pMSD->pSPD);
(void) IDP8ServiceProvider_CancelCommand(pMSD->pSPD->IISPIntf, pMSD->hCommand, pMSD->dwCommandDesc);
Lock(&pMSD->CommandLock); // Lock this down again.
// Are there any connections in progress?
// For a Listen command, connecting endpoints are held on the blFrameList
while(!pMSD->blFrameList.IsEmpty())
{
pEPD = CONTAINING_RECORD(pMSD->blFrameList.GetNext(), EPD, blSPLinkage);
ASSERT_EPD(pEPD);
DPFX(DPFPREP,1, "FOUND CONNECT IN PROGRESS ON CANCELLED LISTEN, EPD=%p", pEPD);
Lock(&pEPD->EPLock);
// Ensure we don't stay in this loop forever
pEPD->ulEPFlags &= ~(EPFLAGS_LINKED_TO_LISTEN);
pEPD->blSPLinkage.RemoveFromList(); // Unlink EPD from Listen Queue
// It is possible that RejectInvalidPacket is happening at the same time as this, so guard against us
// both doing the same clean up and removing the same reference from the MSD.
if (!(pEPD->ulEPFlags & EPFLAGS_STATE_TERMINATING))
{
// We know this only happens once because anyone who does it either transitions us to the
// CONNECTED or TERMINATING state, and also removes us from the Listen list above.
// Unlink MSD from EPD
ASSERT(pEPD->pCommand == pMSD); // This should be pointing back to this listen
pEPD->pCommand = NULL;
DECREMENT_MSD(pMSD, "EPD Ref"); // Unlink from EPD and release associated reference
DropLink(pEPD); // releases EPLock
}
else
{
Unlock(&pEPD->EPLock);
}
} // for each connection in progress
RELEASE_MSD(pMSD, "(Base Ref) Release On Cancel"); // release base reference
return DPN_OK;
case COMMAND_ID_ENUM:
{
Unlock(&pMSD->CommandLock);
DPFX(DPFPREP,DPF_CALLOUT_LVL, "Calling SP->CancelCommand on Enum, pMSD[%p], hCommand[%x], pSPD[%p]", pMSD, pMSD->hCommand, pMSD->pSPD);
return IDP8ServiceProvider_CancelCommand(pMSD->pSPD->IISPIntf, pMSD->hCommand, pMSD->dwCommandDesc);
// We will pass HRESULT from SP directly to user
}
case COMMAND_ID_ENUMRESP:
{
Unlock(&pMSD->CommandLock);
DPFX(DPFPREP,DPF_CALLOUT_LVL, "Calling SP->CancelCommand on EnumResp, pMSD[%p], hCommand[%x], pSPD[%p]", pMSD, pMSD->hCommand, pMSD->pSPD);
return IDP8ServiceProvider_CancelCommand(pMSD->pSPD->IISPIntf, pMSD->hCommand, pMSD->dwCommandDesc);
// We will pass HRESULT from SP directly to user
}
case COMMAND_ID_DISCONNECT:
case COMMAND_ID_COPIED_RETRY: // This should be on FMD's only
case COMMAND_ID_CFRAME: // This should be on FMD's only
case COMMAND_ID_DISC_RESPONSE: // These are never placed on the global list and aren't cancellable
case COMMAND_ID_KEEPALIVE: // These are never placed on the global list and aren't cancellable
default:
ASSERT(0); // Should never get here
hr = DPNERR_CANNOTCANCEL;
break;
}
Unlock(&pMSD->CommandLock);
return hr;
}
/*
** Get Listen Info
**
** Return a buffer full of interesting and provokative tidbits about a particular Listen command.
*/
#undef DPF_MODNAME
#define DPF_MODNAME "DNPGetListenAddressInfo"
HRESULT
DNPGetListenAddressInfo(HANDLE hCommand, PSPGETADDRESSINFODATA pSPData)
{
PMSD pMSD = (PMSD) hCommand;
HRESULT hr = DPNERR_INVALIDHANDLE;
DPFX(DPFPREP,DPF_CALLIN_LVL, "Parameters: hCommand[%x], pSPData[%p]", hCommand, pSPData);
ASSERT(pMSD != NULL);
ASSERT_MSD(pMSD);
if((pMSD->CommandID == COMMAND_ID_LISTEN) && (pMSD->ulMsgFlags1 & MFLAGS_ONE_IN_SERVICE_PROVIDER))
{
pSPData->hEndpoint = pMSD->hListenEndpoint;
DPFX(DPFPREP,DPF_CALLOUT_LVL, "Calling SP->GetAddressInfo, pMSD[%p], hEndpoint[%x], pSPD[%p]", pMSD, pMSD->hListenEndpoint, pMSD->pSPD);
hr = IDP8ServiceProvider_GetAddressInfo(pMSD->pSPD->IISPIntf, pSPData);
}
return hr;
}
/*
** Validate End Point
**
** This routine checks standard flags, validates the Service
** Provider, and bumps the reference count on an end point descriptor
** which is passed in.
*/
#undef DPF_MODNAME
#define DPF_MODNAME "ValidateEndPoint"
HRESULT
ValidateEndPoint(PEPD pEPD)
{
if(pEPD == NULL)
{
DPFX(DPFPREP,1, "Validate EndPoint Fails on NULL EPD", pEPD);
return DPNERR_INVALIDENDPOINT;
}
if (pEPD->Sign != EPD_SIGN)
{
DPFX(DPFPREP,1, "Validate EndPoint Fails on EPD with bad sign (%p)", pEPD);
return DPNERR_INVALIDENDPOINT;
}
// Bump reference count on this baby
if(!LOCK_EPD(pEPD, "LOCK (ValidateEndPoint - DISC)"))
{
// When LOCK_EPD returns FALSE, there is no ref placed on the endpoint, so we don't need to release
DPFX(DPFPREP,1, "Validate EndPoint Fails on unreferenced EPD (%p)", pEPD);
return DPNERR_INVALIDENDPOINT;
}
return DPN_OK;
}
/*
** Disconnect End Point
**
** This function is called when the client no longer wishes
** to communicate with the specified end point. We will initiate
** the disconnect protocol with the endpoint, and when it is
** acknowleged, we will disconnect the SP and release the handle.
**
** Disconnect is defined in Direct Net to allow all previously
** submitted sends to complete, but no additional sends to be submitted.
** Also, any sends the partner has in progress will be delivered, but
** no additional sends will be accepted following the indication that
** a disconnect is in progress on the remote end.
**
** This implies that two indications will be generated on the remote
** machine, Disconnect Initiated and Disconnect Complete. Only the
** Complete will be indicated on the issueing side.
*/
#undef DPF_MODNAME
#define DPF_MODNAME "DNPDisconnectEndPoint"
HRESULT
DNPDisconnectEndPoint(PProtocolData pPData, HANDLE hEndPoint, PVOID pvContext, PHANDLE phCommand)
{
PEPD pEPD;
PMSD pMSD;
HRESULT hr;
DPFX(DPFPREP,DPF_CALLIN_LVL, "Parameters: pPData[%p], hEndPoint[%x], pvContext[%p], phCommand[%p]", pPData, hEndPoint, pvContext, phCommand);
pEPD = (PEPD) hEndPoint;
// This bumps REFCNT if it returns DPN_OK
if((hr = ValidateEndPoint(pEPD)) != DPN_OK)
{
DPFX(DPFPREP,0, "Attempt to disconnect invalid endpoint");
return hr;
}
Lock(&pEPD->EPLock);
// If we aren't connected, or we have already initiated a disconnect, don't allow a new disconnect
if(!(pEPD->ulEPFlags & EPFLAGS_STATE_CONNECTED) || (pEPD->ulEPFlags & (EPFLAGS_SENT_DISCONNECT | EPFLAGS_RECEIVED_DISCONNECT)))
{
RELEASE_EPD(pEPD, "UNLOCK (Validate EP)"); // Releases EPLock
DPFX(DPFPREP,1, "Attempt to disconnect already disconnecting endpoint");
return DPNERR_ALREADYDISCONNECTING;
}
pEPD->ulEPFlags |= EPFLAGS_SENT_DISCONNECT; // Accept no more sends, but don't scrap link yet
if((pMSD = BuildDisconnectFrame(pEPD)) == NULL)
{
RELEASE_EPD(pEPD, "UNLOCK (Validate EP)"); // Releases EPLock
DPFX(DPFPREP,0, "Failed to build disconnect frame");
return DPNERR_OUTOFMEMORY; // The educated user will next try an Abort command
}
pMSD->CommandID = COMMAND_ID_DISCONNECT;
pMSD->Context = pvContext; // retain user's context value
*phCommand = pMSD; // pass back command handle
// We borrow the reference placed above by ValidateEP for this. It will be released
// on completion of the Disconnect.
ASSERT(pEPD->pCommand == NULL);
pEPD->pCommand = pMSD; // Store the disconnect command on the endpoint until it is complete
#ifdef DEBUG
Lock(&pMSD->pSPD->SPLock);
pMSD->blSPLinkage.InsertBefore( &pMSD->pSPD->blMessageList);
pMSD->ulMsgFlags1 |= MFLAGS_ONE_ON_GLOBAL_LIST;
Unlock(&pMSD->pSPD->SPLock);
#endif
DPFX(DPFPREP,5, "(%p) Queueing DISCONNECT message", pEPD);
EnqueueMessage(pMSD, pEPD); // Enqueue Disc frame on SendQ
Unlock(&pEPD->EPLock);
return DPNERR_PENDING;
}
/*
** Get/Set Protocol Caps
**
** Return or Set information about the entire protocol.
**
*/
#undef DPF_MODNAME
#define DPF_MODNAME "DNPGetProtocolCaps"
HRESULT
DNPGetProtocolCaps(PProtocolData pPData, PDPN_CAPS pData)
{
DPFX(DPFPREP,DPF_CALLIN_LVL, "Parameters: pPData[%p], pData[%p]", pPData, pData);
ASSERT(pData->dwSize == sizeof(DPN_CAPS));
ASSERT(pData->dwFlags == 0);
pData->dwConnectTimeout = pPData->dwConnectTimeout;
pData->dwConnectRetries = pPData->dwConnectRetries;
pData->dwTimeoutUntilKeepAlive = pPData->tIdleThreshhold;
return DPN_OK;
}
#undef DPF_MODNAME
#define DPF_MODNAME "DNPSetProtocolCaps"
HRESULT
DNPSetProtocolCaps(PProtocolData pPData, const DPN_CAPS * const pData)
{
DPFX(DPFPREP,DPF_CALLIN_LVL, "Parameters: pPData[%p], pData[%p]", pPData, pData);
ASSERT(pData->dwSize == sizeof(DPN_CAPS));
ASSERT(pData->dwFlags == 0);
pPData->dwConnectTimeout = pData->dwConnectTimeout;
pPData->dwConnectRetries = pData->dwConnectRetries;
pPData->tIdleThreshhold = pData->dwTimeoutUntilKeepAlive;
return DPN_OK;
}
/*
** Get Endpoint Caps
**
** Return information and statistics about a particular endpoint.
**
*/
#undef DPF_MODNAME
#define DPF_MODNAME "DNPGetEPCaps"
HRESULT
DNPGetEPCaps(HANDLE hEndpoint, PDPN_CONNECTION_INFO pBuffer)
{
PEPD pEPD;
DPFX(DPFPREP,DPF_CALLIN_LVL, "Parameters: hEndpoint[%x], pBuffer[%p]", hEndpoint, pBuffer);
pEPD = (PEPD) hEndpoint;
if(pEPD == NULL || !(pEPD->ulEPFlags & EPFLAGS_STATE_CONNECTED))
{
DPFX(DPFPREP,0, "Returning DPNERR_INVALIDENDPOINT - hEndpoint is NULL or Enpoint is not connected");
return DPNERR_INVALIDENDPOINT;
}
if(pBuffer == NULL)
{
DPFX(DPFPREP,0, "Returning DPNERR_INVALIDPARAM - pBuffer is NULL");
return DPNERR_INVALIDPARAM;
}
ASSERT_EPD(pEPD);
ASSERT(pBuffer->dwSize == sizeof(DPN_CONNECTION_INFO) ||
pBuffer->dwSize == sizeof(DPN_CONNECTION_INFO_INTERNAL));
pBuffer->dwRoundTripLatencyMS = pEPD->uiRTT;
pBuffer->dwThroughputBPS = pEPD->uiPeriodRateB * 4; // Convert to apx of bytes/second (really bytes/1024 ms)
pBuffer->dwPeakThroughputBPS = pEPD->uiPeakRateB * 4;
pBuffer->dwBytesSentGuaranteed = pEPD->uiGuaranteedBytesSent;
pBuffer->dwPacketsSentGuaranteed = pEPD->uiGuaranteedFramesSent;
pBuffer->dwBytesSentNonGuaranteed = pEPD->uiDatagramBytesSent;
pBuffer->dwPacketsSentNonGuaranteed = pEPD->uiDatagramFramesSent;
pBuffer->dwBytesRetried = pEPD->uiGuaranteedBytesDropped;
pBuffer->dwPacketsRetried = pEPD->uiGuaranteedFramesDropped;
pBuffer->dwBytesDropped = pEPD->uiDatagramBytesDropped;
pBuffer->dwPacketsDropped = pEPD->uiDatagramFramesDropped;
pBuffer->dwMessagesTransmittedHighPriority = pEPD->uiMsgSentHigh;
pBuffer->dwMessagesTimedOutHighPriority = pEPD->uiMsgTOHigh;
pBuffer->dwMessagesTransmittedNormalPriority = pEPD->uiMsgSentNorm;
pBuffer->dwMessagesTimedOutNormalPriority = pEPD->uiMsgTONorm;
pBuffer->dwMessagesTransmittedLowPriority = pEPD->uiMsgSentLow;
pBuffer->dwMessagesTimedOutLowPriority = pEPD->uiMsgTOLow;
pBuffer->dwBytesReceivedGuaranteed = pEPD->uiGuaranteedBytesReceived;
pBuffer->dwPacketsReceivedGuaranteed = pEPD->uiGuaranteedFramesReceived;
pBuffer->dwBytesReceivedNonGuaranteed = pEPD->uiDatagramBytesReceived;
pBuffer->dwPacketsReceivedNonGuaranteed = pEPD->uiDatagramFramesReceived;
pBuffer->dwMessagesReceived = pEPD->uiMessagesReceived;
if (pBuffer->dwSize == sizeof(DPN_CONNECTION_INFO_INTERNAL))
{
DPFX(DPFPREP,DPF_CALLIN_LVL, "(%p) Test App requesting extended internal parameters", pEPD);
((PDPN_CONNECTION_INFO_INTERNAL)pBuffer)->uiDropCount = pEPD->uiDropCount;
((PDPN_CONNECTION_INFO_INTERNAL)pBuffer)->uiThrottleEvents = pEPD->uiThrottleEvents;
((PDPN_CONNECTION_INFO_INTERNAL)pBuffer)->uiAdaptAlgCount = pEPD->uiAdaptAlgCount;
((PDPN_CONNECTION_INFO_INTERNAL)pBuffer)->uiWindowFilled = pEPD->uiWindowFilled;
((PDPN_CONNECTION_INFO_INTERNAL)pBuffer)->uiPeriodAcksBytes = pEPD->uiPeriodAcksBytes;
((PDPN_CONNECTION_INFO_INTERNAL)pBuffer)->uiPeriodXmitTime = pEPD->uiPeriodXmitTime;
((PDPN_CONNECTION_INFO_INTERNAL)pBuffer)->dwLastThroughputBPS = pEPD->uiLastRateB * 4;
((PDPN_CONNECTION_INFO_INTERNAL)pBuffer)->uiLastBytesAcked = pEPD->uiLastBytesAcked;
((PDPN_CONNECTION_INFO_INTERNAL)pBuffer)->uiQueuedMessageCount = pEPD->uiQueuedMessageCount;
((PDPN_CONNECTION_INFO_INTERNAL)pBuffer)->uiWindowF = pEPD->uiWindowF;
((PDPN_CONNECTION_INFO_INTERNAL)pBuffer)->uiWindowB = pEPD->uiWindowB;
((PDPN_CONNECTION_INFO_INTERNAL)pBuffer)->uiUnackedFrames = pEPD->uiUnackedFrames;
((PDPN_CONNECTION_INFO_INTERNAL)pBuffer)->uiUnackedBytes = pEPD->uiUnackedBytes;
((PDPN_CONNECTION_INFO_INTERNAL)pBuffer)->uiBurstGap = pEPD->uiBurstGap;
((PDPN_CONNECTION_INFO_INTERNAL)pBuffer)->iBurstCredit = pEPD->iBurstCredit;
((PDPN_CONNECTION_INFO_INTERNAL)pBuffer)->uiGoodWindowF = pEPD->uiGoodWindowF;
((PDPN_CONNECTION_INFO_INTERNAL)pBuffer)->uiGoodWindowB = pEPD->uiGoodWindowBI * pEPD->pSPD->uiFrameLength;
((PDPN_CONNECTION_INFO_INTERNAL)pBuffer)->uiGoodBurstGap = pEPD->uiGoodBurstGap;
((PDPN_CONNECTION_INFO_INTERNAL)pBuffer)->uiGoodRTT = pEPD->uiGoodRTT;
((PDPN_CONNECTION_INFO_INTERNAL)pBuffer)->uiRestoreWindowF = pEPD->uiRestoreWindowF;
((PDPN_CONNECTION_INFO_INTERNAL)pBuffer)->uiRestoreWindowB = pEPD->uiRestoreWindowBI * pEPD->pSPD->uiFrameLength;
((PDPN_CONNECTION_INFO_INTERNAL)pBuffer)->uiRestoreBurstGap = pEPD->uiRestoreBurstGap;
((PDPN_CONNECTION_INFO_INTERNAL)pBuffer)->bNextSend = pEPD->bNextSend;
((PDPN_CONNECTION_INFO_INTERNAL)pBuffer)->bNextReceive = pEPD->bNextReceive;
((PDPN_CONNECTION_INFO_INTERNAL)pBuffer)->ulReceiveMask = pEPD->ulReceiveMask;
((PDPN_CONNECTION_INFO_INTERNAL)pBuffer)->ulReceiveMask2 = pEPD->ulReceiveMask2;
((PDPN_CONNECTION_INFO_INTERNAL)pBuffer)->ulSendMask = pEPD->ulSendMask;
((PDPN_CONNECTION_INFO_INTERNAL)pBuffer)->ulSendMask2 = pEPD->ulSendMask2;
((PDPN_CONNECTION_INFO_INTERNAL)pBuffer)->uiCompleteMsgCount = pEPD->uiCompleteMsgCount;
((PDPN_CONNECTION_INFO_INTERNAL)pBuffer)->uiRetryTimeout = pEPD->uiRetryTimeout;
((PDPN_CONNECTION_INFO_INTERNAL)pBuffer)->ulEPFlags = pEPD->ulEPFlags;
}
return DPN_OK;
}
/*
** Build Disconnect Frame
**
** Build a DISC frame, a Message actually, because we return an MSD which can be inserted into
** our reliable stream and will trigger one-side of the disconnect protocol when it is received
** by a partner.
*/
#undef DPF_MODNAME
#define DPF_MODNAME "BuildDisconnectFrame"
PMSD
BuildDisconnectFrame(PEPD pEPD)
{
PFMD pFMD;
PMSD pMSD;
// Allocate and fill out a Message Descriptor for this operation
if( (pMSD = static_cast<PMSD>( MSDPool->Get(MSDPool) )) == NULL)
{
DPFX(DPFPREP,0, "Failed to allocate MSD");
return NULL;
}
pMSD->uiFrameCount = 1;
DPFX(DPFPREP, DPF_FRAMECNT_LVL, "Initialize Frame count, pMSD[%p], framecount[%u]", pMSD, pMSD->uiFrameCount);
pMSD->ulMsgFlags2 |= MFLAGS_TWO_END_OF_STREAM;
pMSD->ulSendFlags = DN_SENDFLAGS_RELIABLE | DN_SENDFLAGS_LOW_PRIORITY; // Priority is LOW so all previously submitted traffic will be sent
pMSD->pSPD = pEPD->pSPD;
if((pFMD = static_cast<PFMD>( FMDPool->Get(FMDPool) )) == NULL)
{
DPFX(DPFPREP,0, "Failed to allocate FMD");
Lock(&pMSD->CommandLock);
RELEASE_MSD(pMSD, "Release On FMD Get Failed");
return NULL;
}
pMSD->pEPD = pEPD;
pFMD->CommandID = COMMAND_ID_SEND_RELIABLE;
pFMD->ulFFlags |= FFLAGS_END_OF_STREAM; // Mark this frame as Disconnect
pFMD->bPacketFlags = PACKET_COMMAND_DATA | PACKET_COMMAND_RELIABLE | PACKET_COMMAND_SEQUENTIAL | PACKET_COMMAND_END_MSG;
pFMD->uiFrameLength = 0; // No user data in this frame
pFMD->blMSDLinkage.InsertAfter( &pMSD->blFrameList); // Attach frame to MSD
pFMD->pMSD = pMSD; // Link frame back to message
pFMD->pEPD = pEPD;
return pMSD;
}
/*
** Abort Sends on Connection
**
** Walk the EPD's send queues and cancel all sends awaiting service. We might add
** code to issue Cancel commands to the SP for frames still owned by SP. On one hand,
** we are not expecting a big backlog to develop in SP, but on the other hand it still
** might happen. Esp, if we dont fix behavior I have observed with SP being really pokey
** about completing transmitted sends.
**
** ** CALLED WITH EPD->EPLock HELD; RETURNS WITH LOCK RELEASED **
*/
#undef DPF_MODNAME
#define DPF_MODNAME "AbortSendsOnConnection"
VOID
AbortSendsOnConnection(PEPD pEPD)
{
PSPD pSPD = pEPD->pSPD;
PFMD pFMD;
PMSD pMSD;
CBilink *pLink;
CBilink TempList;
ASSERT_SPD(pSPD);
AssertCriticalSectionIsTakenByThisThread(&pEPD->EPLock, TRUE);
TempList.Initialize(); // We will empty all send queues onto this temporary list
do
{
if( (pLink = pEPD->blHighPriSendQ.GetNext()) == &pEPD->blHighPriSendQ)
{
if( (pLink = pEPD->blNormPriSendQ.GetNext()) == &pEPD->blNormPriSendQ)
{
if( (pLink = pEPD->blLowPriSendQ.GetNext()) == &pEPD->blLowPriSendQ)
{
if( (pLink = pEPD->blCompleteSendList.GetNext()) == &pEPD->blCompleteSendList)
{
break; // ALL DONE - No more sends
}
}
}
}
// We have found another send on one of our send queues.
pLink->RemoveFromList(); // Remove it from the queue
pMSD = CONTAINING_RECORD(pLink, MSD, blQLinkage);
ASSERT_MSD(pMSD);
pMSD->ulMsgFlags2 |= (MFLAGS_TWO_ABORT | MFLAGS_TWO_ABORT_WILL_COMPLETE); // Do no further processing
#ifdef DEBUG
pMSD->ulMsgFlags2 &= ~(MFLAGS_TWO_ENQUEUED);
#endif
// If this MSD is a Disconnect, it will be caught by the code below that checks
// pEPD->pCommand. We don't want to end up putting it on the TempList twice.
if (pMSD->CommandID != COMMAND_ID_DISCONNECT && pMSD->CommandID != COMMAND_ID_DISC_RESPONSE)
{
DPFX(DPFPREP,5, "(%p) ABORT SENDS. Found (%p)", pEPD, pMSD);
LOCK_MSD(pMSD, "AbortSends Temp Ref");
pMSD->blQLinkage.InsertBefore( &TempList); // Place on the temporary list
}
}
while (1);
pEPD->uiQueuedMessageCount = 0; // keep count of MSDs on all send queues
if((pMSD = pEPD->pCommand) != NULL)
{
// There may be a DISCONNECT command waiting on this special pointer for the final DISC frame
// from partner to arrive.
pMSD->ulMsgFlags2 |= (MFLAGS_TWO_ABORT | MFLAGS_TWO_ABORT_WILL_COMPLETE); // Do no further processing
if(pMSD->CommandID == COMMAND_ID_DISCONNECT || pMSD->CommandID == COMMAND_ID_DISC_RESPONSE)
{
pEPD->pCommand = NULL;
LOCK_MSD(pMSD, "AbortSends Temp Ref");
pMSD->blQLinkage.InsertBefore( &TempList);
// We will be indicating below, so make sure no one else does once we
// leave the EPLock.
ASSERT(!(pEPD->ulEPFlags & EPFLAGS_INDICATED_DISCONNECT));
pEPD->ulEPFlags |= EPFLAGS_INDICATED_DISCONNECT;
}
else
{
DPFX(DPFPREP,0,"(%p) Any Connect or Listen on pCommand should have already been cleaned up", pEPD);
ASSERT(!"Any Connect or Listen on pCommand should have already been cleaned up");
}
}
// If we clear out our SendWindow before we cancel the sends, then we dont need to differentiate
// between sends that have or have not been transmitted.
while(!pEPD->blSendWindow.IsEmpty())
{
pFMD = CONTAINING_RECORD(pEPD->blSendWindow.GetNext(), FMD, blWindowLinkage);
ASSERT_FMD(pFMD);
pFMD->ulFFlags &= ~(FFLAGS_IN_SEND_WINDOW);
pFMD->blWindowLinkage.RemoveFromList(); // Eliminate each frame from the Send Window
RELEASE_FMD(pFMD, "Send Window");
DPFX(DPFPREP,5, "(%p) ABORT CONN: Release frame from Window: pFMD=0x%p", pEPD, pFMD);
}
pEPD->pCurrentSend = NULL;
pEPD->pCurrentFrame = NULL;
while(!pEPD->blRetryQueue.IsEmpty())
{
pFMD = CONTAINING_RECORD(pEPD->blRetryQueue.GetNext(), FMD, blQLinkage);
ASSERT_FMD(pFMD);
pFMD->blQLinkage.RemoveFromList();
pFMD->ulFFlags &= ~(FFLAGS_RETRY_QUEUED); // No longer on the retry queue
ASSERT_MSD(pFMD->pMSD);
pFMD->pMSD->uiFrameCount--; // Protected by EPLock, retries count against outstanding frame count
DPFX(DPFPREP, DPF_FRAMECNT_LVL, "Retry frame reference decremented on abort, pMSD[%p], framecount[%u]", pFMD->pMSD, pFMD->pMSD->uiFrameCount);
DECREMENT_EPD(pEPD, "UNLOCK (Releasing Retry Frame)"); // SPLock not already held
if (pFMD->CommandID == COMMAND_ID_COPIED_RETRY)
{
DECREMENT_EPD(pEPD, "UNLOCK (Copy Complete)"); // SPLock not already held
}
RELEASE_FMD(pFMD, "SP Submit");
}
pEPD->ulEPFlags &= ~(EPFLAGS_RETRIES_QUEUED);
// Now that we have emptied the EPD's queues we will release the EPLock so we can lock each
// MSD before we complete it.
Unlock(&pEPD->EPLock);
while(!TempList.IsEmpty())
{
pMSD = CONTAINING_RECORD(TempList.GetNext(), MSD, blQLinkage);
ASSERT_MSD(pMSD);
pMSD->blQLinkage.RemoveFromList(); // remove this send from temporary queue
Lock(&pMSD->CommandLock); // Complete call will Unlock MSD
ASSERT(pMSD->ulMsgFlags1 & MFLAGS_ONE_IN_USE);
switch(pMSD->CommandID)
{
case COMMAND_ID_SEND_RELIABLE:
case COMMAND_ID_KEEPALIVE:
{
Lock(&pEPD->EPLock);
pLink = pMSD->blFrameList.GetNext();
while (pLink != &pMSD->blFrameList)
{
pFMD = CONTAINING_RECORD(pLink, FMD, blMSDLinkage);
ASSERT_FMD(pFMD);
// We don't allow a send to complete to the Core until uiFrameCount goes to zero indicating that all frames
// of the message are out of the SP. We need to remove references from uiFrameCount for any frames that
// never were transmitted. Frames and retries that were transmitted will have their references removed in
// DNSP_CommandComplete when the SP completes them.
if (!(pFMD->ulFFlags & FFLAGS_TRANSMITTED))
{
pMSD->uiFrameCount--; // Protected by EPLock
DPFX(DPFPREP, DPF_FRAMECNT_LVL, "Frame count decremented on abort for non-transmitted frame, pMSD[%p], framecount[%u]", pMSD, pMSD->uiFrameCount);
}
pLink = pLink->GetNext();
}
if (pMSD->uiFrameCount == 0) // Protected by EPLock
{
if (pMSD->ulMsgFlags2 & MFLAGS_TWO_ABORT_WILL_COMPLETE)
{
DPFX(DPFPREP, DPF_FRAMECNT_LVL, "Completing, pMSD[%p], framecount[%u]", pMSD, pMSD->uiFrameCount);
DECREMENT_MSD(pMSD, "AbortSends Temp Ref");
// See what error code we need to return
if(pMSD->ulMsgFlags2 & MFLAGS_TWO_SEND_COMPLETE)
{
Unlock(&pEPD->EPLock);
CompleteReliableSend(pSPD, pMSD, DPN_OK); // This releases the CommandLock
}
else
{
Unlock(&pEPD->EPLock);
CompleteReliableSend(pSPD, pMSD, DPNERR_CONNECTIONLOST); // This releases the CommandLock
}
}
else
{
DPFX(DPFPREP, DPF_FRAMECNT_LVL, "SP Completion has already completed MSD to the Core, pMSD[%p], framecount[%u]", pMSD, pMSD->uiFrameCount);
Unlock(&pEPD->EPLock);
RELEASE_MSD(pMSD, "AbortSends Temp Ref"); // Releases CommandLock
}
}
else
{
DPFX(DPFPREP, DPF_FRAMECNT_LVL, "Frames still out, pMSD[%p], framecount[%u]", pMSD, pMSD->uiFrameCount);
Unlock(&pEPD->EPLock);
RELEASE_MSD(pMSD, "AbortSends Temp Ref"); // Releases CommandLock
}
}
break;
case COMMAND_ID_SEND_DATAGRAM:
{
Lock(&pEPD->EPLock);
if (pMSD->ulMsgFlags2 & MFLAGS_TWO_ABORT_WILL_COMPLETE)
{
DPFX(DPFPREP, DPF_FRAMECNT_LVL, "Completing NG, pMSD[%p], framecount[%u]", pMSD, pMSD->uiFrameCount);
DECREMENT_MSD(pMSD, "AbortSends Temp Ref");
Unlock(&pEPD->EPLock);
AbortDatagramSend(pMSD, DPNERR_CONNECTIONLOST); // Releases CommandLock
}
else
{
DPFX(DPFPREP, DPF_FRAMECNT_LVL, "SP Completion has already completed NG MSD to the Core, pMSD[%p], framecount[%u]", pMSD, pMSD->uiFrameCount);
Unlock(&pEPD->EPLock);
RELEASE_MSD(pMSD, "AbortSends Temp Ref"); // Releases CommandLock
}
}
break;
case COMMAND_ID_DISCONNECT:
case COMMAND_ID_DISC_RESPONSE:
{
Lock(&pEPD->EPLock);
pLink = pMSD->blFrameList.GetNext();
while (pLink != &pMSD->blFrameList)
{
pFMD = CONTAINING_RECORD(pLink, FMD, blMSDLinkage);
ASSERT_FMD(pFMD);
// We don't allow a send to complete to the Core until uiFrameCount goes to zero indicating that all frames
// of the message are out of the SP. We need to remove references from uiFrameCount for any frames that
// never were transmitted. Frames and retries that were transmitted will have their references removed in
// DNSP_CommandComplete when the SP completes them.
if (!(pFMD->ulFFlags & FFLAGS_TRANSMITTED))
{
pMSD->uiFrameCount--; // Protected by EPLock
DPFX(DPFPREP, DPF_FRAMECNT_LVL, "Frame count decremented on abort for non-transmitted frame, pMSD[%p], framecount[%u]", pMSD, pMSD->uiFrameCount);
}
pLink = pLink->GetNext();
}
if (pMSD->uiFrameCount == 0)
{
if (pMSD->ulMsgFlags2 & MFLAGS_TWO_ABORT_WILL_COMPLETE)
{
DPFX(DPFPREP, DPF_FRAMECNT_LVL, "Completing disconnect, pMSD[%p], framecount[%u]", pMSD, pMSD->uiFrameCount);
Unlock(&pEPD->EPLock);
DECREMENT_MSD(pMSD, "AbortSends Temp Ref");
CompleteDisconnect(pMSD, pSPD, pEPD); // Releases CommandLock
}
else
{
DPFX(DPFPREP, DPF_FRAMECNT_LVL, "SP Completion has already completed MSD to the Core, pMSD[%p], framecount[%u]", pMSD, pMSD->uiFrameCount);
Unlock(&pEPD->EPLock);
RELEASE_MSD(pMSD, "AbortSends Temp Ref"); // Releases CommandLock
}
}
else
{
DPFX(DPFPREP, DPF_FRAMECNT_LVL, "Frames still out, pMSD[%p], framecount[%u]", pMSD, pMSD->uiFrameCount);
Unlock(&pEPD->EPLock);
RELEASE_MSD(pMSD, "AbortSends Temp Ref"); // Releases CommandLock
}
}
break;
default:
{
DPFX(DPFPREP,0, "UNKNOWN COMMAND FOUND ON SEND Q");
ASSERT(0);
RELEASE_MSD(pMSD, "AbortSends Temp Ref"); // Releases CommandLock
}
break;
}
}
}
/*
** Abort Datagram Send
**
**
** THIS IS ENTERED WITH MSD->COMMANDLOCK HELD, EXITS WITH IT RELEASED
*/
#undef DPF_MODNAME
#define DPF_MODNAME "AbortDatagramSend"
VOID
AbortDatagramSend(PMSD pMSD, HRESULT CompletionCode)
{
PFMD pFMD;
CBilink *pLink;
AssertCriticalSectionIsTakenByThisThread(&pMSD->CommandLock, TRUE);
if(pMSD->TimeoutTimer != NULL)
{
DPFX(DPFPREP,7, "Cancelling Timeout Timer");
if(CancelMyTimer(pMSD->TimeoutTimer, pMSD->TimeoutTimerUnique) == DPN_OK)
{
DECREMENT_MSD(pMSD, "Timeout Timer");
}
else
{
DPFX(DPFPREP,7, "Cancelling Timeout Timer Failed");
}
pMSD->TimeoutTimer = NULL;
}
pLink = pMSD->blFrameList.GetNext();
while(pLink != &pMSD->blFrameList)
{
pFMD = CONTAINING_RECORD(pLink, FMD, blMSDLinkage);
ASSERT_FMD(pFMD);
pLink = pLink->GetNext();
if((pFMD->ulFFlags & FFLAGS_TRANSMITTED)==0)
{
pFMD->blMSDLinkage.RemoveFromList();
RELEASE_FMD(pFMD, "MSD Frame List");
pMSD->uiFrameCount--;
}
}
if(pMSD->blFrameList.IsEmpty())
{
CompleteDatagramSend(pMSD->pSPD, pMSD, CompletionCode);
}
else
{
Unlock(&pMSD->CommandLock);
}
}
#undef DPF_MODNAME
#define DPF_MODNAME "SetLinkParms"
VOID SetLinkParms(PEPD pEPD, PINT Data)
{
if(Data[0])
{
pEPD->uiGoodWindowF = pEPD->uiWindowF = Data[0];
pEPD->uiGoodWindowBI = pEPD->uiWindowBIndex = Data[0];
pEPD->uiWindowB = pEPD->uiWindowBIndex * pEPD->pSPD->uiFrameLength;
DPFX(DPFPREP,7, "** ADJUSTING WINDOW TO %d FRAMES", Data[0]);
}
if(Data[1])
{
}
if(Data[2])
{
pEPD->uiGoodBurstGap = pEPD->uiBurstGap = Data[2];
DPFX(DPFPREP,7, "** ADJUSTING GAP TO %d ms", Data[2]);
}
pEPD->uiPeriodAcksBytes = 0;
pEPD->uiPeriodXmitTime = 0;
}
#undef DPF_MODNAME
#define DPF_MODNAME "DNP_Debug"
HRESULT WINAPI DNP_Debug(PProtocolData pPData, UINT OpCode, HANDLE EndPoint, PVOID Data)
{
PEPD pEPD = (PEPD) EndPoint;
switch(OpCode)
{
case 1:
/* Toggle link frozen state */
pEPD->ulEPFlags ^= EPFLAGS_LINK_FROZEN;
break;
case 2:
/* Toggle whether KeepAlives are on or off */
pEPD->ulEPFlags ^= EPFLAGS_KEEPALIVE_RUNNING;
break;
case 5:
/* Manually set link parameters */
SetLinkParms(pEPD, (int *) Data);
break;
case 6:
/* Toggle Dynamic/Static Link control */
pEPD->ulEPFlags ^= EPFLAGS_LINK_STABLE;
break;
default:
return DPNERR_GENERIC;
}
return DPN_OK;
}