1132 lines
36 KiB
C++
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;
|
|
}
|
|
|
|
|