/*++ 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 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 &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 &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 &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 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 &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 Exchange; BEGIN_FN("DrExchangeManager::SendCompleted"); pExchange = (DrExchange *)Context; ASSERT(pExchange != NULL); ASSERT(pExchange->IsValid()); Exchange = pExchange; return Exchange->_ExchangeUser->OnStartExchangeCompletion(Exchange, IoStatusBlock); }