diff options
Diffstat (limited to 'gnss/gnssUmdf/FixSession.cpp')
| -rw-r--r-- | gnss/gnssUmdf/FixSession.cpp | 512 |
1 files changed, 512 insertions, 0 deletions
diff --git a/gnss/gnssUmdf/FixSession.cpp b/gnss/gnssUmdf/FixSession.cpp new file mode 100644 index 00000000..14418a8f --- /dev/null +++ b/gnss/gnssUmdf/FixSession.cpp @@ -0,0 +1,512 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + fixsession.cpp + +Abstract: + + This file contains the Fix sessions and contains logic to provide position. + +Environment: + + Windows User-Mode Driver Framework + +--*/ + +#include "precomp.h" +#include "Trace.h" +#include "Defaults.h" +#include "FixSession.h" +#include "FixHandler.h" + +#include "FixSession.tmh" + + +static void CALLBACK +s_FixThreadStub( + _Inout_ PTP_CALLBACK_INSTANCE Instance, + _Inout_opt_ PVOID Context, + _Inout_ PTP_WORK Work +); + +CFixSession::CFixSession() : + _Id(INVALID_SESSION_ID), + _State(STOPPED), + _HasCachedFix(false), + _StopFixEvent(nullptr), + _ThreadpoolWork(nullptr) +{ + InitializeCriticalSection(&_Lock); + _GnssPosition = g_DefaultGnssFixData; +} + +CFixSession::~CFixSession() +{ + if (_StopFixEvent != nullptr && _ThreadpoolWork != nullptr) + { + SetEvent(_StopFixEvent); + WaitForThreadpoolWorkCallbacks(_ThreadpoolWork, TRUE); + } + + if (_StopFixEvent != nullptr) + { + CloseHandle(_StopFixEvent); + _StopFixEvent = nullptr; + } + + if (_ThreadpoolWork != nullptr) + { + CloseThreadpoolWork(_ThreadpoolWork); + _ThreadpoolWork = nullptr; + } + + DeleteCriticalSection(&_Lock); +} + +NTSTATUS +CFixSession::Initialize( + _In_ WDFQUEUE Queue +) +{ + EnterCriticalSection(&_Lock); + + NTSTATUS status = STATUS_SUCCESS; + + // set the state + _State = STOPPED; + + // Create the TP work + _ThreadpoolWork = CreateThreadpoolWork( + (PTP_WORK_CALLBACK)s_FixThreadStub, + (PVOID)this, + nullptr + ); + + if (_ThreadpoolWork == nullptr) + { + status = STATUS_UNSUCCESSFUL; + goto Exit; + } + + // Create the stop fix Event + _StopFixEvent = CreateEvent(/* lpEventAttributes */ nullptr, /* bManualReset */ FALSE, /* bInitialState */ FALSE, /* lpName */ nullptr); + if (_StopFixEvent == nullptr) + { + status = STATUS_UNSUCCESSFUL; + goto Exit; + } + + // Create the manual dispatch queue + WDF_IO_QUEUE_CONFIG queueConfiguration; + WDF_IO_QUEUE_CONFIG_INIT(&queueConfiguration, WdfIoQueueDispatchManual); + + // Initialize the attributes of the queue. Set the execution level to + // passive to force all IRPs to be sent to the driver at passive IRQL. + WDF_OBJECT_ATTRIBUTES ioQueueAttributes; + WDF_OBJECT_ATTRIBUTES_INIT(&ioQueueAttributes); + ioQueueAttributes.SynchronizationScope = WdfSynchronizationScopeQueue; + ioQueueAttributes.ExecutionLevel = WdfExecutionLevelPassive; + status = WdfIoQueueCreate(WdfIoQueueGetDevice(Queue), + &queueConfiguration, + &ioQueueAttributes, + &_FixAcquisitionRequestQueue); + +Exit: + LeaveCriticalSection(&_Lock); + return status; +} + +NTSTATUS +CFixSession::StartAcquiringFix( + _In_ PGNSS_FIXSESSION_PARAM FixSessionParameters +) +{ + NTSTATUS status = STATUS_SUCCESS; + + EnterCriticalSection(&_Lock); + + // Check the state of the thread. + // If already running we should not get this request + // as multiplexing is not supported for Singleshot + if (_State == ACQUIRING) + { + status = STATUS_UNSUCCESSFUL; + goto Exit; + } + + // Create PTP_WORK only when first start fix is called. + if (_ThreadpoolWork == nullptr) + { + // Not initialized. Error out + status = STATUS_UNSUCCESSFUL; + goto Exit; + } + + _Id = FixSessionParameters->FixSessionID; + _State = ACQUIRING; + _FixParameter = *FixSessionParameters; + + ResetEvent(_StopFixEvent); + + // Post a work object to the thread + SubmitThreadpoolWork(_ThreadpoolWork); + + // Initialize the GetFix Queue + WdfIoQueueStart(_FixAcquisitionRequestQueue); + +Exit: + LeaveCriticalSection(&_Lock); + + return status; +} + +NTSTATUS +CFixSession::StopAcquiringFix( + _In_ PGNSS_STOPFIXSESSION_PARAM StopFixSessionParam +) +{ + NTSTATUS status = STATUS_SUCCESS; + + { + EnterCriticalSection(&_Lock); + + // If Ids don't Match return Error + if (StopFixSessionParam->FixSessionID != _Id) + { + status = STATUS_UNSUCCESSFUL; + TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_FIX, "Id mismatch in Stop Fix Session call"); + goto Exit; + } + + // Check the state of the session + if (_State != ACQUIRING) + { + TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_FIX, "Can't stop an inactive session."); + goto Exit; + } + + // Stop the current processing of the fix session + if (_StopFixEvent != nullptr) + { + SetEvent(_StopFixEvent); + } + + LeaveCriticalSection(&_Lock); + } + + // Wait for any threadpool work to finish. + if (_ThreadpoolWork != nullptr) + { + WaitForThreadpoolWorkCallbacks(_ThreadpoolWork, TRUE); + } + + EnterCriticalSection(&_Lock); + _State = STOPPED; + + // Drain the GetFix Queue + WdfIoQueuePurgeSynchronously(_FixAcquisitionRequestQueue); + +Exit: + LeaveCriticalSection(&_Lock); + + return status; +} + +NTSTATUS +CFixSession::ModifyAcquiringFix( + _In_ PGNSS_FIXSESSION_PARAM ModifyFixSessionParam +) +{ + NTSTATUS status = STATUS_SUCCESS; + + EnterCriticalSection(&_Lock); + + memcpy_s(&_FixParameter, sizeof(GNSS_FIXSESSION_PARAM), ModifyFixSessionParam, sizeof(*ModifyFixSessionParam)); + + LeaveCriticalSection(&_Lock); + + return status; +} + +NTSTATUS +CFixSession::ReportGnssFix( + _In_ PGNSS_FIXDATA GnssFix +) +{ + EnterCriticalSection(&_Lock); + + NTSTATUS status = STATUS_SUCCESS; + WDFREQUEST Request; + WDF_IO_QUEUE_STATE QueueStatus; + ULONG uRequest; + + QueueStatus = WdfIoQueueGetState(_FixAcquisitionRequestQueue, &uRequest, nullptr); + if (!WDF_IO_QUEUE_READY(QueueStatus) || (uRequest == 0)) + { + status = STATUS_UNSUCCESSFUL; + goto Exit; + } + + if (STATUS_SUCCESS == WdfIoQueueRetrieveNextRequest(_FixAcquisitionRequestQueue, &Request)) + { + status = CompleteRequestWithGnssFix(Request, GnssFix); + if (!NT_SUCCESS(status)) + { + TraceEvents(TRACE_LEVEL_ERROR, TRACE_FIX, "Could not respond with fix data with %!STATUS!", status); + goto Exit; + } + } + else + { + TraceEvents(TRACE_LEVEL_ERROR, TRACE_FIX, "Couldn't retrieve the next request with %!STATUS!", status); + goto Exit; + } + +Exit: + LeaveCriticalSection(&_Lock); + + return status; +} + +NTSTATUS +CFixSession::CompleteRequestWithGnssFix( + _In_ WDFREQUEST Request, + PGNSS_FIXDATA GnssFix +) +{ + NTSTATUS status = STATUS_SUCCESS; + PVOID outputBuffer; + size_t outputBufferLength; + PGNSS_EVENT gnssEvent; + size_t size = FIELD_OFFSET(GNSS_EVENT, FixData) + sizeof(GNSS_FIXDATA); + + status = WdfRequestRetrieveOutputBuffer(Request, + size, + &outputBuffer, + &outputBufferLength); + + if (!NT_SUCCESS(status)) + { + TraceEvents(TRACE_LEVEL_ERROR, TRACE_FIX, "WdfRequestRetrieveOutputBuffer() failed with %!STATUS!", status); + goto Exit; + } + + gnssEvent = ((GNSS_EVENT *)outputBuffer); + memset(outputBuffer, 0, size); + gnssEvent->Version = GNSS_DRIVER_VERSION_2; + gnssEvent->EventType = GNSS_Event_FixAvailable; + gnssEvent->EventDataSize = sizeof(GNSS_FIXDATA); + gnssEvent->Size = (ULONG)size; + memcpy_s(&gnssEvent->FixData, sizeof(GNSS_FIXDATA), GnssFix, sizeof(GNSS_FIXDATA)); + gnssEvent->FixData.FixSessionID = _Id; + + WdfRequestCompleteWithInformation(Request, STATUS_SUCCESS, outputBufferLength); + status = STATUS_PENDING; +Exit: + return status; +} + +NTSTATUS +CFixSession::UpdateCurrentPosition( + _In_ PGNSS_FIXDATA GnssFix +) +{ + NTSTATUS status = STATUS_SUCCESS; + + EnterCriticalSection(&_Lock); + + memcpy_s(&_GnssPosition, sizeof(GNSS_FIXDATA), GnssFix, sizeof(*GnssFix)); + + LeaveCriticalSection(&_Lock); + + return status; +} + +NTSTATUS +CFixSession::GetFix( + _In_ WDFREQUEST Request +) +{ + NTSTATUS status = STATUS_SUCCESS; + ULONG sessionId = INVALID_SESSION_ID; + PGNSS_EVENT outputEvent = nullptr; + ULONG *pSessionId = nullptr; + + EnterCriticalSection(&_Lock); + + // Verify the size of where output goes eventually, when this IRP is serviced + status = WdfRequestRetrieveOutputBuffer(Request, sizeof(GNSS_EVENT), (void**)&outputEvent, nullptr); + if (!NT_SUCCESS(status)) + { + TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_FIX, "Could not retrieve output buffer for request with %!STATUS!", status); + goto Exit; + } + + // Get the session ID this get fix is for + status = WdfRequestRetrieveInputBuffer(Request, sizeof(sessionId), (void**)&pSessionId, nullptr); + if (!NT_SUCCESS(status)) + { + TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_FIX, "Could not retrieve input sessionID for request"); + goto Exit; + } + + sessionId = *pSessionId; + + // Check if the Ids match + if (_Id != sessionId) + { + status = STATUS_UNSUCCESSFUL; + goto Exit; + } + // If we have a cached fix around because we tried to respond while there + // weren't any getfixes around, answer immediately with it + if (_HasCachedFix) + { + status = CompleteRequestWithGnssFix(Request, &_GnssPosition); + + _HasCachedFix = !(NT_SUCCESS(status)); + } + // Otherwise, we push the request into a queue + else + { + status = WdfRequestForwardToIoQueue(Request, _FixAcquisitionRequestQueue); + if (!NT_SUCCESS(status)) + { + TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_FIX, "WdfRequestForwardToIoQueue failed with %!STATUS!", status); + goto Exit; + } + + status = STATUS_PENDING; + } + +Exit: + LeaveCriticalSection(&_Lock); + + return status; +} + +static void CALLBACK +s_FixThreadStub( + _Inout_ PTP_CALLBACK_INSTANCE /*Instance*/, + _Inout_opt_ PVOID Context, + _Inout_ PTP_WORK /*Work*/ +) +{ + CFixSession *pFixSession = static_cast<CFixSession *>(Context); + + pFixSession->AcquireSingleFix(); +} + +void +CFixSession::AcquireSingleFix() +{ + NTSTATUS status = STATUS_SUCCESS; + + while (true) + { + // Wait stop fix event for 1 second and then retrieve position + DWORD WaitCode = WaitForSingleObject(_StopFixEvent, DEFAULT_FIX_INTERVAL_SECONDS * 1000); + if (WaitCode == WAIT_TIMEOUT) + { + status = RetrievePosition(&_GnssPosition); + if (!NT_SUCCESS(status)) + { + TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_FIX, "Fail to retrieve fix data from position source"); + } + + // Send the default fix to the session + status = ReportGnssFix(&_GnssPosition); + if (!NT_SUCCESS(status)) + { + // We were not able to send the fix so cache it + _HasCachedFix = true; + } + else + { + if (_GnssPosition.IsFinalFix != FALSE) + { + break; + } + } + } + else if (WaitCode == WAIT_OBJECT_0) + { + // Stop session signalled + break; + } + } + + EnterCriticalSection(&_Lock); + + // Reset the StopFix Event if it is set + ResetEvent(_StopFixEvent); + + // Update the state of the session + _State = STOPPED; + + LeaveCriticalSection(&_Lock); +} + + +NTSTATUS +CFixSession::RetrievePosition( + _Inout_ PGNSS_FIXDATA GnssFixData +) +{ + NTSTATUS status = STATUS_SUCCESS; + + // + // FIX ME: + // You can retrieve a position from a predefined position or GPS HW. + // This sample code calls RetrievePositionFromPredefinedValue to give fake location. + // To give a real location from GPS HW, replace this function with new function that defines + // (1) establishing and enumerating HW interface such as Serial or Bluetooth, and + // (2) reading and parsing NMEA messages from the interface. + // You can also define a custom IOCTL instead, updating _GnssPosition directly in CQueue and skip the call below as no-op. + // + status = RetrievePositionFromPredefinedValue(GnssFixData); + + return status; +} + +NTSTATUS +CFixSession::RetrievePositionFromPredefinedValue( + _Out_ PGNSS_FIXDATA GnssFixData +) +{ + NTSTATUS status = STATUS_SUCCESS; + + // + // FIX ME: + // Specify user-defined GNSS_FIXDATA output based off the DDI format. The following is just an example. + // + *GnssFixData = + { + sizeof(GNSS_FIXDATA), + GNSS_DRIVER_DDK_VERSION, + INVALID_SESSION_ID, + { 0xCD700000, 0x1D48908 }, // an example date/time, Dec/01/2018 + TRUE, + STATUS_SUCCESS, + GNSS_FIXDETAIL_BASIC | GNSS_FIXDETAIL_ACCURACY, + { + sizeof(GNSS_FIXDATA_BASIC), + GNSS_DRIVER_DDK_VERSION, + // Location: Mount Rainier National Park + 46.852273, + -121.757468, + 0.0, + 0.0, + 0.0 + }, + { + sizeof(GNSS_FIXDATA_ACCURACY), + GNSS_DRIVER_DDK_VERSION, + 10 // Meters accuracy + } + }; + return status; +} |
