summaryrefslogtreecommitdiff
path: root/thermal/simsensor/simsensor.c
diff options
context:
space:
mode:
authorDave Wilson <[email protected]>2015-03-17 19:50:07 -0700
committerDave Wilson <[email protected]>2015-03-17 19:50:07 -0700
commit97cf5197cf5b882b2c689d8dc2b555f2edf8f418 (patch)
tree46f3701832d70b420eb0fc0eb93261f9da45db3f /thermal/simsensor/simsensor.c
parentef1905bf1e8825bb31120dfb27e0daf3154d859a (diff)
Initial publish
Diffstat (limited to 'thermal/simsensor/simsensor.c')
-rw-r--r--thermal/simsensor/simsensor.c1549
1 files changed, 1549 insertions, 0 deletions
diff --git a/thermal/simsensor/simsensor.c b/thermal/simsensor/simsensor.c
new file mode 100644
index 00000000..a30ef067
--- /dev/null
+++ b/thermal/simsensor/simsensor.c
@@ -0,0 +1,1549 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+ simsensor.c
+
+Abstract:
+
+ The module implements a simulated sensor device.
+
+@@BEGIN_DDKSPLIT
+Author:
+
+ Nicholas Brekhus (NiBrekhu) 02-Aug-2011
+
+Revision History:
+
+@@END_DDKSPLIT
+--*/
+
+//-------------------------------------------------------------------- Includes
+
+#include "simsensor.h"
+
+
+//--------------------------------------------------------------------- Globals
+
+ULONG SimSensorDebug = SIMSENSOR_PRINT_ALWAYS;
+
+#define VIRTUAL_SENSOR_RESET_TEMPERATURE 42
+
+// {FCB15302-14A9-4bf8-8A0B-888E0D33BEDE}
+DEFINE_GUID(GUID_VIRTUAL_TEMPERATURE_SENSOR,
+0xfcb15302, 0x14a9, 0x4bf8, 0x8a, 0xb, 0x88, 0x8e, 0xd, 0x33, 0xbe, 0xde);
+
+//------------------------------------------------------------------ Prototypes
+
+DRIVER_INITIALIZE DriverEntry;
+
+EVT_WDF_DRIVER_DEVICE_ADD SimSensorDriverDeviceAdd;
+
+
+EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL SimSensorIoDeviceControl;
+EVT_WDF_IO_QUEUE_IO_INTERNAL_DEVICE_CONTROL SimSensorIoInternalDeviceControl;
+EVT_WDF_IO_QUEUE_IO_STOP SimSensorQueueIoStop;
+EVT_WDF_DEVICE_SELF_MANAGED_IO_SUSPEND SimSensorSelfManagedIoSuspend;
+
+EVT_WDF_TIMER SimSensorExpiredRequestTimer;
+
+EVT_WDF_WORKITEM SimSensorTemperatureInterruptWorker;
+
+_IRQL_requires_(PASSIVE_LEVEL)
+VOID
+SimSensorAddReadRequest (
+ _In_ WDFDEVICE Device,
+ _In_ WDFREQUEST ReadRequest
+ );
+
+_IRQL_requires_(PASSIVE_LEVEL)
+NTSTATUS
+SimSensorScanPendingQueue (
+ _In_ WDFDEVICE Device
+ );
+
+_IRQL_requires_(PASSIVE_LEVEL)
+VOID
+SimSensorCheckQueuedRequest (
+ _In_ WDFDEVICE Device,
+ _In_ ULONG Temperature,
+ _Inout_ PULONG LowerBound,
+ _Inout_ PULONG UpperBound,
+ _In_ WDFREQUEST Request
+ );
+
+_IRQL_requires_(PASSIVE_LEVEL)
+BOOLEAN
+SimSensorAreConstraintsSatisfied (
+ _In_ ULONG Temperature,
+ _In_ ULONG LowerBound,
+ _In_ ULONG UpperBound,
+ _In_ LARGE_INTEGER DueTime);
+
+VOID
+SimSensorTemperatureInterrupt (
+ _In_ WDFDEVICE Device
+ );
+
+//
+// Virtual hardware programming interface
+//
+
+VOID
+SimSensorSetVirtualInterruptThresholds (
+ _In_ WDFDEVICE Device,
+ _In_ ULONG LowerBound,
+ _In_ ULONG UpperBound
+ );
+
+ULONG
+SimSensorReadVirtualTemperature (
+ _In_ WDFDEVICE Device
+ );
+
+//
+// Virtual hardware internal routines
+//
+
+EVT_WDF_DEVICE_D0_ENTRY SimSensorDeviceD0Entry;
+EVT_WDF_DEVICE_D0_EXIT SimSensorDeviceD0Exit;
+POWER_SETTING_CALLBACK SimSensorSettingCallback;
+
+
+//--------------------------------------------------------------------- Pragmas
+
+#pragma alloc_text(INIT, DriverEntry)
+#pragma alloc_text(PAGE, SimSensorAddReadRequest)
+#pragma alloc_text(PAGE, SimSensorAreConstraintsSatisfied)
+#pragma alloc_text(PAGE, SimSensorCheckQueuedRequest)
+#pragma alloc_text(PAGE, SimSensorSelfManagedIoSuspend)
+#pragma alloc_text(PAGE, SimSensorDriverDeviceAdd)
+#pragma alloc_text(PAGE, SimSensorExpiredRequestTimer)
+#pragma alloc_text(PAGE, SimSensorScanPendingQueue)
+#pragma alloc_text(PAGE, SimSensorIoDeviceControl)
+
+//------------------------------------------------------------------- Functions
+
+NTSTATUS
+DriverEntry (
+ PDRIVER_OBJECT DriverObject,
+ PUNICODE_STRING RegistryPath
+ )
+
+/*++
+
+Routine Description:
+
+ DriverEntry initializes the driver and is the first routine called by the
+ system after the driver is loaded. DriverEntry configures and creates a WDF
+ driver object.
+
+Parameters Description:
+
+ DriverObject - Supplies a pointer to the driver object.
+
+ RegistryPath - Supplies a pointer to a unicode string representing the path
+ to the driver-specific key in the registry.
+
+Return Value:
+
+ NTSTATUS.
+
+--*/
+
+{
+
+ WDF_OBJECT_ATTRIBUTES DriverAttributes;
+ WDF_DRIVER_CONFIG DriverConfig;
+ NTSTATUS Status;
+
+ UNREFERENCED_PARAMETER(RegistryPath);
+
+ DebugEnter();
+
+ WDF_DRIVER_CONFIG_INIT(&DriverConfig, SimSensorDriverDeviceAdd);
+
+ //
+ // Initialize attributes and a context area for the driver object.
+ //
+
+ WDF_OBJECT_ATTRIBUTES_INIT(&DriverAttributes);
+ DriverAttributes.SynchronizationScope = WdfSynchronizationScopeNone;
+
+ //
+ // Create the driver object
+ //
+
+ Status = WdfDriverCreate(DriverObject,
+ RegistryPath,
+ &DriverAttributes,
+ &DriverConfig,
+ WDF_NO_HANDLE);
+
+ if (!NT_SUCCESS(Status)) {
+ DebugPrint(SIMSENSOR_ERROR,
+ "WdfDriverCreate() Failed. Status 0x%x\n",
+ Status);
+
+ goto DriverEntryEnd;
+ }
+
+DriverEntryEnd:
+ DebugExitStatus(Status);
+ return Status;
+}
+
+NTSTATUS
+SimSensorDriverDeviceAdd (
+ WDFDRIVER Driver,
+ PWDFDEVICE_INIT DeviceInit
+ )
+
+/*++
+
+Routine Description:
+
+ EvtDriverDeviceAdd is called by the framework in response to AddDevice call
+ from the PnP manager. A WDF device object is created and initialized to
+ represent a new instance of the battery device.
+
+Arguments:
+
+ Driver - Supplies a handle to the WDF Driver object.
+
+ DeviceInit - Supplies a pointer to a framework-allocated WDFDEVICE_INIT
+ structure.
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+
+{
+
+ WDF_OBJECT_ATTRIBUTES DeviceAttributes;
+ WDFDEVICE DeviceHandle;
+ PFDO_DATA DevExt;
+ BOOLEAN LockHeld;
+ WDF_IO_QUEUE_CONFIG PendingRequestQueueConfig;
+ WDF_PNPPOWER_EVENT_CALLBACKS PnpPowerCallbacks;
+ WDFQUEUE Queue;
+ WDF_IO_QUEUE_CONFIG QueueConfig;
+ NTSTATUS Status;
+ WDF_OBJECT_ATTRIBUTES WorkitemAttributes;
+ WDF_WORKITEM_CONFIG WorkitemConfig;
+
+ UNREFERENCED_PARAMETER(Driver);
+
+ DebugEnter();
+ PAGED_CODE();
+
+ LockHeld = FALSE;
+
+ //
+ // Initialize attributes and a context area for the device object.
+ //
+
+ WDF_OBJECT_ATTRIBUTES_INIT(&DeviceAttributes);
+ WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(&DeviceAttributes, FDO_DATA);
+
+ //
+ // Initailize power callbacks
+ //
+
+ WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&PnpPowerCallbacks);
+ PnpPowerCallbacks.EvtDeviceD0Entry = SimSensorDeviceD0Entry;
+ PnpPowerCallbacks.EvtDeviceD0Exit = SimSensorDeviceD0Exit;
+ PnpPowerCallbacks.EvtDeviceSelfManagedIoSuspend =
+ SimSensorSelfManagedIoSuspend;
+
+ WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &PnpPowerCallbacks);
+
+ //
+ // Create a framework device object. This call will in turn create
+ // a WDM device object, attach to the lower stack, and set the
+ // appropriate flags and attributes.
+ //
+
+ Status = WdfDeviceCreate(&DeviceInit, &DeviceAttributes, &DeviceHandle);
+ if (!NT_SUCCESS(Status)) {
+ DebugPrint(SIMSENSOR_ERROR,
+ "WdfDeviceCreate() Failed. 0x%x\n",
+ Status);
+
+ goto DriverDeviceAddEnd;
+ }
+
+ DevExt = GetDeviceExtension(DeviceHandle);
+
+ //
+ // Configure a default queue for IO requests. This queue processes requests
+ // to read the sensor state.
+ //
+
+ WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&QueueConfig,
+ WdfIoQueueDispatchParallel);
+
+ QueueConfig.EvtIoDeviceControl = SimSensorIoDeviceControl;
+
+ //
+ // The system uses IoInternalDeviceControl requests to communicate with the
+ // ACPI driver on the device stack. For proper operation of thermal zones,
+ // these requests must be forwarded unless the driver knows how to handle
+ // them.
+ //
+
+ QueueConfig.EvtIoInternalDeviceControl = SimSensorIoInternalDeviceControl;
+ Status = WdfIoQueueCreate(DeviceHandle,
+ &QueueConfig,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &Queue);
+
+ if (!NT_SUCCESS(Status)) {
+ DebugPrint(SIMSENSOR_ERROR,
+ "WdfIoQueueCreate() (Default) Failed. 0x%x\n",
+ Status);
+
+ goto DriverDeviceAddEnd;
+ }
+
+ //
+ // Configure a manual dispatch queue for pending requests. This queue
+ // stores requests to read the sensor state which can't be retired
+ // immediately.
+ //
+
+ WDF_IO_QUEUE_CONFIG_INIT(&PendingRequestQueueConfig,
+ WdfIoQueueDispatchManual);
+
+
+ Status = WdfIoQueueCreate(DeviceHandle,
+ &PendingRequestQueueConfig,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &DevExt->PendingRequestQueue);
+
+ PendingRequestQueueConfig.EvtIoStop = SimSensorQueueIoStop;
+ if (!NT_SUCCESS(Status)) {
+ DebugPrint(SIMSENSOR_ERROR,
+ "WdfIoQueueCreate() (Pending) Failed. 0x%x\n",
+ Status);
+
+ goto DriverDeviceAddEnd;
+ }
+
+
+ //
+ // Configure a workitem to process the simulated interrupt.
+ //
+
+ WDF_OBJECT_ATTRIBUTES_INIT(&WorkitemAttributes);
+ WorkitemAttributes.ParentObject = DeviceHandle;
+ WDF_WORKITEM_CONFIG_INIT(&WorkitemConfig,
+ SimSensorTemperatureInterruptWorker);
+
+ Status = WdfWorkItemCreate(&WorkitemConfig,
+ &WorkitemAttributes,
+ &DevExt->InterruptWorker);
+
+ if (!NT_SUCCESS(Status)) {
+ DebugPrint(SIMSENSOR_ERROR,
+ "WdfWorkItemCreate() Failed. 0x%x\n",
+ Status);
+
+ goto DriverDeviceAddEnd;
+ }
+
+ //
+ // Create the request queue waitlock.
+ //
+
+ Status = WdfWaitLockCreate(NULL, &DevExt->QueueLock);
+ if (!NT_SUCCESS(Status)) {
+ DebugPrint(SIMSENSOR_ERROR,
+ "WdfWaitLockCreate() Failed. Status 0x%x\n",
+ Status);
+
+ goto DriverDeviceAddEnd;
+ }
+
+ //
+ // Initilize the simulated sensor hardware.
+ //
+
+ DevExt->Sensor.LowerBound = 0;
+ DevExt->Sensor.UpperBound = (ULONG)-1;
+ DevExt->Sensor.Temperature = VIRTUAL_SENSOR_RESET_TEMPERATURE;
+ Status = WdfWaitLockCreate(NULL, &DevExt->Sensor.Lock);
+ if (!NT_SUCCESS(Status)) {
+ DebugPrint(SIMSENSOR_ERROR,
+ "WdfWaitLockCreate() Failed. 0x%x\n",
+ Status);
+
+ goto DriverDeviceAddEnd;
+ }
+
+DriverDeviceAddEnd:
+
+ DebugExitStatus(Status);
+ return Status;
+}
+
+VOID
+SimSensorQueueIoStop (
+ _In_ WDFQUEUE Queue,
+ _In_ WDFREQUEST Request,
+ _In_ ULONG ActionFlags
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called when the framework is stopping the request's I/O
+ queue.
+
+Arguments:
+
+ Queue - Supplies handle to the framework queue object that is associated
+ with the I/O request.
+
+ Request - Supplies handle to a framework request object.
+
+ ActionFlags - Supplies the reason that the callback is being called.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ NTSTATUS Status;
+
+ UNREFERENCED_PARAMETER(Queue);
+
+ if(ActionFlags & WdfRequestStopRequestCancelable) {
+ Status = WdfRequestUnmarkCancelable(Request);
+ if (Status == STATUS_CANCELLED) {
+ goto SimSensorQueueIoStopEnd;
+ }
+
+ NT_ASSERT(NT_SUCCESS(Status));
+ }
+
+ WdfRequestStopAcknowledge(Request, FALSE);
+
+SimSensorQueueIoStopEnd:
+
+ return;
+}
+
+VOID
+SimSensorIoDeviceControl(
+ WDFQUEUE Queue,
+ WDFREQUEST Request,
+ size_t OutputBufferLength,
+ size_t InputBufferLength,
+ ULONG IoControlCode
+ )
+
+/*++
+
+Routine Description:
+
+ Handles requests to read or write the simulated device state.
+
+Arguments:
+
+ Queue - Supplies a handle to the framework queue object that is associated
+ with the I/O request.
+
+ Request - Supplies a handle to a framework request object. This one
+ represents the IRP_MJ_DEVICE_CONTROL IRP received by the framework.
+
+ OutputBufferLength - Supplies the length, in bytes, of the request's output
+ buffer, if an output buffer is available.
+
+ InputBufferLength - Supplies the length, in bytes, of the request's input
+ buffer, if an input buffer is available.
+
+ IoControlCode - Supplies the Driver-defined or system-defined I/O control
+ code (IOCtl) that is associated with the request.
+
+Return Value:
+
+ VOID
+
+--*/
+
+{
+ ULONG BytesReturned;
+ WDFDEVICE Device;
+ BOOLEAN Result;
+ WDF_REQUEST_SEND_OPTIONS RequestSendOptions;
+ NTSTATUS Status;
+
+ UNREFERENCED_PARAMETER(InputBufferLength);
+ UNREFERENCED_PARAMETER(OutputBufferLength);
+
+ PAGED_CODE();
+
+ Device = WdfIoQueueGetDevice(Queue);
+ DebugPrint(SIMSENSOR_NOTE, "SimSensorIoDeviceControl: 0x%p\n", Device);
+ BytesReturned = 0;
+ switch(IoControlCode) {
+ case IOCTL_THERMAL_READ_TEMPERATURE:
+
+ //
+ // This call will either complete the request or put it in the pending
+ // queue.
+ //
+
+ SimSensorAddReadRequest(Device, Request);
+ break;
+ default:
+
+ //
+ // Unrecognized IOCtls must be forwarded down the stack.
+ //
+
+ WDF_REQUEST_SEND_OPTIONS_INIT(
+ &RequestSendOptions,
+ WDF_REQUEST_SEND_OPTION_SEND_AND_FORGET);
+
+ WdfRequestFormatRequestUsingCurrentType(Request);
+
+ Result = WdfRequestSend(
+ Request,
+ WdfDeviceGetIoTarget(Device),
+ &RequestSendOptions);
+
+ if (Result == FALSE) {
+ Status = WdfRequestGetStatus(Request);
+ DebugPrint(SIMSENSOR_WARN,
+ "WdfRequestSend() Failed. Request Status = 0x%x\n",
+ Status);
+
+ WdfRequestComplete(Request, Status);
+ }
+ break;
+ }
+}
+
+VOID
+SimSensorIoInternalDeviceControl (
+ WDFQUEUE Queue,
+ WDFREQUEST Request,
+ size_t OutputBufferLength,
+ size_t InputBufferLength,
+ ULONG IoControlCode)
+
+/*++
+
+Description:
+
+ The system uses IoInternalDeviceControl requests to communicate with the
+ ACPI driver on the device stack. For proper operation of thermal zones,
+ these requests must be forwarded unless the driver knows how to handle
+ them.
+
+--*/
+
+{
+ WDF_REQUEST_SEND_OPTIONS RequestSendOptions;
+ BOOLEAN Return;
+ NTSTATUS Status;
+
+ UNREFERENCED_PARAMETER(OutputBufferLength);
+ UNREFERENCED_PARAMETER(InputBufferLength);
+ UNREFERENCED_PARAMETER(IoControlCode);
+
+ DebugEnter();
+
+ WdfRequestFormatRequestUsingCurrentType(Request);
+
+ WDF_REQUEST_SEND_OPTIONS_INIT(
+ &RequestSendOptions,
+ WDF_REQUEST_SEND_OPTION_SEND_AND_FORGET);
+
+ Return = WdfRequestSend(
+ Request,
+ WdfDeviceGetIoTarget(WdfIoQueueGetDevice(Queue)),
+ &RequestSendOptions);
+
+ if (Return == FALSE) {
+ Status = WdfRequestGetStatus(Request);
+ DebugPrint(SIMSENSOR_WARN,
+ "WdfRequestSend() Failed. Request Status=0x%x\n",
+ Status);
+
+ WdfRequestComplete(Request, Status);
+ }
+
+ DebugExit();
+}
+
+_IRQL_requires_(PASSIVE_LEVEL)
+BOOLEAN
+SimSensorAreConstraintsSatisfied (
+ _In_ ULONG Temperature,
+ _In_ ULONG LowerBound,
+ _In_ ULONG UpperBound,
+ _In_ LARGE_INTEGER DueTime
+ )
+
+/*++
+
+Routine Description:
+
+ Checks whether a request can be retired.
+
+Arguments:
+
+ Temperature - Supplies the device's current temperature.
+
+ LowerBound - Supplies the request's lower temperature bound.
+
+ UpperBound - Supplies the request's upper temperature bound.
+
+ DueTime - Supplies when the request expires.
+
+Return Value:
+
+ TRUE - The request is retireable.
+
+ FALSE - The request is not retireable.
+
+--*/
+
+{
+ LARGE_INTEGER CurrentTime;
+
+ PAGED_CODE();
+
+ if (Temperature <= LowerBound || Temperature >= UpperBound) {
+ return TRUE;
+ }
+
+ //
+ // Negative due times are meaningless, except for the special value -1,
+ // which represents no timeout.
+ //
+
+ if (DueTime.QuadPart < 0) {
+ return FALSE;
+ }
+
+ KeQuerySystemTime(&CurrentTime);
+
+ if ((CurrentTime.QuadPart - DueTime.QuadPart) >= 0) {
+
+ //
+ // This request expired in the past.
+ //
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+_IRQL_requires_(PASSIVE_LEVEL)
+VOID
+SimSensorAddReadRequest (
+ _In_ WDFDEVICE Device,
+ _In_ WDFREQUEST ReadRequest
+ )
+
+/*++
+
+Routine Description:
+
+ Handles IOCTL_THERMAL_READ_TEMPERATURE. If the request can be satisfied,
+ it is completed immediately. Else, adds request to pending request queue.
+
+Arguments:
+
+ Device - Supplies a handle to the device that received the request.
+
+ ReadRequest - Supplies a handle to the request.
+
+--*/
+
+{
+ ULONG BytesReturned;
+ PREAD_REQUEST_CONTEXT Context;
+ WDF_OBJECT_ATTRIBUTES ContextAttributes;
+ PFDO_DATA DevExt;
+ LARGE_INTEGER ExpirationTime;
+ size_t Length;
+ BOOLEAN LockHeld;
+ PULONG RequestTemperature;
+ NTSTATUS Status;
+ ULONG Temperature;
+ WDFTIMER Timer;
+ WDF_OBJECT_ATTRIBUTES TimerAttributes;
+ WDF_TIMER_CONFIG TimerConfig;
+ PTHERMAL_WAIT_READ ThermalWaitRead;
+
+
+ DebugEnter();
+ PAGED_CODE();
+
+ DevExt = GetDeviceExtension(Device);
+ BytesReturned = 0;
+ LockHeld = FALSE;
+ Status = WdfRequestRetrieveInputBuffer(ReadRequest,
+ sizeof(THERMAL_WAIT_READ),
+ &ThermalWaitRead,
+ &Length);
+
+ if (!NT_SUCCESS(Status) || Length != sizeof(THERMAL_WAIT_READ)) {
+
+ //
+ // This request is malformed, bail.
+ //
+
+ WdfRequestCompleteWithInformation(ReadRequest, Status, BytesReturned);
+ goto AddReadRequestEnd;
+ }
+
+
+ if (ThermalWaitRead->Timeout != -1 /* INFINITE */ ) {
+
+ //
+ // Estimate the system time this request will expire at.
+ //
+
+ KeQuerySystemTime(&ExpirationTime);
+ ExpirationTime.QuadPart += ThermalWaitRead->Timeout * 10000;
+
+ } else {
+
+ //
+ // Value which indicates the request never expires.
+ //
+
+ ExpirationTime.QuadPart = -1LL /* INFINITE */;
+ }
+
+ //
+ // Handle the immediate timeout case in the fast path.
+ //
+
+ Temperature = SimSensorReadVirtualTemperature(Device);
+ if (SimSensorAreConstraintsSatisfied(Temperature,
+ ThermalWaitRead->LowTemperature,
+ ThermalWaitRead->HighTemperature,
+ ExpirationTime)) {
+
+ Status = WdfRequestRetrieveOutputBuffer(ReadRequest,
+ sizeof(ULONG),
+ &RequestTemperature,
+ &Length);
+
+ if(NT_SUCCESS(Status) && Length == sizeof(ULONG)) {
+ *RequestTemperature = Temperature;
+ BytesReturned = sizeof(ULONG);
+
+ } else {
+ Status = STATUS_INVALID_PARAMETER;
+ DebugPrint(SIMSENSOR_ERROR,
+ "WdfRequestRetrieveOutputBuffer() Failed. 0x%x",
+ Status);
+
+ }
+
+ WdfRequestCompleteWithInformation(ReadRequest, Status, BytesReturned);
+ } else {
+
+ WdfWaitLockAcquire(DevExt->QueueLock, NULL);
+ LockHeld = TRUE;
+
+ //
+ // Create a context to store request-specific information.
+ //
+
+ WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&ContextAttributes,
+ READ_REQUEST_CONTEXT);
+
+ Status = WdfObjectAllocateContext(ReadRequest,
+ &ContextAttributes,
+ &Context);
+
+ if(!NT_SUCCESS(Status)) {
+ DebugPrint(SIMSENSOR_ERROR,
+ "WdfObjectAllocateContext() Failed. 0x%x",
+ Status);
+
+ WdfRequestCompleteWithInformation(ReadRequest,
+ Status,
+ BytesReturned);
+
+ goto AddReadRequestEnd;
+ }
+
+ Context->ExpirationTime.QuadPart = ExpirationTime.QuadPart;
+ Context->LowTemperature = ThermalWaitRead->LowTemperature;
+ Context->HighTemperature = ThermalWaitRead->HighTemperature;
+ if(Context->ExpirationTime.QuadPart != -1LL /* INFINITE */ ) {
+
+ //
+ // This request eventually expires, create a timer to complete it.
+ //
+
+ WDF_TIMER_CONFIG_INIT(&TimerConfig, SimSensorExpiredRequestTimer);
+ WDF_OBJECT_ATTRIBUTES_INIT(&TimerAttributes);
+ TimerAttributes.ExecutionLevel = WdfExecutionLevelPassive;
+ TimerAttributes.SynchronizationScope = WdfSynchronizationScopeNone;
+ TimerAttributes.ParentObject = Device;
+ Status = WdfTimerCreate(&TimerConfig,
+ &TimerAttributes,
+ &Timer);
+
+ if(!NT_SUCCESS(Status)) {
+ DebugPrint(SIMSENSOR_ERROR,
+ "WdfTimerCreate() Failed. 0x%x",
+ Status);
+
+ WdfRequestCompleteWithInformation(ReadRequest,
+ Status,
+ BytesReturned);
+
+ goto AddReadRequestEnd;
+ }
+
+ WdfTimerStart(Timer,
+ WDF_REL_TIMEOUT_IN_MS(ThermalWaitRead->Timeout));
+
+ }
+
+ Status = WdfRequestForwardToIoQueue(ReadRequest,
+ DevExt->PendingRequestQueue);
+
+ if(!NT_SUCCESS(Status)) {
+ DebugPrint(SIMSENSOR_ERROR,
+ "WdfRequestForwardToIoQueue() Failed. 0x%x",
+ Status);
+
+ WdfRequestCompleteWithInformation(ReadRequest,
+ Status,
+ BytesReturned);
+
+ goto AddReadRequestEnd;
+ }
+
+ //
+ // Force a rescan of the queue to update the interrupt thresholds.
+ //
+
+ SimSensorScanPendingQueue(Device);
+ }
+
+AddReadRequestEnd:
+
+ if(LockHeld == TRUE) {
+ WdfWaitLockRelease(DevExt->QueueLock);
+ }
+
+ DebugExitStatus(Status);
+}
+
+_IRQL_requires_(PASSIVE_LEVEL)
+NTSTATUS
+SimSensorScanPendingQueue (
+ _In_ WDFDEVICE Device
+ )
+
+/*++
+
+Routine Description:
+
+ This routine scans the device's pending queue for retirable requests.
+
+ N.B. This routine requires the QueueLock be held.
+
+Arguments:
+
+ Device - Supplies a handle to the device.
+
+--*/
+
+{
+ WDFREQUEST CurrentRequest;
+ PFDO_DATA DevExt;
+ WDFREQUEST LastRequest;
+ ULONG LowerBound;
+ NTSTATUS Status;
+ ULONG Temperature;
+ ULONG UpperBound;
+
+ DebugEnter();
+ PAGED_CODE();
+
+ DevExt = GetDeviceExtension(Device);
+
+ Status = STATUS_SUCCESS;
+
+ LastRequest = NULL;
+ CurrentRequest = NULL;
+ Temperature = SimSensorReadVirtualTemperature(Device);
+
+ //
+ // Prime the walk by finding the first request present. If there are no
+ // requests, bail out immediately.
+ //
+
+ LowerBound = 0;
+ UpperBound = (ULONG)-1;
+ Status = WdfIoQueueFindRequest(DevExt->PendingRequestQueue,
+ NULL,
+ NULL,
+ NULL,
+ &CurrentRequest);
+
+ //
+ // Due to a technical limitation in SDV analysis engine, the following
+ // analysis assume has to be inserted to supress a false defect for
+ // the wdfioqueueretrievefoundrequest rule.
+ //
+
+ _Analysis_assume_(Status == STATUS_NOT_FOUND);
+
+ while (NT_SUCCESS(Status)) {
+
+ //
+ // Walk past the current request. By walking past the current request
+ // before checking it, the walk doesn't have to restart every time a
+ // request is satisfied and removed form the queue.
+ //
+
+ LastRequest = CurrentRequest;
+ Status = WdfIoQueueFindRequest(DevExt->PendingRequestQueue,
+ LastRequest,
+ NULL,
+ NULL,
+ &CurrentRequest);
+
+ //
+ // Process the last request.
+ //
+
+ SimSensorCheckQueuedRequest(Device,
+ Temperature,
+ &LowerBound,
+ &UpperBound,
+ LastRequest);
+
+ WdfObjectDereference(LastRequest);
+
+ if(Status == STATUS_NOT_FOUND) {
+
+ //
+ // LastRequest unexpectedly disappeared from the queue. Start over.
+ //
+
+ LowerBound = 0;
+ UpperBound = (ULONG)-1;
+ Status = WdfIoQueueFindRequest(DevExt->PendingRequestQueue,
+ NULL,
+ NULL,
+ NULL,
+ &CurrentRequest);
+
+ }
+
+ }
+
+ //
+ // Update the thresholds based on the latest contents of the queue.
+ //
+
+ SimSensorSetVirtualInterruptThresholds(Device, LowerBound, UpperBound);
+ DebugExitStatus(Status);
+ return Status;
+}
+
+
+NTSTATUS
+SimSensorSelfManagedIoSuspend (
+ _In_ WDFDEVICE Device
+ )
+
+/*++
+
+Routine Description:
+
+ Stops self-managed IO queues in preparation for D0 exit.
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+
+{
+
+ PFDO_DATA DevExt;
+
+ PAGED_CODE();
+
+ DevExt = GetDeviceExtension(Device);
+ WdfIoQueueStopSynchronously(DevExt->PendingRequestQueue);
+
+ return STATUS_SUCCESS;
+}
+
+_IRQL_requires_(PASSIVE_LEVEL)
+VOID
+SimSensorCheckQueuedRequest (
+ _In_ WDFDEVICE Device,
+ _In_ ULONG Temperature,
+ _Inout_ PULONG LowerBound,
+ _Inout_ PULONG UpperBound,
+ _In_ WDFREQUEST Request
+ )
+
+/*++
+
+Routine Description:
+
+ Examines a request and performs one of the following actions:
+
+ * Retires the request if it is satisfied (the sensor temperature has
+ exceeded the bounds specified in the request)
+
+ * Retires the request if it is expired (the timer due time is in the past)
+
+ * Tightens the upper and lower bounds if the request remains in the queue.
+
+Arguments:
+
+ Device - Supplies a handle to the device which owns this request.
+
+ Temperature - Supplies the current thermal zone temperature.
+
+ LowerBound - Supplies the lower bound threshold to adjust.
+
+ UpperBound - Supplies the upper bound threshold to adjust.
+
+ Request - Supplies a handle to the request.
+
+--*/
+
+{
+ ULONG BytesReturned;
+ LARGE_INTEGER CurrentTime;
+ PFDO_DATA DevExt;
+ size_t Length;
+ PREAD_REQUEST_CONTEXT Context;
+ WDFREQUEST RetrievedRequest;
+ PULONG RequestTemperature;
+ NTSTATUS Status;
+
+ DebugEnter();
+ PAGED_CODE();
+
+ KeQuerySystemTime(&CurrentTime);
+ DevExt = GetDeviceExtension(Device);
+ Context = WdfObjectGetTypedContext(Request, READ_REQUEST_CONTEXT);
+
+ RetrievedRequest = NULL;
+
+ //
+ // Complete the request if:
+ //
+ // 1. The temperature has exceeded one of the request thresholds.
+ // 2. The request timeout is in the past (but not negative).
+ //
+
+ if (SimSensorAreConstraintsSatisfied(Temperature,
+ Context->LowTemperature,
+ Context->HighTemperature,
+ Context->ExpirationTime)) {
+
+ Status = WdfIoQueueRetrieveFoundRequest(DevExt->PendingRequestQueue,
+ Request,
+ &RetrievedRequest);
+
+ if(!NT_SUCCESS(Status)) {
+ DebugPrint(SIMSENSOR_ERROR,
+ "WdfIoQueueRetrieveFoundRequest() Failed. 0x%x",
+ Status);
+
+ //
+ // Bail, likely because the request disappeared from the
+ // queue.
+ //
+
+ goto CheckQueuedRequestEnd;
+ }
+
+ Status = WdfRequestRetrieveOutputBuffer(RetrievedRequest,
+ sizeof(ULONG),
+ &RequestTemperature,
+ &Length);
+
+ if(NT_SUCCESS(Status) && (Length == sizeof(ULONG))) {
+ *RequestTemperature = Temperature;
+ BytesReturned = sizeof(ULONG);
+
+ } else {
+
+ //
+ // The request's return buffer is malformed.
+ //
+
+ BytesReturned = 0;
+ Status = STATUS_INVALID_PARAMETER;
+ DebugPrint(SIMSENSOR_ERROR,
+ "WdfRequestRetrieveOutputBuffer() Failed. 0x%x",
+ Status);
+
+ }
+
+ WdfRequestCompleteWithInformation(RetrievedRequest,
+ Status,
+ BytesReturned);
+
+ } else {
+
+ //
+ // The request will remain in the queue. Update the bounds accordingly.
+ //
+
+ if (*LowerBound < Context->LowTemperature) {
+ *LowerBound = Context->LowTemperature;
+ }
+
+ if (*UpperBound > Context->HighTemperature) {
+ *UpperBound = Context->HighTemperature;
+ }
+ }
+
+CheckQueuedRequestEnd:
+ DebugExit();
+ return;
+}
+
+VOID
+SimSensorExpiredRequestTimer (
+ WDFTIMER Timer
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is invoked when a request timer expires. A scan of the pending
+ queue to complete expired and satisfied requests is initiated.
+
+Arguments:
+
+ Timer - Supplies a handle to the timer which expired.
+
+--*/
+
+{
+
+ PFDO_DATA DevExt;
+ WDFDEVICE Device;
+
+ DebugEnter();
+ PAGED_CODE();
+
+ Device = (WDFDEVICE)WdfTimerGetParentObject(Timer);
+ DevExt = GetDeviceExtension(Device);
+ WdfWaitLockAcquire(DevExt->QueueLock, NULL);
+ SimSensorScanPendingQueue(Device);
+ WdfWaitLockRelease(DevExt->QueueLock);
+
+ DebugExit();
+}
+
+VOID
+SimSensorTemperatureInterruptWorker (
+ _In_ WDFWORKITEM WorkItem
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is invoked to call into the device to notify it of a
+ temperature change.
+
+Arguments:
+
+ WorkItem - Supplies a handle to this work item.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ PFDO_DATA DevExt;
+ WDFDEVICE Device;
+
+ Device = (WDFDEVICE)WdfWorkItemGetParentObject(WorkItem);
+ DevExt = GetDeviceExtension(Device);
+ WdfWaitLockAcquire(DevExt->QueueLock, NULL);
+ SimSensorScanPendingQueue(Device);
+ WdfWaitLockRelease(DevExt->QueueLock);
+ return;
+}
+
+VOID
+SimSensorTemperatureInterrupt (
+ _In_ WDFDEVICE Device
+ )
+
+
+/*++
+
+Routine Description:
+
+ This routine is invoked to simulate an interrupt from the virtual sensor
+ device. It performs all the work a normal ISR would perform.
+
+Arguments:
+
+ Device - Supplies a handle to the device.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ PFDO_DATA DevExt;
+
+ DevExt = GetDeviceExtension(Device);
+ WdfWorkItemEnqueue(DevExt->InterruptWorker);
+ return;
+}
+
+
+
+//-------------------------------------------------- Virtual Temperature Sensor
+
+NTSTATUS
+SimSensorDeviceD0Entry (
+ _In_ WDFDEVICE Device,
+ _In_ WDF_POWER_DEVICE_STATE PreviousState
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is invoked when the device enters the D0 power state, and
+ registers for the power policy setting that drives the virtual interrupt.
+
+Arguments:
+
+ Device - Supplies a handle to the device that is powering up.
+
+ PreviousState - Supplies the previous power state of the device.
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+
+{
+
+ PFDO_DATA DevExt;
+ PVOID Handle;
+ NTSTATUS Status;
+
+ UNREFERENCED_PARAMETER(PreviousState);
+
+ Status = PoRegisterPowerSettingCallback(
+ WdfDeviceWdmGetDeviceObject(Device),
+ &GUID_VIRTUAL_TEMPERATURE_SENSOR,
+ SimSensorSettingCallback,
+ (PVOID)Device,
+ &Handle);
+
+ if (NT_SUCCESS(Status)) {
+ DevExt = GetDeviceExtension(Device);
+ DevExt->Sensor.PolicyHandle = Handle;
+ }
+
+ return Status;
+}
+
+NTSTATUS
+SimSensorDeviceD0Exit (
+ _In_ WDFDEVICE Device,
+ _In_ WDF_POWER_DEVICE_STATE TargetState
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is invoked when the device exits the D0 power state, and
+ unregisters the power policy setting that drives the virtual interrupt.
+
+Arguments:
+
+ Device - Supplies a handle to the device that is powering up.
+
+ TargetState - Supplies the next power state of the device.
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+
+{
+
+ PFDO_DATA DevExt;
+
+ UNREFERENCED_PARAMETER(TargetState);
+
+ DevExt = GetDeviceExtension(Device);
+ if (DevExt->Sensor.PolicyHandle != NULL) {
+ PoUnregisterPowerSettingCallback(DevExt->Sensor.PolicyHandle);
+ DevExt->Sensor.PolicyHandle = NULL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+SimSensorSettingCallback (
+ _In_ LPCGUID SettingGuid,
+ _In_reads_bytes_(ValueLength) PVOID Value,
+ _In_ ULONG ValueLength,
+ _Inout_opt_ PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is invoked to notify the device of a change to the power
+ setting that drives the virtual temperature sensor interrupt.
+
+Arguments:
+
+ SettingGuid - Supplies the GUID of the power setting that changed.
+
+ Value - Supplies the power setting value.
+
+ ValueLength - Supplies the power setting value.
+
+ Context - Supplies the device to update.
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+
+{
+
+ PFDO_DATA DevExt;
+ BOOLEAN Interrupt;
+ NTSTATUS Status;
+ ULONG Temperature;
+
+ UNREFERENCED_PARAMETER(SettingGuid);
+
+ if (ValueLength != sizeof(ULONG)) {
+ Status = STATUS_INVALID_PARAMETER;
+ goto SettingCallbackEnd;
+ }
+
+ Temperature = *(PULONG)Value;
+
+ _Analysis_assume_(Context != NULL);
+ NT_ASSERT(Context != NULL);
+ DevExt = GetDeviceExtension((WDFDEVICE)Context);
+
+ WdfWaitLockAcquire(DevExt->Sensor.Lock, NULL);
+
+ //
+ // If the policy setting has reached the reset value, enable the
+ // temperature sensor. This prevents the policy from being set to a high
+ // temperture, causing a critical shutdown, and then starting out above
+ // the critical shutdown tempertaure at the next boot.
+ //
+
+ if (Temperature == VIRTUAL_SENSOR_RESET_TEMPERATURE) {
+ DevExt->Sensor.Enabled = TRUE;
+ }
+
+ //
+ // If the reset value hasn't been reached yet, use a known safe low
+ // temperature as a placeholder.
+ //
+
+ if (DevExt->Sensor.Enabled == FALSE) {
+ Temperature = VIRTUAL_SENSOR_RESET_TEMPERATURE;
+ }
+
+ //
+ // Special case boundary temperature values to avoid logic for handling
+ // them elsewhere. Avoid the boundary values because it is impossible for
+ // the system to request a lower or higher value. This should not be an
+ // issue for a real sensor device.
+ //
+
+ if (Temperature == 0) {
+ Temperature = 1;
+ }
+
+ if (Temperature == (ULONG)-1) {
+ Temperature = (ULONG)-2;
+ }
+
+ DevExt->Sensor.Temperature = Temperature;
+
+ //
+ // Check to see if the temperature has exceeded either of the thresholds
+ // for noticing a temperature change. If so, the virtual interrupt will
+ // need to be fired.
+ //
+
+ if ((DevExt->Sensor.Temperature <= DevExt->Sensor.LowerBound) ||
+ (DevExt->Sensor.Temperature >= DevExt->Sensor.UpperBound)) {
+
+ Interrupt = TRUE;
+
+ } else {
+ Interrupt = FALSE;
+ }
+
+ WdfWaitLockRelease(DevExt->Sensor.Lock);
+
+ //
+ // Fire the virtual interrupt outside the lock, to avoid any locking issues.
+ //
+
+ if (Interrupt != FALSE) {
+ SimSensorTemperatureInterrupt((WDFDEVICE)Context);
+ }
+
+ Status = STATUS_SUCCESS;
+
+SettingCallbackEnd:
+ return Status;
+}
+
+VOID
+SimSensorSetVirtualInterruptThresholds (
+ _In_ WDFDEVICE Device,
+ _In_ ULONG LowerBound,
+ _In_ ULONG UpperBound
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is invoked to change the thresholds the virtual sensor driver
+ uses to compare against for an interrupt to occur.
+
+Arguments:
+
+ Device - Supplies a handle to the device.
+
+ LowerBound - Supplies the temperature below which the device should issue
+ an interrupt.
+
+ UpperBound - Supplies the temperature above which the device should issue
+ an interrupt.
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+
+{
+
+ PFDO_DATA DevExt;
+ BOOLEAN Interrupt;
+
+ DevExt = GetDeviceExtension(Device);
+ WdfWaitLockAcquire(DevExt->Sensor.Lock, NULL);
+
+ DevExt->Sensor.LowerBound = LowerBound;
+ DevExt->Sensor.UpperBound = UpperBound;
+ if ((DevExt->Sensor.Temperature <= DevExt->Sensor.LowerBound) ||
+ (DevExt->Sensor.Temperature >= DevExt->Sensor.UpperBound)) {
+
+ Interrupt = TRUE;
+
+ } else {
+ Interrupt = FALSE;
+ }
+
+ WdfWaitLockRelease(DevExt->Sensor.Lock);
+
+ //
+ // Fire the virtual interrupt outside the lock, to avoid any locking issues.
+ //
+
+ if (Interrupt != FALSE) {
+ SimSensorTemperatureInterrupt(Device);
+ }
+
+ return;
+}
+
+ULONG
+SimSensorReadVirtualTemperature (
+ _In_ WDFDEVICE Device
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is invoked to read the current temperature of the device.
+
+Arguments:
+
+ Device - Supplies a handle to the device.
+
+ LowerBound - Supplies the temperature below which the device should issue
+ an interrupt.
+
+ UpperBound - Supplies the temperature above which the device should issue
+ an interrupt.
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+
+{
+
+ PFDO_DATA DevExt;
+ ULONG Temperature;
+
+ DevExt = GetDeviceExtension(Device);
+ WdfWaitLockAcquire(DevExt->Sensor.Lock, NULL);
+ Temperature = DevExt->Sensor.Temperature;
+ WdfWaitLockRelease(DevExt->Sensor.Lock);
+ return Temperature;
+}
+
+