diff options
| author | Dave Wilson <[email protected]> | 2015-03-17 19:50:07 -0700 |
|---|---|---|
| committer | Dave Wilson <[email protected]> | 2015-03-17 19:50:07 -0700 |
| commit | 97cf5197cf5b882b2c689d8dc2b555f2edf8f418 (patch) | |
| tree | 46f3701832d70b420eb0fc0eb93261f9da45db3f /sensors/ADXL345Acc/device.cpp | |
| parent | ef1905bf1e8825bb31120dfb27e0daf3154d859a (diff) | |
Initial publish
Diffstat (limited to 'sensors/ADXL345Acc/device.cpp')
| -rw-r--r-- | sensors/ADXL345Acc/device.cpp | 474 |
1 files changed, 474 insertions, 0 deletions
diff --git a/sensors/ADXL345Acc/device.cpp b/sensors/ADXL345Acc/device.cpp new file mode 100644 index 00000000..389f28cd --- /dev/null +++ b/sensors/ADXL345Acc/device.cpp @@ -0,0 +1,474 @@ +//Copyright (C) Microsoft Corporation, All Rights Reserved. +// +//Abstract: +// +// This module contains the implementation of WDF callback functions +// for the ADXL345 accelerometer sensor driver. +// +//Environment: +// +// Windows User-Mode Driver Framework (UMDF) + +#include "Device.h" + +#include "Device.tmh" + + +// This routine is the AddDevice entry point for the custom sensor client +// driver. This routine is called by the framework in response to AddDevice +// call from the PnP manager. It will create and initialize the device object +// to represent a new instance of the sensor client. +NTSTATUS ADXL345AccDevice::OnDeviceAdd( + _In_ WDFDRIVER /* Driver */, // Supplies a handle to the driver object created in DriverEntry + _Inout_ PWDFDEVICE_INIT pAccDeviceInit) // Supplies a pointer to a framework-allocated WDFDEVICE_INIT structure +{ + WDFDEVICE Device = nullptr; + + SENSOR_FunctionEnter(); + + WdfDeviceInitSetPowerPolicyOwnership(pAccDeviceInit, true); + + WDF_OBJECT_ATTRIBUTES FdoAttributes; + WDF_OBJECT_ATTRIBUTES_INIT(&FdoAttributes); + + // Initialize FDO (functional device object) attributes and set up file object with sensor extension + NTSTATUS Status = SensorsCxDeviceInitConfig(pAccDeviceInit, &FdoAttributes, 0); + if (!NT_SUCCESS(Status)) + { + TraceError("ACC %!FUNC! SensorsCxDeviceInitConfig failed %!STATUS!", Status); + } + + else // if (NT_SUCCESS(Status)) + { + // Register the PnP callbacks with the framework. + WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks; + WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks); + pnpPowerCallbacks.EvtDevicePrepareHardware = ADXL345AccDevice::OnPrepareHardware; + pnpPowerCallbacks.EvtDeviceReleaseHardware = ADXL345AccDevice::OnReleaseHardware; + pnpPowerCallbacks.EvtDeviceD0Entry = ADXL345AccDevice::OnD0Entry; + pnpPowerCallbacks.EvtDeviceD0Exit = ADXL345AccDevice::OnD0Exit; + + WdfDeviceInitSetPnpPowerEventCallbacks(pAccDeviceInit, &pnpPowerCallbacks); + + // Call the framework to create the device + Status = WdfDeviceCreate(&pAccDeviceInit, &FdoAttributes, &Device); + if (!NT_SUCCESS(Status)) + { + TraceError("ACC %!FUNC! WdfDeviceCreate failed %!STATUS!", Status); + } + } + + if (NT_SUCCESS(Status)) + { + // Register CLX callback function pointers + SENSOR_CONTROLLER_CONFIG SensorConfig; + SENSOR_CONTROLLER_CONFIG_INIT(&SensorConfig); + SensorConfig.DriverIsPowerPolicyOwner = WdfUseDefault; + + SensorConfig.EvtSensorStart = ADXL345AccDevice::OnStart; + SensorConfig.EvtSensorStop = ADXL345AccDevice::OnStop; + SensorConfig.EvtSensorGetSupportedDataFields = ADXL345AccDevice::OnGetSupportedDataFields; + SensorConfig.EvtSensorGetDataInterval = ADXL345AccDevice::OnGetDataInterval; + SensorConfig.EvtSensorSetDataInterval = ADXL345AccDevice::OnSetDataInterval; + SensorConfig.EvtSensorGetDataFieldProperties = ADXL345AccDevice::OnGetDataFieldProperties; + SensorConfig.EvtSensorGetDataThresholds = ADXL345AccDevice::OnGetDataThresholds; + SensorConfig.EvtSensorSetDataThresholds = ADXL345AccDevice::OnSetDataThresholds; + SensorConfig.EvtSensorGetProperties = ADXL345AccDevice::OnGetProperties; + SensorConfig.EvtSensorDeviceIoControl = ADXL345AccDevice::OnIoControl; + + // Initialize the sensor device with the Sensor CLX + // This lets the CLX call the above callbacks when + // necessary and allows applications to retrieve and + // set device data. + Status = SensorsCxDeviceInitialize(Device, &SensorConfig); + if (!NT_SUCCESS(Status)) + { + TraceError("ACC %!FUNC! SensorsCxDeviceInitialize failed %!STATUS!", Status); + } + } + + // Ensure device is disable-able + // By default, devices enumerated by ACPI are not disable-able + // Our accelerometer is enumerated by the ACPI so we must + // explicitly make it disable-able. + if (NT_SUCCESS(Status)) + { + WDF_DEVICE_STATE DeviceState; + WDF_DEVICE_STATE_INIT(&DeviceState); + DeviceState.NotDisableable = WdfFalse; + WdfDeviceSetDeviceState(Device, &DeviceState); + } + + SENSOR_FunctionExit(Status); + return Status; +} + +// This routine is called by the framework when the PnP manager sends an +// IRP_MN_START_DEVICE request to the driver stack. This routine is +// responsible for performing operations that are necessary to make the +// driver's device operational (for e.g. mapping the hardware resources +// into memory). +NTSTATUS ADXL345AccDevice::OnPrepareHardware( + _In_ WDFDEVICE Device, // Supplies a handle to the framework device object + _In_ WDFCMRESLIST ResourcesRaw, // Supplies a handle to a collection of framework resource + // objects. This collection identifies the raw (bus-relative) hardware + // resources that have been assigned to the device. + _In_ WDFCMRESLIST ResourcesTranslated) // Supplies a handle to a collection of framework + // resource objects. This collection identifies the translated + // (system-physical) hardware resources that have been assigned to the + // device. The resources appear from the CPU's point of view. +{ + PADXL345AccDevice pAccDevice = nullptr; + + SENSOR_FunctionEnter(); + + // Create WDFOBJECT for the sensor + WDF_OBJECT_ATTRIBUTES sensorAttributes; + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&sensorAttributes, ADXL345AccDevice); + + // Register sensor instance with clx + SENSOROBJECT SensorInstance = NULL; + NTSTATUS Status = SensorsCxSensorCreate(Device, &sensorAttributes, &SensorInstance); + if (!NT_SUCCESS(Status)) + { + TraceError("ACC %!FUNC! SensorsCxSensorCreate failed %!STATUS!", Status); + } + + else // if (NT_SUCCESS(Status)) + { + pAccDevice = GetADXL345AccContextFromSensorInstance(SensorInstance); + if (nullptr == pAccDevice) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + TraceError("ACC %!FUNC! SensorsCxSensorCreate failed %!STATUS!", Status); + } + } + + // Fill out sensor context + if (NT_SUCCESS(Status)) + { + Status = pAccDevice->Initialize(Device, SensorInstance); + if (!NT_SUCCESS(Status)) + { + TraceError("ACC %!FUNC! Initialize device object failed %!STATUS!", Status); + } + } + + // Initialize sensor instance with clx + if (NT_SUCCESS(Status)) + { + SENSOR_CONFIG SensorConfig; + SENSOR_CONFIG_INIT(&SensorConfig); + SensorConfig.pEnumerationList = pAccDevice->m_pEnumerationProperties; + Status = SensorsCxSensorInitialize(SensorInstance, &SensorConfig); + if (!NT_SUCCESS(Status)) + { + TraceError("ACC %!FUNC! SensorsCxSensorInitialize failed %!STATUS!", Status); + } + } + + // ACPI and IoTarget configuration + if (NT_SUCCESS(Status)) + { + Status = pAccDevice->ConfigureIoTarget(ResourcesRaw, ResourcesTranslated); + if (!NT_SUCCESS(Status)) + { + TraceError("ACC %!FUNC! Failed to configure IoTarget %!STATUS!", Status); + } + } + + SENSOR_FunctionExit(Status); + return Status; +} + +// This routine is called by the framework when the PnP manager is revoking +// ownership of our resources. This may be in response to either +// IRP_MN_STOP_DEVICE or IRP_MN_REMOVE_DEVICE. This routine is responsible for +// performing cleanup of resources allocated in PrepareHardware callback. +// This callback is invoked before passing the request down to the lower driver. +// This routine will also be invoked by the framework if the prepare hardware +// callback returns a failure. +NTSTATUS ADXL345AccDevice::OnReleaseHardware( + _In_ WDFDEVICE Device, // Supplies a handle to the framework device object + _In_ WDFCMRESLIST /*ResourcesTranslated*/) // Supplies a handle to a collection of framework + // resource objects. This collection identifies the translated + // (system-physical) hardware resources that have been assigned to the + // device. The resources appear from the CPU's point of view. +{ + PADXL345AccDevice pAccDevice = nullptr; + + SENSOR_FunctionEnter(); + + // Get the sensor instance + ULONG SensorInstanceCount = 1; + SENSOROBJECT SensorInstance = NULL; + NTSTATUS Status = SensorsCxDeviceGetSensorList(Device, &SensorInstance, &SensorInstanceCount); + if (!NT_SUCCESS(Status) || 0 == SensorInstanceCount || NULL == SensorInstance) + { + Status = STATUS_INVALID_PARAMETER; + TraceError("ACC %!FUNC! SensorsCxDeviceGetSensorList failed %!STATUS!", Status); + } + + else // if (NT_SUCCESS(Status)) + { + pAccDevice = GetADXL345AccContextFromSensorInstance(SensorInstance); + if (nullptr == pAccDevice) + { + Status = STATUS_INVALID_PARAMETER; + TraceError("ACC %!FUNC! GetADXL345AccContextFromSensorInstance failed %!STATUS!", Status); + } + } + + if (NT_SUCCESS(Status)) + { + pAccDevice->DeInit(); + } + + SENSOR_FunctionExit(Status); + return Status; +} + +// This routine is invoked by the framework to program the device to goto +// D0, which is the working state. The framework invokes callback every +// time the hardware needs to be (re-)initialized. This includes after +// IRP_MN_START_DEVICE, IRP_MN_CANCEL_STOP_DEVICE, IRP_MN_CANCEL_REMOVE_DEVICE, +// and IRP_MN_SET_POWER-D0. +NTSTATUS ADXL345AccDevice::OnD0Entry( + _In_ WDFDEVICE Device, // Supplies a handle to the framework device object + _In_ WDF_POWER_DEVICE_STATE /*PreviousState*/) // WDF_POWER_DEVICE_STATE-typed enumerator that identifies + // the device power state that the device was in before this transition to D0 +{ + PADXL345AccDevice pAccDevice = nullptr; + + SENSOR_FunctionEnter(); + + // Get the sensor instance + ULONG SensorInstanceCount = 1; + SENSOROBJECT SensorInstance = NULL; + NTSTATUS Status = SensorsCxDeviceGetSensorList(Device, &SensorInstance, &SensorInstanceCount); + if (!NT_SUCCESS(Status) || 0 == SensorInstanceCount || NULL == SensorInstance) + { + Status = STATUS_INVALID_PARAMETER; + TraceError("ACC %!FUNC! SensorsCxDeviceGetSensorList failed %!STATUS!", Status); + } + + // Get the device context + else // if (NT_SUCCESS(Status)) + { + pAccDevice = GetADXL345AccContextFromSensorInstance(SensorInstance); + if (nullptr == pAccDevice) + { + Status = STATUS_INVALID_PARAMETER; + TraceError("ACC %!FUNC! GetADXL345AccContextFromSensorInstance failed %!STATUS!", Status); + } + } + + if (NT_SUCCESS(Status)) + { + Status = pAccDevice->PowerOn(); + } + + SENSOR_FunctionExit(Status); + return Status; +} + +// This routine is invoked by the framework to program the device to go into +// a certain Dx state. The framework invokes callback every the the device is +// leaving the D0 state, which happens when the device is stopped, when it is +// removed, and when it is powered off. EvtDeviceD0Exit event callback must +// perform any operations that are necessary before the specified device is +// moved out of the D0 state. If the driver needs to save hardware state +// before the device is powered down, then that should be done here. +NTSTATUS ADXL345AccDevice::OnD0Exit( + _In_ WDFDEVICE Device, // Supplies a handle to the framework device object + _In_ WDF_POWER_DEVICE_STATE)/*TargetState*/ // Supplies the device power state which the device will be put + // in once the callback is complete +{ + PADXL345AccDevice pAccDevice = nullptr; + + SENSOR_FunctionEnter(); + + // Get the sensor instance + ULONG SensorInstanceCount = 1; + SENSOROBJECT SensorInstance = NULL; + NTSTATUS Status = SensorsCxDeviceGetSensorList(Device, &SensorInstance, &SensorInstanceCount); + if (!NT_SUCCESS(Status) || 0 == SensorInstanceCount || NULL == SensorInstance) + { + Status = STATUS_INVALID_PARAMETER; + TraceError("ACC %!FUNC! SensorsCxDeviceGetSensorList failed %!STATUS!", Status); + } + + // Get the device context + else // if (NT_SUCCESS(Status)) + { + pAccDevice = GetADXL345AccContextFromSensorInstance(SensorInstance); + if (nullptr == pAccDevice) + { + Status = STATUS_INVALID_PARAMETER; + TraceError("ACC %!FUNC! GetADXL345AccContextFromSensorInstance failed %!STATUS!", Status); + } + } + + if (NT_SUCCESS(Status)) + { + Status = pAccDevice->PowerOff(); + } + + SENSOR_FunctionExit(Status); + return Status; +} + +// Get the HW resource from the ACPI, then configure and store the IoTarget +NTSTATUS ADXL345AccDevice::ConfigureIoTarget( + _In_ WDFCMRESLIST ResourcesRaw, // Supplies a handle to a collection of framework resource + // objects. This collection identifies the raw (bus-relative) hardware + // resources that have been assigned to the device. + _In_ WDFCMRESLIST ResourcesTranslated) // Supplies a handle to a collection of framework + // resource objects. This collection identifies the translated + // (system-physical) hardware resources that have been assigned to the + // device. The resources appear from the CPU's point of view. +{ + NTSTATUS Status = STATUS_SUCCESS; + ULONG I2CConnectionResourceCount = 0; + LARGE_INTEGER I2CConnId = {}; + + DECLARE_UNICODE_STRING_SIZE(deviceName, RESOURCE_HUB_PATH_SIZE); + + SENSOR_FunctionEnter(); + + // Get hardware resource from ACPI and set up IO target + ULONG ResourceCount = WdfCmResourceListGetCount(ResourcesTranslated); + for (ULONG i = 0; i < ResourceCount; i++) + { + PCM_PARTIAL_RESOURCE_DESCRIPTOR DescriptorRaw = WdfCmResourceListGetDescriptor(ResourcesRaw, i); + PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor = WdfCmResourceListGetDescriptor(ResourcesTranslated, i); + switch (Descriptor->Type) + { + // Check we have I2C bus assigned in ACPI + case CmResourceTypeConnection: + TraceInformation("ACC %!FUNC! I2C resource found."); + if (Descriptor->u.Connection.Class == CM_RESOURCE_CONNECTION_CLASS_SERIAL && + Descriptor->u.Connection.Type == CM_RESOURCE_CONNECTION_TYPE_SERIAL_I2C) + { + I2CConnId.LowPart = Descriptor->u.Connection.IdLowPart; + I2CConnId.HighPart = Descriptor->u.Connection.IdHighPart; + I2CConnectionResourceCount++; + } + break; + + // Check we have an interrupt assigned in ACPI and create interrupt + case CmResourceTypeInterrupt: + TraceInformation("ACC %!FUNC! GPIO interrupt resource found."); + WDF_INTERRUPT_CONFIG InterruptConfig; + WDF_INTERRUPT_CONFIG_INIT(&InterruptConfig, OnInterruptIsr, NULL); + InterruptConfig.InterruptRaw = DescriptorRaw; + InterruptConfig.InterruptTranslated = Descriptor; + // Configure an interrupt work item which runs at IRQL = PASSIVE_LEVEL + // Note: to configure to run at IRQL = DISPATCH_LEVEL, set up an InterruptDpc instead of an InterruptWorkItem + InterruptConfig.EvtInterruptWorkItem = OnInterruptWorkItem; + InterruptConfig.PassiveHandling = true; + Status = WdfInterruptCreate(m_Device, &InterruptConfig, WDF_NO_OBJECT_ATTRIBUTES, &m_Interrupt); + if (!NT_SUCCESS(Status)) + { + TraceError("ACC %!FUNC! WdfInterruptCreate failed %!STATUS!", Status); + } + break; + + default: + break; + } + } + + if (NT_SUCCESS(Status) && I2CConnectionResourceCount != 1) + { + Status = STATUS_UNSUCCESSFUL; + TraceError("ACC %!FUNC! Did not find I2C resource! %!STATUS!", Status); + } + + // Set up I2C I/O target. Issued with I2C R/W transfers + if (NT_SUCCESS(Status)) + { + m_I2CIoTarget = NULL; + Status = WdfIoTargetCreate(m_Device, WDF_NO_OBJECT_ATTRIBUTES, &m_I2CIoTarget); + + if (!NT_SUCCESS(Status)) + { + TraceError("ACC %!FUNC! WdfIoTargetCreate failed! %!STATUS!", Status); + } + } + + // Setup Target string (\\\\.\\RESOURCE_HUB\\<ConnID from ResHub> + if (NT_SUCCESS(Status)) + { + Status = StringCbPrintfW(deviceName.Buffer, RESOURCE_HUB_PATH_SIZE, L"%s\\%0*I64x", RESOURCE_HUB_DEVICE_NAME, static_cast<unsigned int>(sizeof(LARGE_INTEGER) * 2), I2CConnId.QuadPart); + deviceName.Length = _countof(deviceName_buffer); + + if (!NT_SUCCESS(Status)) + { + TraceError("ACC %!FUNC! RESOURCE_HUB_CREATE_PATH_FROM_ID failed!"); + } + } + + // Connect to I2C target + if (NT_SUCCESS(Status)) + { + WDF_IO_TARGET_OPEN_PARAMS OpenParams; + WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME(&OpenParams, &deviceName, FILE_ALL_ACCESS); + + Status = WdfIoTargetOpen(m_I2CIoTarget, &OpenParams); + if (!NT_SUCCESS(Status)) + { + TraceError("ACC %!FUNC! WdfIoTargetOpen failed! %!STATUS!", Status); + } + } + + SENSOR_FunctionExit(Status); + return Status; +} + +// Write the default device configuration to the device +NTSTATUS ADXL345AccDevice::PowerOn() +{ + NTSTATUS Status = STATUS_SUCCESS; + + WdfWaitLockAcquire(m_I2CWaitLock, NULL); + + for (DWORD i = 0; i < ARRAYSIZE(g_ConfigurationSettings); i++) + { + REGISTER_SETTING setting = g_ConfigurationSettings[i]; + Status = I2CSensorWriteRegister(m_I2CIoTarget, setting.Register, &setting.Value, sizeof(setting.Value)); + if (!NT_SUCCESS(Status)) + { + TraceError("ACC %!FUNC! I2CSensorReadRegister from 0x%02x failed! %!STATUS!", setting.Register, Status); + WdfWaitLockRelease(m_I2CWaitLock); + return Status; + } + } + + WdfWaitLockRelease(m_I2CWaitLock); + + InitPropVariantFromUInt32(SensorState_Idle, &(m_pSensorProperties->List[SENSOR_PROPERTY_STATE].Value)); + m_PoweredOn = true; + + return Status; +} + +NTSTATUS ADXL345AccDevice::PowerOff() +{ + REGISTER_SETTING RegisterSetting = { ADXL345_POWER_CTL, ADXL345_POWER_CTL_STANDBY }; + + WdfWaitLockAcquire(m_I2CWaitLock, NULL); + NTSTATUS Status = I2CSensorWriteRegister(m_I2CIoTarget, RegisterSetting.Register, &RegisterSetting.Value, sizeof(RegisterSetting.Value)); + WdfWaitLockRelease(m_I2CWaitLock); + + if (!NT_SUCCESS(Status)) + { + TraceError("ACC %!FUNC! Failed to put device into standby %!STATUS!", Status); + } + else + { + m_PoweredOn = false; + } + + return Status; +} |
