You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
499 lines
12 KiB
499 lines
12 KiB
/*++
|
|
|
|
Copyright (c) 1998-2000 Microsoft Corporation
|
|
|
|
Module Name :
|
|
|
|
exchnge.cpp
|
|
|
|
Abstract:
|
|
|
|
Implements methods associated with the exchange context structure. The
|
|
exchange context provides context for an I/O transaction with the client
|
|
|
|
Revision History:
|
|
--*/
|
|
|
|
#include "precomp.hxx"
|
|
#define TRC_FILE "exchnge"
|
|
#include "trc.h"
|
|
|
|
DrExchangeManager::DrExchangeManager()
|
|
{
|
|
BEGIN_FN("DrExchangeManager::DrExchangeManager");
|
|
SetClassName("DrExchangeManager");
|
|
_RxMidAtlas = NULL;
|
|
_demsState = demsStopped;
|
|
_Session = NULL;
|
|
}
|
|
|
|
BOOL DrExchangeManager::Initialize(DrSession *Session)
|
|
{
|
|
BEGIN_FN("DrExchangeManager::Initialize");
|
|
ASSERT(_Session == NULL);
|
|
ASSERT(Session != NULL);
|
|
_Session = Session;
|
|
return !NT_ERROR(_Session->RegisterPacketReceiver(this));
|
|
}
|
|
|
|
VOID DrExchangeManager::Uninitialize()
|
|
/*++
|
|
|
|
Routine Description:
|
|
Called if the exchange manager wasn't started because something
|
|
went wrong during startup
|
|
|
|
--*/
|
|
{
|
|
BEGIN_FN("DrExchangeManager::Uninitialize");
|
|
ASSERT(_Session != NULL);
|
|
ASSERT(_demsState == demsStopped);
|
|
_Session->RemovePacketReceiver(this);
|
|
_Session = NULL;
|
|
}
|
|
|
|
BOOL DrExchangeManager::Start()
|
|
/*++
|
|
|
|
Routine Description:
|
|
Start and stop really exist because there's no way to clear everything
|
|
out of a RxMidAtlas without destroying it. So start creates it and stop
|
|
destroys it.
|
|
Start simply allocates the Atlas and returns whether that worked
|
|
|
|
Arguments:
|
|
None.
|
|
|
|
Return Value:
|
|
Boolean indication of whether we can do IO
|
|
|
|
--*/
|
|
{
|
|
DrExchangeManagerState demsState;
|
|
|
|
BEGIN_FN("DrExchangeManager::Start");
|
|
demsState = (DrExchangeManagerState)InterlockedExchange((long *)&_demsState, demsStarted);
|
|
|
|
if (demsState == demsStopped) {
|
|
TRC_DBG((TB, "Creating Atlas"));
|
|
ASSERT(_RxMidAtlas == NULL);
|
|
_RxMidAtlas = RxCreateMidAtlas(DR_MAX_OPERATIONS,
|
|
DR_TYPICAL_OPERATIONS);
|
|
} else {
|
|
|
|
// The exchange has already started, so ignore this
|
|
}
|
|
|
|
TRC_DBG((TB, "Atlas 0x%p", _RxMidAtlas));
|
|
return _RxMidAtlas != NULL;
|
|
}
|
|
|
|
VOID DrExchangeManager::Stop()
|
|
{
|
|
PRX_MID_ATLAS RxMidAtlas;
|
|
DrExchangeManagerState demsState;
|
|
|
|
BEGIN_FN("DrExchangeManager::Stop");
|
|
demsState = (DrExchangeManagerState)InterlockedExchange((long *)&_demsState, demsStopped);
|
|
|
|
if (demsState == demsStarted) {
|
|
ASSERT(_RxMidAtlas != NULL);
|
|
|
|
DrAcquireMutex();
|
|
RxMidAtlas = _RxMidAtlas;
|
|
_RxMidAtlas = NULL;
|
|
DrReleaseMutex();
|
|
|
|
TRC_NRM((TB, "Destroying Atlas 0x%p", RxMidAtlas));
|
|
|
|
RxDestroyMidAtlas(RxMidAtlas, (PCONTEXT_DESTRUCTOR)DestroyAtlasCallback);
|
|
} else {
|
|
|
|
//
|
|
// We allow this multiple times because this is how you cancel
|
|
// outstanding client I/O
|
|
//
|
|
|
|
TRC_DBG((TB, "Atlas already destroyed"));
|
|
}
|
|
}
|
|
|
|
VOID DrExchangeManager::DestroyAtlasCallback(DrExchange *pExchange)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Part of clearing out all the outstanding IO. Since we won't be able
|
|
to complete this normally, we have to delete the Exchange
|
|
|
|
Arguments:
|
|
RxContext - Context to cancel and delete and whatnot
|
|
|
|
Return Value:
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
DrExchangeManager *ExchangeManager;
|
|
PRX_CONTEXT RxContext = NULL;
|
|
SmartPtr<DrExchange> Exchange;
|
|
|
|
BEGIN_FN_STATIC("DrExchangeManager::DestroyAtlasCallback");
|
|
|
|
//
|
|
// Convert to a smart pointer and get rid of the explicit refcount
|
|
//
|
|
|
|
Exchange = pExchange;
|
|
pExchange->Release();
|
|
|
|
//
|
|
// Notification that the conversation is over
|
|
//
|
|
|
|
Exchange->_ExchangeUser->OnIoDisconnected(Exchange);
|
|
}
|
|
|
|
BOOL DrExchangeManager::CreateExchange(IExchangeUser *ExchangeUser,
|
|
PVOID Context, SmartPtr<DrExchange> &Exchange)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Creates an Exchange context data structure and initializes it
|
|
with the basic data
|
|
|
|
Arguments:
|
|
ExchangeUser - An interface for callbacks associated with the conversation
|
|
Context - ExchangeUser contextual data
|
|
Exchange - Reference to where to put the results
|
|
|
|
Return Value:
|
|
Boolean success or failure
|
|
|
|
--*/
|
|
{
|
|
BOOL rc = TRUE;
|
|
NTSTATUS Status;
|
|
USHORT Mid;
|
|
|
|
BEGIN_FN("DrExchangeManager::CreateExchange");
|
|
ASSERT(ExchangeUser != NULL);
|
|
|
|
Exchange = new DrExchange(this, ExchangeUser, Context);
|
|
if (Exchange != NULL) {
|
|
DrAcquireMutex();
|
|
|
|
if (_RxMidAtlas != NULL) {
|
|
Status = RxAssociateContextWithMid(_RxMidAtlas, Exchange, &Mid);
|
|
} else {
|
|
Status = STATUS_DEVICE_NOT_CONNECTED;
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
Exchange->_Mid = Mid;
|
|
|
|
//
|
|
// Explicit reference count for the atlas
|
|
//
|
|
Exchange->AddRef();
|
|
} else {
|
|
rc = FALSE;
|
|
}
|
|
DrReleaseMutex();
|
|
|
|
if (!rc) {
|
|
Exchange = NULL;
|
|
}
|
|
} else {
|
|
rc = FALSE;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
DrExchange::DrExchange(DrExchangeManager *ExchangeManager,
|
|
IExchangeUser *ExchangeUser, PVOID Context)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Constructor initializes member variables
|
|
|
|
Arguments:
|
|
ExchangeManager - Relevant manager
|
|
Context - Context to track this op
|
|
|
|
Return Value:
|
|
None
|
|
|
|
--*/
|
|
{
|
|
BEGIN_FN("DrExchange::DrExchange");
|
|
ASSERT(ExchangeManager != NULL);
|
|
ASSERT(ExchangeUser != NULL);
|
|
|
|
_Context = Context;
|
|
_ExchangeManager = ExchangeManager;
|
|
_ExchangeUser = ExchangeUser;
|
|
_Mid = INVALID_MID;
|
|
}
|
|
|
|
DrExchange::~DrExchange()
|
|
{
|
|
BEGIN_FN("DrExchange::~DrExchange");
|
|
}
|
|
|
|
|
|
BOOL DrExchangeManager::Find(USHORT Mid, SmartPtr<DrExchange> &ExchangeFound)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Marks an Exchange context as busy so it won't be cancelled
|
|
while we're copying in to its buffer
|
|
|
|
Arguments:
|
|
Mid - Id to find
|
|
ExchangeFound - storage for the pointer to the context
|
|
|
|
Return Value:
|
|
BOOL indicating whether it was found
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
DrExchange *Exchange = NULL;
|
|
|
|
BEGIN_FN("DrExchangeManager::Find");
|
|
|
|
DrAcquireMutex();
|
|
if (_RxMidAtlas != NULL) {
|
|
Exchange = (DrExchange *)RxMapMidToContext(_RxMidAtlas, Mid);
|
|
TRC_DBG((TB, "Found context: 0x%p", Exchange));
|
|
}
|
|
|
|
//
|
|
// This is where the Exchange is reference counted, must be
|
|
// inside the lock
|
|
//
|
|
|
|
ExchangeFound = Exchange;
|
|
DrReleaseMutex();
|
|
|
|
return ExchangeFound != NULL;
|
|
}
|
|
|
|
BOOL DrExchangeManager::ReadMore(ULONG cbSaveData, ULONG cbWantData)
|
|
{
|
|
BEGIN_FN("DrExchangeManager::ReadMore");
|
|
return _Session->ReadMore(cbSaveData, cbWantData);
|
|
}
|
|
|
|
|
|
VOID DrExchangeManager::Discard(SmartPtr<DrExchange> &Exchange)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Stops tracking this as a conversation by its ID. the exchange will be
|
|
deleted when its reference count goes to zero
|
|
|
|
Arguments:
|
|
Exchange - Marker for the operation
|
|
|
|
Return Value:
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
USHORT Mid;
|
|
NTSTATUS Status;
|
|
DrExchange *ExchangeFound = NULL;
|
|
|
|
BEGIN_FN("DrExchangeManager::Discard");
|
|
ASSERT(Exchange != NULL);
|
|
|
|
DrAcquireMutex();
|
|
Mid = Exchange->_Mid;
|
|
|
|
if (_RxMidAtlas != NULL) {
|
|
|
|
//
|
|
// We already have the DrExchange, but we need to remove
|
|
// it from the atlas
|
|
//
|
|
|
|
Status = RxMapAndDissociateMidFromContext(_RxMidAtlas,
|
|
Mid, (PVOID *)&ExchangeFound);
|
|
|
|
TRC_ASSERT(ExchangeFound == Exchange, (TB, "Mismatched "
|
|
"DrExchange"));
|
|
|
|
//
|
|
// Explicit reference count for the atlas
|
|
//
|
|
if (ExchangeFound != NULL)
|
|
ExchangeFound->Release();
|
|
|
|
} else {
|
|
TRC_ALT((TB, "Tried to complete mid when atlas was "
|
|
"NULL"));
|
|
Status = STATUS_DEVICE_NOT_CONNECTED;
|
|
}
|
|
|
|
DrReleaseMutex();
|
|
}
|
|
|
|
BOOL DrExchangeManager::RecognizePacket(PRDPDR_HEADER RdpdrHeader)
|
|
{
|
|
BEGIN_FN("DrExchangeManager::RecognizePacket");
|
|
//
|
|
// If you add a packet here, update the ASSERTS in HandlePacket
|
|
//
|
|
|
|
switch (RdpdrHeader->Component) {
|
|
case RDPDR_CTYP_CORE:
|
|
switch (RdpdrHeader->PacketId) {
|
|
case DR_CORE_DEVICE_IOCOMPLETION:
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
NTSTATUS DrExchangeManager::HandlePacket(PRDPDR_HEADER RdpdrHeader,
|
|
ULONG Length, BOOL *DoDefaultRead)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
BEGIN_FN("DrExchangeManager::HandlePacket");
|
|
|
|
//
|
|
// RdpdrHeader read, dispatch based on the header
|
|
//
|
|
|
|
ASSERT(RdpdrHeader != NULL);
|
|
ASSERT(Length >= sizeof(RDPDR_HEADER));
|
|
ASSERT(RdpdrHeader->Component == RDPDR_CTYP_CORE);
|
|
|
|
switch (RdpdrHeader->Component) {
|
|
case RDPDR_CTYP_CORE:
|
|
ASSERT(RdpdrHeader->PacketId == DR_CORE_DEVICE_IOCOMPLETION);
|
|
|
|
switch (RdpdrHeader->PacketId) {
|
|
case DR_CORE_DEVICE_IOCOMPLETION:
|
|
Status = OnDeviceIoCompletion(RdpdrHeader, Length,
|
|
DoDefaultRead);
|
|
break;
|
|
}
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS DrExchangeManager::OnDeviceIoCompletion(PRDPDR_HEADER RdpdrHeader,
|
|
ULONG cbPacket, BOOL *DoDefaultRead)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called in response to recognizing a DeviceIoCompletion packet has been
|
|
received. Finds the associated RxContext, fills out relevant information,
|
|
and completes the request.
|
|
|
|
Arguments:
|
|
|
|
RdpdrHeader - The header of the packet, a pointer to the packet
|
|
cbPacket - The number of bytes of data in the packet
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Success/failure indication of the operation
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PRX_CONTEXT RxContext;
|
|
PRDPDR_IOCOMPLETION_PACKET CompletionPacket =
|
|
(PRDPDR_IOCOMPLETION_PACKET)RdpdrHeader;
|
|
SmartPtr<DrExchange> Exchange;
|
|
USHORT Mid;
|
|
ULONG cbMinimum;
|
|
|
|
BEGIN_FN("DrExchangeManager::OnDeviceIoCompletion");
|
|
|
|
cbMinimum = FIELD_OFFSET(RDPDR_IOCOMPLETION_PACKET,
|
|
IoCompletion.Parameters);
|
|
|
|
if (cbMinimum > cbPacket) {
|
|
*DoDefaultRead = FALSE;
|
|
return _Session->ReadMore(cbPacket, cbMinimum);
|
|
}
|
|
|
|
Mid = (USHORT)CompletionPacket->IoCompletion.CompletionId;
|
|
TRC_DBG((TB, "IoCompletion mid: %x", Mid));
|
|
|
|
if (Find(Mid, Exchange)) {
|
|
Status = Exchange->_ExchangeUser->OnDeviceIoCompletion(CompletionPacket,
|
|
cbPacket, DoDefaultRead, Exchange);
|
|
} else {
|
|
|
|
//
|
|
// Client gave us a bogus mid
|
|
//
|
|
Status = STATUS_DEVICE_PROTOCOL_ERROR;
|
|
*DoDefaultRead = FALSE;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS DrExchangeManager::StartExchange(SmartPtr<DrExchange> &Exchange,
|
|
class IExchangeUser *ExchangeUser, PVOID Buffer, ULONG Length,
|
|
BOOL LowPrioSend)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sends the information to the client, and recognizes the response.
|
|
|
|
Arguments:
|
|
|
|
Exchange - The conversanion token
|
|
Buffer - Data to send
|
|
Length - size of the data
|
|
LowPrioSend - Should the data be sent to the client at low priority.
|
|
|
|
Return Value:
|
|
|
|
Status of sending, a failure means no callback will be made
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
BEGIN_FN("DrExchangeManager::StartExchange");
|
|
|
|
Exchange->_ExchangeUser = ExchangeUser;
|
|
|
|
//
|
|
// This is a synchronous write
|
|
//
|
|
Status = _Session->SendToClient(Buffer, Length, this, FALSE,
|
|
LowPrioSend, (PVOID)Exchange);
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
NTSTATUS DrExchangeManager::SendCompleted(PVOID Context,
|
|
PIO_STATUS_BLOCK IoStatusBlock)
|
|
{
|
|
DrExchange *pExchange;
|
|
SmartPtr<DrExchange> Exchange;
|
|
|
|
BEGIN_FN("DrExchangeManager::SendCompleted");
|
|
pExchange = (DrExchange *)Context;
|
|
ASSERT(pExchange != NULL);
|
|
ASSERT(pExchange->IsValid());
|
|
Exchange = pExchange;
|
|
|
|
return Exchange->_ExchangeUser->OnStartExchangeCompletion(Exchange,
|
|
IoStatusBlock);
|
|
}
|