// Called by Sensor CLX to get sampling rate of the sensor. NTSTATUS CustomSensorDevice::OnGetDataInterval( _In_ SENSOROBJECT SensorInstance, // sensor device object _Out_ PULONG DataRateMs // sampling rate in milliseconds ) { PCustomSensorDevice pDevice = GetCustomSensorContextFromSensorInstance(SensorInstance); NTSTATUS Status = STATUS_SUCCESS; SENSOR_FunctionEnter(); if (nullptr == pDevice) { Status = STATUS_INVALID_PARAMETER; TraceError("CSTM %!FUNC! Sensor(%08X) parameter is invalid. Failed %!STATUS!", (INT) SensorInstance, Status); goto Exit; } if (nullptr == DataRateMs) { Status = STATUS_INVALID_PARAMETER; TraceError("CSTM %!FUNC! DataRateMs(%08X) parameter is invalid. Failed %!STATUS!", (INT)DataRateMs, Status); goto Exit; } *DataRateMs = pDevice->m_Interval; Exit: SENSOR_FunctionExit(Status); return Status; }
// Called by Sensor CLX to set sampling rate of the sensor. NTSTATUS CustomSensorDevice::OnSetDataInterval( _In_ SENSOROBJECT SensorInstance, // sensor device object _In_ ULONG DataRateMs // sampling rate in milliseconds ) { PCustomSensorDevice pDevice = GetCustomSensorContextFromSensorInstance(SensorInstance); NTSTATUS Status = STATUS_SUCCESS; SENSOR_FunctionEnter(); if (nullptr == pDevice || Cstm_Default_MinDataInterval_Ms > DataRateMs) { Status = STATUS_INVALID_PARAMETER; TraceError("CSTM %!FUNC! Sensor(%08X) parameter is invalid. Failed %!STATUS!", (INT) SensorInstance, Status); goto Exit; } 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(Cstm_Default_MinDataInterval_Ms)); } Exit: SENSOR_FunctionExit(Status); return Status; }
// Called by Sensor CLX to begin continously sampling the sensor. NTSTATUS CustomSensorDevice::OnStart( _In_ SENSOROBJECT SensorInstance // sensor device object ) { PHardwareSimulator pSimulator = nullptr; PCustomSensorDevice pDevice = GetCustomSensorContextFromSensorInstance(SensorInstance); NTSTATUS Status = STATUS_SUCCESS; SENSOR_FunctionEnter(); if (nullptr == pDevice) { Status = STATUS_INVALID_PARAMETER; TraceError("PED %!FUNC! Sensor(%08X) parameter is invalid. Failed %!STATUS!", (INT)SensorInstance, Status); } if (NT_SUCCESS(Status)) { // Get the simulator context pSimulator = GetHardwareSimulatorContextFromInstance(pDevice->m_SimulatorInstance); if (nullptr == pSimulator) { Status = STATUS_INSUFFICIENT_RESOURCES; TraceError("PED %!FUNC! GetHardwareSimulatorContextFromInstance failed %!STATUS!", Status); } } if (NT_SUCCESS(Status)) { // Start the simulator pSimulator->Start(); pDevice->m_FirstSample = TRUE; // Start polling pDevice->m_Started = TRUE; InitPropVariantFromUInt32(SensorState_Active, &(pDevice->m_pProperties->List[SENSOR_PROPERTY_STATE].Value)); // Start the sample polling timer. // Note1: the WDF timer is only as precise as the system resolution allows it to be. // In the case of the CO2 sensor, the reporting interval is 200 milliseconds. The default // system resolution (15.6 milliseconds) is therefore fine enough to guarantee an accurate sample // reporting interval. Some sensors using a lower reporting interval may want to reduce the system // time resolution by calling into timeBeginPeriod() before starting the polling timer. // // Important consideration: calling into timeBeginPeriod() should be used with care as it has // an adverse on the system performance and power consumption. // // Note2: The polling timer is configured to allow for the first sample to be reported immediately. // Some hardware may want to delay the first sample report a little to account for hardware start time. WdfTimerStart(pDevice->m_Timer, 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 CustomSensorDevice::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 ) { PCustomSensorDevice pDevice = GetCustomSensorContextFromSensorInstance(SensorInstance); NTSTATUS Status = STATUS_SUCCESS; SENSOR_FunctionEnter(); if (nullptr == pSize) { Status = STATUS_INVALID_PARAMETER; TraceError("CSTM %!FUNC! pSize: Invalid parameter! %!STATUS!", Status); goto Exit; } *pSize = 0; if (nullptr == pDevice) { Status = STATUS_INVALID_PARAMETER; TraceError("CSTM %!FUNC! pDevice: Invalid parameter! %!STATUS!", Status); goto Exit; } if (nullptr == pProperties) { // Just return size *pSize = CollectionsListGetMarshalledSize(pDevice->m_pProperties); } else { if (pProperties->AllocatedSizeInBytes < CollectionsListGetMarshalledSize(pDevice->m_pProperties)) { Status = STATUS_INSUFFICIENT_RESOURCES; TraceError("CSTM %!FUNC! Buffer is too small. Failed %!STATUS!", Status); goto Exit; } // Fill out all data Status = CollectionsListCopyAndMarshall(pProperties, pDevice->m_pProperties); if (!NT_SUCCESS(Status)) { TraceError("CSTM %!FUNC! CollectionsListCopyAndMarshall failed %!STATUS!", Status); goto Exit; } *pSize = CollectionsListGetMarshalledSize(pDevice->m_pProperties); } Exit: SENSOR_FunctionExit(Status); return Status; }
// 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 CustomSensorDevice::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 ) { PCustomSensorDevice pDevice = GetCustomSensorContextFromSensorInstance(SensorInstance); NTSTATUS Status = STATUS_SUCCESS; SENSOR_FunctionEnter(); if (nullptr == pSize) { Status = STATUS_INVALID_PARAMETER; TraceError("CSTM %!FUNC! pSize: Invalid parameter! %!STATUS!", Status); goto Exit; } *pSize = 0; if (nullptr == pDevice) { Status = STATUS_INVALID_PARAMETER; TraceError("CSTM %!FUNC! pDevice: Invalid parameter! %!STATUS!", Status); goto Exit; } if (nullptr == pFields) { // Just return size *pSize = pDevice->m_pSupportedDataFields->AllocatedSizeInBytes; } else { if (pFields->AllocatedSizeInBytes < pDevice->m_pSupportedDataFields->AllocatedSizeInBytes) { Status = STATUS_INSUFFICIENT_RESOURCES; TraceError("CSTM %!FUNC! Buffer is too small. Failed %!STATUS!", Status); goto Exit; } // Fill out data Status = PropertiesListCopy (pFields, pDevice->m_pSupportedDataFields); if (!NT_SUCCESS(Status)) { TraceError("CSTM %!FUNC! PropertiesListCopy failed %!STATUS!", Status); goto Exit; } *pSize = pDevice->m_pSupportedDataFields->AllocatedSizeInBytes; } Exit: SENSOR_FunctionExit(Status); return Status; }
// Called by Sensor CLX to stop continously sampling the sensor. NTSTATUS CustomSensorDevice::OnStop( _In_ SENSOROBJECT SensorInstance // sensor device object ) { PHardwareSimulator pSimulator = nullptr; PCustomSensorDevice pDevice = GetCustomSensorContextFromSensorInstance(SensorInstance); NTSTATUS Status = STATUS_SUCCESS; SENSOR_FunctionEnter(); if (nullptr == pDevice) { Status = STATUS_INVALID_PARAMETER; TraceError("CSTM %!FUNC! Sensor(%08X) parameter is invalid. Failed %!STATUS!", (INT) SensorInstance, Status); } if (NT_SUCCESS(Status)) { // Stop polling pDevice->m_Started = FALSE; // Waiting for the callback to complete, then stopping the timer WdfTimerStop(pDevice->m_Timer, TRUE); InitPropVariantFromUInt32(SensorState_Idle, &(pDevice->m_pProperties->List[SENSOR_PROPERTY_STATE].Value)); // Stop the simulator pSimulator = GetHardwareSimulatorContextFromInstance(pDevice->m_SimulatorInstance); if (nullptr == pSimulator) { Status = STATUS_INSUFFICIENT_RESOURCES; TraceError("CSTM %!FUNC! GetHardwareSimulatorContextFromInstance failed %!STATUS!", Status); goto Exit; } pSimulator->Stop(); } Exit: 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 CustomSensorDevice::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 { PCustomSensorDevice pDevice; SENSOROBJECT SensorInstance = nullptr; ULONG SensorInstanceCount = 1; NTSTATUS Status = STATUS_SUCCESS; SENSOR_FunctionEnter(); // Get sensor instance Status = SensorsCxDeviceGetSensorList(Device, &SensorInstance, &SensorInstanceCount); if (!NT_SUCCESS(Status) || 0 == SensorInstanceCount || NULL == SensorInstance) { Status = STATUS_INVALID_PARAMETER; TraceError("CSTM %!FUNC! SensorsCxDeviceGetSensorList failed %!STATUS!", Status); goto Exit; } pDevice = GetCustomSensorContextFromSensorInstance(SensorInstance); if (nullptr == pDevice) { Status = STATUS_INVALID_PARAMETER; TraceError("CSTM %!FUNC! GetCustomSensorContextFromSensorInstance failed %!STATUS!", Status); goto Exit; } // Power on sensor pDevice->m_PoweredOn = TRUE; InitPropVariantFromUInt32(SensorState_Idle, &(pDevice->m_pProperties->List[SENSOR_PROPERTY_STATE].Value)); Exit: 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. NTSTATUS CustomSensorDevice::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 { PCustomSensorDevice pDevice; SENSOROBJECT SensorInstance = nullptr; ULONG SensorInstanceCount = 1; NTSTATUS Status = STATUS_SUCCESS; SENSOR_FunctionEnter(); // Get sensor instance Status = SensorsCxDeviceGetSensorList(Device, &SensorInstance, &SensorInstanceCount); if (!NT_SUCCESS(Status) || 0 == SensorInstanceCount || NULL == SensorInstance) { Status = STATUS_INVALID_PARAMETER; TraceError("CSTM %!FUNC! SensorsCxDeviceGetSensorList failed %!STATUS!", Status); goto Exit; } pDevice = GetCustomSensorContextFromSensorInstance(SensorInstance); if (nullptr == pDevice) { Status = STATUS_INVALID_PARAMETER; TraceError("CSTM %!FUNC! GetCustomSensorContextFromSensorInstance failed %!STATUS!", Status); goto Exit; } // Power off sensor pDevice->m_PoweredOn = FALSE; Exit: 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 CustomSensorDevice::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. { PHardwareSimulator pSimulator = nullptr; PCustomSensorDevice pDevice = nullptr; SENSOROBJECT SensorInstance = nullptr; ULONG SensorInstanceCount = 1; // only expect 1 sensor instance NTSTATUS Status = STATUS_SUCCESS; SENSOR_FunctionEnter(); // Get sensor instance Status = SensorsCxDeviceGetSensorList(Device, &SensorInstance, &SensorInstanceCount); if (!NT_SUCCESS(Status) || 0 == SensorInstanceCount || NULL == SensorInstance) { Status = STATUS_INVALID_PARAMETER; TraceError("CSTM %!FUNC! SensorsCxDeviceGetSensorList failed %!STATUS!", Status); goto Exit; } pDevice = GetCustomSensorContextFromSensorInstance(SensorInstance); if (nullptr == pDevice) { Status = STATUS_INVALID_PARAMETER; TraceError("CSTM %!FUNC! GetCustomSensorContextFromSensorInstance failed %!STATUS!", Status); goto Exit; } // Delete lock if (pDevice->m_Lock) { WdfObjectDelete(pDevice->m_Lock); pDevice->m_Lock = NULL; } // Cleanup the CO2 simulator pSimulator = GetHardwareSimulatorContextFromInstance(pDevice->m_SimulatorInstance); if (nullptr == pSimulator) { Status = STATUS_INSUFFICIENT_RESOURCES; TraceError("CSTM %!FUNC! GetHardwareSimulatorContextFromInstance failed %!STATUS!", Status); goto Exit; } pSimulator->Cleanup(); // Delete hardware simulator instance if (NULL != pDevice->m_SimulatorInstance) { WdfObjectDelete(pDevice->m_SimulatorInstance); pDevice->m_SimulatorInstance = NULL; } // Delete sensor instance if (NULL != pDevice->m_SensorInstance) { WdfObjectDelete(pDevice->m_SensorInstance); } Exit: 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 CustomSensorDevice::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. { PCustomSensorDevice pDevice = nullptr; WDF_OBJECT_ATTRIBUTES SensorAttr = {}; SENSOR_CONFIG SensorConfig = {}; SENSOROBJECT SensorInstance = nullptr; NTSTATUS Status = STATUS_SUCCESS; SENSOR_FunctionEnter(); // Construct sensor instance // Create WDFOBJECT for the sensor WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&SensorAttr, CustomSensorDevice); // Register sensor instance with clx Status = SensorsCxSensorCreate(Device, &SensorAttr, &SensorInstance); if (!NT_SUCCESS(Status)) { TraceError("CSTM %!FUNC! SensorsCxSensorCreate failed %!STATUS!", Status); goto Exit; } pDevice = GetCustomSensorContextFromSensorInstance(SensorInstance); if (nullptr == pDevice) { Status = STATUS_INSUFFICIENT_RESOURCES; TraceError("CSTM %!FUNC! GetCustomSensorContextFromSensorInstance failed %!STATUS!", Status); goto Exit; } // Fill out properties Status = pDevice->Initialize(Device, SensorInstance); if (!NT_SUCCESS(Status)) { TraceError("CSTM %!FUNC! Initialize device object failed %!STATUS!", Status); goto Exit; } SENSOR_CONFIG_INIT(&SensorConfig); SensorConfig.pEnumerationList = pDevice->m_pEnumerationProperties; Status = SensorsCxSensorInitialize(SensorInstance, &SensorConfig); if (!NT_SUCCESS(Status)) { TraceError("CSTM %!FUNC! SensorsCxSensorInitialize failed %!STATUS!", Status); goto Exit; } Exit: 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, // pushes it up to CLX framework, and schedule next wake up time. VOID CustomSensorDevice::OnTimerExpire( _In_ WDFTIMER Timer // WDF timer object ) { PCustomSensorDevice pDevice = nullptr; NTSTATUS Status = STATUS_SUCCESS; SENSOR_FunctionEnter(); pDevice = GetCustomSensorContextFromSensorInstance(WdfTimerGetParentObject(Timer)); if (nullptr == pDevice) { Status = STATUS_INSUFFICIENT_RESOURCES; TraceError("CSTM %!FUNC! GetCustomSensorContextFromSensorInstance failed %!STATUS!", Status); } if (NT_SUCCESS(Status)) { // Get data and push to clx WdfWaitLockAcquire(pDevice->m_Lock, NULL); Status = pDevice->GetData(); if (!NT_SUCCESS(Status) && Status != STATUS_DATA_NOT_ACCEPTED) { TraceError("CSTM %!FUNC! GetCstmData Failed %!STATUS!", Status); } WdfWaitLockRelease(pDevice->m_Lock); // Schedule next wake up time if (FALSE != pDevice->m_PoweredOn && FALSE != pDevice->m_Started) { LONGLONG WaitTimeHundredNanoseconds = 0; // in unit of 100ns if (0 == pDevice->m_StartTime) { // in case we fail to get sensor start time, use static wait time WaitTimeHundredNanoseconds = WDF_REL_TIMEOUT_IN_MS(pDevice->m_Interval); } else { ULONG CurrentTimeMs = 0; // dynamically calculate wait time to avoid jitter Status = GetPerformanceTime(&CurrentTimeMs); if (!NT_SUCCESS(Status)) { TraceError("PED %!FUNC! GetPerformanceTime %!STATUS!", Status); WaitTimeHundredNanoseconds = WDF_REL_TIMEOUT_IN_MS(pDevice->m_Interval); } else { WaitTimeHundredNanoseconds = pDevice->m_Interval - ((CurrentTimeMs - pDevice->m_StartTime) % pDevice->m_Interval); WaitTimeHundredNanoseconds = WDF_REL_TIMEOUT_IN_MS(WaitTimeHundredNanoseconds); } } WdfTimerStart(pDevice->m_Timer, WaitTimeHundredNanoseconds); } } SENSOR_FunctionExit(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 CustomSensorDevice::OnGetDataFieldProperties( _In_ SENSOROBJECT SensorInstance, // sensor device object _In_ const PROPERTYKEY *DataField, // 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 ) { PCustomSensorDevice pDevice = GetCustomSensorContextFromSensorInstance(SensorInstance); NTSTATUS Status = STATUS_SUCCESS; SENSOR_FunctionEnter(); if (nullptr == pSize) { Status = STATUS_INVALID_PARAMETER; TraceError("CSTM %!FUNC! pSize: Invalid parameter! %!STATUS!", Status); goto Exit; } *pSize = 0; if (nullptr == pDevice || nullptr == DataField) { Status = STATUS_INVALID_PARAMETER; TraceError("CSTM %!FUNC! Invalid parameters! %!STATUS!", Status); goto Exit; } if ((*DataField == pDevice->m_pSupportedDataFields->List[CSTM_DATA_CO2_LEVEL_PERCENT])) { if (nullptr == pProperties) { // Just return size *pSize = CollectionsListGetMarshalledSize(pDevice->m_pDataFieldProperties); } else { if (pProperties->AllocatedSizeInBytes < CollectionsListGetMarshalledSize(pDevice->m_pDataFieldProperties)) { Status = STATUS_INSUFFICIENT_RESOURCES; TraceError("CSTM %!FUNC! Buffer is too small. Failed %!STATUS!", Status); goto Exit; } // Fill out all data Status = CollectionsListCopyAndMarshall (pProperties, pDevice->m_pDataFieldProperties); if (!NT_SUCCESS(Status)) { TraceError("CSTM %!FUNC! CollectionsListCopyAndMarshall failed %!STATUS!", Status); goto Exit; } *pSize = CollectionsListGetMarshalledSize(pDevice->m_pDataFieldProperties); } } else { Status = STATUS_NOT_SUPPORTED; TraceError("CSTM %!FUNC! CustomSensor does NOT have properties for this data field. Failed %!STATUS!", Status); goto Exit; } Exit: SENSOR_FunctionExit(Status); return Status; }