diff options
Diffstat (limited to 'Sensors/Activity/client.cpp')
| -rw-r--r-- | Sensors/Activity/client.cpp | 1021 |
1 files changed, 1021 insertions, 0 deletions
diff --git a/Sensors/Activity/client.cpp b/Sensors/Activity/client.cpp new file mode 100644 index 00000000..ca4c5bc2 --- /dev/null +++ b/Sensors/Activity/client.cpp @@ -0,0 +1,1021 @@ +// Copyright (C) Microsoft Corporation, All Rights Reserved. +// +// Abstract: +// This module contains the implementation of driver callback function from clx to activity detection driver. +// +// Environment: +// Windows User-Mode Driver Framework (UMDF) + +#include "Device.h" +#include "HardwareSimulator.h" +#include "Client.tmh" + +// This routine is called by worker thread to read a single sample, compare threshold +// and push it back to CLX. It simulates hardware thresholding by only generating data +// when the change of data is greater than threshold. +NTSTATUS ActivityDevice::GetData() +{ + BOOLEAN dataReady = FALSE; + NTSTATUS status = STATUS_SUCCESS; + + SENSOR_FunctionEnter(); + + if (NULL != m_SimulatorInstance) + { + // Use simulator to get m_pFiltered Sample + PHardwareSimulator pSimulator = GetHardwareSimulatorContextFromInstance(m_SimulatorInstance); + if (nullptr != pSimulator) + { + status = pSimulator->GetSample(m_pFilteredSample); + } + else + { + status = STATUS_INVALID_PARAMETER; + } + } + + if (NT_SUCCESS(status)) + { + status = CollectionsListSortSubscribedActivitiesByConfidence(m_pThresholds, m_pFilteredSample); + if (!NT_SUCCESS(status)) + { + TraceError("ACT %!FUNC! CollectionsListSortSubscribedActivitiesByConfidence failed! %!STATUS!", status); + } + else + { + // new sample? + if (FALSE != m_FirstSample) + { + dataReady = TRUE; + } + else + { + dataReady = EvaluateActivityThresholds(m_pFilteredSample, m_pLastSample, m_pThresholds); + } + } + } + + if (FALSE != dataReady) + { + // update last sample + FILETIME TimeStamp = {}; + memcpy_s(m_pLastSample, m_pFilteredSample->AllocatedSizeInBytes, m_pFilteredSample, m_pFilteredSample->AllocatedSizeInBytes); + GetSystemTimePreciseAsFileTime(&TimeStamp); + InitPropVariantFromFileTime(&TimeStamp, &(m_pLastSample->List[ACTIVITY_DATA_TIMESTAMP].Value)); + + // push to clx + SensorsCxSensorDataReady(m_SensorInstance, m_pLastSample); + m_FirstSample = FALSE; + } + else + { + status = STATUS_DATA_NOT_ACCEPTED; + TraceInformation("ACT %!FUNC! Data did NOT meet the threshold"); + } + + SENSOR_FunctionExit(status); + return status; +} + +// This callback is called when interval wait time has expired and driver is ready +// to collect new sample. The callback reads current value, compare value to threshold, +// pushes it up to CLX framework, and schedule next wake up time. +VOID ActivityDevice::OnTimerExpire(_In_ WDFTIMER timer) +{ + NTSTATUS status = STATUS_SUCCESS; + + SENSOR_FunctionEnter(); + + PActivityDevice pDevice = GetActivityContextFromSensorInstance(WdfTimerGetParentObject(timer)); + if (nullptr == pDevice) + { + status = STATUS_INSUFFICIENT_RESOURCES; + TraceError("ACT %!FUNC! GetActivityContextFromSensorInstance failed %!STATUS!", status); + } + else + { + // Get data and push to clx + WdfWaitLockAcquire(pDevice->m_Lock, NULL); + status = pDevice->GetData(); + if (!NT_SUCCESS(status) && STATUS_DATA_NOT_ACCEPTED != status) + { + TraceError("ACT %!FUNC! GetAccData Failed %!STATUS!", status); + } + WdfWaitLockRelease(pDevice->m_Lock); + + // Schedule next wake up time + if (Act_Default_MinDataInterval_Ms <= pDevice->m_Interval && + FALSE != pDevice->m_PoweredOn && + FALSE != pDevice->m_Started) + { + WdfTimerStart(pDevice->m_Timer, WDF_REL_TIMEOUT_IN_MS(pDevice->m_Interval)); + } + } + + SENSOR_FunctionExit(status); +} + +// Called by Sensor CLX to begin continously sampling the sensor. +NTSTATUS ActivityDevice::OnStart(_In_ SENSOROBJECT sensorInstance) +{ + NTSTATUS status = STATUS_SUCCESS; + + SENSOR_FunctionEnter(); + + PActivityDevice pDevice = GetActivityContextFromSensorInstance(sensorInstance); + if (nullptr == pDevice) + { + status = STATUS_INVALID_PARAMETER; + TraceError("ACT %!FUNC! Sensor parameter is invalid. Failed %!STATUS!", status); + } + else + { + if (FALSE == pDevice->m_PoweredOn) + { + status = STATUS_DEVICE_NOT_READY; + TraceError("ACT %!FUNC! Sensor is not powered on! %!STATUS!", status); + } + else + { + // Start simulation + if (NULL != pDevice->m_SimulatorInstance) + { + PHardwareSimulator pSimulator = GetHardwareSimulatorContextFromInstance(pDevice->m_SimulatorInstance); + if (nullptr != pSimulator) + { + pSimulator->Start(); + } + } + + // Start sensing + pDevice->m_FirstSample = TRUE; + pDevice->m_Started = TRUE; + InitPropVariantFromUInt32(SensorState_Active, &(pDevice->m_pProperties->List[SENSOR_PROPERTY_STATE].Value)); + WdfTimerStart(pDevice->m_Timer, WDF_REL_TIMEOUT_IN_MS(Act_Default_MinDataInterval_Ms)); + } + } + + SENSOR_FunctionExit(status); + return status; +} + +// Called by Sensor CLX to stop continously sampling the sensor. +NTSTATUS ActivityDevice::OnStop(_In_ SENSOROBJECT sensorInstance) +{ + NTSTATUS status = STATUS_SUCCESS; + + SENSOR_FunctionEnter(); + + PActivityDevice pDevice = GetActivityContextFromSensorInstance(sensorInstance); + if (nullptr == pDevice) + { + status = STATUS_INVALID_PARAMETER; + TraceError("ACT %!FUNC! Sensor parameter is invalid. Failed %!STATUS!", status); + } + else + { + // Stop sensing + pDevice->m_Started = FALSE; + WdfTimerStop(pDevice->m_Timer, TRUE); + InitPropVariantFromUInt32(SensorState_Idle, &(pDevice->m_pProperties->List[SENSOR_PROPERTY_STATE].Value)); + + // Stop simulation + if (NULL != pDevice->m_SimulatorInstance) + { + PHardwareSimulator pSimulator = GetHardwareSimulatorContextFromInstance(pDevice->m_SimulatorInstance); + if (nullptr != pSimulator) + { + pSimulator->Stop(); + } + } + } + + SENSOR_FunctionExit(status); + return status; +} + +// This routine is called by history retrieval thread to remove an entry from the history buffer. +// Note this function must be called under lock +_Requires_lock_held_(m_HistoryLock) +NTSTATUS ActivityDevice::RemoveDataElementFromHistoryBuffer(_Out_ PActivitySample pData) +{ + NTSTATUS status = STATUS_SUCCESS; + + if (nullptr == pData) + { + status = STATUS_INVALID_PARAMETER; + TraceError("ACT %!FUNC! NULL ptr passed in %!STATUS!", status); + } + else + { + if (0 == m_History.NumOfElems) + { + // buffer empty + status = STATUS_NO_MORE_ENTRIES; + } + else + { + // Remove the oldest element from the circular buffer + *pData = m_History.pData[m_History.FirstElemIndex]; + m_History.FirstElemIndex++; + m_History.FirstElemIndex %= m_History.BufferLength; + m_History.NumOfElems--; + if (0 == m_History.NumOfElems) + { + // Buffer Empty. 'LastElemIndex' should be same as 'FirstElemIndex' + m_History.LastElemIndex = m_History.FirstElemIndex; + } + } + } + + return status; +} + +// This routine is called by worker thread to add an entry to the history buffer. +// Note this function must be called under lock +_Requires_lock_held_(m_HistoryLock) +NTSTATUS ActivityDevice::AddDataElementToHistoryBuffer(_In_ PActivitySample pData) +{ + NTSTATUS status = STATUS_SUCCESS; + + if (nullptr == pData) + { + status = STATUS_INVALID_PARAMETER; + TraceError("ACT %!FUNC! GetActivityContextFromSensorInstance failed %!STATUS!", status); + } + else + { + if (0 == m_History.NumOfElems) + { + // buffer empty + m_History.NumOfElems++; + } + else if (m_History.BufferLength > m_History.NumOfElems) + { + // buffer not full yet. Increment the index of the last element + m_History.LastElemIndex++; + m_History.LastElemIndex %= m_History.BufferLength; + m_History.NumOfElems++; + } + else if (m_History.BufferLength == m_History.NumOfElems) + { + // buffer full. Over-write the oldest element + m_History.FirstElemIndex++; + m_History.FirstElemIndex %= m_History.BufferLength; + m_History.LastElemIndex++; + m_History.LastElemIndex %= m_History.BufferLength; + } + memcpy_s(&(m_History.pData[m_History.LastElemIndex]), + sizeof(m_History.pData[m_History.LastElemIndex]), + pData, + sizeof(*pData)); + } + + return status; +} + +// This routine is for clearing the history buffer. Note this function must be called under lock. +_Requires_lock_held_(m_HistoryLock) +NTSTATUS ActivityDevice::ClearHistoryBuffer() +{ + m_History.FirstElemIndex = 0; + m_History.LastElemIndex = 0; + m_History.NumOfElems = 0; + RtlZeroMemory(m_History.pData, (m_History.BufferLength*sizeof(ActivitySample))); + return STATUS_SUCCESS; +} + +// This callback is called when interval wait time has expired and driver is ready +// to collect new sample. The callback stores activity data in history buffer, +// and schedules next wake up time. +VOID ActivityDevice::OnHistoryTimerExpire(_In_ WDFTIMER historyTimer) +{ + NTSTATUS status = STATUS_SUCCESS; + + SENSOR_FunctionEnter(); + + PActivityDevice pDevice = GetActivityContextFromSensorInstance(WdfTimerGetParentObject(historyTimer)); + if (nullptr == pDevice) + { + status = STATUS_INSUFFICIENT_RESOURCES; + TraceError("ACT %!FUNC! GetActivityContextFromSensorInstance failed %!STATUS!", status); + } + else + { + ActivitySample data = {}; + if (NULL != pDevice->m_SimulatorInstance) + { + PHardwareSimulator pSimulator = GetHardwareSimulatorContextFromInstance(pDevice->m_SimulatorInstance); + if (nullptr != pSimulator) + { + status = pSimulator->GetSample(&data); + } + else + { + status = STATUS_INVALID_PARAMETER; + } + } + GetSystemTimePreciseAsFileTime(&(data.Timestamp)); + + if (NT_SUCCESS(status)) + { + // Add data to the buffer + WdfWaitLockAcquire(pDevice->m_HistoryLock, NULL); + status = pDevice->AddDataElementToHistoryBuffer(&data); + if (!NT_SUCCESS(status)) + { + TraceError("ACT %!FUNC! AddDataElementToHistoryBuffer Failed %!STATUS!", status); + } + WdfWaitLockRelease(pDevice->m_HistoryLock); + } + + // Schedule next wake up time + if (FALSE != pDevice->m_HistoryStarted) + { + WdfTimerStart(pDevice->m_HistoryTimer, WDF_REL_TIMEOUT_IN_MS(pDevice->m_HistoryIntervalInMs)); + } + } + + SENSOR_FunctionExit(status); +} + +// Called by Sensor CLX to begin keeping history +NTSTATUS ActivityDevice::OnStartHistory(_In_ SENSOROBJECT sensorInstance) +{ + NTSTATUS status = STATUS_SUCCESS; + + SENSOR_FunctionEnter(); + + PActivityDevice pDevice = GetActivityContextFromSensorInstance(sensorInstance); + if (nullptr == pDevice) + { + status = STATUS_INVALID_PARAMETER; + TraceError("ACT %!FUNC! Sensor parameter is invalid. Failed %!STATUS!", status); + } + else if (0 == pDevice->m_HistorySizeInRecords) + { + status = STATUS_NOT_SUPPORTED; + TraceError("ACT %!FUNC! Sensor does not support History"); + } + else if (FALSE == pDevice->m_PoweredOn) + { + status = STATUS_DEVICE_NOT_READY; + TraceError("ACT %!FUNC! Sensor is not powered on! %!STATUS!", status); + } + else + { + // Start keeping history + pDevice->m_HistoryStarted = TRUE; + WdfTimerStart(pDevice->m_HistoryTimer, WDF_REL_TIMEOUT_IN_MS(pDevice->m_HistoryIntervalInMs)); + } + + SENSOR_FunctionExit(status); + return status; +} + +// Called by Sensor CLX to stop keeping history. +NTSTATUS ActivityDevice::OnStopHistory(_In_ SENSOROBJECT sensorInstance) +{ + NTSTATUS status = STATUS_SUCCESS; + + SENSOR_FunctionEnter(); + + PActivityDevice pDevice = GetActivityContextFromSensorInstance(sensorInstance); + if (nullptr == pDevice) + { + status = STATUS_INVALID_PARAMETER; + TraceError("ACT %!FUNC! Sensor parameter is invalid. Failed %!STATUS!", status); + } + else + { + // Stop keeping history + pDevice->m_HistoryStarted = FALSE; + WdfTimerStop(pDevice->m_HistoryTimer, TRUE); + } + + SENSOR_FunctionExit(status); + return status; +} + +// Called by Sensor CLX to clear all history stored in the sensor. +NTSTATUS ActivityDevice::OnClearHistory(_In_ SENSOROBJECT sensorInstance) +{ + NTSTATUS status = STATUS_SUCCESS; + + SENSOR_FunctionEnter(); + + PActivityDevice pDevice = GetActivityContextFromSensorInstance(sensorInstance); + if (nullptr == pDevice) + { + status = STATUS_INVALID_PARAMETER; + TraceError("ACT %!FUNC! Sensor parameter is invalid. Failed %!STATUS!", status); + } + else + { + WdfWaitLockAcquire(pDevice->m_HistoryLock, NULL); + status = pDevice->ClearHistoryBuffer(); + if (!NT_SUCCESS(status)) + { + TraceError("ACT %!FUNC! ClearHistoryBuffer Failed %!STATUS!", status); + } + WdfWaitLockRelease(pDevice->m_HistoryLock); + } + + SENSOR_FunctionExit(status); + return status; +} + +// Called by Sensor CLX to start retrieving history. +NTSTATUS ActivityDevice::OnStartHistoryRetrieval( + _In_ SENSOROBJECT sensorInstance, + _Inout_updates_bytes_(historySizeInBytes) PSENSOR_COLLECTION_LIST pHistoryBuffer, + _In_ ULONG historySizeInBytes + ) +{ + NTSTATUS status = STATUS_SUCCESS; + + SENSOR_FunctionEnter(); + + PActivityDevice pDevice = GetActivityContextFromSensorInstance(sensorInstance); + if (nullptr == pDevice) + { + status = STATUS_INVALID_PARAMETER; + TraceError("ACT %!FUNC! Sensor parameter is invalid. Failed %!STATUS!", status); + } + else + { + // Check if the previously created history retrieval thread finished. + // When StartHistoryRetrieval is called again before we signal to CLX that history retrieval is + // completed, fail the second call. + WdfWaitLockAcquire(pDevice->m_HistoryLock, NULL); + BOOLEAN IsStarted = pDevice->m_HistoryRetrievalStarted; + WdfWaitLockRelease(pDevice->m_HistoryLock); + + if (FALSE != IsStarted) + { + status = STATUS_DEVICE_BUSY; + TraceError("ACT %!FUNC! StartHistoryRetrieval called again before the previous call" + "finishes retrieving history %!STATUS!", status); + } + else + { + // Before creating a new thread, close handle to the previously created thread. + if (NULL != pDevice->m_hThread) + { + CloseHandle(pDevice->m_hThread); + pDevice->m_hThread = NULL; + } + + // check that the buffer is not null + if (NULL == pHistoryBuffer) + { + status = STATUS_INVALID_PARAMETER; + TraceError("ACT %!FUNC! History buffer cannot be NULL. %!STATUS!", status); + } + else + { + // check that the buffer can hold at least one entry + if (historySizeInBytes < pDevice->m_HistoryMarshalledRecordSize) + { + status = STATUS_BUFFER_TOO_SMALL; + TraceError("ACT %!FUNC! History buffer is too small to even fill one complete entry. %!STATUS!", status); + } + else + { + pDevice->m_ClientHistoryBuffer = pHistoryBuffer; + pDevice->m_ClientHistoryBufferSize = historySizeInBytes; + + // Reset exit event + ResetEvent(pDevice->m_ExitEvent); + + WdfWaitLockAcquire(pDevice->m_HistoryLock, NULL); + + // Create a new thread to retrieve data from the driver's history buffer + pDevice->m_hThread = CreateThread(NULL, // thread attributes + 0, // default stack size + HistoryRetrievalThread, //start address + reinterpret_cast<PVOID>(sensorInstance), // pointer to variable to be passed + 0, // run immediately + NULL); // Thread ID + if (NULL == pDevice->m_hThread) + { + status = STATUS_UNSUCCESSFUL; + TraceError("ACT %!FUNC! CreateThread Failed %!STATUS!", status); + } + else + { + pDevice->m_HistoryRetrievalStarted = TRUE; + } + + WdfWaitLockRelease(pDevice->m_HistoryLock); + } + } + } + } + + SENSOR_FunctionExit(status); + + if (nullptr != pDevice && !NT_SUCCESS(status) && NULL != pDevice->m_hThread) + { + CloseHandle(pDevice->m_hThread); + pDevice->m_hThread = NULL; + } + return status; +} + +// Called by Sensor CLX to cancel history retrieval. +NTSTATUS ActivityDevice::OnCancelHistoryRetrieval(_In_ SENSOROBJECT sensorInstance, _Out_ PULONG pBytesWritten) +{ + NTSTATUS status = STATUS_SUCCESS; + *pBytesWritten = 0; + + SENSOR_FunctionEnter(); + + PActivityDevice pDevice = GetActivityContextFromSensorInstance(sensorInstance); + if (nullptr == pDevice) + { + status = STATUS_INVALID_PARAMETER; + TraceError("ACT %!FUNC! Sensor parameter is invalid. Failed %!STATUS!", status); + } + else + { + // Check if history retrieval operation is in progress + WdfWaitLockAcquire(pDevice->m_HistoryLock, NULL); + BOOLEAN IsStarted = pDevice->m_HistoryRetrievalStarted; + WdfWaitLockRelease(pDevice->m_HistoryLock); + + if (FALSE == IsStarted) + { + status = STATUS_INVALID_DEVICE_REQUEST; + TraceError("ACT %!FUNC! History retrieval is not in progress %!STATUS!", status); + } + else + { + // Signal the history retrieval thread to exit + SetEvent(pDevice->m_ExitEvent); + + // Wait for the history retrieval thread to finish writing the entry since no partial entries are allowed. + DWORD result = WaitForSingleObjectEx(pDevice->m_hThread, Act_TimeoutForHistoryThread_Ms, FALSE); + if (WAIT_OBJECT_0 != result) + { + // continue to mark history retrieval stop + status = NTSTATUS_FROM_WIN32(result); + TraceError("ACT %!FUNC! WaitForSingleObjectEx failed! %!STATUS!", status); + } + + WdfWaitLockAcquire(pDevice->m_HistoryLock, NULL); + *pBytesWritten = CollectionsListGetMarshalledSizeWithoutSerialization(pDevice->m_ClientHistoryBuffer); + pDevice->m_HistoryRetrievalStarted = FALSE; + WdfWaitLockRelease(pDevice->m_HistoryLock); + } + } + + SENSOR_FunctionExit(status); + return status; +} + +// Created on calling StartHistoryRetrieval function. +ULONG WINAPI ActivityDevice::HistoryRetrievalThread(_In_ LPVOID lpParam) +{ + NTSTATUS status = STATUS_SUCCESS; + BOOLEAN exitSignal = FALSE; + UINT existingCount = 0; + UINT fillableCount = 0; + + SENSOROBJECT sensorInstance = reinterpret_cast<SENSOROBJECT>(lpParam); + PActivityDevice pDevice = GetActivityContextFromSensorInstance(sensorInstance); + if (nullptr == pDevice || + NULL == pDevice->m_ClientHistoryBuffer || + pDevice->m_HistoryMarshalledRecordSize > pDevice->m_ClientHistoryBufferSize) + { + status = STATUS_INVALID_PARAMETER; + TraceError("ACT %!FUNC! Sensor parameter is invalid. Failed %!STATUS!", status); + } + else + { + // Capture the number of entries in the driver's history buffer at this moment + WdfWaitLockAcquire(pDevice->m_HistoryLock, NULL); + existingCount = pDevice->m_History.NumOfElems; + WdfWaitLockRelease(pDevice->m_HistoryLock); + + // How many entries can the client supplied buffer hold + fillableCount = (pDevice->m_ClientHistoryBufferSize - SENSOR_COLLECTION_LIST_HEADER_SIZE) / (pDevice->m_HistoryMarshalledRecordSize - SENSOR_COLLECTION_LIST_HEADER_SIZE); + + // Find the minimum of the number of entries in the driver's history buffer and + // the number of entries that can be held by the client supplied buffer + UINT count = min(existingCount, fillableCount); + + // All entries into the client's buffer must be complete i.e. no partial entries are allowed. + // Check whether the exit event is signaled before looping through each time to retrieve an entry from the driver's history buffer. + // Each history record will only contain first entry of the most probable state. + for (UINT index = 0; index < count; index++) + { + ActivitySample data = {}; + UINT elementIndex = ACTIVITY_DATA_COUNT * index; + if (WAIT_OBJECT_0 == WaitForSingleObject(pDevice->m_ExitEvent, 0)) + { + exitSignal = TRUE; + break; + } + + WdfWaitLockAcquire(pDevice->m_HistoryLock, NULL); + if (!NT_SUCCESS(pDevice->RemoveDataElementFromHistoryBuffer(&data))) + { + // Only return error if there is no more entry which is not an error. Therefore no need to set status. + WdfWaitLockRelease(pDevice->m_HistoryLock); + break; + } + + elementIndex = (index * ACTIVITY_DATA_COUNT) + ACTIVITY_DATA_TIMESTAMP; + pDevice->m_ClientHistoryBuffer->List[elementIndex].Key = PKEY_SensorData_Timestamp; + InitPropVariantFromFileTime(&(data.Timestamp), &(pDevice->m_ClientHistoryBuffer->List[elementIndex].Value)); + + elementIndex = (index * ACTIVITY_DATA_COUNT) + ACTIVITY_DATA_CURRENT_STATE; + pDevice->m_ClientHistoryBuffer->List[elementIndex].Key = PKEY_SensorData_CurrentActivityState; + InitPropVariantFromUInt32(data.Activity, &(pDevice->m_ClientHistoryBuffer->List[elementIndex].Value)); + + elementIndex = (index * ACTIVITY_DATA_COUNT) + ACTIVITY_DATA_CURRENT_CONFIDENCE; + pDevice->m_ClientHistoryBuffer->List[elementIndex].Key = PKEY_SensorData_CurrentActivityStateConfidence_Percentage; + InitPropVariantFromUInt16(data.Confidence, &(pDevice->m_ClientHistoryBuffer->List[elementIndex].Value)); + + // Keep the count in the sensor collection list updated + // so that the count is valid even when exit is signaled. + pDevice->m_ClientHistoryBuffer->Count = (index + 1) * ACTIVITY_DATA_COUNT; + WdfWaitLockRelease(pDevice->m_HistoryLock); + } + } + + if (FALSE == exitSignal) + { + if (fillableCount < existingCount) + { + status = STATUS_BUFFER_OVERFLOW; + } + else if (0 == existingCount) + { + status = STATUS_NO_MORE_ENTRIES; + } + // call completed callback only if retrieval was not canceled + SensorsCxSensorHistoryRetrievalCompleted(pDevice->m_SensorInstance, CollectionsListGetMarshalledSizeWithoutSerialization(pDevice->m_ClientHistoryBuffer), status); + } + + WdfWaitLockAcquire(pDevice->m_HistoryLock, NULL); + pDevice->m_HistoryRetrievalStarted = FALSE; + WdfWaitLockRelease(pDevice->m_HistoryLock); + + return ERROR_SUCCESS; +} + +// Called by Sensor CLX to get supported data fields. The typical usage is to call +// this function once with buffer pointer as NULL to acquire the required size +// for the buffer, allocate buffer, then call the function again to retrieve +// sensor information. +NTSTATUS ActivityDevice::OnGetSupportedDataFields( + _In_ SENSOROBJECT sensorInstance, // sensor device object + _Inout_opt_ PSENSOR_PROPERTY_LIST pFields, // pointer to a list of supported properties + _Out_ PULONG pSize) // number of bytes for the list of supported properties +{ + NTSTATUS status = STATUS_SUCCESS; + + SENSOR_FunctionEnter(); + + PActivityDevice pDevice = GetActivityContextFromSensorInstance(sensorInstance); + if (nullptr == pDevice || nullptr == pSize) + { + status = STATUS_INVALID_PARAMETER; + TraceError("ACT %!FUNC! Invalid parameters! %!STATUS!", status); + } + else + { + if (nullptr == pFields) + { + // Just return size + *pSize = pDevice->m_pSupportedDataFields->AllocatedSizeInBytes; + } + else + { + if (pFields->AllocatedSizeInBytes < pDevice->m_pSupportedDataFields->AllocatedSizeInBytes) + { + status = STATUS_INSUFFICIENT_RESOURCES; + TraceError("ACT %!FUNC! Buffer is too small. Failed %!STATUS!", status); + } + else + { + // Fill out data + status = PropertiesListCopy(pFields, pDevice->m_pSupportedDataFields); + if (!NT_SUCCESS(status)) + { + TraceError("ACT %!FUNC! PropertiesListCopy failed %!STATUS!", status); + } + else + { + *pSize = pDevice->m_pSupportedDataFields->AllocatedSizeInBytes; + } + } + } + } + + if (!NT_SUCCESS(status)) + { + *pSize = 0; + } + + SENSOR_FunctionExit(status); + return status; +} + +// Called by Sensor CLX to get sensor properties. The typical usage is to call +// this function once with buffer pointer as NULL to acquire the required size +// for the buffer, allocate buffer, then call the function again to retrieve +// sensor information. +NTSTATUS ActivityDevice::OnGetProperties( + _In_ SENSOROBJECT sensorInstance, // sensor device object + _Inout_opt_ PSENSOR_COLLECTION_LIST pProperties, // pointer to a list of sensor properties + _Out_ PULONG pSize) // number of bytes for the list of sensor properties +{ + NTSTATUS status = STATUS_SUCCESS; + + SENSOR_FunctionEnter(); + + PActivityDevice pDevice = GetActivityContextFromSensorInstance(sensorInstance); + if (nullptr == pDevice || nullptr == pSize) + { + status = STATUS_INVALID_PARAMETER; + TraceError("ACT %!FUNC! Invalid parameters! %!STATUS!", status); + } + else + { + if (nullptr == pProperties) + { + // Just return size + *pSize = CollectionsListGetMarshalledSize(pDevice->m_pProperties); + } + else + { + if (pProperties->AllocatedSizeInBytes < + CollectionsListGetMarshalledSize(pDevice->m_pProperties)) + { + status = STATUS_INSUFFICIENT_RESOURCES; + TraceError("ACT %!FUNC! Buffer is too small. Failed %!STATUS!", status); + } + else + { + // Fill out all data + status = CollectionsListCopyAndMarshall(pProperties, pDevice->m_pProperties); + if (!NT_SUCCESS(status)) + { + TraceError("ACT %!FUNC! CollectionsListCopyAndMarshall failed %!STATUS!", status); + } + else + { + *pSize = CollectionsListGetMarshalledSize(pDevice->m_pProperties); + } + } + } + } + + if (!NT_SUCCESS(status)) + { + *pSize = 0; + } + + SENSOR_FunctionExit(status); + return status; +} + +// Called by Sensor CLX to get data field properties. The typical usage is to call +// this function once with buffer pointer as NULL to acquire the required size +// for the buffer, allocate buffer, then call the function again to retrieve +// sensor information. +NTSTATUS ActivityDevice::OnGetDataFieldProperties( + _In_ SENSOROBJECT sensorInstance, // sensor device object + _In_ const PROPERTYKEY *pDataField, // pointer to the propertykey of requested property + _Inout_opt_ PSENSOR_COLLECTION_LIST pProperties, // pointer to a list of sensor properties + _Out_ PULONG pSize) // number of bytes for the list of sensor properties +{ + NTSTATUS status = STATUS_SUCCESS; + + SENSOR_FunctionEnter(); + + PActivityDevice pDevice = GetActivityContextFromSensorInstance(sensorInstance); + if (nullptr == pDevice || nullptr == pSize || nullptr == pDataField) + { + status = STATUS_INVALID_PARAMETER; + TraceError("ACT %!FUNC! Invalid parameters! %!STATUS!", status); + } + else + { + if (*pDataField == PKEY_SensorData_CurrentActivityStateConfidence_Percentage) + { + if (nullptr == pProperties) + { + // Just return size + *pSize = CollectionsListGetMarshalledSize(pDevice->m_pDataFieldProperties); + } + else + { + if (pProperties->AllocatedSizeInBytes < CollectionsListGetMarshalledSize(pDevice->m_pDataFieldProperties)) + { + status = STATUS_INSUFFICIENT_RESOURCES; + TraceError("ACT %!FUNC! Buffer is too small. Failed %!STATUS!", status); + } + else + { + // Fill out all data + status = CollectionsListCopyAndMarshall(pProperties, pDevice->m_pDataFieldProperties); + if (!NT_SUCCESS(status)) + { + TraceError("ACT %!FUNC! CollectionsListCopyAndMarshall failed %!STATUS!", status); + } + else + { + *pSize = CollectionsListGetMarshalledSize(pDevice->m_pDataFieldProperties); + } + } + } + } + else + { + status = STATUS_NOT_SUPPORTED; + TraceError("ACT %!FUNC! AccFake does NOT have properties for this data field. Failed %!STATUS!", status); + } + } + + if (!NT_SUCCESS(status)) + { + *pSize = 0; + } + + SENSOR_FunctionExit(status); + return status; +} + +// Called by Sensor CLX to get sampling rate of the sensor. +NTSTATUS ActivityDevice::OnGetDataInterval(_In_ SENSOROBJECT sensorInstance, _Out_ PULONG pDataRateMs) +{ + NTSTATUS status = STATUS_SUCCESS; + *pDataRateMs = 0; + + SENSOR_FunctionEnter(); + + PActivityDevice pDevice = GetActivityContextFromSensorInstance(sensorInstance); + if (nullptr == pDevice || nullptr == pDataRateMs) + { + status = STATUS_INVALID_PARAMETER; + TraceError("ACT %!FUNC! Sensor parameter is invalid. Failed %!STATUS!", status); + } + else + { + *pDataRateMs = pDevice->m_Interval; + } + + SENSOR_FunctionExit(status); + return status; +} + +// Called by Sensor CLX to set sampling rate of the sensor. +NTSTATUS ActivityDevice::OnSetDataInterval(_In_ SENSOROBJECT sensorInstance, _In_ ULONG dataRateMs) +{ + NTSTATUS status = STATUS_SUCCESS; + + SENSOR_FunctionEnter(); + + PActivityDevice pDevice = GetActivityContextFromSensorInstance(sensorInstance); + if (nullptr == pDevice || Act_Default_MinDataInterval_Ms > dataRateMs) + { + status = STATUS_INVALID_PARAMETER; + TraceError("ACT %!FUNC! Invalid parameters! %!STATUS!", status); + } + else + { + pDevice->m_Interval = dataRateMs; + + // reschedule sample to return as soon as possible if it's started + if (FALSE != pDevice->m_Started) + { + pDevice->m_Started = FALSE; + WdfTimerStop(pDevice->m_Timer, TRUE); + + pDevice->m_Started = TRUE; + pDevice->m_FirstSample = TRUE; + WdfTimerStart(pDevice->m_Timer, WDF_REL_TIMEOUT_IN_MS(Act_Default_MinDataInterval_Ms)); + } + } + + SENSOR_FunctionExit(status); + return status; +} + +// Called by Sensor CLX to get data thresholds. The typical usage is to call +// this function once with buffer pointer as NULL to acquire the required size +// for the buffer, allocate buffer, then call the function again to retrieve +// sensor information. +NTSTATUS ActivityDevice::OnGetDataThresholds( + _In_ SENSOROBJECT sensorInstance, // sensor device object + _Inout_opt_ PSENSOR_COLLECTION_LIST pThresholds, // pointer to a list of sensor thresholds + _Out_ PULONG pSize) // number of bytes for the list of sensor thresholds +{ + NTSTATUS status = STATUS_SUCCESS; + + SENSOR_FunctionEnter(); + + PActivityDevice pDevice = GetActivityContextFromSensorInstance(sensorInstance); + if (nullptr == pDevice || nullptr == pSize) + { + status = STATUS_INVALID_PARAMETER; + TraceError("ACT %!FUNC! Invalid parameters! %!STATUS!", status); + } + else + { + if (nullptr == pThresholds) + { + // Just return size + *pSize = CollectionsListGetMarshalledSize(pDevice->m_pThresholds); + } + else + { + if (pThresholds->AllocatedSizeInBytes < CollectionsListGetMarshalledSize(pDevice->m_pThresholds)) + { + status = STATUS_INSUFFICIENT_RESOURCES; + TraceError("ACT %!FUNC! Buffer is too small. Failed %!STATUS!", status); + } + else + { + // Fill out all data + status = CollectionsListCopyAndMarshall(pThresholds, pDevice->m_pThresholds); + if (!NT_SUCCESS(status)) + { + TraceError("ACT %!FUNC! CollectionsListCopyAndMarshall failed %!STATUS!", status); + } + else + { + *pSize = CollectionsListGetMarshalledSize(pDevice->m_pThresholds); + } + } + } + } + + if (!NT_SUCCESS(status)) + { + *pSize = 0; + } + + SENSOR_FunctionExit(status); + return status; +} + +// Called by Sensor CLX to set data thresholds. +NTSTATUS ActivityDevice::OnSetDataThresholds( + _In_ SENSOROBJECT sensorInstance, // sensor device object + _In_ PSENSOR_COLLECTION_LIST pThresholds) // pointer to a list of sensor thresholds +{ + NTSTATUS status = STATUS_SUCCESS; + + SENSOR_FunctionEnter(); + + PActivityDevice pDevice = GetActivityContextFromSensorInstance(sensorInstance); + if (nullptr == pDevice) + { + status = STATUS_INVALID_PARAMETER; + TraceError("ACT %!FUNC! Sensor(0x%p) parameter is invalid. Failed %!STATUS!", sensorInstance, status); + } + else + { + WdfWaitLockAcquire(pDevice->m_Lock, NULL); + + for (ULONG element = 0; element < pThresholds->Count; element++) + { + status = PropKeyFindKeySetPropVariant(pDevice->m_pThresholds, + &(pThresholds->List[element].Key), + TRUE, + &(pThresholds->List[element].Value)); + if (!NT_SUCCESS(status)) + { + status = STATUS_INVALID_PARAMETER; + TraceError("ACT %!FUNC! ActivityFake does NOT have threshold for this data field. Failed %!STATUS!", status); + break; + } + } + + WdfWaitLockRelease(pDevice->m_Lock); + } + + SENSOR_FunctionExit(status); + return status; +} + +// Called by Sensor CLX to handle IOCTLs that clx does not support. +NTSTATUS ActivityDevice::OnIoControl( + _In_ SENSOROBJECT /*sensorInstance*/, // WDF queue object + _In_ WDFREQUEST request, // WDF request object + _In_ size_t /*outputBufferLength*/, // number of bytes to retrieve from output buffer + _In_ size_t /*inputBufferLength*/, // number of bytes to retrieve from input buffer + _In_ ULONG /*ioControlCode*/) // IOCTL control code +{ + SENSOR_FunctionEnter(); + + WdfRequestComplete(request, STATUS_NOT_SUPPORTED); + + SENSOR_FunctionExit(STATUS_NOT_SUPPORTED); + return STATUS_NOT_SUPPORTED; +} |
