diff options
| -rw-r--r-- | simemi/README.MD | 16 | ||||
| -rw-r--r-- | simemi/simemi.c | 76 | ||||
| -rw-r--r-- | simemi/simemi.h | 85 | ||||
| -rw-r--r-- | simemi/simemi.inf | 62 | ||||
| -rw-r--r-- | simemi/simemi.sln | 35 | ||||
| -rw-r--r-- | simemi/simemi.vcxproj | 122 | ||||
| -rw-r--r-- | simemi/simemi.vcxproj.filters | 45 | ||||
| -rw-r--r-- | simemi/simemifdo.c | 888 | ||||
| -rw-r--r-- | simemi/simemipdo.c | 866 | ||||
| -rw-r--r-- | simemi/simemipublic.h | 89 |
10 files changed, 2284 insertions, 0 deletions
diff --git a/simemi/README.MD b/simemi/README.MD new file mode 100644 index 00000000..c9073284 --- /dev/null +++ b/simemi/README.MD @@ -0,0 +1,16 @@ +The energy metering functionality in this driver is implemented through the use of the SimEmi virtual child device. Let's break down the key components and their roles: +1. SimEmiPdoCalculateDataSize: This function calculates the required size of the child device's PDO (Plug and Play Device Object) data. It takes the number of channels and channel information as input and determines the size of the PDO data structure. +2. SimEmiPdoCalculateMetadataSize: This function calculates the size of the child device's EMI (Energy Metering Interface) metadata structure. It considers the EMI version and the number of channels to determine the size of the metadata structure. +3. SimEmiPdoCopyChannelMeasurements: This function copies the channel measurement data for each channel in the device to the provided output buffer. It updates the channel measurement by calculating the energy gain based on the time elapsed since the last measurement. The measurement data includes the absolute energy and the absolute time of the measurement. +4. SimEmiPdoCopyDeviceMetadata: This function copies the child device's EMI metadata to the provided output buffer. It considers the EMI version and copies the appropriate metadata structure. For EMI version 1, it includes the measurement unit, hardware OEM, hardware model, hardware revision, and metered hardware name. For EMI version 2, it includes the same information along with channel-specific details. +5. SimEmiPdoControl: This function handles device control requests for the EMI child device. It receives IOCTL (Input/Output Control) codes and performs the corresponding actions. For example, it handles requests to retrieve the EMI version, metadata size, metadata, and channel measurements. It calls the respective functions mentioned above to retrieve the required information and copies it to the output buffer. +6. SimEmiPdoCreateDevice: This function creates the PDO for the EMI child device. It initializes the PDO device attributes, assigns hardware and device IDs, sets up device text, creates the PDO device, and initializes the PDO device data. It also creates an IO queue to handle device control requests. +7. SimEmiPdoDestroyDevice: This function cleans up the resources owned by a child device's PDO. It is called when the PDO device is destroyed. +Overall, this driver provides the necessary functions to calculate the size of the PDO data and metadata, copy channel measurements and device metadata, handle device control requests, and create/destroy the PDO device. These functions work together to implement the energy metering functionality for the SimEmi virtual child device. + + +This driver supports two different versions of the Energy Metering Interface (EMI): EMI version 1 and EMI version 2. Let's discuss the differences between these versions: +1. EMI Version 1: In EMI version 1, each child device has only one channel. The metadata structure for EMI version 1 is represented by the EMI_METADATA_V1 structure. It includes fields such as the measurement unit, hardware OEM, hardware model, hardware revision, and the metered hardware name. The metered hardware name is specific to the channel and is copied from the channel information. +2. EMI Version 2: In EMI version 2, each child device can have multiple channels. The metadata structure for EMI version 2 is represented by the EMI_METADATA_V2 structure. It includes fields for the hardware OEM, hardware model, hardware revision, and the number of channels. Additionally, it includes an array of EMI_CHANNEL_V2 structures, each representing a channel. Each EMI_CHANNEL_V2 structure includes fields for the measurement unit, channel name size, and the channel name itself. +The main difference between EMI version 1 and EMI version 2 is the support for multiple channels in version 2. EMI version 2 allows for more flexibility in representing devices with multiple energy metering channels. This can be useful in scenarios where a single device has multiple energy-consuming components that need to be measured separately. +It's important to note that the driver handles both versions of EMI and provides the necessary functions to calculate the size of the metadata structures, copy channel measurements, and copy device metadata based on the specified EMI version.
\ No newline at end of file diff --git a/simemi/simemi.c b/simemi/simemi.c new file mode 100644 index 00000000..9d22fd0a --- /dev/null +++ b/simemi/simemi.c @@ -0,0 +1,76 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + simemi.c + +Abstract: + + This module contains the driver initialization routines. + +--*/ + +#include <initguid.h> +#include "simemi.h" +#include <devguid.h> + +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ); + +#pragma alloc_text(INIT, DriverEntry) + +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + This routine is the entry point for the driver. + +Arguments: + + DriverObject - Supplies a pointer to the driver object instance. + + RegistryPath - Supplies a pointer to the driver's registry path. + +Return Value: + + NTSTATUS. + +--*/ + +{ + + NTSTATUS Status; + WDF_DRIVER_CONFIG WdfConfig; + + // + // Initialize WDF. + // + + WDF_DRIVER_CONFIG_INIT(&WdfConfig, &SimEmiFdoCreateDevice); + Status = WdfDriverCreate(DriverObject, + RegistryPath, + WDF_NO_OBJECT_ATTRIBUTES, + &WdfConfig, + WDF_NO_HANDLE); + + if (!NT_SUCCESS(Status)) { + KdPrint(("WdfDriverCreate failed with status 0x%08X\n", Status)); + goto DriverEntryEnd; + } + + KdPrint(("Successfully created SimEmi Driver.\n")); + +DriverEntryEnd: + return Status; +}
\ No newline at end of file diff --git a/simemi/simemi.h b/simemi/simemi.h new file mode 100644 index 00000000..ecc6c957 --- /dev/null +++ b/simemi/simemi.h @@ -0,0 +1,85 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + simemi.h + +Abstract: + + This module contains the internal declarations of the simemi driver. + +--*/ + +#pragma once + +#include <ntddk.h> +#include <wdf.h> +#include "simemipublic.h" + +#define SIM_EMI_TAG 'IMES' + +typedef struct _SIM_EMI_BUS_FDO_DATA { + FAST_MUTEX BusMutex; + LIST_ENTRY ChildDevices; +} SIM_EMI_BUS_FDO_DATA, *PSIM_EMI_BUS_FDO_DATA; + +typedef struct _SIM_EMI_CHANNEL_DATA { + ULONG64 LastPollTime; + ULONG64 LastAbsoluteEnergy; + + SIM_EMI_BUS_CHANNEL_INFO Info; +} SIM_EMI_CHANNEL_DATA, *PSIM_EMI_CHANNEL_DATA; + +#define SIM_EMI_CHANNEL_DATA_SIZE(_ChannelNameSize) \ + (FIELD_OFFSET(SIM_EMI_CHANNEL_DATA, Info) + \ + SIM_EMI_BUS_CHANNEL_INFO_SIZE(_ChannelNameSize)) + +#define SIM_EMI_CHANNEL_DATA_NEXT_CHANNEL_DATA(_Channel) \ + ((PSIM_EMI_CHANNEL_DATA)((PUCHAR)(_Channel) + \ + SIM_EMI_CHANNEL_DATA_SIZE((_Channel)->Info.ChannelNameSize))) + +typedef struct _SIM_EMI_BUS_PDO_DATA { + LIST_ENTRY Link; + PFAST_MUTEX BusMutex; + + USHORT EmiVersion; + ULONG ChildDeviceHandle; + USHORT ChannelCount; + SIM_EMI_CHANNEL_DATA ChannelData[ANYSIZE_ARRAY]; +} SIM_EMI_BUS_PDO_DATA, *PSIM_EMI_BUS_PDO_DATA; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(SIM_EMI_BUS_FDO_DATA, SimEmiGetFdoData); +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(SIM_EMI_BUS_PDO_DATA, SimEmiGetPdoData); + +typedef struct _SIM_EMI_BUS_PDO_IDENTIFICATION_INFO { + WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER Header; + + USHORT EmiVersion; + ULONG ChildDeviceHandle; + + USHORT ChannelCount; + ULONG ChannelInfoSize; + PSIM_EMI_BUS_CHANNEL_INFO ChannelInfo; +} SIM_EMI_BUS_PDO_IDENTIFICATION_INFO, *PSIM_EMI_BUS_PDO_IDENTIFICATION_INFO; + +static LPCWSTR SimEmiHardwareOEM = L"SimEmi"; +static LPCWSTR SimEmiHardwareModelV1 = L"SimEmiV1"; +static LPCWSTR SimEmiHardwareModelV2 = L"SimEmiV2"; +static const USHORT SimEmiHardwareRevisionV1 = 1; +static const USHORT SimEmiHardwareRevisionV2 = 2; + +// +// simemifdo.c routine declarations +// + +EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL SimEmiFdoControl; +EVT_WDF_DRIVER_DEVICE_ADD SimEmiFdoCreateDevice; + +// +// simemipdo.c routine declarations +// + +EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL SimEmiPdoControl; +EVT_WDF_CHILD_LIST_CREATE_DEVICE SimEmiPdoCreateDevice;
\ No newline at end of file diff --git a/simemi/simemi.inf b/simemi/simemi.inf new file mode 100644 index 00000000..c458829d --- /dev/null +++ b/simemi/simemi.inf @@ -0,0 +1,62 @@ +; +; simemi.inf +; + +[Version] +Signature = "$WINDOWS NT$" +Class = System ; TODO: specify appropriate Class +ClassGuid = {4d36e97d-e325-11ce-bfc1-08002be10318} ; TODO: specify appropriate ClassGuid +Provider = %ManufacturerName% +CatalogFile = simemi.cat +DriverVer = ; TODO: set DriverVer in stampinf property pages +PnpLockdown = 1 + +[DestinationDirs] +DefaultDestDir = 13 + +[SourceDisksNames] +1 = %DiskName%,,,"" + +[SourceDisksFiles] +simemi.sys = 1,, + +;***************************************** +; Install Section +;***************************************** + +[Manufacturer] +%ManufacturerName% = Standard,NT$ARCH$.10.0...16299 ; %13% support introduced in build 16299 + +[Standard.NT$ARCH$.10.0...16299] +%simemi.DeviceDesc% = simemi_Device, Root\simemi ; TODO: edit hw-id + +[simemi_Device.NT] +CopyFiles = File_Copy + +[File_Copy] +simemi.sys + +;-------------- Service installation +[simemi_Device.NT.Services] +AddService = simemi,%SPSVCINST_ASSOCSERVICE%, simemi_Service_Inst + +; -------------- simemi driver install sections +[simemi_Service_Inst] +DisplayName = %simemi.SVCDESC% +ServiceType = 1 ; SERVICE_KERNEL_DRIVER +StartType = 3 ; SERVICE_DEMAND_START +ErrorControl = 1 ; SERVICE_ERROR_NORMAL +ServiceBinary = %13%\simemi.sys + +[simemi_Device.NT.Wdf] +KmdfService = simemi, simemi_wdfsect + +[simemi_wdfsect] +KmdfLibraryVersion = $KMDFVERSION$ + +[Strings] +SPSVCINST_ASSOCSERVICE = 0x00000002 +ManufacturerName = "<Your manufacturer name>" ;TODO: Replace with your manufacturer name +DiskName = "simemi Installation Disk" +simemi.DeviceDesc = "simemi Device" +simemi.SVCDESC = "simemi Service" diff --git a/simemi/simemi.sln b/simemi/simemi.sln new file mode 100644 index 00000000..94d91ee9 --- /dev/null +++ b/simemi/simemi.sln @@ -0,0 +1,35 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.9.34723.18 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "simemi", "simemi.vcxproj", "{DDE9FB9E-2A83-4893-95C4-120C97480971}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM64 = Debug|ARM64 + Debug|x64 = Debug|x64 + Release|ARM64 = Release|ARM64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {DDE9FB9E-2A83-4893-95C4-120C97480971}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {DDE9FB9E-2A83-4893-95C4-120C97480971}.Debug|ARM64.Build.0 = Debug|ARM64 + {DDE9FB9E-2A83-4893-95C4-120C97480971}.Debug|ARM64.Deploy.0 = Debug|ARM64 + {DDE9FB9E-2A83-4893-95C4-120C97480971}.Debug|x64.ActiveCfg = Debug|x64 + {DDE9FB9E-2A83-4893-95C4-120C97480971}.Debug|x64.Build.0 = Debug|x64 + {DDE9FB9E-2A83-4893-95C4-120C97480971}.Debug|x64.Deploy.0 = Debug|x64 + {DDE9FB9E-2A83-4893-95C4-120C97480971}.Release|ARM64.ActiveCfg = Release|ARM64 + {DDE9FB9E-2A83-4893-95C4-120C97480971}.Release|ARM64.Build.0 = Release|ARM64 + {DDE9FB9E-2A83-4893-95C4-120C97480971}.Release|ARM64.Deploy.0 = Release|ARM64 + {DDE9FB9E-2A83-4893-95C4-120C97480971}.Release|x64.ActiveCfg = Release|x64 + {DDE9FB9E-2A83-4893-95C4-120C97480971}.Release|x64.Build.0 = Release|x64 + {DDE9FB9E-2A83-4893-95C4-120C97480971}.Release|x64.Deploy.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {FF2C3D55-270C-4B51-AE35-32050EC85A83} + EndGlobalSection +EndGlobal diff --git a/simemi/simemi.vcxproj b/simemi/simemi.vcxproj new file mode 100644 index 00000000..1e8280c4 --- /dev/null +++ b/simemi/simemi.vcxproj @@ -0,0 +1,122 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|ARM64"> + <Configuration>Debug</Configuration> + <Platform>ARM64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|ARM64"> + <Configuration>Release</Configuration> + <Platform>ARM64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{DDE9FB9E-2A83-4893-95C4-120C97480971}</ProjectGuid> + <TemplateGuid>{1bc93793-694f-48fe-9372-81e2b05556fd}</TemplateGuid> + <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> + <MinimumVisualStudioVersion>12.0</MinimumVisualStudioVersion> + <Configuration>Debug</Configuration> + <Platform Condition="'$(Platform)' == ''">x64</Platform> + <RootNamespace>simemi</RootNamespace> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + <DriverType>KMDF</DriverType> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + <DriverType>KMDF</DriverType> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + <DriverType>KMDF</DriverType> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + <DriverType>KMDF</DriverType> + <DriverTargetPlatform>Universal</DriverTargetPlatform> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor> + <TimeStampServer /> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'"> + <DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'"> + <DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'"> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'"> + <DriverSign> + <FileDigestAlgorithm>sha256</FileDigestAlgorithm> + </DriverSign> + </ItemDefinitionGroup> + <ItemGroup> + <Inf Include="simemi.inf" /> + </ItemGroup> + <ItemGroup> + <FilesToPackage Include="$(TargetPath)" /> + </ItemGroup> + <ItemGroup> + <ClCompile Include="simemi.c" /> + <ClCompile Include="simemifdo.c" /> + <ClCompile Include="simemipdo.c" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="simemi.h" /> + <ClInclude Include="simemipublic.h" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project>
\ No newline at end of file diff --git a/simemi/simemi.vcxproj.filters b/simemi/simemi.vcxproj.filters new file mode 100644 index 00000000..fbd37383 --- /dev/null +++ b/simemi/simemi.vcxproj.filters @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + <Filter Include="Driver Files"> + <UniqueIdentifier>{8E41214B-6785-4CFE-B992-037D68949A14}</UniqueIdentifier> + <Extensions>inf;inv;inx;mof;mc;</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <Inf Include="simemi.inf"> + <Filter>Driver Files</Filter> + </Inf> + </ItemGroup> + <ItemGroup> + <ClCompile Include="simemi.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="simemifdo.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="simemipdo.c"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="simemipublic.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="simemi.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/simemi/simemifdo.c b/simemi/simemifdo.c new file mode 100644 index 00000000..87f5698f --- /dev/null +++ b/simemi/simemifdo.c @@ -0,0 +1,888 @@ +/*++ + +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 bus device. + +--*/ + +#include "simemi.h" +#include <limits.h> +#include <ntintsafe.h> + +NTSTATUS +SimEmiFdoCreateChildDevice ( + _In_ WDFDEVICE Device, + _In_ WDFREQUEST Request + ); + +NTSTATUS +SimEmiFdoDeleteChildDevice ( + _In_ WDFDEVICE Device, + _In_ WDFREQUEST Request + ); + +PSIM_EMI_BUS_PDO_DATA +SimEmiFdoFindChildDevice ( + _In_ WDFDEVICE Device, + _In_ ULONG ChildDeviceHandle + ); + +EVT_WDF_CHILD_LIST_IDENTIFICATION_DESCRIPTION_CLEANUP SimEmiPdoIdentificationCleanup; +EVT_WDF_CHILD_LIST_IDENTIFICATION_DESCRIPTION_COMPARE SimEmiPdoIdentificationCompare; +EVT_WDF_CHILD_LIST_IDENTIFICATION_DESCRIPTION_DUPLICATE SimEmiPdoIdentificationDuplicate; + +NTSTATUS +SimEmiFdoQueryChildDevice ( + _In_ WDFDEVICE Device, + _In_ WDFREQUEST Request, + _Out_ PULONG OutputBufferSizeOut + ); + +NTSTATUS +SimEmiFdoUpdateChildDevice ( + _In_ WDFDEVICE Device, + _In_ WDFREQUEST Request + ); + +#pragma alloc_text(PAGE, SimEmiFdoControl) +#pragma alloc_text(PAGE, SimEmiFdoCreateChildDevice) +#pragma alloc_text(PAGE, SimEmiFdoDeleteChildDevice) +#pragma alloc_text(PAGE, SimEmiFdoCreateDevice) +#pragma alloc_text(PAGE, SimEmiFdoFindChildDevice) +#pragma alloc_text(PAGE, SimEmiFdoQueryChildDevice) +#pragma alloc_text(PAGE, SimEmiFdoUpdateChildDevice) + +VOID +SimEmiFdoControl ( + _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 the EMI Bus. + +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; + PSIM_EMI_BUS_FDO_DATA FdoData; + ULONG OutputLength; + NTSTATUS Status; + + UNREFERENCED_PARAMETER(OutputBufferLength); + UNREFERENCED_PARAMETER(InputBufferLength); + + Device = WdfIoQueueGetDevice(Queue); + FdoData = SimEmiGetFdoData(Device); + ExAcquireFastMutex(&FdoData->BusMutex); + OutputLength = 0; + switch (IoControlCode) { + case IOCTL_EMI_BUS_CREATE_DEVICE: + Status = SimEmiFdoCreateChildDevice(Device, Request); + break; + + case IOCTL_EMI_BUS_DELETE_DEVICE: + Status = SimEmiFdoDeleteChildDevice(Device, Request); + break; + + case IOCTL_EMI_BUS_QUERY_DEVICE_INFO: + Status = SimEmiFdoQueryChildDevice(Device, Request, &OutputLength); + break; + + case IOCTL_EMI_BUS_SET_DEVICE_RATE: + Status = SimEmiFdoUpdateChildDevice(Device, Request); + break; + + default: + Status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + WdfRequestCompleteWithInformation(Request, Status, OutputLength); + ExReleaseFastMutex(&FdoData->BusMutex); + return; +} + +NTSTATUS +SimEmiFdoCreateChildDevice ( + _In_ WDFDEVICE Device, + _In_ WDFREQUEST Request + ) + +/*++ + +Routine Description: + + This routine handles an IOCTL_EMI_BUS_CREATE_DEVICE request. + +Arguments: + + Device - Supplies a reference to the BUS FDO device. + + Request - Supplies a reference to the WDF Request. + +Return Value: + + NTSTATUS. + +--*/ + +{ + + USHORT ChannelCount; + USHORT ChannelIndex; + PSIM_EMI_BUS_CHANNEL_INFO ChannelInfo; + ULONG ChannelInfoSize; + ULONG EntrySize; + SIM_EMI_BUS_PDO_IDENTIFICATION_INFO Identification; + PSIM_EMI_BUS_CREATE_DEVICE_INPUT_BUFFER InputBuffer; + size_t InputBufferLength; + ULONG RemainingBufferSize; + NTSTATUS Status; + + RtlZeroMemory(&Identification, + sizeof(SIM_EMI_BUS_PDO_IDENTIFICATION_INFO)); + + Status = WdfRequestRetrieveInputBuffer( + Request, + sizeof(SIM_EMI_BUS_CREATE_DEVICE_INPUT_BUFFER), + (PVOID*)&InputBuffer, + &InputBufferLength); + + if (!NT_SUCCESS(Status)) { + goto FdoCreateChildDeviceEnd; + } + + ChannelCount = InputBuffer->ChannelCount; + if (InputBuffer->EmiVersion == EMI_VERSION_V1) { + if (ChannelCount != 1) { + Status = STATUS_INVALID_PARAMETER; + goto FdoCreateChildDeviceEnd; + } + + } else if (InputBuffer->EmiVersion == EMI_VERSION_V2) { + if (ChannelCount == 0) { + Status = STATUS_INVALID_PARAMETER; + goto FdoCreateChildDeviceEnd; + } + + } else { + Status = STATUS_INVALID_PARAMETER; + goto FdoCreateChildDeviceEnd; + } + + if (InputBufferLength > ULONG_MAX) { + Status = STATUS_BUFFER_OVERFLOW; + goto FdoCreateChildDeviceEnd; + } + + ChannelInfo = &InputBuffer->ChannelInfo[0]; + ChannelInfoSize = 0; + RemainingBufferSize = (ULONG)InputBufferLength; + RemainingBufferSize -= FIELD_OFFSET(SIM_EMI_BUS_CREATE_DEVICE_INPUT_BUFFER, + ChannelInfo); + + for (ChannelIndex = 0; ChannelIndex < ChannelCount; ++ChannelIndex) { + EntrySize = SIM_EMI_BUS_CHANNEL_INFO_SIZE(ChannelInfo->ChannelNameSize); + if (EntrySize > RemainingBufferSize) { + Status = STATUS_INVALID_PARAMETER; + goto FdoCreateChildDeviceEnd; + } + + RemainingBufferSize -= EntrySize; + ChannelInfoSize += EntrySize; + ChannelInfo = SIM_EMI_BUS_CHANNEL_INFO_NEXT_CHANNEL_INFO(ChannelInfo); + } + + WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER_INIT( + &Identification.Header, + sizeof(SIM_EMI_BUS_PDO_IDENTIFICATION_INFO)); + + //#pragma warning( suppress : 4996 ) + Identification.ChannelInfo = ExAllocatePool2(POOL_FLAG_NON_PAGED, + ChannelInfoSize, + SIM_EMI_TAG); + + if (Identification.ChannelInfo == NULL) { + Status = STATUS_INSUFFICIENT_RESOURCES; + goto FdoCreateChildDeviceEnd; + } + + // + // Initialize description and add the device. + // + + Identification.EmiVersion = InputBuffer->EmiVersion; + Identification.ChildDeviceHandle = InputBuffer->ChildDeviceHandle; + Identification.ChannelCount = InputBuffer->ChannelCount; + Identification.ChannelInfoSize = ChannelInfoSize; + RtlCopyMemory(&Identification.ChannelInfo[0], + &InputBuffer->ChannelInfo[0], + ChannelInfoSize); + + Status = WdfChildListAddOrUpdateChildDescriptionAsPresent( + WdfFdoGetDefaultChildList(Device), + &Identification.Header, + NULL); + + if (!NT_SUCCESS(Status)) { + goto FdoCreateChildDeviceEnd; + } + + Status = STATUS_SUCCESS; + +FdoCreateChildDeviceEnd: + if (Identification.ChannelInfo != NULL) { + ExFreePoolWithTag(Identification.ChannelInfo, SIM_EMI_TAG); + } + + return Status; +} + +NTSTATUS +SimEmiFdoDeleteChildDevice ( + _In_ WDFDEVICE Device, + _In_ WDFREQUEST Request + ) + +/*++ + +Routine Description: + + This routine handles an IOCTL_EMI_BUS_DELETE_DEVICE request. + +Arguments: + + Device - Supplies a reference to the BUS FDO device. + + Request - Supplies a reference to the WDF Request. + +Return Value: + + NTSTATUS. + +--*/ + +{ + + WDFCHILDLIST ChildList; + SIM_EMI_BUS_PDO_IDENTIFICATION_INFO Identification; + PSIM_EMI_BUS_DELETE_DEVICE_INPUT_BUFFER InputBuffer; + NTSTATUS Status; + + ChildList = WdfFdoGetDefaultChildList(Device); + Status = WdfRequestRetrieveInputBuffer( + Request, + sizeof(SIM_EMI_BUS_DELETE_DEVICE_INPUT_BUFFER), + (PVOID*)&InputBuffer, + NULL); + + if (!NT_SUCCESS(Status)) { + goto FdoDeleteChildDeviceEnd; + } + + WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER_INIT( + &Identification.Header, + sizeof(SIM_EMI_BUS_PDO_IDENTIFICATION_INFO)); + + Identification.ChildDeviceHandle = InputBuffer->ChildDeviceHandle; + Status = WdfChildListUpdateChildDescriptionAsMissing( + ChildList, + &Identification.Header); + + if (Status == STATUS_NO_SUCH_DEVICE) { + Status = STATUS_INVALID_PARAMETER; + } + +FdoDeleteChildDeviceEnd: + return Status; +} + +NTSTATUS +SimEmiFdoCreateDevice ( + _In_ WDFDRIVER Driver, + _In_ PWDFDEVICE_INIT DeviceInit + ) + +/*++ + +Routine Description: + + This routine creates and initializes the bus FDO device. + +Arguments: + + Driver - Supplies a reference to the WDFDRIVER for this device. + + DeviceInit - Supplies a pointer to the WDFDEVICE_INIT instance for this + device. + +Return Value: + + NTSTATUS. + +--*/ + +{ + + WDF_CHILD_LIST_CONFIG ChildListConfig; + WDFDEVICE DeviceHandle; + PSIM_EMI_BUS_FDO_DATA FdoData; + WDF_OBJECT_ATTRIBUTES FdoDeviceAttributes; + WDFQUEUE Queue; + WDF_IO_QUEUE_CONFIG QueueConfig; + NTSTATUS Status; + + DECLARE_CONST_UNICODE_STRING(DeviceName, SIM_EMI_DEVICE_PATH); + DECLARE_CONST_UNICODE_STRING(DeviceAlias, SIM_EMI_DEVICE_ALIAS_PATH); + + PAGED_CODE(); + + UNREFERENCED_PARAMETER(Driver); + + KdPrint(("Initializing SimEmi PDO.\n")); + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&FdoDeviceAttributes, + SIM_EMI_BUS_FDO_DATA); + + WdfDeviceInitSetDeviceType(DeviceInit, FILE_DEVICE_BUS_EXTENDER); + WdfDeviceInitSetExclusive(DeviceInit, TRUE); + WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoBuffered); + + // + // Initialize child list. + // + + WDF_CHILD_LIST_CONFIG_INIT(&ChildListConfig, + sizeof(SIM_EMI_BUS_PDO_IDENTIFICATION_INFO), + &SimEmiPdoCreateDevice); + + ChildListConfig.EvtChildListIdentificationDescriptionCleanup = + &SimEmiPdoIdentificationCleanup; + + ChildListConfig.EvtChildListIdentificationDescriptionCompare = + &SimEmiPdoIdentificationCompare; + + ChildListConfig.EvtChildListIdentificationDescriptionDuplicate = + &SimEmiPdoIdentificationDuplicate; + + WdfFdoInitSetDefaultChildListConfig(DeviceInit, + &ChildListConfig, + WDF_NO_OBJECT_ATTRIBUTES); + + Status = WdfDeviceInitAssignName(DeviceInit, &DeviceName); + if (!NT_SUCCESS(Status)) { + KdPrint(("WdfDeviceInitAssignName failed with status code 0x%08X.\n", + Status)); + + goto FdoCreateDeviceEnd; + } + + Status = WdfDeviceCreate(&DeviceInit, &FdoDeviceAttributes, &DeviceHandle); + if (!NT_SUCCESS(Status)) { + KdPrint(("WdfDeviceCreate failed with status code 0x%08X.\n", Status)); + goto FdoCreateDeviceEnd; + } + + FdoData = SimEmiGetFdoData(DeviceHandle); + RtlZeroMemory(FdoData, sizeof(SIM_EMI_BUS_FDO_DATA)); + InitializeListHead(&FdoData->ChildDevices); + ExInitializeFastMutex(&FdoData->BusMutex); + + // + // Setup symbolic links for UM. + // + + Status = WdfDeviceCreateSymbolicLink(DeviceHandle, &DeviceAlias); + if (!NT_SUCCESS(Status)) { + KdPrint(("WdfDeviceCreateSymbolicLink failed with status code :0x%08X.\n", + Status)); + + goto FdoCreateDeviceEnd; + } + + + + // + // Initialize IO Queue. + // + + WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&QueueConfig, + WdfIoQueueDispatchSequential); + + QueueConfig.EvtIoDeviceControl = &SimEmiFdoControl; + Status = WdfIoQueueCreate(DeviceHandle, + &QueueConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &Queue); + + if (!NT_SUCCESS(Status)) { + KdPrint(("WdfIoQueueCreate failed with status code 0x%08X.\n", Status)); + goto FdoCreateDeviceEnd; + } + + Status = WdfDeviceCreateDeviceInterface(DeviceHandle, + &GUID_DEVICE_SIM_EMI_BUS, + NULL); + + if (!NT_SUCCESS(Status)) { + KdPrint(("WdfDeviceCreateDeviceInterface failed with status code 0x%08X.\n", + Status)); + + goto FdoCreateDeviceEnd; + } + + Status = STATUS_SUCCESS; + +FdoCreateDeviceEnd: + return Status; +} + +PSIM_EMI_BUS_PDO_DATA +SimEmiFdoFindChildDevice ( + _In_ WDFDEVICE Device, + _In_ ULONG ChildDeviceHandle + ) + +/*++ + +Routine Description: + + This routine locates the PDO data for the child device with the provided + device handle. + +Arguments: + + Device - Supplies a reference to the WDFDEVICE of the bus FDO. + + ChildDeviceHandle - Supplies the device handle of the child device to + locate. + +Return Value: + + Returns a pointer to the PDO data for the child device with the provided + device handle or NULL if no child exists with the provided device handle. + +--*/ + +{ + + PSIM_EMI_BUS_PDO_DATA ChildData; + PLIST_ENTRY ListEntry; + PSIM_EMI_BUS_FDO_DATA FdoData; + + FdoData = SimEmiGetFdoData(Device); + ChildData = NULL; + for (ListEntry = FdoData->ChildDevices.Flink; + ListEntry != &FdoData->ChildDevices; + ListEntry = ListEntry->Flink) { + + ChildData = CONTAINING_RECORD(ListEntry, + SIM_EMI_BUS_PDO_DATA, + Link); + + if (ChildData->ChildDeviceHandle == ChildDeviceHandle) { + break; + } + + ChildData = NULL; + } + + return ChildData; +} + +NTSTATUS +SimEmiFdoQueryChildDevice ( + _In_ WDFDEVICE Device, + _In_ WDFREQUEST Request, + _Out_ PULONG OutputBufferSizeOut + ) + +/*++ + +Routine Description: + + This routine handles an IOCTL_EMI_BUS_QUERY_DEVICE_INFO request. + +Arguments: + + Device - Supplies a reference to the BUS FDO device. + + Request - Supplies a reference to the WDF Request. + + OutputBufferSizeOut - Supplies a pointer in which to place the size of the + output data written. + +Return Value: + + NTSTATUS. + +--*/ +{ + + USHORT ChannelCount; + PSIM_EMI_CHANNEL_DATA ChannelData; + PSIM_EMI_BUS_CHANNEL_INFO ChannelInfo; + PSIM_EMI_BUS_PDO_DATA ChildData; + ULONG EntrySize; + PSIM_EMI_BUS_QUERY_DEVICE_INFO_INPUT_BUFFER InputBuffer; + PSIM_EMI_BUS_QUERY_DEVICE_INFO_OUTPUT_BUFFER OutputBuffer; + ULONG OutputBufferSize; + NTSTATUS Status; + + Status = WdfRequestRetrieveInputBuffer( + Request, + sizeof(SIM_EMI_BUS_QUERY_DEVICE_INFO_INPUT_BUFFER), + (PVOID*)&InputBuffer, + NULL); + + if (!NT_SUCCESS(Status)) { + goto FdoQueryChildDeviceEnd; + } + + ChildData = SimEmiFdoFindChildDevice(Device, + InputBuffer->ChildDeviceHandle); + + if (ChildData == NULL) { + Status = STATUS_NOT_FOUND; + goto FdoQueryChildDeviceEnd; + } + + OutputBufferSize = FIELD_OFFSET(SIM_EMI_BUS_QUERY_DEVICE_INFO_OUTPUT_BUFFER, + ChannelInfo); + + ChannelCount = ChildData->ChannelCount; + ChannelData = &ChildData->ChannelData[0]; + while (ChannelCount != 0) { + EntrySize = + SIM_EMI_BUS_CHANNEL_INFO_SIZE(ChannelData->Info.ChannelNameSize); + + Status = RtlULongAdd(OutputBufferSize, EntrySize, &OutputBufferSize); + if (!NT_SUCCESS(Status)) { + goto FdoQueryChildDeviceEnd; + } + + ChannelData = SIM_EMI_CHANNEL_DATA_NEXT_CHANNEL_DATA(ChannelData); + ChannelCount -= 1; + } + + *OutputBufferSizeOut = OutputBufferSize; + Status = WdfRequestRetrieveOutputBuffer(Request, + OutputBufferSize, + (PVOID*)&OutputBuffer, + NULL); + + if (!NT_SUCCESS(Status)) { + goto FdoQueryChildDeviceEnd; + } + + OutputBuffer->EmiVersion = ChildData->EmiVersion; + OutputBuffer->ChannelCount = ChildData->ChannelCount; + ChannelCount = ChildData->ChannelCount; + ChannelData = &ChildData->ChannelData[0]; + ChannelInfo = &OutputBuffer->ChannelInfo[0]; + while (ChannelCount != 0) { + EntrySize = + SIM_EMI_BUS_CHANNEL_INFO_SIZE(ChannelData->Info.ChannelNameSize); + + RtlCopyMemory(ChannelInfo, &ChannelData->Info, EntrySize); + ChannelData = SIM_EMI_CHANNEL_DATA_NEXT_CHANNEL_DATA(ChannelData); + ChannelInfo = SIM_EMI_BUS_CHANNEL_INFO_NEXT_CHANNEL_INFO(ChannelInfo); + ChannelCount -= 1; + } + + Status = STATUS_SUCCESS; + +FdoQueryChildDeviceEnd: + return Status; +} + +NTSTATUS +SimEmiFdoUpdateChildDevice ( + _In_ WDFDEVICE Device, + _In_ WDFREQUEST Request + ) + +/*++ + +Routine Description: + + This routine handles an IOCTL_EMI_BUS_SET_DEVICE_RATE request. + +Arguments: + + Device - Supplies a reference to the BUS FDO device. + + Request - Supplies a reference to the WDF Request. + +Return Value: + + NTSTATUS. + +--*/ +{ + + PULONG64 AbsoluteEnergyRates; + USHORT ChannelCount; + PSIM_EMI_CHANNEL_DATA ChannelData; + PSIM_EMI_BUS_PDO_DATA ChildData; + PSIM_EMI_BUS_SET_DEVICE_RATE_INPUT_BUFFER InputBuffer; + ULONG InputBufferSize; + NTSTATUS Status; + + Status = WdfRequestRetrieveInputBuffer( + Request, + sizeof(SIM_EMI_BUS_SET_DEVICE_RATE_INPUT_BUFFER), + (PVOID*)&InputBuffer, + NULL); + + if (!NT_SUCCESS(Status)) { + goto FdoUpdateChildDeviceEnd; + } + + ChildData = SimEmiFdoFindChildDevice(Device, + InputBuffer->ChildDeviceHandle); + + if (ChildData == NULL) { + Status = STATUS_INVALID_PARAMETER; + goto FdoUpdateChildDeviceEnd; + } + + if ((ChildData->EmiVersion != InputBuffer->EmiVersion) || + (ChildData->ChannelCount != InputBuffer->ChannelCount)) { + + Status = STATUS_INVALID_PARAMETER; + goto FdoUpdateChildDeviceEnd; + } + + ChannelCount = ChildData->ChannelCount; + Status = RtlULongMult(sizeof(ULONG64), ChannelCount, &InputBufferSize); + if (!NT_SUCCESS(Status)) { + goto FdoUpdateChildDeviceEnd; + } + + InputBufferSize += FIELD_OFFSET(SIM_EMI_BUS_SET_DEVICE_RATE_INPUT_BUFFER, + AbsoluteEnergyRates); + + Status = WdfRequestRetrieveInputBuffer(Request, + InputBufferSize, + (PVOID*)&InputBuffer, + NULL); + + if (!NT_SUCCESS(Status)) { + goto FdoUpdateChildDeviceEnd; + } + + AbsoluteEnergyRates = &InputBuffer->AbsoluteEnergyRates[0]; + ChannelData = &ChildData->ChannelData[0]; + while (ChannelCount != 0) { + ChannelData->Info.AbsoluteEnergyRate = *AbsoluteEnergyRates; + AbsoluteEnergyRates += 1; + ChannelData = SIM_EMI_CHANNEL_DATA_NEXT_CHANNEL_DATA(ChannelData); + ChannelCount -= 1; + } + + Status = STATUS_SUCCESS; + +FdoUpdateChildDeviceEnd: + return Status; +} + +VOID +SimEmiPdoIdentificationCleanup ( + _In_ WDFCHILDLIST ChildList, + _Inout_ PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER IdentificationDescription + ) + +/*++ + +Routine Description: + + This routine cleans up the resources owned by a bus PDO description + instance. + +Arguments: + + ChildList - Supplies a reference to the WDFCHILDLIST this description is + being added to. + + IdentificationDescription - Supplies a pointer to the identification info + instance to clean up. + +Return Value: + + None. + +--*/ + +{ + + PSIM_EMI_BUS_PDO_IDENTIFICATION_INFO Description; + + UNREFERENCED_PARAMETER(ChildList); + + Description = + (PSIM_EMI_BUS_PDO_IDENTIFICATION_INFO)IdentificationDescription; + + if (Description->ChannelInfo != NULL) { + ExFreePoolWithTag(Description->ChannelInfo, SIM_EMI_TAG); + } + + return; +} + +BOOLEAN +SimEmiPdoIdentificationCompare ( + _In_ WDFCHILDLIST ChildList, + _In_ PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER FirstIdentificationDescription, + _In_ PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER SecondIdentificationDescription + ) + +/*++ + +Routine Description: + + This routine compares two SIM_EMI_BUS_PDO_IDENTIFICATION_INFOs for + equality. + +Arguments: + + ChildList - Supplies a reference to the WDFCHILDLIST this description is + being added to. + + FirstIdentificationDescription - Supplies a pointer to a PDO identification + info instance. + + SecondIdentificationDescription - Supplies a pointer to a PDO + identification instance. + +Return Value: + + Returns TRUE if FirstIdentificationInfo == SecondIdentificationInfo, FALSE + otherwise. + +--*/ + +{ + + PSIM_EMI_BUS_PDO_IDENTIFICATION_INFO Description1; + PSIM_EMI_BUS_PDO_IDENTIFICATION_INFO Description2; + BOOLEAN Result; + + UNREFERENCED_PARAMETER(ChildList); + + Description1 = + (PSIM_EMI_BUS_PDO_IDENTIFICATION_INFO)FirstIdentificationDescription; + + Description2 = + (PSIM_EMI_BUS_PDO_IDENTIFICATION_INFO)SecondIdentificationDescription; + + Result = FALSE; + if (Description1->ChildDeviceHandle == Description2->ChildDeviceHandle) { + Result = TRUE; + } + + return Result; +} + +NTSTATUS +SimEmiPdoIdentificationDuplicate ( + _In_ WDFCHILDLIST ChildList, + _In_ PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER SourceIdentificationDescription, + _Out_ PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER DestinationIdentificationDescription + ) + +/*++ + +Routine Description: + + This routine duplicates a SIM_EMI_BUS_PDO_IDENTIFICATION_INFO instance. + +Arguments: + + ChildList - Supplies a reference to the WDFCHILDLIST this description is + being added to. + + SourceIdentificationDescription - Supplies a pointer to the identification + info instance to duplicate. + + DestinationIdentificationDescription - Supplies a pointer to the location + to place the copied identification info. + +Return Value: + + NTSTATUS. + +--*/ + +{ + + ULONG ChannelInfoSize; + PSIM_EMI_BUS_PDO_IDENTIFICATION_INFO DestinationDescription; + PSIM_EMI_BUS_PDO_IDENTIFICATION_INFO SourceDescription; + NTSTATUS Status; + + UNREFERENCED_PARAMETER(ChildList); + + DestinationDescription = + (PSIM_EMI_BUS_PDO_IDENTIFICATION_INFO)DestinationIdentificationDescription; + + SourceDescription = + (PSIM_EMI_BUS_PDO_IDENTIFICATION_INFO)SourceIdentificationDescription; + + ChannelInfoSize = SourceDescription->ChannelInfoSize; + //#pragma warning( suppress : 4996 ) + DestinationDescription->ChannelInfo = ExAllocatePool2(POOL_FLAG_NON_PAGED, + ChannelInfoSize, + SIM_EMI_TAG); + + if (DestinationDescription->ChannelInfo == NULL) { + Status = STATUS_INSUFFICIENT_RESOURCES; + goto PdoIdentificationDuplicateEnd; + } + + DestinationDescription->EmiVersion = SourceDescription->EmiVersion; + DestinationDescription->ChildDeviceHandle = + SourceDescription->ChildDeviceHandle; + + DestinationDescription->ChannelCount = SourceDescription->ChannelCount; + DestinationDescription->ChannelInfoSize = ChannelInfoSize; + RtlCopyMemory(&DestinationDescription->ChannelInfo[0], + &SourceDescription->ChannelInfo[0], + ChannelInfoSize); + + Status = STATUS_SUCCESS; + +PdoIdentificationDuplicateEnd: + return Status; +}
\ No newline at end of file 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; +} diff --git a/simemi/simemipublic.h b/simemi/simemipublic.h new file mode 100644 index 00000000..2cc7c8f7 --- /dev/null +++ b/simemi/simemipublic.h @@ -0,0 +1,89 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +Module Name: + + simemipublic.h + +Abstract: + + This module contains the declarations of the public interface of the simemi + driver. + +--*/ + +#pragma once + +#if defined(__cplusplus) +extern "C" { +#endif + +#include <emi.h> + +#define SIM_EMI_DEVICE_NAME L"simemibus" +#define SIM_EMI_DEVICE_PATH L"\\Device\\" SIM_EMI_DEVICE_NAME +#define SIM_EMI_DEVICE_ALIAS_PATH L"\\DosDevices\\" SIM_EMI_DEVICE_NAME +#define SIM_EMI_DEVICE_MAPPED_PATH L"\\\\.\\" SIM_EMI_DEVICE_NAME + +// +// {7af2e4c8-3b33-4118-bb3b-ca967a5140da} +// + +DEFINE_GUID(GUID_DEVICE_SIM_EMI_BUS, + 0x7af2e4c8, 0x3b33, 0x4118, 0xbb, 0x3b, 0xca, 0x96, 0x7a, 0x51, 0x40, 0xda); + +#define IOCTL_EMI_BUS_CREATE_DEVICE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x600, METHOD_BUFFERED, FILE_WRITE_ACCESS) +#define IOCTL_EMI_BUS_DELETE_DEVICE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x601, METHOD_BUFFERED, FILE_WRITE_ACCESS) +#define IOCTL_EMI_BUS_QUERY_DEVICE_INFO CTL_CODE(FILE_DEVICE_UNKNOWN, 0x602, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_EMI_BUS_SET_DEVICE_RATE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x603, METHOD_BUFFERED, FILE_WRITE_ACCESS) + +typedef struct _SIM_EMI_BUS_CHANNEL_INFO { + ULONGLONG AbsoluteEnergyRate; // Change in AbsoluteEnergy in 100-ns units. + + EMI_MEASUREMENT_UNIT MeasurementUnit; + + USHORT ChannelNameSize; + WCHAR ChannelName[ANYSIZE_ARRAY]; +} SIM_EMI_BUS_CHANNEL_INFO, *PSIM_EMI_BUS_CHANNEL_INFO; + +#define SIM_EMI_BUS_CHANNEL_INFO_SIZE(_ChannelNameSize) \ + (FIELD_OFFSET(SIM_EMI_BUS_CHANNEL_INFO, ChannelName) + (_ChannelNameSize)) + +#define SIM_EMI_BUS_CHANNEL_INFO_NEXT_CHANNEL_INFO(_Channel) \ + ((PSIM_EMI_BUS_CHANNEL_INFO)((PUCHAR)(_Channel) + \ + SIM_EMI_BUS_CHANNEL_INFO_SIZE((_Channel)->ChannelNameSize))) + +typedef struct _SIM_EMI_BUS_CREATE_DEVICE_INPUT_BUFFER { + USHORT EmiVersion; + ULONG ChildDeviceHandle; + + USHORT ChannelCount; + SIM_EMI_BUS_CHANNEL_INFO ChannelInfo[ANYSIZE_ARRAY]; +} SIM_EMI_BUS_CREATE_DEVICE_INPUT_BUFFER, *PSIM_EMI_BUS_CREATE_DEVICE_INPUT_BUFFER; + +typedef struct _SIM_EMI_BUS_DELETE_DEVICE_INPUT_BUFFER { + ULONG ChildDeviceHandle; +} SIM_EMI_BUS_DELETE_DEVICE_INPUT_BUFFER, *PSIM_EMI_BUS_DELETE_DEVICE_INPUT_BUFFER; + +typedef struct _SIM_EMI_BUS_QUERY_DEVICE_INFO_INPUT_BUFFER { + ULONG ChildDeviceHandle; +} SIM_EMI_BUS_QUERY_DEVICE_INFO_INPUT_BUFFER, *PSIM_EMI_BUS_QUERY_DEVICE_INFO_INPUT_BUFFER; + +typedef struct _SIM_EMI_BUS_QUERY_DEVICE_INFO_OUTPUT_BUFFER { + USHORT EmiVersion; + USHORT ChannelCount; + SIM_EMI_BUS_CHANNEL_INFO ChannelInfo[ANYSIZE_ARRAY]; +} SIM_EMI_BUS_QUERY_DEVICE_INFO_OUTPUT_BUFFER, *PSIM_EMI_BUS_QUERY_DEVICE_INFO_OUTPUT_BUFFER; + +typedef struct _SIM_EMI_BUS_SET_DEVICE_RATE_INPUT_BUFFER { + USHORT EmiVersion; + ULONG ChildDeviceHandle; + + USHORT ChannelCount; + ULONG64 AbsoluteEnergyRates[ANYSIZE_ARRAY]; +} SIM_EMI_BUS_SET_DEVICE_RATE_INPUT_BUFFER, *PSIM_EMI_BUS_SET_DEVICE_RATE_INPUT_BUFFER; + +#if defined(__cplusplus) +} +#endif |
