summaryrefslogtreecommitdiff
path: root/simemi/simemipdo.c
diff options
context:
space:
mode:
Diffstat (limited to 'simemi/simemipdo.c')
-rw-r--r--simemi/simemipdo.c866
1 files changed, 866 insertions, 0 deletions
diff --git a/simemi/simemipdo.c b/simemi/simemipdo.c
new file mode 100644
index 00000000..2cb9f3d6
--- /dev/null
+++ b/simemi/simemipdo.c
@@ -0,0 +1,866 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+ simemifdo.c
+
+Abstract:
+
+ This module contains the implementation of the routines related to the
+ simemi virtual child device.
+
+--*/
+
+#include "simemi.h"
+#include <devguid.h>
+#include <ntintsafe.h>
+#include <ntstrsafe.h>
+
+NTSTATUS
+SimEmiPdoCalculateDataSize (
+ _In_ USHORT ChannelCount,
+ _In_ PSIM_EMI_BUS_CHANNEL_INFO Info,
+ _Out_ PULONG PdoDataSizeOut
+ );
+
+NTSTATUS
+SimEmiPdoCalculateMetadataSize (
+ _In_ PSIM_EMI_BUS_PDO_DATA PdoData,
+ _Out_ PULONG MetadataSizeOut
+ );
+
+NTSTATUS
+SimEmiPdoCopyChannelMeasurements (
+ _In_ PSIM_EMI_BUS_PDO_DATA PdoData,
+ _Out_ EMI_CHANNEL_MEASUREMENT_DATA *MeasurementData
+ );
+
+NTSTATUS
+SimEmiPdoCopyDeviceMetadata (
+ _In_ PSIM_EMI_BUS_PDO_DATA PdoData,
+ _Out_ PVOID MetadataBuffer
+ );
+
+EVT_WDF_DEVICE_CONTEXT_DESTROY SimEmiPdoDestroyDevice;
+
+VOID
+SimEmiPdoInitializePdoData (
+ _In_ PSIM_EMI_BUS_PDO_IDENTIFICATION_INFO Identification,
+ _Out_ PSIM_EMI_BUS_PDO_DATA PdoData
+ );
+
+#pragma alloc_text(PAGE, SimEmiPdoCalculateDataSize)
+#pragma alloc_text(PAGE, SimEmiPdoCalculateMetadataSize)
+#pragma alloc_text(PAGE, SimEmiPdoControl)
+#pragma alloc_text(PAGE, SimEmiPdoCopyChannelMeasurements)
+#pragma alloc_text(PAGE, SimEmiPdoDestroyDevice)
+#pragma alloc_text(PAGE, SimEmiPdoCopyDeviceMetadata)
+#pragma alloc_text(PAGE, SimEmiPdoCreateDevice)
+#pragma alloc_text(PAGE, SimEmiPdoInitializePdoData)
+
+NTSTATUS
+SimEmiPdoCalculateDataSize (
+ _In_ USHORT ChannelCount,
+ _In_ PSIM_EMI_BUS_CHANNEL_INFO Info,
+ _Out_ PULONG PdoDataSizeOut
+ )
+
+/*++
+
+Routine Description:
+
+ This routine calculates the required size of the child device's PDO data.
+
+Arguments:
+
+ ChannelCount - Supplies the number of channels for this child device.
+
+ Info - Supplies a pointer to an array of channel info structures for this
+ child device.
+
+ PdoDataSizeOut - Supplies a pointer in which to place the required size of
+ the child device's PDO data.
+
+Return Value:
+
+ NTSTATUS.
+
+--*/
+
+{
+
+ ULONG EntrySize;
+ ULONG PdoDataSize;
+ NTSTATUS Status;
+
+ Status = STATUS_SUCCESS;
+ PdoDataSize = FIELD_OFFSET(SIM_EMI_BUS_PDO_DATA, ChannelData);
+ while (ChannelCount != 0) {
+ EntrySize = SIM_EMI_CHANNEL_DATA_SIZE(Info->ChannelNameSize);
+ Status = RtlULongAdd(PdoDataSize, EntrySize, &PdoDataSize);
+ if (!NT_SUCCESS(Status)) {
+ goto PdoCalculateDataSizeEnd;
+ }
+
+ Info = SIM_EMI_BUS_CHANNEL_INFO_NEXT_CHANNEL_INFO(Info);
+ ChannelCount -= 1;
+ }
+
+ *PdoDataSizeOut = PdoDataSize;
+
+PdoCalculateDataSizeEnd:
+ return Status;
+}
+
+NTSTATUS
+SimEmiPdoCalculateMetadataSize (
+ _In_ PSIM_EMI_BUS_PDO_DATA PdoData,
+ _Out_ PULONG MetadataSizeOut
+ )
+
+/*++
+
+Routine Description:
+
+ This routine calculates the size of a child device's EMI metadata
+ structure.
+
+Arguments:
+
+ PdoData - Supplies a pointer to the child device's PDO data.
+
+ MetadataSizeOut - Supplies a pointer in which to place the required size of
+ this child device's EMI metadata structure.
+
+Return Value:
+
+ NTSTATUS.
+
+--*/
+
+{
+
+ USHORT ChannelCount;
+ PSIM_EMI_CHANNEL_DATA ChannelData;
+ ULONG EntrySize;
+ ULONG MetadataSize;
+ NTSTATUS Status;
+
+ NT_ASSERT((PdoData->EmiVersion == EMI_VERSION_V1) ||
+ (PdoData->EmiVersion == EMI_VERSION_V2));
+
+ if (PdoData->EmiVersion == EMI_VERSION_V1) {
+
+ NT_ASSERT(PdoData->ChannelCount == 1);
+
+ MetadataSize = FIELD_OFFSET(EMI_METADATA_V1, MeteredHardwareName);
+ Status = RtlULongAdd(MetadataSize,
+ PdoData->ChannelData[0].Info.ChannelNameSize,
+ &MetadataSize);
+
+ if (!NT_SUCCESS(Status)) {
+ goto PdoCalculateMetadataSizeEnd;
+ }
+
+ } else if (PdoData->EmiVersion == EMI_VERSION_V2) {
+
+ NT_ASSERT(PdoData->ChannelCount > 0);
+
+ MetadataSize = FIELD_OFFSET(EMI_METADATA_V2, Channels);
+ ChannelCount = PdoData->ChannelCount;
+ ChannelData = &PdoData->ChannelData[0];
+ while (ChannelCount != 0) {
+ EntrySize =
+ EMI_CHANNEL_V2_LENGTH(ChannelData->Info.ChannelNameSize);
+
+ Status = RtlULongAdd(MetadataSize, EntrySize, &MetadataSize);
+ if (!NT_SUCCESS(Status)) {
+ goto PdoCalculateMetadataSizeEnd;
+ }
+
+ ChannelData = SIM_EMI_CHANNEL_DATA_NEXT_CHANNEL_DATA(ChannelData);
+ ChannelCount -= 1;
+ }
+
+ } else {
+ Status = STATUS_INVALID_DEVICE_STATE;
+ goto PdoCalculateMetadataSizeEnd;
+ }
+
+ *MetadataSizeOut = MetadataSize;
+ Status = STATUS_SUCCESS;
+
+PdoCalculateMetadataSizeEnd:
+ return Status;
+}
+
+VOID
+SimEmiPdoControl (
+ _In_ WDFQUEUE Queue,
+ _In_ WDFREQUEST Request,
+ _In_ size_t OutputBufferLength,
+ _In_ size_t InputBufferLength,
+ _In_ ULONG IoControlCode
+ )
+
+/*++
+
+Routine Description:
+
+ This routine handles device control requests for an EMI child device.
+
+Arguments:
+
+ Queue - Supplies a reference to the WDF queue that this request originates.
+
+ Request - Supplies a reference to the WDF request that was sent.
+
+ OutputBufferLength - Supplies the length of the output buffer in bytes.
+
+ InputBufferLength - Supplies the length of the input buffer in bytes.
+
+ IoControlCode - Supplies the IOCTL for the request.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ WDFDEVICE Device;
+ EMI_CHANNEL_MEASUREMENT_DATA *EmiMeasurementDataBuffer;
+ ULONG EmiMeasurementDataBufferSize;
+ PVOID EmiMetadataBuffer;
+ EMI_METADATA_SIZE *EmiMetadataSizeBuffer;
+ EMI_VERSION *EmiVersionBuffer;
+ ULONG MetadataSize;
+ PSIM_EMI_BUS_PDO_DATA PdoData;
+ ULONG OutputSize;
+ NTSTATUS Status;
+
+ UNREFERENCED_PARAMETER(OutputBufferLength);
+ UNREFERENCED_PARAMETER(InputBufferLength);
+
+ Device = WdfIoQueueGetDevice(Queue);
+ PdoData = SimEmiGetPdoData(Device);
+ ExAcquireFastMutex(PdoData->BusMutex);
+ OutputSize = 0;
+ switch (IoControlCode) {
+ case IOCTL_EMI_GET_VERSION:
+ Status = WdfRequestRetrieveOutputBuffer(Request,
+ sizeof(EMI_VERSION),
+ (PVOID*)&EmiVersionBuffer,
+ NULL);
+
+ if (!NT_SUCCESS(Status)) {
+ goto PdoControlEnd;
+ }
+
+ EmiVersionBuffer->EmiVersion = PdoData->EmiVersion;
+ OutputSize = sizeof(EMI_VERSION);
+ Status = STATUS_SUCCESS;
+ break;
+
+ case IOCTL_EMI_GET_METADATA_SIZE:
+ Status = WdfRequestRetrieveOutputBuffer(Request,
+ sizeof(EMI_METADATA_SIZE),
+ (PVOID*)&EmiMetadataSizeBuffer,
+ NULL);
+
+ if (!NT_SUCCESS(Status)) {
+ goto PdoControlEnd;
+ }
+
+ Status = SimEmiPdoCalculateMetadataSize(PdoData, &MetadataSize);
+ if (!NT_SUCCESS(Status)) {
+ goto PdoControlEnd;
+ }
+
+ EmiMetadataSizeBuffer->MetadataSize = MetadataSize;
+ OutputSize = sizeof(EMI_METADATA_SIZE);
+ Status = STATUS_SUCCESS;
+ break;
+
+ case IOCTL_EMI_GET_METADATA:
+ Status = SimEmiPdoCalculateMetadataSize(PdoData, &MetadataSize);
+ if (!NT_SUCCESS(Status)) {
+ goto PdoControlEnd;
+ }
+
+ Status = WdfRequestRetrieveOutputBuffer(Request,
+ MetadataSize,
+ &EmiMetadataBuffer,
+ NULL);
+
+ if (!NT_SUCCESS(Status)) {
+ goto PdoControlEnd;
+ }
+
+ Status = SimEmiPdoCopyDeviceMetadata(PdoData, EmiMetadataBuffer);
+ if (!NT_SUCCESS(Status)) {
+ goto PdoControlEnd;
+ }
+
+ OutputSize = MetadataSize;
+ Status = STATUS_SUCCESS;
+ break;
+
+ case IOCTL_EMI_GET_MEASUREMENT:
+ Status = RtlULongMult(sizeof(EMI_CHANNEL_MEASUREMENT_DATA),
+ PdoData->ChannelCount,
+ &EmiMeasurementDataBufferSize);
+
+ if (!NT_SUCCESS(Status)) {
+ goto PdoControlEnd;
+ }
+
+ Status = WdfRequestRetrieveOutputBuffer(
+ Request,
+ EmiMeasurementDataBufferSize,
+ (PVOID*)&EmiMeasurementDataBuffer,
+ NULL);
+
+ if (!NT_SUCCESS(Status)) {
+ goto PdoControlEnd;
+ }
+
+ Status = SimEmiPdoCopyChannelMeasurements(PdoData,
+ EmiMeasurementDataBuffer);
+
+ if (!NT_SUCCESS(Status)) {
+ goto PdoControlEnd;
+ }
+
+ OutputSize = EmiMeasurementDataBufferSize;
+ Status = STATUS_SUCCESS;
+ break;
+
+ default:
+ Status = STATUS_INVALID_DEVICE_REQUEST;
+ break;
+ }
+
+PdoControlEnd:
+ WdfRequestCompleteWithInformation(Request, Status, OutputSize);
+ ExReleaseFastMutex(PdoData->BusMutex);
+ return;
+}
+
+NTSTATUS
+SimEmiPdoCopyChannelMeasurements (
+ _In_ PSIM_EMI_BUS_PDO_DATA PdoData,
+ _Out_ EMI_CHANNEL_MEASUREMENT_DATA *MeasurementData
+ )
+
+/*++
+
+Routine Description:
+
+ This routine copies the channel measurement data for each channel in this
+ device to the provided output buffer.
+
+Arguments:
+
+ PdoData - Supplies a pointer to the child device's PDO data.
+
+ MeasurementData - Supplies a pointer to an array of EMI measurement data
+ instances in which to place each channels measurement data.
+
+Return Value:
+
+ NTSTATUS.
+
+--*/
+{
+
+ USHORT ChannelCount;
+ PSIM_EMI_CHANNEL_DATA ChannelData;
+ PSIM_EMI_BUS_CHANNEL_INFO ChannelInfo;
+ ULONG64 CurrentTime;
+ ULONG64 EnergyGain;
+ ULONG64 PollTimeDelta;
+ NTSTATUS Status;
+
+ NT_ASSERT((PdoData->EmiVersion == EMI_VERSION_V1) ||
+ (PdoData->EmiVersion == EMI_VERSION_V2));
+
+ if ((PdoData->EmiVersion == EMI_VERSION_V1) ||
+ (PdoData->EmiVersion == EMI_VERSION_V2)) {
+
+ ChannelCount = PdoData->ChannelCount;
+ ChannelData = &PdoData->ChannelData[0];
+ CurrentTime = KeQueryInterruptTime();
+ while (ChannelCount != 0) {
+ ChannelInfo = &ChannelData->Info;
+
+ //
+ // Update channel measurement.
+ //
+
+ NT_ASSERT(CurrentTime >= ChannelData->LastPollTime);
+
+ PollTimeDelta = CurrentTime - ChannelData->LastPollTime;
+ EnergyGain = PollTimeDelta * ChannelInfo->AbsoluteEnergyRate;
+ ChannelData->LastAbsoluteEnergy += EnergyGain;
+ ChannelData->LastPollTime = CurrentTime;
+
+ //
+ // Copy channel measurement.
+ //
+
+ MeasurementData->AbsoluteEnergy = ChannelData->LastAbsoluteEnergy;
+ MeasurementData->AbsoluteTime = ChannelData->LastPollTime;
+
+ //
+ // Move to next channel.
+ //
+
+ ChannelData = SIM_EMI_CHANNEL_DATA_NEXT_CHANNEL_DATA(ChannelData);
+ MeasurementData += 1;
+ ChannelCount -= 1;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ } else {
+ Status = STATUS_INVALID_DEVICE_STATE;
+ goto PdoCopyChannelMeasurementsEnd;
+ }
+
+PdoCopyChannelMeasurementsEnd:
+ return Status;
+}
+
+
+NTSTATUS
+SimEmiPdoCopyDeviceMetadata (
+ _In_ PSIM_EMI_BUS_PDO_DATA PdoData,
+ _Out_ PVOID MetadataBuffer
+ )
+
+/*++
+
+Routine Description:
+
+ This routine copies the child device's EMI metadata to the provided output
+ buffer.
+
+Arguments:
+
+ PdoData - Supplies a pointer to the child device's PDO data.
+
+ MetadataBuffer - Supplies a pointer in which to place the child device's
+ EMI metadata.
+
+Return Value:
+
+ NTSTATUS.
+
+--*/
+
+{
+
+ USHORT ChannelCount;
+ PSIM_EMI_CHANNEL_DATA ChannelData;
+ PSIM_EMI_BUS_CHANNEL_INFO ChannelInfo;
+ EMI_CHANNEL_V2 *EmiV2Channel;
+ NTSTATUS Status;
+ EMI_METADATA_V1 *V1Buffer;
+ EMI_METADATA_V2 *V2Buffer;
+
+ NT_ASSERT((PdoData->EmiVersion == EMI_VERSION_V1) ||
+ (PdoData->EmiVersion == EMI_VERSION_V2));
+
+ if (PdoData->EmiVersion == EMI_VERSION_V1) {
+
+ NT_ASSERT(PdoData->ChannelCount == 1);
+
+ V1Buffer = (EMI_METADATA_V1*)MetadataBuffer;
+ ChannelData = &PdoData->ChannelData[0];
+ ChannelInfo = &ChannelData->Info;
+ V1Buffer->MeasurementUnit = ChannelInfo->MeasurementUnit;
+ RtlStringCchCopyW(&V1Buffer->HardwareOEM[0],
+ EMI_NAME_MAX,
+ &SimEmiHardwareOEM[0]);
+
+ RtlStringCchCopyW(&V1Buffer->HardwareModel[0],
+ EMI_NAME_MAX,
+ &SimEmiHardwareModelV1[0]);
+
+ V1Buffer->HardwareRevision = SimEmiHardwareRevisionV1;
+ V1Buffer->MeteredHardwareNameSize = ChannelInfo->ChannelNameSize;
+ RtlCopyMemory(&V1Buffer->MeteredHardwareName[0],
+ &ChannelInfo->ChannelName[0],
+ ChannelInfo->ChannelNameSize);
+
+ } else if (PdoData->EmiVersion == EMI_VERSION_V2) {
+
+ NT_ASSERT(PdoData->ChannelCount > 0);
+
+ V2Buffer = (EMI_METADATA_V2*)MetadataBuffer;
+ RtlStringCchCopyW(&V2Buffer->HardwareOEM[0],
+ EMI_NAME_MAX,
+ &SimEmiHardwareOEM[0]);
+
+ RtlStringCchCopyW(&V2Buffer->HardwareModel[0],
+ EMI_NAME_MAX,
+ &SimEmiHardwareModelV1[0]);
+
+ V2Buffer->HardwareRevision = SimEmiHardwareRevisionV1;
+ V2Buffer->ChannelCount = PdoData->ChannelCount;
+
+ //
+ // Copy channel information.
+ //
+
+ ChannelCount = PdoData->ChannelCount;
+ ChannelData = &PdoData->ChannelData[0];
+ EmiV2Channel = &V2Buffer->Channels[0];
+ while (ChannelCount != 0) {
+ ChannelInfo = &ChannelData->Info;
+ EmiV2Channel->MeasurementUnit = ChannelInfo->MeasurementUnit;
+ EmiV2Channel->ChannelNameSize = ChannelInfo->ChannelNameSize;
+ RtlCopyMemory(&EmiV2Channel->ChannelName[0],
+ &ChannelInfo->ChannelName[0],
+ ChannelInfo->ChannelNameSize);
+
+ ChannelData = SIM_EMI_CHANNEL_DATA_NEXT_CHANNEL_DATA(ChannelData);
+ EmiV2Channel = EMI_CHANNEL_V2_NEXT_CHANNEL(EmiV2Channel);
+ ChannelCount -= 1;
+ }
+
+ } else {
+ Status = STATUS_INVALID_DEVICE_STATE;
+ goto PdoCopyDeviceMetadataEnd;
+ }
+
+ Status = STATUS_SUCCESS;
+
+PdoCopyDeviceMetadataEnd:
+ return Status;
+}
+
+NTSTATUS
+SimEmiPdoCreateDevice (
+ _In_ WDFCHILDLIST ChildList,
+ _In_ PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER IdentificationDescription,
+ _In_ PWDFDEVICE_INIT ChildInit
+ )
+
+/*++
+
+Routine Description:
+
+ This routine creates the PDO for an EMI child device.
+
+Arguments:
+
+ ChildList - Supplies a reference to a WDFCHILDLIST in which this device is
+ being added.
+
+ IdentificationDescription - Supplies a pointer to the identification info
+ instance for this newly created child device.
+
+ ChildInit - Supplies a pointer to a WDFDEVICE_INIT instance for this child
+ device's PDO.
+
+Return Value:
+
+ NTSTATUS.
+
+--*/
+
+{
+
+ UNICODE_STRING Buffer;
+ WCHAR BufferStore[256];
+ WDFDEVICE FdoDevice;
+ PSIM_EMI_BUS_FDO_DATA FdoData;
+ PSIM_EMI_BUS_PDO_IDENTIFICATION_INFO Identification;
+ WDF_OBJECT_ATTRIBUTES PdoAttributes;
+ PSIM_EMI_BUS_PDO_DATA PdoData;
+ ULONG PdoDataSize;
+ WDFDEVICE PdoDevice;
+ WDFQUEUE PdoQueue;
+ WDF_IO_QUEUE_CONFIG PdoQueueConfig;
+ NTSTATUS Status;
+
+ DECLARE_CONST_UNICODE_STRING(DeviceLocation, L"SimEmi Bus 0");
+ DECLARE_CONST_UNICODE_STRING(DeviceSddl, L"D:P(A;;GA;;;AU)(A;;GA;;;S-1-15-2-1)");
+
+ FdoDevice = WdfChildListGetDevice(ChildList);
+ FdoData = SimEmiGetFdoData(FdoDevice);
+ Identification =
+ (PSIM_EMI_BUS_PDO_IDENTIFICATION_INFO)IdentificationDescription;
+
+ KdPrint(("Creating Sim Emi PDO."));
+
+ //
+ // Calculate the size of the required PDO data structure.
+ //
+
+ Status = SimEmiPdoCalculateDataSize(Identification->ChannelCount,
+ &Identification->ChannelInfo[0],
+ &PdoDataSize);
+
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("SimEmiPdoCalculateDataSize failed with status code 0x%08X.\n",
+ Status));
+
+ goto PdoCreateDeviceEnd;
+ }
+
+ //
+ // Initialize the pdo device attributes and create the device.
+ //
+
+ WdfDeviceInitSetIoType(ChildInit, WdfDeviceIoBuffered);
+ WdfDeviceInitSetDeviceType(ChildInit, FILE_DEVICE_BUS_EXTENDER);
+
+ //
+ // Setup ids for device.
+ //
+
+ Buffer.Buffer = &BufferStore[0];
+ Buffer.Length = 0;
+ Buffer.MaximumLength = sizeof(BufferStore);
+ Status = RtlUnicodeStringPrintf(&Buffer,
+ L"simemibus\\%08X",
+ Identification->ChildDeviceHandle);
+
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("RtlUnicodeStringPrintf failed with status code 0x%08X.\n",
+ Status));
+
+ goto PdoCreateDeviceEnd;
+ }
+
+ Status = WdfPdoInitAddHardwareID(ChildInit, &Buffer);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("WdfPdoInitAddHardwareID failed with status code 0x%08X.\n",
+ Status));
+
+ goto PdoCreateDeviceEnd;
+ }
+
+ Status = WdfPdoInitAssignDeviceID(ChildInit, &Buffer);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("WdfPdoInitAssignDeviceID failed with status code 0x%08X.\n",
+ Status));
+
+ goto PdoCreateDeviceEnd;
+ }
+
+ Status = RtlUnicodeStringPrintf(&Buffer,
+ L"%08X",
+ Identification->ChildDeviceHandle);
+
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("RtlUnicodeStringPrintf failed with status code 0x%08X.\n",
+ Status));
+
+ goto PdoCreateDeviceEnd;
+ }
+
+ Status = WdfPdoInitAssignInstanceID(ChildInit, &Buffer);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("WdfPdoInitAssignInstanceID failed with status code 0x%08X.\n",
+ Status));
+
+ goto PdoCreateDeviceEnd;
+ }
+
+ Status = RtlUnicodeStringPrintf(&Buffer,
+ L"SimEmi V%d Child Device %08X: %d Channels",
+ Identification->EmiVersion,
+ Identification->ChildDeviceHandle,
+ Identification->ChannelCount);
+
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("RtlUnicodeStringPrintf failed with status code 0x%08X.\n",
+ Status));
+
+ goto PdoCreateDeviceEnd;
+ }
+
+ Status = WdfPdoInitAddDeviceText(ChildInit,
+ &Buffer,
+ &DeviceLocation,
+ 0x409);
+
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("WdfPdoInitAddDeviceText failed with status code 0x%08X.\n",
+ Status));
+
+ goto PdoCreateDeviceEnd;
+ }
+
+ WdfPdoInitSetDefaultLocale(ChildInit, 0x409);
+ WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&PdoAttributes,
+ SIM_EMI_BUS_PDO_DATA);
+
+ PdoAttributes.ContextSizeOverride = PdoDataSize;
+ PdoAttributes.EvtDestroyCallback = &SimEmiPdoDestroyDevice;
+ Status = WdfPdoInitAssignRawDevice(ChildInit, &GUID_DEVCLASS_SENSOR);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("WdfPdoInitAssignRawDevice failed with status code 0x%08X.\n",
+ Status));
+
+ goto PdoCreateDeviceEnd;
+ }
+
+ Status = WdfDeviceInitAssignSDDLString(ChildInit, &DeviceSddl);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("WdfDeviceInitAssignSDDLString failed with status code 0x%08X.\n",
+ Status));
+
+ goto PdoCreateDeviceEnd;
+ }
+
+ Status = WdfDeviceCreate(&ChildInit, &PdoAttributes, &PdoDevice);
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("WdfDeviceCreate failed with status code 0x%08X.\n", Status));
+ goto PdoCreateDeviceEnd;
+ }
+
+ //
+ // Initialize the PDO device data.
+ //
+
+ PdoData = SimEmiGetPdoData(PdoDevice);
+ PdoData->BusMutex = &FdoData->BusMutex;
+ SimEmiPdoInitializePdoData(Identification, PdoData);
+
+ //
+ // Initialize IO Queue.
+ //
+
+ WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&PdoQueueConfig,
+ WdfIoQueueDispatchSequential);
+
+ PdoQueueConfig.EvtIoDeviceControl = &SimEmiPdoControl;
+ Status = WdfIoQueueCreate(PdoDevice,
+ &PdoQueueConfig,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &PdoQueue);
+
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("WdfIoQueueCreate failed with status code 0x%08X.\n", Status));
+ goto PdoCreateDeviceEnd;
+ }
+
+ Status = WdfDeviceCreateDeviceInterface(PdoDevice,
+ &GUID_DEVICE_ENERGY_METER,
+ NULL);
+
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("WdfDeviceCreateDeviceInterface failed with status code 0x%08X.\n",
+ Status));
+
+ goto PdoCreateDeviceEnd;
+ }
+
+ ExAcquireFastMutex(&FdoData->BusMutex);
+ InsertTailList(&FdoData->ChildDevices, &PdoData->Link);
+ ExReleaseFastMutex(&FdoData->BusMutex);
+ Status = STATUS_SUCCESS;
+
+PdoCreateDeviceEnd:
+ return Status;
+}
+
+VOID
+SimEmiPdoDestroyDevice (
+ _In_ WDFOBJECT Object
+ )
+
+/*++
+
+Routine Description:
+
+ This routine cleans up the resources owned by a child device's PDO.
+
+Arguments:
+
+ Object - Supplies a reference to the child device's PDO device.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ PSIM_EMI_BUS_PDO_DATA PdoData;
+
+ PdoData = SimEmiGetPdoData(Object);
+ ExAcquireFastMutex(PdoData->BusMutex);
+ RemoveEntryList(&PdoData->Link);
+ ExReleaseFastMutex(PdoData->BusMutex);
+ return;
+}
+
+VOID
+SimEmiPdoInitializePdoData (
+ _In_ PSIM_EMI_BUS_PDO_IDENTIFICATION_INFO Identification,
+ _Out_ PSIM_EMI_BUS_PDO_DATA PdoData
+ )
+
+/*++
+
+Routine Description:
+
+ This routine initializes a child device's PDO data instance.
+
+Arguments:
+
+ Identification - Supplies a pointer to this child device's identification
+ info.
+
+ PdoData - Supplies a pointer to this child device's PDO data instance.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ USHORT ChannelCount;
+ PSIM_EMI_CHANNEL_DATA ChannelData;
+ PSIM_EMI_BUS_CHANNEL_INFO ChannelInfo;
+ ULONG ChannelInfoSize;
+ ULONG64 LastPollTime;
+
+ PdoData->EmiVersion = Identification->EmiVersion;
+ PdoData->ChildDeviceHandle = Identification->ChildDeviceHandle;
+ PdoData->ChannelCount = Identification->ChannelCount;
+
+ //
+ // Initialize the per channel data.
+ //
+
+ LastPollTime = KeQueryInterruptTime();
+ ChannelCount = Identification->ChannelCount;
+ ChannelData = &PdoData->ChannelData[0];
+ ChannelInfo = &Identification->ChannelInfo[0];
+ while (ChannelCount != 0) {
+ ChannelData->LastPollTime = LastPollTime;
+ ChannelData->LastAbsoluteEnergy = 0;
+ ChannelInfoSize =
+ SIM_EMI_BUS_CHANNEL_INFO_SIZE(ChannelInfo->ChannelNameSize);
+
+ RtlCopyMemory(&ChannelData->Info, ChannelInfo, ChannelInfoSize);
+ ChannelInfo = SIM_EMI_BUS_CHANNEL_INFO_NEXT_CHANNEL_INFO(ChannelInfo);
+ ChannelData = SIM_EMI_CHANNEL_DATA_NEXT_CHANNEL_DATA(ChannelData);
+ ChannelCount -= 1;
+ }
+
+ return;
+}