Sample Code
Windows Driver Samples/ SpbAccelerometer Sample Driver (UMDF Version 1)/ C++/ AccelerometerDevice.cpp/
/*++ // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A // PARTICULAR PURPOSE. Copyright (c) Microsoft Corporation. All rights reserved Module Name: AccelerometerDevice.cpp Abstract: This module contains the implementation of the SPB accelerometer's accelerometer device class. --*/ #include "Internal.h" #include "Adxl345.h" #include "Device.h" #include <float.h> #include "strsafe.h" #include "AccelerometerDevice.h" #include "AccelerometerDevice.tmh" // Array of settings that describe the initial // device configuration. const REGISTER_SETTING g_ConfigurationSettings[] = { // Standby mode {ADXL345_POWER_CTL, ADXL345_POWER_CTL_STANDBY}, // +-16g, 13-bit resolution {ADXL345_DATA_FORMAT, ADXL345_DATA_FORMAT_FULL_RES | ADXL345_DATA_FORMAT_JUSTIFY_RIGHT | ADXL345_DATA_FORMAT_RANGE_16G}, // No FIFO {ADXL345_FIFO_CTL, ADXL345_FIFO_CTL_MODE_BYPASS}, // Data rate set to default {ADXL345_BW_RATE, _GetDataRateFromReportInterval(DEFAULT_ACCELEROMETER_CURRENT_REPORT_INTERVAL).RateCode}, // Activity threshold set to // default change sensitivity {ADXL345_THRESH_ACT, (BYTE)(DEFAULT_ACCELEROMETER_CHANGE_SENSITIVITY / ACCELEROMETER_CHANGE_SENSITIVITY_RESOLUTION)}, // Activity detection enabled, AC coupled {ADXL345_ACT_INACT_CTL, ADXL345_ACT_INACT_CTL_ACT_ACDC | ADXL345_ACT_INACT_CTL_ACT_X | ADXL345_ACT_INACT_CTL_ACT_Y | ADXL345_ACT_INACT_CTL_ACT_Z}, // Activity interrupt mapped to pin 1 {ADXL345_INT_MAP, ADXL345_INT_ACTIVITY}, }; ///////////////////////////////////////////////////////////////////////// // // CAccelerometerDevice::CAccelerometerDevice() // // Constructor // ///////////////////////////////////////////////////////////////////////// CAccelerometerDevice::CAccelerometerDevice() : m_pSensorDeviceCallback(nullptr), m_spRequest(nullptr), m_pSpbRequest(nullptr), m_pDataBuffer(nullptr), m_fInitialized(FALSE), m_InterruptsEnabled(0), m_TestRegister(0), m_TestDataSize(0) { } ///////////////////////////////////////////////////////////////////////// // // CAccelerometerDevice::~CAccelerometerDevice() // // Destructor // ///////////////////////////////////////////////////////////////////////// CAccelerometerDevice::~CAccelerometerDevice() { // Stop the device from measuring data // if it isn't already SetDeviceStateStandby(); SAFE_RELEASE(m_pSpbRequest); if (m_pDataBuffer != nullptr) { delete[] m_pDataBuffer; m_pDataBuffer = nullptr; } } ///////////////////////////////////////////////////////////////////////// // // CAccelerometerDevice::Initialize // // This method is used to initialize the accelerometer device object // and its child objects. // // Parameters: // pDevice - pointer to a device object // pWdfResourcesRaw - pointer the raw resource list // pWdfResourcesTranslated - pointer to the translated resource list // pSensorDeviceCallback - pointer to the sensor device callback // interface // // Return Values: // status // ///////////////////////////////////////////////////////////////////////// HRESULT CAccelerometerDevice::Initialize( _In_ IWDFDevice* pWdfDevice, _In_ IWDFCmResourceList* pWdfResourcesRaw, _In_ IWDFCmResourceList* pWdfResourcesTranslated, _In_ ISensorDeviceCallback* pSensorDeviceCallback ) { FuncEntry(); HRESULT hr = S_OK; LARGE_INTEGER requestId; requestId.QuadPart = 0; if (m_fInitialized == FALSE) { if ((pWdfDevice == nullptr) || (pSensorDeviceCallback == nullptr)) { hr = E_INVALIDARG; } else { // Save weak reference to callback m_pSensorDeviceCallback = pSensorDeviceCallback; } if (SUCCEEDED(hr)) { // Get the device configuration settings // from ACPI. hr = GetConfigurationData(pWdfDevice); } if (SUCCEEDED(hr)) { // Parse the driver's resources to get the // resource hub connection IDs. hr = ParseResources( pWdfDevice, pWdfResourcesRaw, pWdfResourcesTranslated, &requestId); } if (SUCCEEDED(hr)) { // Create and initialize the request object hr = InitializeRequest(pWdfDevice, requestId); } if (SUCCEEDED(hr)) { // Mark the sensor device as initialized m_fInitialized = TRUE; } } FuncExit(); return hr; } ///////////////////////////////////////////////////////////////////////// // // CAccelerometerDevice::GetConfigurationData // // This method is used to retrieve configuration data from ACPI. // // Parameters: // pWdfDevice - pointer to the device object // // Return Values: // status // ///////////////////////////////////////////////////////////////////////// HRESULT CAccelerometerDevice::GetConfigurationData( _In_ IWDFDevice* pWdfDevice ) { FuncEntry(); CComPtr<IWDFIoRequest> spRequest = nullptr; CComPtr<IWDFMemory> spInputMemory = nullptr; CComPtr<IWDFMemory> spOutputMemory = nullptr; CComPtr<IWDFDriver> spDriver; CComPtr<IWDFIoTarget> spLocalTarget; CComPtr<IWDFDriverCreatedFile> spFile = nullptr; CComPtr<IWDFRequestCompletionParams> spCompletionParams; PACPI_EVAL_INPUT_BUFFER_COMPLEX pInputBuffer; PACPI_EVAL_OUTPUT_BUFFER pOutputBuffer; ULONG inputBufferSize = 0; ULONG outputBufferSize = 0; HRESULT hr = S_OK; // // Allocate memory for the ACPI input and // output buffers. // pWdfDevice->GetDriver(&spDriver); if (spDriver == nullptr) { Trace( TRACE_LEVEL_ERROR, "Failed to get IWDFDriver for IWDFDevice %p - %!HRESULT!", pWdfDevice, hr); goto exit; } inputBufferSize = sizeof(ACPI_EVAL_INPUT_BUFFER_COMPLEX) + (sizeof(ACPI_METHOD_ARGUMENT) * (ACPI_DSM_ARUGMENTS_COUNT - 1)) + sizeof(GUID); hr = spDriver->CreateWdfMemory( inputBufferSize, nullptr, pWdfDevice, &spInputMemory); if (FAILED(hr)) { Trace( TRACE_LEVEL_ERROR, "Failed to create input memory for IWDFDriver %p - %!HRESULT!", spDriver, hr); goto exit; } outputBufferSize = sizeof(ACPI_EVAL_OUTPUT_BUFFER) + sizeof(SPB_ACCELEROMETER_CONFIG); hr = spDriver->CreateWdfMemory( outputBufferSize, nullptr, pWdfDevice, &spOutputMemory); if (FAILED(hr)) { Trace( TRACE_LEVEL_ERROR, "Failed to create output memory for IWDFDriver %p - %!HRESULT!", spDriver, hr); goto exit; } // // Build the ACPI input buffer to invoke // the config function. // pInputBuffer = (PACPI_EVAL_INPUT_BUFFER_COMPLEX)spInputMemory->GetDataBuffer(nullptr); PrepareInputParametersForDsm( pInputBuffer, inputBufferSize, ACPI_DSM_CONFIG_FUNCTION); // // Format and send the request. // pWdfDevice->GetDefaultIoTarget(&spLocalTarget); if (spLocalTarget == nullptr) { Trace( TRACE_LEVEL_ERROR, "Failed to get default IO target for IWDFDevice %p - %!HRESULT!", pWdfDevice, hr); goto exit; } hr = pWdfDevice->CreateRequest(nullptr, pWdfDevice, &spRequest); if (FAILED(hr)) { Trace( TRACE_LEVEL_ERROR, "Failed to create request for IWDFDevice %p - %!HRESULT!", pWdfDevice, hr); goto exit; } hr = pWdfDevice->CreateWdfFile(nullptr, &spFile); if (FAILED(hr)) { Trace( TRACE_LEVEL_ERROR, "Failed to create WDF file for IWDFDevice %p - %!HRESULT!", pWdfDevice, hr); goto exit; } hr = spLocalTarget->FormatRequestForIoctl( spRequest, IOCTL_ACPI_EVAL_METHOD, spFile, spInputMemory, nullptr, spOutputMemory, nullptr); if (FAILED(hr)) { Trace( TRACE_LEVEL_ERROR, "Failed to format target %p for request %p - %!HRESULT!", spLocalTarget, spRequest, hr); goto exit; } hr = spRequest->Send( spLocalTarget, (WDF_REQUEST_SEND_OPTION_SYNCHRONOUS | WDF_REQUEST_SEND_OPTION_TIMEOUT), ACPI_DSM_REQUEST_TIMEOUT); if (FAILED(hr)) { Trace( TRACE_LEVEL_ERROR, "Failed to send request %p - %!HRESULT!", spRequest, hr); goto exit; } // // Analyze the ACPI output buffer. // spRequest->GetCompletionParams(&spCompletionParams); hr = spCompletionParams->GetCompletionStatus(); if (FAILED(hr)) { Trace( TRACE_LEVEL_ERROR, "IOCTL_ACPI_EVAL_METHOD completed with failure - %!HRESULT!", hr); goto exit; } if (spCompletionParams->GetInformation() < sizeof(ACPI_EVAL_OUTPUT_BUFFER)) { hr = HRESULT_FROM_WIN32(ERROR_BAD_LENGTH); Trace( TRACE_LEVEL_ERROR, "IOCTL_ACPI_EVAL_METHOD completed with %d bytes, " "expected minimum of %d bytes - %!HRESULT!", (ULONG)spCompletionParams->GetInformation(), sizeof(ACPI_EVAL_OUTPUT_BUFFER), hr); goto exit; } pOutputBuffer = (PACPI_EVAL_OUTPUT_BUFFER)spOutputMemory->GetDataBuffer(nullptr); hr = ParseAcpiOutputBuffer(pOutputBuffer); if (FAILED(hr)) { goto exit; } exit: // // Cleanup // if (spFile != nullptr) { spFile->Close(); } if (spInputMemory != nullptr) { spInputMemory->DeleteWdfObject(); } if (spOutputMemory != nullptr) { spOutputMemory->DeleteWdfObject(); } if (spRequest != nullptr) { spRequest->DeleteWdfObject(); } FuncExit(); return hr; } ///////////////////////////////////////////////////////////////////////// // // CAccelerometerDevice::PrepareInputParametersForDsm // // This method is used to prepare the input buffer to invoke the DSM // function. // // Parameters: // pInputBuffer - the ACPI parameter input buffer// // InputBufferSize - the input buffer size // FunctionIndex - the DSM funciton index // // Return Values: // None // ///////////////////////////////////////////////////////////////////////// VOID CAccelerometerDevice::PrepareInputParametersForDsm( _Inout_ PACPI_EVAL_INPUT_BUFFER_COMPLEX pInputBuffer, _In_ ULONG InputBufferSize, _In_ ULONG FunctionIndex ) { FuncEntry(); PACPI_METHOD_ARGUMENT pArg; pInputBuffer->MethodNameAsUlong = (ULONG)('MSD_'); pInputBuffer->Signature = ACPI_EVAL_INPUT_BUFFER_COMPLEX_SIGNATURE; pInputBuffer->ArgumentCount = 4; pInputBuffer->Size = InputBufferSize; // // Argument 0: UUID. // pArg = &pInputBuffer->Argument[0]; ACPI_METHOD_SET_ARGUMENT_BUFFER(pArg, &ACPI_DSM_GUID, sizeof(GUID)); // // Argument 1: Revision. // pArg = ACPI_METHOD_NEXT_ARGUMENT(pArg); ACPI_METHOD_SET_ARGUMENT_INTEGER(pArg, ACPI_DSM_REVISION); // // Argument 2: Function index. // pArg = ACPI_METHOD_NEXT_ARGUMENT(pArg); ACPI_METHOD_SET_ARGUMENT_INTEGER(pArg, FunctionIndex); // // Argument 3: Empty package for _DSM definition. // pArg = ACPI_METHOD_NEXT_ARGUMENT(pArg); pArg->Type = ACPI_METHOD_ARGUMENT_PACKAGE; pArg->DataLength = sizeof(ULONG); pArg->Argument = 0; FuncExit(); } ///////////////////////////////////////////////////////////////////////// // // CAccelerometerDevice::ParseAcpiOutputBuffer // // This method is used to parse configurtion data from the ACPI output // buffer. // // Parameters: // pOutputBuffer - pointer to the ACPI output buffer to parse // // Return Values: // status // ///////////////////////////////////////////////////////////////////////// HRESULT CAccelerometerDevice::ParseAcpiOutputBuffer( _In_ PACPI_EVAL_OUTPUT_BUFFER pOutputBuffer ) { ACPI_METHOD_ARGUMENT arg; PSPB_ACCELEROMETER_CONFIG pConfig; HRESULT hr = S_OK; if (pOutputBuffer->Signature != ACPI_EVAL_OUTPUT_BUFFER_SIGNATURE) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_NAME); Trace( TRACE_LEVEL_ERROR, "Invalid ACPI signature - %!HRESULT!", hr); goto exit; } if (pOutputBuffer->Count != ACPI_DSM_CONFIG_COUNT) { hr = HRESULT_FROM_WIN32(ERROR_BAD_LENGTH); Trace( TRACE_LEVEL_ERROR, "Invalid ACPI argument count %d, expected %d - %!HRESULT!", pOutputBuffer->Count, ACPI_DSM_CONFIG_COUNT, hr); goto exit; } arg = pOutputBuffer->Argument[0]; if (arg.Type != ACPI_METHOD_ARGUMENT_BUFFER) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); Trace( TRACE_LEVEL_ERROR, "Invalid ACPI argument type %d, expected %d - %!HRESULT!", arg.Type, ACPI_METHOD_ARGUMENT_BUFFER, hr); goto exit; } if (arg.DataLength != sizeof(SPB_ACCELEROMETER_CONFIG)) { hr = HRESULT_FROM_WIN32(ERROR_BAD_LENGTH); Trace( TRACE_LEVEL_ERROR, "Invalid ACPI argument data length %d, expected %d - %!HRESULT!", arg.DataLength, sizeof(SPB_ACCELEROMETER_CONFIG), hr); goto exit; } pConfig = (PSPB_ACCELEROMETER_CONFIG)&arg.Data; // // For the purposes of this sample just trace out the // four byte configuration data. // Trace( TRACE_LEVEL_INFORMATION, "Configuration retrieved from ACPI: {0x%.2x, 0x%.2x, 0x%.2x, 0x%.2x}", pConfig->ConfigParam1, pConfig->ConfigParam2, pConfig->ConfigParam3, pConfig->ConfigParam4); exit: FuncExit(); return hr; } ///////////////////////////////////////////////////////////////////////// // // CAccelerometerDevice::ParseResources // // This method is used to parse the device's resources and save the // the important ones. // // Parameters: // pWdfDevice - pointer to the device object // pWdfResourcesRaw - pointer the raw resource list // pWdfResourcesTranslated - pointer to the translated resource list // pRequestId - pointer to the obtained request's resource hub // connection id // // Return Values: // status // ///////////////////////////////////////////////////////////////////////// HRESULT CAccelerometerDevice::ParseResources( _In_ IWDFDevice* pWdfDevice, _In_ IWDFCmResourceList* pWdfResourcesRaw, _In_ IWDFCmResourceList* pWdfResourcesTranslated, _Out_ LARGE_INTEGER* pRequestId ) { FuncEntry(); HRESULT hr = S_OK; PCM_PARTIAL_RESOURCE_DESCRIPTOR pDescriptor; PCM_PARTIAL_RESOURCE_DESCRIPTOR pDescriptorRaw; ULONG resourceCount; BOOLEAN fRequestFound = FALSE; BOOLEAN fInterruptFound = FALSE; UCHAR connectionClass; UCHAR connectionType; UNREFERENCED_PARAMETER(pWdfResourcesRaw); if (pWdfResourcesTranslated == nullptr) { hr = E_INVALIDARG; } if (SUCCEEDED(hr)) { resourceCount = pWdfResourcesTranslated->GetCount(); // Loop through the resources and save the relevant ones for (ULONG i = 0; i < resourceCount; i++) { pDescriptor = pWdfResourcesTranslated->GetDescriptor(i); pDescriptorRaw = pWdfResourcesRaw->GetDescriptor(i); if ((pDescriptor == nullptr) || (pDescriptorRaw == nullptr)) { hr = E_POINTER; break; } switch (pDescriptor->Type) { case CmResourceTypeConnection: // Check against the expected connection types connectionClass = pDescriptor->u.Connection.Class; connectionType = pDescriptor->u.Connection.Type; if ((connectionClass == CM_RESOURCE_CONNECTION_CLASS_SERIAL) && (connectionType == CM_RESOURCE_CONNECTION_TYPE_SERIAL_I2C)) { if (fRequestFound == FALSE) { // Save the request id pRequestId->LowPart = pDescriptor->u.Connection.IdLowPart; pRequestId->HighPart = pDescriptor->u.Connection.IdHighPart; fRequestFound = TRUE; } else { hr = HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER); Trace( TRACE_LEVEL_ERROR, "Duplicate resource found - %!HRESULT!", hr); } } break; case CmResourceTypeInterrupt: if (fInterruptFound == FALSE) { hr = ConnectInterrupt( pWdfDevice, pDescriptorRaw, pDescriptor); if (SUCCEEDED(hr)) { fInterruptFound = TRUE; } } else { Trace( TRACE_LEVEL_WARNING, "Duplicate interrupt resource found, ignoring"); } break; default: // Ignore all other descriptors break; } if (FAILED(hr)) { break; } } if (SUCCEEDED(hr) && ((fRequestFound == FALSE) || (fInterruptFound == FALSE))) { hr = HRESULT_FROM_WIN32(ERROR_RESOURCE_NOT_PRESENT); Trace( TRACE_LEVEL_ERROR, "Failed to find required resource - %!HRESULT!", hr); } } FuncExit(); return hr; } ///////////////////////////////////////////////////////////////////////// // // CAccelerometerDevice::InitializeRequest // // This method is used to initialize the request object. // // Parameters: // pWdfDevice - pointer to the device object // id - the resource hub connection id // // Return Values: // status // ///////////////////////////////////////////////////////////////////////// HRESULT CAccelerometerDevice::InitializeRequest( _In_ IWDFDevice* pWdfDevice, _In_ LARGE_INTEGER id ) { FuncEntry(); HRESULT hr = S_OK; if (pWdfDevice == nullptr) { hr = E_INVALIDARG; } if (SUCCEEDED(hr)) { // Create the request object hr = CComObject<CSpbRequest>::CreateInstance( &m_pSpbRequest); if (SUCCEEDED(hr)) { m_pSpbRequest->AddRef(); hr = m_pSpbRequest->QueryInterface( IID_PPV_ARGS(&m_spRequest)); } // TODO: CoCreateInstance rather than calling // CreateInstance on the class and querying // the required interface. //// Create the request object //hr = CoCreateInstance( // __uuidof(SpbRequest), // CLSID_SpbRequest // nullptr, // CLSCTX_INPROC_SERVER, // IID_PPV_ARGS(&m_spRequest)); if (FAILED(hr)) { Trace( TRACE_LEVEL_ERROR, "Failed to create the request object, %!HRESULT!", hr); } if (SUCCEEDED(hr)) { WCHAR DevicePathBuffer[RESOURCE_HUB_PATH_CHARS] = {0}; UNICODE_STRING DevicePath; DevicePath.Buffer = DevicePathBuffer; DevicePath.Length = 0; DevicePath.MaximumLength = RESOURCE_HUB_PATH_SIZE; // Create the device path using the well known // resource hub path and the connection ID hr = HRESULT_FROM_NT( RESOURCE_HUB_CREATE_PATH_FROM_ID( &DevicePath, id.LowPart, id.HighPart)); // Initialize the request object if (SUCCEEDED(hr)) { hr = m_spRequest->Initialize( pWdfDevice, DevicePathBuffer); } } if (SUCCEEDED(hr)) { // Create the data buffer m_pDataBuffer = new BYTE[ADXL345_DATA_REPORT_SIZE_BYTES]; if (m_pDataBuffer == nullptr) { hr = E_OUTOFMEMORY; } } } FuncExit(); return hr; } ///////////////////////////////////////////////////////////////////////// // // CAccelerometerDevice::ConnectInterrupt // // This method is used to connect the data notification interrupt. // // Parameters: // pWdfDevice - pointer to the device object // RawResource - raw resource decriptor // TranslatedResource - translated resource descriptor // // Return Values: // status // ///////////////////////////////////////////////////////////////////////// HRESULT CAccelerometerDevice::ConnectInterrupt( _In_ IWDFDevice* pWdfDevice, _In_opt_ PCM_PARTIAL_RESOURCE_DESCRIPTOR RawResource, _In_opt_ PCM_PARTIAL_RESOURCE_DESCRIPTOR TranslatedResource ) { FuncEntry(); CComPtr<IWDFDevice3> pIWDFDevice3; CComPtr<IWDFInterrupt> spInterrupt; HRESULT hr = S_OK; if (pWdfDevice == nullptr) { hr = E_INVALIDARG; } if (SUCCEEDED(hr)) { hr = pWdfDevice->QueryInterface(IID_PPV_ARGS(&pIWDFDevice3)); } if (SUCCEEDED(hr)) { // Create interrupt WUDF_INTERRUPT_CONFIG config; WUDF_INTERRUPT_CONFIG_INIT( &config, CAccelerometerDevice::OnInterruptIsr, CAccelerometerDevice::OnInterruptWorkItem); config.InterruptRaw = RawResource; config.InterruptTranslated = TranslatedResource; hr = pIWDFDevice3->CreateInterrupt(&config, &spInterrupt); if (FAILED(hr)) { Trace( TRACE_LEVEL_ERROR, "Failed to create interrupt object, %!HRESULT!", hr); } } if (SUCCEEDED(hr)) { hr = spInterrupt->AssignContext(nullptr, (void*)this); if (FAILED(hr)) { Trace( TRACE_LEVEL_ERROR, "Failed to assign interrupt context, %!HRESULT!", hr); } } FuncExit(); return hr; } ///////////////////////////////////////////////////////////////////////// // // CAccelerometerDevice::ConfigureHardware // // This method is used to place device in standy mode and configure it // for operation. // // Parameters: // // Return Values: // status // ///////////////////////////////////////////////////////////////////////// HRESULT CAccelerometerDevice::ConfigureHardware() { FuncEntry(); HRESULT hr = (m_fInitialized == TRUE) ? S_OK : E_UNEXPECTED; if (SUCCEEDED(hr)) { // Allocate the data buffers BYTE* pWriteBuffer = new BYTE[1]; BYTE* pReadBuffer = new BYTE[1]; if ((pWriteBuffer == nullptr) || (pReadBuffer == nullptr)) { hr = E_OUTOFMEMORY; } { // Synchronize access to device CComCritSecLock<CComAutoCriticalSection> scopeLock(m_CriticalSection); // Loop through configuration values and set registers for (DWORD i = 0; i < ARRAY_SIZE(g_ConfigurationSettings); i++) { if (SUCCEEDED(hr)) { REGISTER_SETTING setting = g_ConfigurationSettings[i]; // Write the configuration value to the register pWriteBuffer[0] = setting.Value; hr = WriteRegister(setting.Register, pWriteBuffer, 1); if (SUCCEEDED(hr)) { // Confirm register value hr = ReadRegister( setting.Register, pReadBuffer, 1, 0); if (SUCCEEDED(hr) && (pReadBuffer[0] != pWriteBuffer[0])) { // The register value is incorrect. Record the error // and break out of the loop. Trace( TRACE_LEVEL_ERROR, "Unexpected value at register 0x%02x: " "expected 0x%02x, got 0x%02x", setting.Register, pWriteBuffer[0], pReadBuffer[0]); hr = E_UNEXPECTED; break; } } } } } if (SUCCEEDED(hr)) { Trace( TRACE_LEVEL_INFORMATION, "Accelerometer device configured"); } // Delete the buffer allocations if (pWriteBuffer != nullptr) { delete[] pWriteBuffer; } if (pReadBuffer != nullptr) { delete[] pReadBuffer; } } FuncExit(); return hr; } ///////////////////////////////////////////////////////////////////////// // // CAccelerometerDevice::SetDataUpdateMode // // This method is used to set the data update mode. // // Parameters: // Mode - new data update mode // // Return Values: // status // ///////////////////////////////////////////////////////////////////////// HRESULT CAccelerometerDevice::SetDataUpdateMode( _In_ DATA_UPDATE_MODE Mode ) { FuncEntry(); HRESULT hr = (m_fInitialized == TRUE) ? S_OK : E_UNEXPECTED; if (SUCCEEDED(hr)) { switch (Mode) { case DataUpdateModeOff: hr = SetDeviceStateStandby(); break; case DataUpdateModePolling: hr = SetDeviceStatePolling(); break; case DataUpdateModeEventing: hr = SetDeviceStateEventing(); break; default: hr = E_INVALIDARG; break; } } FuncExit(); return hr; } ///////////////////////////////////////////////////////////////////////// // // CAccelerometerDevice::SetDeviceStateStandby // // This method is used to place the device in standby mode. // // Parameters: // // Return Values: // status // ///////////////////////////////////////////////////////////////////////// HRESULT CAccelerometerDevice::SetDeviceStateStandby() { FuncEntry(); HRESULT hr = S_OK; // Allocate the data buffer BYTE* pBuffer = new BYTE[1]; if (pBuffer == nullptr) { hr = E_OUTOFMEMORY; } if (SUCCEEDED(hr)) { // Synchronize access to device CComCritSecLock<CComAutoCriticalSection> scopeLock(m_CriticalSection); // Disable interrupts pBuffer[0] = 0; hr = WriteRegister(ADXL345_INT_ENABLE, pBuffer, 1); if (FAILED(hr)) { Trace( TRACE_LEVEL_ERROR, "Failed to disable interrupts, %!HRESULT!", hr); } else { m_InterruptsEnabled = pBuffer[0]; } // Clear any stale interrupts if (SUCCEEDED(hr)) { hr = ReadRegister( ADXL345_INT_SOURCE, pBuffer, 1, 0); if (FAILED(hr)) { Trace( TRACE_LEVEL_ERROR, "Failed to read interrupt source register, %!HRESULT!", hr); } } // Place device in standy mode { pBuffer[0] = ADXL345_POWER_CTL_STANDBY; hr = WriteRegister(ADXL345_POWER_CTL, pBuffer, 1); if (FAILED(hr)) { Trace( TRACE_LEVEL_ERROR, "Failed to place device in standby mode, %!HRESULT!", hr); } } } if (SUCCEEDED(hr)) { Trace( TRACE_LEVEL_INFORMATION, "Device in standby mode"); } else { Trace( TRACE_LEVEL_WARNING, "Unexpected failure while stopping accelerometer device, " "%!HRESULT!", hr); } // Delete the buffer allocation if (pBuffer != nullptr) { delete[] pBuffer; } FuncExit(); return hr; } ///////////////////////////////////////////////////////////////////////// // // CAccelerometerDevice::SetDeviceStatePolling // // This method is used to place the device in measurement mode without // eventing. // // Parameters: // // Return Values: // status // ///////////////////////////////////////////////////////////////////////// HRESULT CAccelerometerDevice::SetDeviceStatePolling() { FuncEntry(); HRESULT hr = S_OK; // Allocate the data buffer BYTE* pBuffer = new BYTE[1]; if (pBuffer == nullptr) { hr = E_OUTOFMEMORY; } if (SUCCEEDED(hr)) { // Synchronize access to device CComCritSecLock<CComAutoCriticalSection> scopeLock(m_CriticalSection); // Disable interrupts pBuffer[0] = 0; hr = WriteRegister(ADXL345_INT_ENABLE, pBuffer, 1); if (FAILED(hr)) { Trace( TRACE_LEVEL_ERROR, "Failed to disable interrupts, %!HRESULT!", hr); } else { m_InterruptsEnabled = pBuffer[0]; } // Place device in measurement mode if (SUCCEEDED(hr)) { pBuffer[0] = ADXL345_POWER_CTL_MEASURE; hr = WriteRegister(ADXL345_POWER_CTL, pBuffer, 1); if (FAILED(hr)) { Trace( TRACE_LEVEL_ERROR, "Failed to enable measurement mode, %!HRESULT!", hr); } } } if (SUCCEEDED(hr)) { Trace( TRACE_LEVEL_INFORMATION, "Device in measurement mode (polling)"); } // Delete the buffer allocation if (pBuffer != nullptr) { delete[] pBuffer; } FuncExit(); return hr; } ///////////////////////////////////////////////////////////////////////// // // CAccelerometerDevice::SetDeviceStateEventing // // This method is used to place the device in measurement mode with // eventing. // // Parameters: // // Return Values: // status // ///////////////////////////////////////////////////////////////////////// HRESULT CAccelerometerDevice::SetDeviceStateEventing() { FuncEntry(); HRESULT hr = S_OK; // Allocate the data buffer BYTE* pBuffer = new BYTE[1]; if (pBuffer == nullptr) { hr = E_OUTOFMEMORY; } if (SUCCEEDED(hr)) { // Synchronize access to device CComCritSecLock<CComAutoCriticalSection> scopeLock(m_CriticalSection); pBuffer[0] = ADXL345_POWER_CTL_MEASURE; hr = WriteRegister(ADXL345_POWER_CTL, pBuffer, 1); if (FAILED(hr)) { Trace( TRACE_LEVEL_ERROR, "Failed to enable measurement mode, %!HRESULT!", hr); } // Enable activity detection interrupt if (SUCCEEDED(hr)) { pBuffer[0] = ADXL345_INT_ACTIVITY; hr = WriteRegister(ADXL345_INT_ENABLE, pBuffer, 1); if (FAILED(hr)) { Trace( TRACE_LEVEL_ERROR, "Failed to enable activity interrupt, %!HRESULT!", hr); } else { m_InterruptsEnabled = pBuffer[0]; } } } if (SUCCEEDED(hr)) { Trace( TRACE_LEVEL_INFORMATION, "Device in measurement mode (eventing)"); } // Delete the buffer allocation if (pBuffer != nullptr) { delete[] pBuffer; } FuncExit(); return hr; } ///////////////////////////////////////////////////////////////////////// // // CAccelerometerDevice::SetReportInterval // // This method is used to set the report interval of the device. // // Parameters: // ReportInterval - desired report interval // // Return Values: // status // ///////////////////////////////////////////////////////////////////////// HRESULT CAccelerometerDevice::SetReportInterval( _In_ ULONG ReportInterval ) { FuncEntry(); HRESULT hr = (m_fInitialized == TRUE) ? S_OK : E_UNEXPECTED; if (SUCCEEDED(hr)) { DATA_RATE newDataRate; // The accelerometer only supports a subset of data rates. // Pick the rate that is just less than the desired report // interval. newDataRate = _GetDataRateFromReportInterval(ReportInterval); // Allocate the data buffer BYTE* pWriteBuffer = new BYTE[1]; if (pWriteBuffer == nullptr) { hr = E_OUTOFMEMORY; } if (SUCCEEDED(hr)) { // Synchronize access to device CComCritSecLock<CComAutoCriticalSection> scopeLock(m_CriticalSection); // Disable interrupts while data rate is modified pWriteBuffer[0] = 0; hr = WriteRegister(ADXL345_INT_ENABLE, pWriteBuffer, 1); if (FAILED(hr)) { Trace( TRACE_LEVEL_ERROR, "Failed to disable interrupts, %!HRESULT!", hr); } if (SUCCEEDED(hr)) { // Update the data rate. pWriteBuffer[0] = newDataRate.RateCode; hr = WriteRegister(ADXL345_BW_RATE, pWriteBuffer, 1); if (FAILED(hr)) { Trace( TRACE_LEVEL_ERROR, "Failed to update data rate, %!HRESULT!", hr); } else { Trace( TRACE_LEVEL_INFORMATION, "Data rate interval set to %d ms", newDataRate.DataRateInterval); } } if (SUCCEEDED(hr)) { // Reenable interrupts pWriteBuffer[0] = ADXL345_INT_ACTIVITY; hr = WriteRegister(ADXL345_INT_ENABLE, pWriteBuffer, 1); if (FAILED(hr)) { Trace( TRACE_LEVEL_ERROR, "Failed to enable activity interrupt, %!HRESULT!", hr); } } } // Delete the buffer allocation if (pWriteBuffer != nullptr) { delete[] pWriteBuffer; } } FuncExit(); return hr; } ///////////////////////////////////////////////////////////////////////// // // CAccelerometerDevice::SetChangeSensitivity // // This method is used to set the change sensitivity of the device. // // Parameters: // pVar - value containing change sensitivities // // Return Values: // status // ///////////////////////////////////////////////////////////////////////// HRESULT CAccelerometerDevice::SetChangeSensitivity( _In_ PROPVARIANT* pVar ) { FuncEntry(); HRESULT hr = (m_fInitialized == TRUE) ? S_OK : E_UNEXPECTED; BYTE newThreshold = 0x00; if (SUCCEEDED(hr)) { if (pVar == nullptr) { hr = E_INVALIDARG; } } if (SUCCEEDED(hr)) { // Change sensitivity is a per data field // property and is stored as an IPortableDeviceValues if (pVar->vt != VT_UNKNOWN) { hr = E_INVALIDARG; } if (SUCCEEDED(hr)) { CComPtr<IPortableDeviceValues> spPerDataFieldValues; spPerDataFieldValues = static_cast<IPortableDeviceValues*>(pVar->punkVal); DWORD count = 0; if (SUCCEEDED(hr)) { // Get the count of change sensitivity values. hr = spPerDataFieldValues->GetCount(&count); } if (SUCCEEDED(hr)) { // The accelerometer only supports a single value, so // pick the smallest, i.e. most sensitive. DOUBLE minChangeSensitivity = DBL_MAX; for (DWORD i = 0; i < count; i++) { PROPERTYKEY key; PROPVARIANT var; PropVariantInit(&var); hr = spPerDataFieldValues->GetAt(i, &key, &var); if (SUCCEEDED(hr)) { if ((var.vt == VT_R8) && (var.dblVal < minChangeSensitivity)) { minChangeSensitivity = var.dblVal; } } PropVariantClear(&var); } if (SUCCEEDED(hr)) { // The threshold can only be set in increments, so // round down to a more sensitive setting newThreshold = (BYTE)(minChangeSensitivity / ACCELEROMETER_CHANGE_SENSITIVITY_RESOLUTION); } } } } if (SUCCEEDED(hr)) { // Allocate the data buffer BYTE* pWriteBuffer = new BYTE[1]; if (pWriteBuffer == nullptr) { hr = E_OUTOFMEMORY; } if (SUCCEEDED(hr)) { // Synchronize access to device CComCritSecLock<CComAutoCriticalSection> scopeLock(m_CriticalSection); // Disable interrupts while activity // threshold is modified pWriteBuffer[0] = 0; hr = WriteRegister(ADXL345_INT_ENABLE, pWriteBuffer, 1); if (FAILED(hr)) { Trace( TRACE_LEVEL_ERROR, "Failed to disable interrupts, %!HRESULT!", hr); } if (SUCCEEDED(hr)) { // Update the activity detection threshold. pWriteBuffer[0] = newThreshold; hr = WriteRegister(ADXL345_THRESH_ACT, pWriteBuffer, 1); if (FAILED(hr)) { Trace( TRACE_LEVEL_ERROR, "Failed to update activity threshold, %!HRESULT!", hr); } else { Trace( TRACE_LEVEL_INFORMATION, "Activity threshold set to 0x%02x", newThreshold); } } if (SUCCEEDED(hr)) { // Reenable interrupts pWriteBuffer[0] = ADXL345_INT_ACTIVITY; hr = WriteRegister(ADXL345_INT_ENABLE, pWriteBuffer, 1); if (FAILED(hr)) { Trace( TRACE_LEVEL_ERROR, "Failed to enable activity interrupt, %!HRESULT!", hr); } } } // Delete the buffer allocation if (pWriteBuffer != nullptr) { delete[] pWriteBuffer; } } FuncExit(); return hr; } ///////////////////////////////////////////////////////////////////////// // // CAccelerometerDevice::RequestNewData // // This method is used to synchronously request new data from the device. // // Parameters: // ppValues - an IPortableDeviceValues pointer that receives new data // // Return Values: // status // ///////////////////////////////////////////////////////////////////////// HRESULT CAccelerometerDevice::RequestNewData( _In_ IPortableDeviceValues* pValues ) { FuncEntry(); HRESULT hr = (m_fInitialized == TRUE) ? S_OK : E_UNEXPECTED; if (SUCCEEDED(hr) && (pValues == nullptr)) { hr = E_INVALIDARG; } if (SUCCEEDED(hr)) { hr = RequestData(pValues); if (FAILED(hr)) { Trace( TRACE_LEVEL_ERROR, "Failed to request data synchronously, " "%!HRESULT!", hr); } } FuncExit(); return hr; } ///////////////////////////////////////////////////////////////////////// // // CAccelerometerDevice::GetTestProperty // // This method is used to get one of the device's test properties. // // Parameters: // key - the test property key // pVar - location for the value of the test property key // // Return Values: // status // ///////////////////////////////////////////////////////////////////////// HRESULT CAccelerometerDevice::GetTestProperty( _In_ REFPROPERTYKEY key, _Out_ PROPVARIANT* pVar) { FuncEntry(); HRESULT hr = (m_fInitialized == TRUE) ? S_OK : E_UNEXPECTED; if (SUCCEEDED(hr) && (pVar == nullptr)) { hr = E_INVALIDARG; } if (SUCCEEDED(hr)) { if(IsEqualPropertyKey(key, SENSOR_PROPERTY_TEST_REGISTER)) { InitPropVariantFromUInt32(m_TestRegister, pVar); } else if(IsEqualPropertyKey(key, SENSOR_PROPERTY_TEST_DATA_SIZE)) { InitPropVariantFromUInt32(m_TestDataSize, pVar); } else if(IsEqualPropertyKey(key, SENSOR_PROPERTY_TEST_DATA)) { BYTE* pDataBuffer = (BYTE*)CoTaskMemAlloc(m_TestDataSize); if (pDataBuffer == nullptr) { hr = E_OUTOFMEMORY; } if (SUCCEEDED(hr)) { // Synchronize access to device CComCritSecLock<CComAutoCriticalSection> scopeLock(m_CriticalSection); hr = ReadRegister( (BYTE)m_TestRegister, pDataBuffer, m_TestDataSize, 0); if (SUCCEEDED(hr)) { pVar->vt = (VT_VECTOR | VT_UI1); pVar->caub.cElems = m_TestDataSize; pVar->caub.pElems = pDataBuffer; } } } else { hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); } } FuncExit(); return hr; } ///////////////////////////////////////////////////////////////////////// // // CAccelerometerDevice::SetTestProperty // // This method is used to set one of the device's test properties. // // Parameters: // key - the test property key // pVar - pointer to the key value // // Return Values: // status // ///////////////////////////////////////////////////////////////////////// HRESULT CAccelerometerDevice::SetTestProperty( _In_ REFPROPERTYKEY key, _In_ PROPVARIANT* pVar) { FuncEntry(); HRESULT hr = (m_fInitialized == TRUE) ? S_OK : E_UNEXPECTED; if (SUCCEEDED(hr) && (pVar == nullptr)) { hr = E_INVALIDARG; } if (SUCCEEDED(hr)) { // Synchronize access to device CComCritSecLock<CComAutoCriticalSection> scopeLock(m_CriticalSection); if(IsEqualPropertyKey(key, SENSOR_PROPERTY_TEST_REGISTER)) { m_TestRegister = pVar->ulVal; } else if(IsEqualPropertyKey(key, SENSOR_PROPERTY_TEST_DATA_SIZE)) { m_TestDataSize = pVar->ulVal; } else if(IsEqualPropertyKey(key, SENSOR_PROPERTY_TEST_DATA)) { hr = WriteRegister( (BYTE)m_TestRegister, pVar->caub.pElems, m_TestDataSize); } else { hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); } } FuncExit(); return hr; } ///////////////////////////////////////////////////////////////////////// // // CAccelerometerDevice::AddDataFieldValue // // This method is used to validate each data field value and add it to // the specified IPortableDeviceValues instance. // // Parameters: // // Return Values: // status // ///////////////////////////////////////////////////////////////////////// HRESULT CAccelerometerDevice::AddDataFieldValue( _In_ REFPROPERTYKEY key, _In_ PROPVARIANT* pVar, _Out_ IPortableDeviceValues* pValues ) { FuncEntry(); HRESULT hr = S_OK; if (pValues == nullptr) { hr = E_INVALIDARG; } if (SUCCEEDED(hr)) { if (IsEqualPropertyKey(key, SENSOR_DATA_TYPE_ACCELERATION_X_G) || IsEqualPropertyKey(key, SENSOR_DATA_TYPE_ACCELERATION_Y_G) || IsEqualPropertyKey(key, SENSOR_DATA_TYPE_ACCELERATION_Z_G)) { if (pVar->vt != VT_R8) { hr = E_INVALIDARG; } if (SUCCEEDED(hr)) { if ((pVar->dblVal < ACCELEROMETER_MIN_ACCELERATION_G) || (pVar->dblVal > ACCELEROMETER_MAX_ACCELERATION_G)) { hr = E_INVALIDARG; } } } } if (SUCCEEDED(hr)) { hr = pValues->SetValue(key, pVar); } FuncExit(); return hr; } ///////////////////////////////////////////////////////////////////////// // // CAccelerometerDevice::OnInterruptIsr // // This method is called when an interrupt occurs. It determines if the // driver owns the interrupt and queues a work item to defer processing // of the data. // // Parameters: // pInterrupt - pointer to the interrupt object // MessageID - interrupt message ID // Reserved - // // Return Values: // TRUE if interrupt recognized, else FALSE. // ///////////////////////////////////////////////////////////////////////// BOOLEAN CAccelerometerDevice::OnInterruptIsr( _In_ IWDFInterrupt* pInterrupt, _In_ ULONG MessageID, _In_ ULONG Reserved ) { FuncEntry(); UNREFERENCED_PARAMETER(MessageID); UNREFERENCED_PARAMETER(Reserved); CComPtr<IWDFDevice> pWdfDevice = nullptr; CAccelerometerDevice* pAccelerometerDevice; BOOLEAN interruptRecognized = FALSE; HRESULT hr; hr = pInterrupt->RetrieveContext((void**)&pAccelerometerDevice); if (FAILED(hr)) { Trace( TRACE_LEVEL_ERROR, "Failed to retrieve device context, " "reporting device failure, %!HRESULT!", hr); pWdfDevice = pInterrupt->GetDevice(); pWdfDevice->SetPnpState(WdfPnpStateFailed, WdfTrue); pWdfDevice->CommitPnpState(); } if (SUCCEEDED(hr) && (pAccelerometerDevice != nullptr)) { BYTE interruptSource = 0; BYTE validInterrupts = 0;; { // Synchronize access to device CComCritSecLock<CComAutoCriticalSection> scopeLock( pAccelerometerDevice->m_CriticalSection); // Read the interrupt source register to // check for relevant interrupt. Doing so clears // the interrupt. hr = pAccelerometerDevice->ReadRegister( ADXL345_INT_SOURCE, &interruptSource, sizeof(interruptSource), 0); if (FAILED(hr)) { Trace( TRACE_LEVEL_ERROR, "Failed to read INT_SOURCE register, %!HRESULT!", hr); } if (SUCCEEDED(hr)) { // Throw away any interrupts that are not enabled. validInterrupts = interruptSource & pAccelerometerDevice->m_InterruptsEnabled; if ((interruptSource > 0) && (validInterrupts == 0)) { Trace( TRACE_LEVEL_INFORMATION, "Interrupt detected with INT_SOURCE=0x%x but " "INT_ENABLE=0x%x, treating as unrecognized", interruptSource, pAccelerometerDevice->m_InterruptsEnabled); } } } if (SUCCEEDED(hr)) { // Confirm that an activity interrupt was fired if ((validInterrupts & ADXL345_INT_ACTIVITY) > 0) { interruptRecognized = TRUE; // // NOTE: // It is best practice when handling interrupts to quickly // service the interrupt in the ISR and then queue a work item // to retrieve and process the data. // BOOLEAN workItemQueued = pInterrupt->QueueWorkItemForIsr(); Trace( TRACE_LEVEL_VERBOSE, "Work item %s queued for interrupt", workItemQueued ? "" : "already "); } } } FuncExit(); return interruptRecognized; } ///////////////////////////////////////////////////////////////////////// // // CAccelerometerDevice::OnInterruptWorkitem // // This method is called on behalf of an interrupt to defer processing. // It retrieves latest data and posts it. // // Parameters: // pInterrupt - pointer to the interrupt object // AssociatedObject - pointer to the associated object // // Return Values: // None. // ///////////////////////////////////////////////////////////////////////// VOID CAccelerometerDevice::OnInterruptWorkItem( _In_ IWDFInterrupt* pInterrupt, _In_ IWDFObject* AssociatedObject ) { FuncEntry(); UNREFERENCED_PARAMETER(AssociatedObject); CComPtr<IWDFDevice> pWdfDevice = nullptr; CAccelerometerDevice* pAccelerometerDevice; HRESULT hr; hr = pInterrupt->RetrieveContext((void**)&pAccelerometerDevice); if (FAILED(hr)) { Trace( TRACE_LEVEL_ERROR, "Failed to retrieve device context, " "reporting device failure, %!HRESULT!", hr); pWdfDevice = pInterrupt->GetDevice(); pWdfDevice->SetPnpState(WdfPnpStateFailed, WdfTrue); pWdfDevice->CommitPnpState(); } if (SUCCEEDED(hr) && (pAccelerometerDevice != nullptr)) { CComPtr<IPortableDeviceValues> spValues = nullptr; // Create an IPortableDeviceValues to hold the data hr = CoCreateInstance( CLSID_PortableDeviceValues, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&spValues)); if (SUCCEEDED(hr)) { hr = pAccelerometerDevice->RequestData(spValues); if (FAILED(hr)) { Trace( TRACE_LEVEL_ERROR, "Failed to request data in interrupt work item, " "%!HRESULT!", hr); } } if (SUCCEEDED(hr)) { hr = pAccelerometerDevice->PostData(spValues); if (FAILED(hr)) { Trace( TRACE_LEVEL_ERROR, "Failed to post new data to the DDI, " "%!HRESULT!", hr); } } } FuncExit(); } ///////////////////////////////////////////////////////////////////////// // // CAccelerometerDevice::RequestData // // This method is used to request data from the device. // // Parameters: // pValues - an IPortableDeviceValues collection to place the list of // new data field values // // Return Values: // status // ///////////////////////////////////////////////////////////////////////// HRESULT CAccelerometerDevice::RequestData( _In_ IPortableDeviceValues * pValues ) { FuncEntry(); HRESULT hr = m_fInitialized ? S_OK : E_UNEXPECTED; if (SUCCEEDED(hr)) { // Synchronize access to device CComCritSecLock<CComAutoCriticalSection> scopeLock(m_CriticalSection); // Read the data registers asynchronously hr = ReadRegister( ADXL345_DATA_X0, m_pDataBuffer, ADXL345_DATA_REPORT_SIZE_BYTES, 0); if (FAILED(hr)) { Trace( TRACE_LEVEL_ERROR, "Failed to read new data from device, %!HRESULT!", hr); } if (SUCCEEDED(hr)) { // Get the data values as doubles SHORT xRaw, yRaw, zRaw; DOUBLE xAccel, yAccel, zAccel; const DOUBLE scaleFactor = 1/256.0F; xRaw = (SHORT)((m_pDataBuffer[1] << 8) | m_pDataBuffer[0]); yRaw = (SHORT)((m_pDataBuffer[3] << 8) | m_pDataBuffer[2]); zRaw = (SHORT)((m_pDataBuffer[5] << 8) | m_pDataBuffer[4]); xAccel = (DOUBLE)xRaw * scaleFactor; yAccel = (DOUBLE)yRaw * scaleFactor; zAccel = (DOUBLE)zRaw * scaleFactor; // Verify each accelerometer data value and // add it to the list if (SUCCEEDED(hr)) { PROPVARIANT var; PropVariantInit(&var); var.vt = VT_R8; var.dblVal = xAccel; hr = AddDataFieldValue( SENSOR_DATA_TYPE_ACCELERATION_X_G, &var, pValues); if (SUCCEEDED(hr)) { var.dblVal = yAccel; hr = AddDataFieldValue( SENSOR_DATA_TYPE_ACCELERATION_Y_G, &var, pValues); } if (SUCCEEDED(hr)) { var.dblVal = zAccel; hr = AddDataFieldValue( SENSOR_DATA_TYPE_ACCELERATION_Z_G, &var, pValues); } PropVariantClear(&var); } } } FuncExit(); return hr; } ///////////////////////////////////////////////////////////////////////// // // CAccelerometerDevice::PostData // // This method is used to post new data to the DDI. // to the DDI. // // Parameters: // pValues - an IPortableDeviceValues collection to place the list of // new data field values // // Return Values: // status // ///////////////////////////////////////////////////////////////////////// HRESULT CAccelerometerDevice::PostData( _In_ IPortableDeviceValues * pValues ) { FuncEntry(); HRESULT hr = m_fInitialized ? S_OK : E_UNEXPECTED; // Post the if (SUCCEEDED(hr)) { m_pSensorDeviceCallback->OnNewData(pValues); } FuncExit(); return hr; } ///////////////////////////////////////////////////////////////////////// // // CAccelerometerDevice::ReadRegister // // This method is used to read a buffer of data from the device's register // interface. The first byte is read from the specified register, the second // from the next, etc. // // Parameters: // reg - the first register to be read from // pDataBuffer - pointer to the output buffer // dataBufferLength - size of the output buffer // // Return Values: // status // ///////////////////////////////////////////////////////////////////////// HRESULT CAccelerometerDevice::ReadRegister( _In_ BYTE reg, _Out_writes_(dataBufferLength) BYTE* pDataBuffer, _In_ ULONG dataBufferLength, _In_ ULONG delayInUs ) { FuncEntry(); HRESULT hr = (m_fInitialized == TRUE) ? S_OK : E_UNEXPECTED; if (SUCCEEDED(hr) && pDataBuffer == nullptr) { hr = E_INVALIDARG; } if (SUCCEEDED(hr)) { Trace( TRACE_LEVEL_VERBOSE, "Read %lu bytes from register 0x%02x", dataBufferLength, reg); // Execute the write-read sequence hr = m_spRequest->CreateAndSendWriteReadSequence( ®, 1, pDataBuffer, dataBufferLength, delayInUs); } if (FAILED(hr)) { Trace( TRACE_LEVEL_ERROR, "Failed to read from register 0x%02x, %!HRESULT!", reg, hr); } FuncExit(); return hr; } ///////////////////////////////////////////////////////////////////////// // // CAccelerometerDevice::WriteRegister // // This method is used to write a buffer of data to the device's register // interface. The first byte is written to the specified register, the // second from the next, etc. // // Parameters: // reg - the first register to be written to // pDataBuffer - pointer to the output buffer // dataBufferLength - size of the output buffer // // Return Values: // status // ///////////////////////////////////////////////////////////////////////// HRESULT CAccelerometerDevice::WriteRegister( _In_ BYTE reg, _In_reads_(dataBufferLength) BYTE* pDataBuffer, _In_ ULONG dataBufferLength ) { FuncEntry(); HRESULT hr = (m_fInitialized == TRUE) ? S_OK : E_UNEXPECTED; if (SUCCEEDED(hr) && pDataBuffer == nullptr) { hr = E_INVALIDARG; } if (SUCCEEDED(hr)) { // A write-write sequence is implemented with // a single write request. Allocate a buffer to // hold the register and data. ULONG bufferLength = dataBufferLength + 1; BYTE* pBuffer = new BYTE[bufferLength]; if (pBuffer == nullptr) { hr = E_OUTOFMEMORY; } if (SUCCEEDED(hr)) { // Fill the buffer memcpy(pBuffer, ®, 1); memcpy((pBuffer + 1), pDataBuffer, dataBufferLength); Trace( TRACE_LEVEL_VERBOSE, "Write %lu bytes to register 0x%02x", dataBufferLength, reg); // Execute the write hr = m_spRequest->CreateAndSendWrite( pBuffer, bufferLength); } // Destroy the allocated buffer if (pBuffer != nullptr) { delete[] pBuffer; } } if (FAILED(hr)) { Trace( TRACE_LEVEL_ERROR, "Failed to write to register 0x%02x, %!HRESULT!", reg, hr); } FuncExit(); return hr; }
Our Services
-
What our customers say about us?
Read our customer testimonials to find out why our clients keep returning for their projects.
View Testimonials