summaryrefslogtreecommitdiff
path: root/powerlimit/plclient/plclient.c
diff options
context:
space:
mode:
Diffstat (limited to 'powerlimit/plclient/plclient.c')
-rw-r--r--powerlimit/plclient/plclient.c451
1 files changed, 451 insertions, 0 deletions
diff --git a/powerlimit/plclient/plclient.c b/powerlimit/plclient/plclient.c
new file mode 100644
index 00000000..41676590
--- /dev/null
+++ b/powerlimit/plclient/plclient.c
@@ -0,0 +1,451 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+ plclient.c
+
+Abstract:
+
+ This module implements power limit related operations for the simulated power
+ limit client driver.
+
+--*/
+
+//-------------------------------------------------------------------- Includes
+
+#include "plclient.h"
+
+//--------------------------------------------------------------------- Pragmas
+
+#pragma alloc_text(PAGE, InitPowerLimitValues)
+#pragma alloc_text(PAGE, CleanupPowerLimitValues)
+#pragma alloc_text(PAGE, PLCQueryAttributes)
+#pragma alloc_text(PAGE, PLCSetLimits)
+#pragma alloc_text(PAGE, PLCQueryLimitValues)
+
+//------------------------------------------------------------------- Functions
+
+_Use_decl_annotations_
+NTSTATUS
+InitPowerLimitValues (
+ PFDO_DATA DevExt
+ )
+
+/*++
+
+Routine Description:
+
+ This routine initializes simulated limit values and attributes for the supplied
+ device extension.
+
+Parameters Description:
+
+ DevExt - Supplies a pointer to the device extension to be udpated.
+
+Return Value:
+
+ NTSTATUS.
+
+--*/
+
+{
+
+ ULONG DomainId;
+ ULONG Index;
+ PPOWER_LIMIT_ATTRIBUTES LimitAttributes;
+ ULONG LimitCount;
+ PPOWER_LIMIT_VALUE LimitValues;
+ NTSTATUS Status;
+ ULONG Type;
+
+ PAGED_CODE();
+
+ LimitAttributes = NULL;
+ LimitValues = NULL;
+ LimitCount = PLCLIENT_DEFAULT_DOMAIN_COUNT * PLCLIENT_DEFAULT_LIMIT_COUNT_PER_DOMAIN;
+ LimitAttributes = ExAllocatePool2(POOL_FLAG_PAGED,
+ LimitCount * sizeof(POWER_LIMIT_ATTRIBUTES),
+ PLCLIENT_TAG);
+
+ LimitValues = ExAllocatePool2(POOL_FLAG_PAGED,
+ LimitCount * sizeof(POWER_LIMIT_VALUE),
+ PLCLIENT_TAG);
+
+ if ((LimitAttributes == NULL) || (LimitValues == NULL)) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto InitPowerLimitValuesEnd;
+ }
+
+ for (DomainId = 0; DomainId < PLCLIENT_DEFAULT_DOMAIN_COUNT; DomainId += 1) {
+ for (Type = 0; Type < PLCLIENT_DEFAULT_LIMIT_COUNT_PER_DOMAIN; Type += 1) {
+ Index = (PLCLIENT_DEFAULT_LIMIT_COUNT_PER_DOMAIN * DomainId) + Type;
+
+ //
+ // Set init attributes.
+ //
+
+ LimitAttributes[Index].Type = Type;
+ LimitAttributes[Index].DomainId = DomainId;
+ LimitAttributes[Index].MaxValue = PLCLIENT_DEFAULT_MAX_VALUE;
+ LimitAttributes[Index].MinValue = PLCLIENT_DEFAULT_MIN_VALUE;
+ LimitAttributes[Index].DefaultACValue = POWER_LIMIT_VALUE_NO_CONTROL;
+ LimitAttributes[Index].DefaultDCValue = POWER_LIMIT_VALUE_NO_CONTROL;
+
+ if (Type == PowerLimitContinuous) {
+ LimitAttributes[Index].MinTimeParameter = PLCLIENT_DEFAULT_MIN_VALUE;
+ LimitAttributes[Index].MaxTimeParameter = PLCLIENT_DEFAULT_MAX_VALUE;
+ LimitAttributes[Index].Flags.SupportTimeParameter = 1;
+ }
+
+ //
+ // Set init values.
+ //
+
+ LimitValues[Index].Type = Type;
+ LimitValues[Index].DomainId = DomainId;
+ LimitValues[Index].TargetValue = POWER_LIMIT_VALUE_NO_CONTROL;
+ LimitValues[Index].TimeParameter = POWER_LIMIT_VALUE_NO_CONTROL;
+ }
+ }
+
+ DevExt->LimitCount = LimitCount;
+ DevExt->LimitAttributes = LimitAttributes;
+ DevExt->LimitValues = LimitValues;
+ LimitAttributes = NULL;
+ LimitValues = NULL;
+ Status = STATUS_SUCCESS;
+
+InitPowerLimitValuesEnd:
+ if (LimitAttributes != NULL) {
+ ExFreePoolWithTag(LimitAttributes, PLCLIENT_TAG);
+ LimitAttributes = NULL;
+ }
+
+ if (LimitValues != NULL) {
+ ExFreePoolWithTag(LimitValues, PLCLIENT_TAG);
+ LimitValues = NULL;
+ }
+
+ return Status;
+}
+
+_Use_decl_annotations_
+VOID
+CleanupPowerLimitValues (
+ PFDO_DATA DevExt
+ )
+
+/*++
+
+Routine Description:
+
+ This routine cleans up simulated limit values and attributes for the supplied
+ device extension.
+
+Parameters Description:
+
+ DevExt - Supplies a pointer to the device extension to be udpated.
+
+Return Value:
+
+ NTSTATUS.
+
+--*/
+
+{
+
+ PAGED_CODE();
+
+ if (DevExt == NULL) {
+ goto CleanupPowerLimitValuesEnd;
+ }
+
+ if (DevExt->LimitAttributes != NULL) {
+ ExFreePoolWithTag(DevExt->LimitAttributes, PLCLIENT_TAG);
+ DevExt->LimitAttributes = NULL;
+ }
+
+ if (DevExt->LimitValues != NULL) {
+ ExFreePoolWithTag(DevExt->LimitValues, PLCLIENT_TAG);
+ DevExt->LimitValues = NULL;
+ }
+
+ DevExt->LimitCount = 0;
+
+CleanupPowerLimitValuesEnd:
+ return;
+}
+
+_Use_decl_annotations_
+NTSTATUS
+PLCQueryAttributes (
+ PVOID Context,
+ ULONG BufferCount,
+ PVOID Buffer,
+ PULONG AttributeCount
+ )
+
+/*++
+
+Routine Description:
+
+ This is the callback function which returns power limit attributes.
+
+Parameters Description:
+
+ Context - Supplies a pointer to the device handle.
+
+ BufferCount - Supplies count of Buffer entries.
+
+ Buffer - Supplies a pointer to the buffer to store power limit attributes.
+
+ AttributeCount - Supplies a pointer to save the number of attributes.
+
+Return Value:
+
+ Returns STATUS_BUFFER_TOO_SMALL if the supplied buffer is not big enough, otherwise
+ other NTSTATUS values.
+
+--*/
+
+{
+
+ PFDO_DATA DevExt;
+ WDFDEVICE DeviceHandle;
+ BOOLEAN ReleaseLock;
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ ReleaseLock = FALSE;
+ if (Context == NULL) {
+ Status = STATUS_INVALID_PARAMETER;
+ goto QueryAttributesEnd;
+ }
+
+ DeviceHandle = (WDFDEVICE)Context;
+ DevExt = GetDeviceExtension(DeviceHandle);
+ AcquireGlobalMutex();
+ ReleaseLock = TRUE;
+ if (BufferCount < DevExt->LimitCount) {
+ if (AttributeCount != NULL) {
+ *AttributeCount = DevExt->LimitCount;
+ }
+
+ Status = STATUS_BUFFER_TOO_SMALL;
+ goto QueryAttributesEnd;
+ }
+
+ RtlCopyMemory(Buffer,
+ DevExt->LimitAttributes,
+ sizeof(POWER_LIMIT_ATTRIBUTES) * DevExt->LimitCount);
+
+ Status = STATUS_SUCCESS;
+
+QueryAttributesEnd:
+ if (ReleaseLock != FALSE) {
+ ReleaseGlobalMutex();
+ }
+
+ return Status;
+}
+
+_Use_decl_annotations_
+NTSTATUS
+PLCSetLimits (
+ PVOID Context,
+ ULONG ValueCount,
+ PVOID Values
+ )
+
+/*++
+
+Routine Description:
+
+ This is the callback function which takes requests to set power limit values.
+
+Parameters Description:
+
+ Context - Supplies a pointer to the device handle.
+
+ ValueCount - Supplies count of Value entries.
+
+ Values - Supplies a pointer to the buffer contains values to be updated.
+
+Return Value:
+
+ NTSTATUS.
+
+--*/
+
+{
+
+ PPOWER_LIMIT_ATTRIBUTES Attributes;
+ PFDO_DATA DevExt;
+ WDFDEVICE DeviceHandle;
+ ULONG Index;
+ BOOLEAN ReleaseLock;
+ NTSTATUS Status;
+ BOOLEAN Valid;
+ PPOWER_LIMIT_VALUE ValueBuffer;
+ ULONG ValueIndex;
+
+ PAGED_CODE();
+
+ ReleaseLock = FALSE;
+ if ((Context == NULL) || (ValueCount == 0) || (Values == NULL)) {
+ Status = STATUS_INVALID_PARAMETER;
+ goto SetLimitsEnd;
+ }
+
+ ValueBuffer = (PPOWER_LIMIT_VALUE)Values;
+ DeviceHandle = (WDFDEVICE)Context;
+ DevExt = GetDeviceExtension(DeviceHandle);
+ AcquireGlobalMutex();
+ ReleaseLock = TRUE;
+
+ //
+ // Sanity check on proposed values before update.
+ //
+
+ if (DevExt->LimitCount < ValueCount) {
+ Status = STATUS_INVALID_PARAMETER;
+ goto SetLimitsEnd;
+ }
+
+ //
+ // N.B. On a production driver, those values should be used as power limit targets
+ // for the hardware.
+ //
+
+ for (Index = 0; Index < ValueCount; Index += 1) {
+ Valid = FALSE;
+ for (ValueIndex = 0; ValueIndex < DevExt->LimitCount; ValueIndex += 1) {
+ if ((ValueBuffer[Index].Type != DevExt->LimitAttributes[ValueIndex].Type) ||
+ (ValueBuffer[Index].DomainId != DevExt->LimitAttributes[ValueIndex].DomainId)) {
+
+ continue;
+ }
+
+ Attributes = &DevExt->LimitAttributes[ValueIndex];
+ if ((ValueBuffer[Index].TargetValue == POWER_LIMIT_VALUE_NO_CONTROL) ||
+ ((ValueBuffer[Index].TargetValue >= Attributes->MinValue) &&
+ (ValueBuffer[Index].TargetValue <= Attributes->MaxValue))) {
+
+ Valid = TRUE;
+ }
+
+ if (ValueBuffer[Index].TimeParameter != POWER_LIMIT_VALUE_NO_CONTROL) {
+ if ((Attributes->Flags.SupportTimeParameter != 0) &&
+ (ValueBuffer[Index].TimeParameter >= Attributes->MinTimeParameter) &&
+ (ValueBuffer[Index].TimeParameter <= Attributes->MaxTimeParameter)) {
+
+ Valid = TRUE;
+ }
+ }
+
+ break;
+ }
+
+ //
+ // N.B. Bail out if this proposed value is not valid.
+ //
+
+ if (Valid == FALSE) {
+ Status = STATUS_INVALID_PARAMETER;
+ goto SetLimitsEnd;
+ }
+ }
+
+ for (Index = 0; Index < ValueCount; Index += 1) {
+ for (ValueIndex = 0; ValueIndex < DevExt->LimitCount; ValueIndex += 1) {
+ if ((ValueBuffer[Index].Type != DevExt->LimitValues[ValueIndex].Type) ||
+ (ValueBuffer[Index].DomainId != DevExt->LimitValues[ValueIndex].DomainId)) {
+
+ continue;
+ }
+
+ DevExt->LimitValues[ValueIndex].TargetValue = ValueBuffer[Index].TargetValue;
+ DevExt->LimitValues[ValueIndex].TimeParameter = ValueBuffer[Index].TimeParameter;
+ break;
+ }
+ }
+
+ Status = STATUS_SUCCESS;
+
+SetLimitsEnd:
+ if (ReleaseLock != FALSE) {
+ ReleaseGlobalMutex();
+ }
+
+ return Status;
+}
+
+_Use_decl_annotations_
+NTSTATUS
+PLCQueryLimitValues (
+ PVOID Context,
+ ULONG ValueCount,
+ PVOID Values
+ )
+
+/*++
+
+Routine Description:
+
+ This is the callback function which returns power limit values.
+
+Parameters Description:
+
+ Context - Supplies a pointer to the device handle.
+
+ ValueCount - Supplies count of Value entries.
+
+ Values - Supplies a pointer to the buffer to store power limit values.
+
+Return Value:
+
+ Returns STATUS_BUFFER_TOO_SMALL if the supplied buffer is not big enough, otherwise
+ other NTSTATUS values.
+
+--*/
+
+{
+
+ PFDO_DATA DevExt;
+ WDFDEVICE DeviceHandle;
+ BOOLEAN ReleaseLock;
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ ReleaseLock = FALSE;
+ if ((Context == NULL) || (ValueCount == 0) || (Values == NULL)){
+ Status = STATUS_INVALID_PARAMETER;
+ goto QueryLimitsEnd;
+ }
+
+ DeviceHandle = (WDFDEVICE)Context;
+ DevExt = GetDeviceExtension(DeviceHandle);
+ AcquireGlobalMutex();
+ ReleaseLock = TRUE;
+ if (ValueCount < DevExt->LimitCount) {
+ Status = STATUS_BUFFER_TOO_SMALL;
+ goto QueryLimitsEnd;
+ }
+
+ RtlCopyMemory(Values,
+ DevExt->LimitValues,
+ sizeof(POWER_LIMIT_VALUE) * DevExt->LimitCount);
+
+ Status = STATUS_SUCCESS;
+
+QueryLimitsEnd:
+ if (ReleaseLock != FALSE) {
+ ReleaseGlobalMutex();
+ }
+
+ return Status;
+}