From 97cf5197cf5b882b2c689d8dc2b555f2edf8f418 Mon Sep 17 00:00:00 2001 From: Dave Wilson Date: Tue, 17 Mar 2015 19:50:07 -0700 Subject: Initial publish --- nfp/net/driver/Queue.cpp | 824 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 824 insertions(+) create mode 100644 nfp/net/driver/Queue.cpp (limited to 'nfp/net/driver/Queue.cpp') diff --git a/nfp/net/driver/Queue.cpp b/nfp/net/driver/Queue.cpp new file mode 100644 index 00000000..129a5439 --- /dev/null +++ b/nfp/net/driver/Queue.cpp @@ -0,0 +1,824 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + queue.cpp + +Abstract: + + This file implements the I/O queue interface and performs + the read/write/ioctl operations. + +Environment: + + user mode only + +Revision History: + +--*/ +#include "internal.h" + +#include "queue.tmh" + +CMyQueue::CMyQueue( + VOID + ) +{ + InitializeListHead(&m_SubsHead); + InitializeListHead(&m_ArrivalSubsHead); + InitializeListHead(&m_DepartureSubsHead); + InitializeListHead(&m_PubsHead); + InitializeListHead(&m_ConnectionHead); + + InitializeCriticalSection(&m_SubsLock); + InitializeCriticalSection(&m_PubsLock); + InitializeCriticalSection(&m_ConnectionLock); +} + +CMyQueue::~CMyQueue( + VOID + ) +{ + MethodEntry("void"); + + m_SocketListener.StopAccepting(); + + while (!IsListEmpty(&m_ConnectionHead)) + { + delete CConnection::FromListEntry(RemoveHeadList(&m_ConnectionHead)); + } + + DeleteCriticalSection(&m_SubsLock); + DeleteCriticalSection(&m_PubsLock); + DeleteCriticalSection(&m_ConnectionLock); +} + +// +// Initialize +// + +HRESULT +CMyQueue::Initialize( + _In_ CMyDevice * Device + ) +/*++ + +Routine Description: + + Queue Initialize helper routine. + This routine will Create a default parallel queue associated with the Fx device object + and pass the IUnknown for this queue + +Aruments: + Device object pointer + +Return Value: + + S_OK if Initialize succeeds + +--*/ +{ + MethodEntry("..."); + + CComPtr fxQueue; + + HRESULT hr; + + // + // Create the I/O Queue object. + // + { + CComPtr pUnk; + + HRESULT hrQI = this->QueryInterface(__uuidof(IUnknown),(void**)&pUnk); + + WUDF_SAMPLE_DRIVER_ASSERT(SUCCEEDED(hrQI)); + + hr = Device->GetFxDevice()->CreateIoQueue( + pUnk, + TRUE, + WdfIoQueueDispatchParallel, + TRUE, + FALSE, + &fxQueue + ); + } + + if (FAILED(hr)) + { + TraceErrorHR(hr, "Failed to initialize driver queue"); + } + + if (SUCCEEDED(hr)) + { + hr = m_SocketListener.Bind(); + if (FAILED(hr)) + { + TraceErrorHR(hr, "Failed to Bind"); + } + } + + if (SUCCEEDED(hr)) + { + hr = m_SocketListener.EnableAccepting(this); + if (FAILED(hr)) + { + TraceErrorHR(hr, "Failed to EnableAccepting"); + } + } + + if (SUCCEEDED(hr)) + { + m_FxQueue = fxQueue; + } + + MethodReturnHR(hr); +} + +HRESULT +CMyQueue::Configure( + VOID + ) +/*++ + +Routine Description: + + Queue configuration function . + It is called after queue object has been succesfully initialized. + +Aruments: + + NONE + + Return Value: + + S_OK if succeeds. + +--*/ +{ + MethodEntry("void"); + + HRESULT hr = S_OK; + + MethodReturnHR(hr); +} + + +STDMETHODIMP_(void) +CMyQueue::OnCreateFile( + _In_ IWDFIoQueue* /*pWdfQueue*/, + _In_ IWDFIoRequest* pWdfRequest, + _In_ IWDFFile* pWdfFile + ) + +/*++ + +Routine Description: + + Create callback from the framework for this default parallel queue + + The create request will create a socket connection , create a file i/o target associated + with the socket handle for this connection and store in the file object context. + +Aruments: + + pWdfQueue - Framework Queue instance + pWdfRequest - Framework Request instance + pWdfFile - WDF file object for this create + + Return Value: + + VOID + +--*/ +{ + MethodEntry("pWdfRequest = %p, pWdfFile = %p", + pWdfRequest, pWdfFile); + + HRESULT hr = S_OK; + + // + // Create file context for this file object + // + + CFileContext *pContext = new CFileContext(); + if (NULL == pContext) + { + hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY); + TraceErrorHR(hr, "Could not create file context"); + } + + if (SUCCEEDED(hr)) + { + DWORD cchFileName = 0; + hr = pWdfFile->RetrieveFileName(NULL, &cchFileName); + if (SUCCEEDED(hr) && (cchFileName > 0) && (cchFileName <= MaxCchType)) + { + // Allocate a buffer big enough for the filename plus some extra to prevent + // overruns in the parsing below: The extra needs to be larger than the biggest + // *_CHARS value. The value 20 is overly big, but allows room to grow without + // hitting the OACR issue again. If the OACR issue is hit, just increase this + // to be larger than the biggest *_CHARS value used below in parsing. + DWORD cchFileNameBuffer = cchFileName + 20; + PWSTR pszFileNameBuffer = new WCHAR[cchFileNameBuffer]; + hr = (pszFileNameBuffer != NULL) ? S_OK : E_OUTOFMEMORY; + if (SUCCEEDED(hr)) + { + ZeroMemory(pszFileNameBuffer, cchFileNameBuffer * sizeof(WCHAR)); + + hr = pWdfFile->RetrieveFileName(pszFileNameBuffer, &cchFileName); + } + + if (SUCCEEDED(hr)) + { + PCWSTR pszFileName = pszFileNameBuffer; + if (pszFileNameBuffer[0] == L'\\') + { + // If it exists, remove the inital slash + pszFileName++; + cchFileName--; + } + + TraceInfo("cchFileName = %d, pszFileName = %S", cchFileName, pszFileName); + + PCWSTR pszProtocol = NULL; + if (CompareStringOrdinal(pszFileName, PUBS_NAMESPACE_CHARS, + PUBS_NAMESPACE, PUBS_NAMESPACE_CHARS, + TRUE) == CSTR_EQUAL) + { + pContext->SetRolePublication(); + pszProtocol = pszFileName + PUBS_NAMESPACE_CHARS; + } + else if (CompareStringOrdinal(pszFileName, SUBS_NAMESPACE_CHARS, + SUBS_NAMESPACE, SUBS_NAMESPACE_CHARS, + TRUE) == CSTR_EQUAL) + { + pContext->SetRoleSubcription(); + pszProtocol = pszFileName + SUBS_NAMESPACE_CHARS; + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + } + + if (SUCCEEDED(hr)) + { + if (CompareStringOrdinal(pszProtocol, WINDOWS_PROTOCOL_CHARS, + WINDOWS_PROTOCOL, WINDOWS_PROTOCOL_CHARS, + TRUE) == CSTR_EQUAL) + { + pContext->SetType(pszProtocol); + } + else if (CompareStringOrdinal(pszProtocol, -1, + WINDOWSURI_PROTOCOL, -1, + TRUE) == CSTR_EQUAL) + { + pContext->SetType(pszProtocol); + } + else if (CompareStringOrdinal(pszProtocol, WINDOWSMIME_PROTOCOL_CHARS, + WINDOWSMIME_PROTOCOL, WINDOWSMIME_PROTOCOL_CHARS, + TRUE) == CSTR_EQUAL) + { + pContext->SetType(pszProtocol); + } + else if (CompareStringOrdinal(pszProtocol, -1, DEVICE_ARRIVED, -1, + TRUE) == CSTR_EQUAL) + { + if (!pContext->SetRoleArrivedSubcription()) + { + hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + } + } + else if (CompareStringOrdinal(pszProtocol, -1, DEVICE_DEPARTED, -1, + TRUE) == CSTR_EQUAL) + { + if (!pContext->SetRoleDepartedSubcription()) + { + hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + } + } + else if (CompareStringOrdinal(pszProtocol, PAIRING_PROTOCOL_CHARS, + PAIRING_PROTOCOL, PAIRING_PROTOCOL_CHARS, + TRUE) == CSTR_EQUAL) + { + pContext->SetType(pszProtocol); + } + else if (CompareStringOrdinal(pszProtocol, NDEF_PROTOCOL_CHARS, + NDEF_PROTOCOL, NDEF_PROTOCOL_CHARS, + TRUE) == CSTR_EQUAL) + { + PCWSTR pszType = pszProtocol + NDEF_PROTOCOL_CHARS; + + if (CompareStringOrdinal(pszType, NDEF_EMPTY_TYPE_CHARS, + NDEF_EMPTY_TYPE, NDEF_EMPTY_TYPE_CHARS, + TRUE) != CSTR_EQUAL) + { + pContext->SetType(pszProtocol); + } + else + { + hr = HRESULT_FROM_NT(STATUS_INVALID_PARAMETER); + } + } + else + { + hr = HRESULT_FROM_NT(STATUS_OBJECT_PATH_NOT_FOUND); + } + } + } + + if (pszFileNameBuffer != NULL) + { + delete [] pszFileNameBuffer; + } + } + } + + if (SUCCEEDED(hr)) + { + hr = pWdfFile->AssignContext(NULL, (void*)pContext); + if (FAILED(hr)) + { + TraceErrorHR(hr, "Unable to Assign Context to this File Object"); + } + } + + if (SUCCEEDED(hr)) + { + EnterCriticalSection(&m_SubsLock); + + if (pContext->IsNormalSubscription()) + { + // Place this CFileContext into a list of Subscriptions + InsertHeadList(&m_SubsHead, pContext->GetListEntry()); + } + else if (pContext->IsArrivedSubscription()) + { + // Place this CFileContext into a list of arrival registrations + InsertHeadList(&m_ArrivalSubsHead, pContext->GetListEntry()); + } + else if (pContext->IsDepartedSubscription()) + { + // Place this CFileContext into a list of removal registrations + InsertHeadList(&m_DepartureSubsHead, pContext->GetListEntry()); + } + + LeaveCriticalSection(&m_SubsLock); + } + + if (FAILED(hr)) + { + if (pContext != NULL) + { + delete pContext; + pContext = NULL; + } + } + + pWdfRequest->Complete(hr); + + MethodReturnVoid(); +} + +STDMETHODIMP_(void) +CMyQueue::OnCloseFile( + _In_ IWDFFile* pWdfFileObject + ) +/*++ + + Routine Description: + + This method is called when an app closes the file handle to this device. + This will free the context memory associated with this file object, close + the connection object associated with this file object and delete the file + handle i/o target object associated with this file object. + + Arguments: + + pWdfFileObject - the framework file object for which close is handled. + + Return Value: + + None + +--*/ +{ + MethodEntry("..."); + + HRESULT hr = S_OK ; + CFileContext* pContext = NULL; + hr = pWdfFileObject->RetrieveContext((void**)&pContext); + if (SUCCEEDED(hr) && (pContext != NULL)) + { + CRITICAL_SECTION* pCritSec = NULL; + LIST_ENTRY* pHead = NULL; + + if (pContext->IsNormalSubscription()) + { + pCritSec = &m_SubsLock; + pHead = &m_SubsHead; + } + else if (pContext->IsArrivedSubscription()) + { + pCritSec = &m_SubsLock; + pHead = &m_ArrivalSubsHead; + } + else if (pContext->IsDepartedSubscription()) + { + pCritSec = &m_SubsLock; + pHead = &m_DepartureSubsHead; + } + else if (pContext->IsPublication()) + { + pCritSec = &m_PubsLock; + pHead = &m_PubsHead; + } + + if (pHead != NULL) + { + EnterCriticalSection(pCritSec); + + LIST_ENTRY* pFindEntry = pContext->GetListEntry(); + LIST_ENTRY* pEntry = pHead->Flink; + while (pEntry != pHead) + { + if (pEntry == pFindEntry) + { + RemoveEntryList(pEntry); + break; + } + + pEntry = pEntry->Flink; + } + + LeaveCriticalSection(pCritSec); + } + + delete pContext; + } + + MethodReturnVoid(); +} + +STDMETHODIMP_ (void) +CMyQueue::OnCancel( + _In_ IWDFIoRequest* pWdfRequest + ) +{ + MethodEntry("pWdfRequest = %p", + pWdfRequest); + + IWDFFile* pFxFile; + pWdfRequest->GetFileObject(&pFxFile); + if (pFxFile != NULL) + { + CFileContext* pFileContext; + HRESULT hr = pFxFile->RetrieveContext((void**)&pFileContext); + if (SUCCEEDED(hr)) + { + pFileContext->OnCancel(); + } + } + + MethodReturnVoid(); +} + +#define IOCTL_BEGIN_PROXIMITY CTL_CODE(FILE_DEVICE_UNKNOWN, 0x1000, METHOD_BUFFERED, FILE_ANY_ACCESS) + +STDMETHODIMP_ (void) +CMyQueue::OnDeviceIoControl( + _In_ IWDFIoQueue* /*pWdfQueue*/, + _In_ IWDFIoRequest* pWdfRequest, + _In_ ULONG ControlCode, + _In_ SIZE_T /*InBufferSize*/, + _In_ SIZE_T /*OutBufferSize*/ + ) +{ + MethodEntry("pWdfRequest = %p, ControlCode = %d", + pWdfRequest, ControlCode); + + IWDFFile* pFxFile; + + pWdfRequest->GetFileObject(&pFxFile); + + bool fCompleteRequest = true; + + CFileContext *pFileContext; + HRESULT hr = pFxFile->RetrieveContext((void**)&pFileContext); + if (SUCCEEDED(hr)) + { + switch (ControlCode) + { + case IOCTL_NFP_GET_MAX_MESSAGE_BYTES: + { + IWDFMemory* pWdfOutputMemory; + pWdfRequest->GetOutputMemory(&pWdfOutputMemory); + if (pWdfOutputMemory != NULL) + { + SIZE_T cbOutputBuffer = 0; + // Set the first 4 bytes as the maximum message size of this device/driver + DWORD dwMaxCbPayload = MaxCbPayload; + hr = pWdfOutputMemory->CopyFromBuffer(0, &dwMaxCbPayload, 4); + if (SUCCEEDED(hr)) + { + cbOutputBuffer = 4; + } + pWdfOutputMemory->Release(); + + pWdfRequest->CompleteWithInformation(hr, cbOutputBuffer); + fCompleteRequest = false; + } + } + break; + + case IOCTL_NFP_GET_KILO_BYTES_PER_SECOND: + { + IWDFMemory* pWdfOutputMemory; + pWdfRequest->GetOutputMemory(&pWdfOutputMemory); + if (pWdfOutputMemory != NULL) + { + SIZE_T cbOutputBuffer = 0; + // Set the first 4 bytes as transfer speed of this device/driver + DWORD dwKilobytesPerSecond = KilobytesPerSecond; + hr = pWdfOutputMemory->CopyFromBuffer(0, &dwKilobytesPerSecond, 4); + if (SUCCEEDED(hr)) + { + cbOutputBuffer = 4; + } + pWdfOutputMemory->Release(); + + pWdfRequest->CompleteWithInformation(hr, cbOutputBuffer); + fCompleteRequest = false; + } + } + break; + + case IOCTL_NFP_DISABLE: + hr = pFileContext->Disable(); + break; + + case IOCTL_NFP_ENABLE: + hr = pFileContext->Enable(); + break; + + case IOCTL_NFP_SET_PAYLOAD: + hr = pFileContext->SetPayload(pWdfRequest); + if (SUCCEEDED(hr)) + { + MESSAGE* pMessage = new MESSAGE(); + hr = (pMessage != NULL) ? S_OK : E_OUTOFMEMORY; + if (SUCCEEDED(hr)) + { + pMessage->Initialize(pFileContext->GetType(), pFileContext->GetSize(), pFileContext->GetPayload()); + + EnterCriticalSection(&m_ConnectionLock); + + for (LIST_ENTRY* pEntry = m_ConnectionHead.Flink; + pEntry != &m_ConnectionHead; + pEntry = pEntry->Flink) + { + CConnection* pConnection = CConnection::FromListEntry(pEntry); + if (SUCCEEDED(pConnection->TransmitMessage(pMessage))) + { + pFileContext->HandleMessageTransmitted(); + } + } + + LeaveCriticalSection(&m_ConnectionLock); + + // Place this CFileContext into a list of published messages + EnterCriticalSection(&m_PubsLock); + InsertHeadList(&m_PubsHead, pFileContext->GetListEntry()); + LeaveCriticalSection(&m_PubsLock); + + delete pMessage; + } + } + break; + + case IOCTL_BEGIN_PROXIMITY: + hr = pFileContext->BeginProximity(pWdfRequest, this); + break; + + case IOCTL_NFP_GET_NEXT_SUBSCRIBED_MESSAGE: + hr = pFileContext->GetNextSubscribedMessage(this, pWdfRequest); + if (SUCCEEDED(hr)) + { + fCompleteRequest = false; + } + break; + + case IOCTL_NFP_GET_NEXT_TRANSMITTED_MESSAGE: + hr = pFileContext->GetNextTransmittedMessage(this, pWdfRequest); + if (SUCCEEDED(hr)) + { + fCompleteRequest = false; + } + break; + + default: + hr = HRESULT_FROM_NT(STATUS_INVALID_DEVICE_STATE); + break; + } + } + + if (fCompleteRequest) + { + TraceInfo("Completing Request: %!HRESULT!", hr); + pWdfRequest->Complete(hr); + } + + MethodReturnVoid(); +} + +void +CMyQueue::ValidateAccept(_In_ SOCKET Socket, _In_ GUID* pMagicPacket) +{ + MethodEntry("..."); + + CConnection* pConnection; + HRESULT hr = CConnection::Create(this, &pConnection); + if (SUCCEEDED(hr)) + { + // Mark it as an Inbound connection, so we know to delete it when + // it's removed from the list + pConnection->SetInboundConnection(); + + pConnection->ValidateAccept(Socket, pMagicPacket); + Socket = INVALID_SOCKET; + } + + if (Socket != INVALID_SOCKET) + { + closesocket(Socket); + } + + MethodReturnVoid(); +} + +void +CMyQueue::HandleReceivedMessage(_In_ MESSAGE* pMessage) +{ + MethodEntry("pMessage->m_szType = '%S'", + pMessage->m_szType); + + if ((pMessage->m_cbPayload > 0) && (pMessage->m_cbPayload <= MaxCbPayload)) + { + EnterCriticalSection(&m_SubsLock); + + LIST_ENTRY* pEntry = m_SubsHead.Flink; + while (pEntry != &m_SubsHead) + { + CFileContext* pSub = CFileContext::FromListEntry(pEntry); + + pSub->HandleReceivedPublication(pMessage->m_szType, + pMessage->m_cbPayload, + pMessage->m_Payload); + + pEntry = pEntry->Flink; + } + + LeaveCriticalSection(&m_SubsLock); + } + + MethodReturnVoid(); +} + +void +CMyQueue::ConnectionEstablished(_In_ CConnection* pConnection) +{ + MethodEntry("..."); + + EnterCriticalSection(&m_ConnectionLock); + BOOL fFirstConnection = IsListEmpty(&m_ConnectionHead); + InsertHeadList(&m_ConnectionHead, pConnection->GetListEntry()); + LeaveCriticalSection(&m_ConnectionLock); + + if (fFirstConnection) + { + AddArrivalEvent(); + } + + MESSAGE* pMessage = new MESSAGE(); + if (pMessage != NULL) + { + EnterCriticalSection(&m_PubsLock); + + LIST_ENTRY* pEntry = m_PubsHead.Flink; + while (pEntry != &m_PubsHead) + { + CFileContext* pPub = CFileContext::FromListEntry(pEntry); + if (pPub->IsEnabled()) + { + pMessage->Initialize(pPub->GetType(), pPub->GetSize(), pPub->GetPayload()); + if (SUCCEEDED(pConnection->TransmitMessage(pMessage))) + { + pPub->HandleMessageTransmitted(); + } + } + pEntry = pEntry->Flink; + } + + LeaveCriticalSection(&m_PubsLock); + delete pMessage; + } + + MethodReturnVoid(); +} + +BOOL +CMyQueue::ConnectionTerminated(_In_ CConnection* pConnection) +{ + MethodEntry("pConnection = 0x%p", + pConnection); + + LIST_ENTRY* pRemoveListEntry = pConnection->GetListEntry(); + + EnterCriticalSection(&m_ConnectionLock); + LIST_ENTRY* pEntry = m_ConnectionHead.Flink; + while (pEntry != &m_ConnectionHead) + { + if (pEntry == pRemoveListEntry) + { + RemoveEntryList(pEntry); + break; + } + pEntry = pEntry->Flink; + } + BOOL fNoMoreConnections = IsListEmpty(&m_ConnectionHead); + + LeaveCriticalSection(&m_ConnectionLock); + + BOOL fConnectionDeleted = FALSE; + if (pConnection->IsInboundConnection()) + { + delete pConnection; + fConnectionDeleted = TRUE; + } + + if (fNoMoreConnections) + { + AddRemovalEvent(); + } + + MethodReturnBool(fConnectionDeleted); +} + +void +CMyQueue::AddArrivalEvent() +{ + MethodEntry("void"); + + EnterCriticalSection(&m_SubsLock); + + for (LIST_ENTRY* pEntry = m_ArrivalSubsHead.Flink; + pEntry != &m_ArrivalSubsHead; + pEntry = pEntry->Flink) + { + CFileContext::FromListEntry(pEntry)->HandleArrivalEvent(); + } + + LeaveCriticalSection(&m_SubsLock); + + MethodReturnVoid(); +} + +void +CMyQueue::AddRemovalEvent() +{ + MethodEntry("void"); + + EnterCriticalSection(&m_SubsLock); + + for (LIST_ENTRY* pEntry = m_DepartureSubsHead.Flink; + pEntry != &m_DepartureSubsHead; + pEntry = pEntry->Flink) + { + CFileContext::FromListEntry(pEntry)->HandleRemovalEvent(); + } + + LeaveCriticalSection(&m_SubsLock); + + MethodReturnVoid(); +} + +STDMETHODIMP_(void) +CMyQueue::OnCleanup( + _In_ IWDFObject* /*pWdfObject*/ + ) +{ + MethodEntry("..."); + + // + // CMyQueue has a reference to framework device object via m_Queue. + // Framework queue object has a reference to CMyQueue object via the callbacks. + // This leads to circular reference and both the objects can't be destroyed until this circular reference is broken. + // To break the circular reference we release the reference to the framework queue object here in OnCleanup. + // + + m_FxQueue = NULL; + + MethodReturnVoid(); +} -- cgit v1.3.1