diff options
| author | Dave Wilson <[email protected]> | 2015-03-17 19:50:07 -0700 |
|---|---|---|
| committer | Dave Wilson <[email protected]> | 2015-03-17 19:50:07 -0700 |
| commit | 97cf5197cf5b882b2c689d8dc2b555f2edf8f418 (patch) | |
| tree | 46f3701832d70b420eb0fc0eb93261f9da45db3f /gpio/samples | |
| parent | ef1905bf1e8825bb31120dfb27e0daf3154d859a (diff) | |
Initial publish
Diffstat (limited to 'gpio/samples')
39 files changed, 12018 insertions, 0 deletions
diff --git a/gpio/samples/ReadMe.md b/gpio/samples/ReadMe.md new file mode 100644 index 00000000..97464c5e --- /dev/null +++ b/gpio/samples/ReadMe.md @@ -0,0 +1,28 @@ +GPIO Sample Drivers +=================== + +The GPIO samples contain annotated code to illustrate how to write a [GPIO controller driver](http://msdn.microsoft.com/en-us/library/windows/hardware/hh439509) that works in conjunction with the [GPIO framework extension](http://msdn.microsoft.com/en-us/library/windows/hardware/hh439512) (GpioClx) to handle GPIO I/O control requests, and a peripheral driver that runs in kernel mode and uses GPIO resources. For a sample that shows how to write a GPIO peripheral driver that runs in user mode, please refer to the SPB accelerometer sample driver (SPB\\peripherals\\accelerometer). + +The GPIO sample set contains the following three samples. + +<table> +<colgroup> +<col width="50%" /> +<col width="50%" /> +</colgroup> +<thead> +<tr class="header"> +<th align="left">Minifilter Sample +Description</th> +</tr> +</thead> +<tbody> +<tr class="odd"> +<td align="left"><p><em>SimGpio</em></p> +<p>The files in this sample contain the source code for a GPIO controller driver that communicates with GpioClx through the GpioClx device driver interface (DDI). The GPIO controller driver is written for a hypothetical memory-mapped GPIO controller (simgpio). The code is meant to be purely instructional. An ASL file illustrates how to specify a GPIO interrupt and I/O descriptor in the ACPI firmware.</p></td> +<td align="left"><p><em>SimGpio_I2C</em></p> +<p>The files in this sample contain the source code for a GPIO controller driver that communicates with GpioClx through the GpioClx DDI. In contrast to the SimGpio sample, the GPIO controller in this sample is not memory-mapped. The GPIO controller driver is written for a hypothetical GPIO controller that resides on an I<sup>2</sup>C bus (simgpio_i2c). The code is meant to be purely instructional. An ASL file illustrates how to specify a GPIO interrupt and I/O descriptor in the ACPI firmware.</p></td> +</tr> +</tbody> +</table> + diff --git a/gpio/samples/sim.sln b/gpio/samples/sim.sln new file mode 100644 index 00000000..edbafcc1 --- /dev/null +++ b/gpio/samples/sim.sln @@ -0,0 +1,72 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Simdevice", "Simdevice", "{B836C8C4-99F3-4B16-8B3D-558EFC65F9D3}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Simdeviceumdf", "Simdeviceumdf", "{5302F4A4-2BE9-43C9-BF4E-4ED0D4A131A6}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Simgpio", "Simgpio", "{3544A23D-F159-4D5F-B41D-A045D7BB4220}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Simgpio_i2c", "Simgpio_i2c", "{E0A658B7-1A02-4468-AB84-54444838B9BD}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "simdevice", "simdevice\simdevice.vcxproj", "{57BFED37-06F0-435D-A4CA-610B56B8987F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SimdeviceUMDF", "simdeviceumdf\SimdeviceUMDF.vcxproj", "{D768CF1C-AE06-4C12-B11D-21C5D70082F7}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "simgpio", "simgpio\simgpio.vcxproj", "{BCCD1049-70D2-4C07-BE8C-E9B609BD1714}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "simgpio_i2c", "simgpio_i2c\simgpio_i2c.vcxproj", "{FBC7D66C-8D89-4ED5-B378-A4EBB8DED3B2}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {57BFED37-06F0-435D-A4CA-610B56B8987F}.Debug|Win32.ActiveCfg = Debug|Win32 + {57BFED37-06F0-435D-A4CA-610B56B8987F}.Debug|Win32.Build.0 = Debug|Win32 + {57BFED37-06F0-435D-A4CA-610B56B8987F}.Release|Win32.ActiveCfg = Release|Win32 + {57BFED37-06F0-435D-A4CA-610B56B8987F}.Release|Win32.Build.0 = Release|Win32 + {57BFED37-06F0-435D-A4CA-610B56B8987F}.Debug|x64.ActiveCfg = Debug|x64 + {57BFED37-06F0-435D-A4CA-610B56B8987F}.Debug|x64.Build.0 = Debug|x64 + {57BFED37-06F0-435D-A4CA-610B56B8987F}.Release|x64.ActiveCfg = Release|x64 + {57BFED37-06F0-435D-A4CA-610B56B8987F}.Release|x64.Build.0 = Release|x64 + {D768CF1C-AE06-4C12-B11D-21C5D70082F7}.Debug|Win32.ActiveCfg = Debug|Win32 + {D768CF1C-AE06-4C12-B11D-21C5D70082F7}.Debug|Win32.Build.0 = Debug|Win32 + {D768CF1C-AE06-4C12-B11D-21C5D70082F7}.Release|Win32.ActiveCfg = Release|Win32 + {D768CF1C-AE06-4C12-B11D-21C5D70082F7}.Release|Win32.Build.0 = Release|Win32 + {D768CF1C-AE06-4C12-B11D-21C5D70082F7}.Debug|x64.ActiveCfg = Debug|x64 + {D768CF1C-AE06-4C12-B11D-21C5D70082F7}.Debug|x64.Build.0 = Debug|x64 + {D768CF1C-AE06-4C12-B11D-21C5D70082F7}.Release|x64.ActiveCfg = Release|x64 + {D768CF1C-AE06-4C12-B11D-21C5D70082F7}.Release|x64.Build.0 = Release|x64 + {BCCD1049-70D2-4C07-BE8C-E9B609BD1714}.Debug|Win32.ActiveCfg = Debug|Win32 + {BCCD1049-70D2-4C07-BE8C-E9B609BD1714}.Debug|Win32.Build.0 = Debug|Win32 + {BCCD1049-70D2-4C07-BE8C-E9B609BD1714}.Release|Win32.ActiveCfg = Release|Win32 + {BCCD1049-70D2-4C07-BE8C-E9B609BD1714}.Release|Win32.Build.0 = Release|Win32 + {BCCD1049-70D2-4C07-BE8C-E9B609BD1714}.Debug|x64.ActiveCfg = Debug|x64 + {BCCD1049-70D2-4C07-BE8C-E9B609BD1714}.Debug|x64.Build.0 = Debug|x64 + {BCCD1049-70D2-4C07-BE8C-E9B609BD1714}.Release|x64.ActiveCfg = Release|x64 + {BCCD1049-70D2-4C07-BE8C-E9B609BD1714}.Release|x64.Build.0 = Release|x64 + {FBC7D66C-8D89-4ED5-B378-A4EBB8DED3B2}.Debug|Win32.ActiveCfg = Debug|Win32 + {FBC7D66C-8D89-4ED5-B378-A4EBB8DED3B2}.Debug|Win32.Build.0 = Debug|Win32 + {FBC7D66C-8D89-4ED5-B378-A4EBB8DED3B2}.Release|Win32.ActiveCfg = Release|Win32 + {FBC7D66C-8D89-4ED5-B378-A4EBB8DED3B2}.Release|Win32.Build.0 = Release|Win32 + {FBC7D66C-8D89-4ED5-B378-A4EBB8DED3B2}.Debug|x64.ActiveCfg = Debug|x64 + {FBC7D66C-8D89-4ED5-B378-A4EBB8DED3B2}.Debug|x64.Build.0 = Debug|x64 + {FBC7D66C-8D89-4ED5-B378-A4EBB8DED3B2}.Release|x64.ActiveCfg = Release|x64 + {FBC7D66C-8D89-4ED5-B378-A4EBB8DED3B2}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {57BFED37-06F0-435D-A4CA-610B56B8987F} = {B836C8C4-99F3-4B16-8B3D-558EFC65F9D3} + {D768CF1C-AE06-4C12-B11D-21C5D70082F7} = {5302F4A4-2BE9-43C9-BF4E-4ED0D4A131A6} + {BCCD1049-70D2-4C07-BE8C-E9B609BD1714} = {3544A23D-F159-4D5F-B41D-A045D7BB4220} + {FBC7D66C-8D89-4ED5-B378-A4EBB8DED3B2} = {E0A658B7-1A02-4468-AB84-54444838B9BD} + EndGlobalSection +EndGlobal diff --git a/gpio/samples/simdevice/GpioSample.asl b/gpio/samples/simdevice/GpioSample.asl new file mode 100644 index 00000000..c393d9da --- /dev/null +++ b/gpio/samples/simdevice/GpioSample.asl @@ -0,0 +1,124 @@ + +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + GpioSample.asl + +Abstract: + + This sample ASL file describes a sample GPIO device and a sample peripheral + device which consumes IO and interrupt resources from the GPIO device. Please + note that: + + 1. The memory and IO descriptor under the GPIO device are simply examples of + what can be described. They are commented out to illustrate this point. Actual + values will vary according to the platform specifications (e.g. GIC and memory) + + 2. The sample ASL DSDT definition block defines only the components relevant + to demonstrate GPIO IO and interrupt resource usage. Rest of the DSDT will vary + according to platform specifications. + + 3. The PNP IDs for the GPIO and peripheral device are for demonstration purposes + only. Actual values need to reflect those chosen for the actual GPIO or + peripheral device. + +--*/ + + +DefinitionBlock ("DSDT.AML", "DSDT", 0x02, "MSFT", "SAMPLE", 0x1) { + + // + // System Bus + // + + Scope (\_SB) { + + // + // Sample GPIO device + // + + Device(GPIO) { + Name (_ADR, 0) + Name (_HID, "TEST0001") + Name (_CID, "TEST0001") + Name(_UID, 4) + + Method (_CRS, 0x0, NotSerialized) { + Name (RBUF, ResourceTemplate () { + + // + // Interrupt resource. In this example, banks 0 & 1 share the same + // interrupt to the parent controller and similarly banks 2 & 3. + // + // N.B. The definition below is chosen for an arbitrary + // test platform. It needs to be changed to reflect the hardware + // configuration of the actual platform. + // + + Interrupt(ResourceConsumer, Level, ActiveHigh, Shared, , , ) {50} + Interrupt(ResourceConsumer, Level, ActiveHigh, Shared, , , ) {50} + Interrupt(ResourceConsumer, Level, ActiveHigh, Shared, , , ) {51} + Interrupt(ResourceConsumer, Level, ActiveHigh, Shared, , , ) {51} + + // + // Memory resource. The definition below is chosen for an arbitrary + // test platform. It needs to be changed to reflect the hardware + // configuration of the actual platform. + // + + Memory32Fixed(ReadWrite, 0x00100000, 0x18) + }) + + Return (RBUF) + } + + Method (_STA, 0x0, NotSerialized) { + Return(0xf) + } + + // + // Sample peripheral device + // + + Device (TDEV) { + Name (_ADR, 0) + Name (_HID, "TEST0003") + Name (_CID, "TEST0003") + Name (_UID, 1) + + Method (_CRS, 0x0, NotSerialized) { + Name (RBUF, ResourceTemplate () { + + // + // GPIO Interrupt Resources + // + + GpioInt(Edge, ActiveHigh, Shared, PullUp, 0, "\\_SB.GPIO", 0, ResourceConsumer,, RawDataBuffer() {1}) {1} + // GpioInt(Edge, ActiveHigh, Shared, PullUp, 0, "\\_SB.GPIO", 0, ResourceConsumer,, RawDataBuffer() {1}) {2} + + // + // GPIO IO Resources + // + + GpioIo(Exclusive, PullUp, 0, 0,, "\\_SB.GPIO",0, ResourceConsumer, , RawDataBuffer() {1}) {10} + GpioIo(Exclusive, PullUp, 0, 0,, "\\_SB.GPIO",0, ResourceConsumer, , RawDataBuffer() {1}) {11} + }) + + Return (RBUF) + } + + Method (_STA, 0x0, NotSerialized) { + Return(0xf) + } + } + } + } + } diff --git a/gpio/samples/simdevice/common.h b/gpio/samples/simdevice/common.h new file mode 100644 index 00000000..315044c8 --- /dev/null +++ b/gpio/samples/simdevice/common.h @@ -0,0 +1,84 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + common.h + +Abstract: + + Header file that provide some utility functionalities to the sample device driver + +Environment: + + Kernel mode + +--*/ + +#pragma once + +#include <ntddk.h> +#pragma warning(disable:4201) // disable nameless struct/union warnings +#include <wdf.h> +#pragma warning(default:4201) + +#define NTSTRSAFE_LIB +#include <ntstrsafe.h> + +#ifndef MAX_USHORT +#define MAX_USHORT ((USHORT)-1) +#endif + +#ifndef MAX_ULONG +#define MAX_ULONG ((ULONG)-1) +#endif + +#ifndef MAX_ULONG64 +#define MAX_ULONG64 ((ULONG64)-1) +#endif + +// +// Useful macros for setting and checking flags. +// + +#define SET_FLAGS(_x, _f) ((_x) |= (_f)) +#define CLEAR_FLAGS(_x, _f) ((_x) &= ~(_f)) +#define CLEAR_OTHER_FLAGS(_x, _f) ((_x) &= (_f)) +#define CHECK_FLAG(_x, _f) ((_x) & (_f)) + +// +// Macros for rounding up or down. +// + +#define ROUND_DOWN(_x, _alignment) \ + ((_alignment == 1) ? (_x) : (((_x) / (_alignment)) * (_alignment))) + +#define ROUND_UP(_x, _alignment) \ + ROUND_DOWN((_x) + (_alignment) - 1, (_alignment)) + +// +// Macros for find minimum and maximum of two integers. +// + +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#define MAX(a,b) (((a) < (b)) ? (b) : (a)) + +// +// Define macros to allow easy pointer arithmetic. +// + +#define Add2Ptr(_Ptr, _Value) ((PVOID)((PUCHAR)(_Ptr) + (_Value))) +#define PtrOffset(_Base, _Ptr) ((ULONG_PTR)(_Ptr) - (ULONG_PTR)(_Base)) + +// 4127 -- Conditional Expression is Constant warning +#define WHILE(constant) \ +__pragma(warning(disable: 4127)) while(constant) __pragma(warning(default: 4127)) + +#define FIELD_OFFSET_AND_SIZE(t, f) \ + (FIELD_OFFSET(t, f) + FIELD_SIZE(t, f))
\ No newline at end of file diff --git a/gpio/samples/simdevice/simdevice.c b/gpio/samples/simdevice/simdevice.c new file mode 100644 index 00000000..f54f1a84 --- /dev/null +++ b/gpio/samples/simdevice/simdevice.c @@ -0,0 +1,819 @@ +/*++ + +Copyright (c) 1990-2010 Microsoft Corporation + +Module Name: + + SimpleDevice.c + +Abstract: + + This is a simple device driver that consumes GPIO pins for I/O and interrupt. + + +Environment: + + Kernel mode + +--*/ + +// +// ------------------------------------------------------------------- Includes +// + +#include "common.h" +#include <gpio.h> +#define RESHUB_USE_HELPER_ROUTINES +#include <reshub.h> + +// +// -------------------------------------------------------------------- Defines +// + +#define MAX_NUMBER_IO_RESOURCES 2 + +// +// -------------------------------------------------------------------- Types +// + +typedef struct _SAMPLE_DRV_DEVICE_EXTENSION { + ULONG IoResourceCount; + ULONG InterruptCount; + LARGE_INTEGER ConnectionIds[MAX_NUMBER_IO_RESOURCES]; +} SAMPLE_DRV_DEVICE_EXTENSION, *PSAMPLE_DRV_DEVICE_EXTENSION; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(SAMPLE_DRV_DEVICE_EXTENSION, SampleDrvGetDeviceExtension) + +// +// ----------------------------------------------------------------- Prototypes +// + +DRIVER_INITIALIZE DriverEntry; + +EVT_WDF_DRIVER_DEVICE_ADD SampleDrvEvtDeviceAdd; +EVT_WDF_DEVICE_D0_ENTRY SampleDrvEvtDeviceD0Entry; +EVT_WDF_DEVICE_PREPARE_HARDWARE SampleDrvEvtDevicePrepareHardware; +EVT_WDF_INTERRUPT_DPC SampleDrvInterruptDpc; +EVT_WDF_INTERRUPT_ISR SampleDrvInterruptIsr; +EVT_WDF_INTERRUPT_ISR SampleDrvInterruptPassiveCallback; + +NTSTATUS +TestReadWrite ( + _In_ WDFDEVICE Device, + _In_ PCUNICODE_STRING RequestString, + _In_ BOOLEAN ReadOperation, + _Inout_ PUCHAR Data, + _In_ _In_range_(>, 0) ULONG Size, + _Out_ WDFIOTARGET *IoTargetOut + ); + +// +// -------------------------------------------------------------------- Pragmas +// + +#pragma alloc_text(PAGE, SampleDrvEvtDeviceAdd) +#pragma alloc_text(PAGE, SampleDrvEvtDevicePrepareHardware) + +// +// ------------------------------------------------------------------ Functions +// + +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + This routine is the driver initialization entry point. + +Arguments: + + DriverObject - Pointer to the driver object created by the I/O manager. + + RegistryPath - Pointer to the driver specific registry key. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + WDFDRIVER Driver; + WDF_DRIVER_CONFIG DriverConfig; + NTSTATUS Status; + + // + // Initialize the driver configuration structure. + // + + WDF_DRIVER_CONFIG_INIT(&DriverConfig, SampleDrvEvtDeviceAdd); + + // + // Create a framework driver object to represent our driver. + // + + Status = WdfDriverCreate(DriverObject, + RegistryPath, + WDF_NO_OBJECT_ATTRIBUTES, + &DriverConfig, + &Driver); + + if (!NT_SUCCESS(Status)) { + goto DriverEntryEnd; + } + +DriverEntryEnd: + return Status; +} + +BOOLEAN +SampleDrvInterruptIsr ( + _In_ WDFINTERRUPT Interrupt, + _In_ ULONG MessageID + ) + +/*++ + +Routine Description: + + This routine is the interrupt service routine for the sample device + + N.B. This driver assumes that the interrupt line is not shared with any + other device. Hence it always claims the interrupt. + +Arguments: + + Interupt - Supplies a handle to interrupt object (WDFINTERRUPT) for this + device. + + MessageID - Supplies the MSI message ID for MSI-based interrupts. + +Return Value: + + Always TRUE. + +--*/ + +{ + + + UNREFERENCED_PARAMETER(Interrupt); + UNREFERENCED_PARAMETER(MessageID); + + // + // The sample driver always returns TRUE (e.g. claiming the interrupt) + // from its ISR. In reality, the driver needs to do whatever necessary to + // quiesce the interrupt before claiming the interrupt. In case of spurious + // interrupts, the ISR returns FALSE. If additional work needs to be done + // at a lower IRQL, schedule a DPC. + // + + return TRUE; +} + +#if 0 +BOOLEAN +SampleDrvInterruptPassiveCallback ( + _In_ WDFINTERRUPT Interrupt, + _In_ ULONG MessageID + ) + +/*++ + +Routine Description: + + This routine is the passive interrupt callback routine for the sample device. + As its name suggests, this routine is always invoked at PASSIVE_LEVEL. + This is useful in scenarios where the device is located behind a slow serial + peripheral bus(SPB) and requires communication (possible only at PASSIVE_LEVEL) + over the bus in quiescing the interrupt source. + + N.B. It is possible for passive interrupt callback and DIRQL ISRs to + coexist for the same device and/or IDT entry. Interrupt objects chained to a + given IDT entry are always ordered (at interrupt connect time) by the OS such + that the DIRQL ISR interrupt objects are located before the passive callback + ones. Consequently, during interrupt dispatching, the OS would walk the list + in that order until the first ISR/passive callback returns TRUE to claim the + interrupt. + +Arguments: + + Interupt - Supplies a handle to interrupt object (WDFINTERRUPT) for this + device. + + MessageID - Supplies the MSI message ID for MSI-based interrupts. + +Return Value: + + Always TRUE. + +--*/ + +{ + + + UNREFERENCED_PARAMETER(Interrupt); + UNREFERENCED_PARAMETER(MessageID); + + // + // The sample driver always returns TRUE (e.g. claiming the interrupt) + // from its passive callback. In reality, the driver needs to do whatever necessary to + // quiesce the interrupt before claiming the interrupt. + // + + return TRUE; +} +#endif + +VOID +SampleDrvInterruptDpc ( + _In_ WDFINTERRUPT WdfInterrupt, + _In_ WDFOBJECT WdfDevice + ) + +/*++ + +Routine Description: + + This routine is the DPC callback for the ISR. This routine is unused. + +Arguments: + + Interupt - Supplies a handle to interrupt object (WDFINTERRUPT) for this + device. + + Device - Supplies a handle to the framework device object. + +Return Value: + + None. + +--*/ + +{ + + UNREFERENCED_PARAMETER(WdfInterrupt); + UNREFERENCED_PARAMETER(WdfDevice); + return; +} + +_Use_decl_annotations_ +NTSTATUS +SampleDrvEvtDeviceAdd ( + WDFDRIVER Driver, + PWDFDEVICE_INIT DeviceInit +) + +/*++ + +Routine Description: + + This routine is the AddDevice entry point for the sample device driver. + It sets the ISR and DPC routine handlers for the interrupt and the passive + level callback for the passive interrupt + + N.B. The sample device expects two interrupt resources in connecting its + DIRQL ISR and PASSIVE_LEVEL callback. + +Arguments: + + Driver - Supplies a handle to the driver object created in DriverEntry. + + DeviceInit - Supplies a pointer to a framework-allocated WDFDEVICE_INIT + structure. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + WDF_PNPPOWER_EVENT_CALLBACKS Callbacks; + WDFDEVICE Device; + WDF_OBJECT_ATTRIBUTES FdoAttributes; + WDF_INTERRUPT_CONFIG InterruptConfiguration; + NTSTATUS Status; + WDFINTERRUPT WdfInterrupt; + + UNREFERENCED_PARAMETER(Driver); + + PAGED_CODE(); + + // + // Set PnP callbacks for prepare/release hardware and D0 entry/exit. All + // callbacks not overriden here will be handled by the framework in the + // default manner. + // + + WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&Callbacks); + Callbacks.EvtDevicePrepareHardware = SampleDrvEvtDevicePrepareHardware; + Callbacks.EvtDeviceD0Entry = SampleDrvEvtDeviceD0Entry; + + // + // Register the PnP callbacks with the framework. + // + + WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &Callbacks); + + // + // Initialize FDO attributes with the sample device extension. + // + + WDF_OBJECT_ATTRIBUTES_INIT(&FdoAttributes); + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&FdoAttributes, SAMPLE_DRV_DEVICE_EXTENSION); + + // + // Call the framework to create the device and attach it to the lower stack. + // + + Status = WdfDeviceCreate(&DeviceInit, &FdoAttributes, &Device); + if (!NT_SUCCESS(Status)) { + goto EvtDeviceAddEnd; + } + + // + // Create an interrupt object for the DIRQL ISR + // + + WDF_INTERRUPT_CONFIG_INIT(&InterruptConfiguration, + SampleDrvInterruptIsr, + SampleDrvInterruptDpc); + + Status = WdfInterruptCreate(Device, + &InterruptConfiguration, + WDF_NO_OBJECT_ATTRIBUTES, + &WdfInterrupt); + + if (!NT_SUCCESS(Status)) { + + goto EvtDeviceAddEnd; + } + +#if 0 + // + // Create an interrupt object for the passive interrupt callback. Note that + // the interrupt object is chained to the same interrupt line/IDT as the + // DIRQL one. + // + + WDF_INTERRUPT_CONFIG_INIT(&InterruptConfiguration, + SampleDrvInterruptIsr, + NULL); + + // + // Set passive handling to true + // + + InterruptConfiguration.PassiveHandling = TRUE; + + Status = WdfInterruptCreate(Device, + &InterruptConfiguration, + WDF_NO_OBJECT_ATTRIBUTES, + &WdfInterrupt); +#endif + +EvtDeviceAddEnd: + return Status; +} + +NTSTATUS +SampleDrvEvtDevicePrepareHardware ( + _In_ WDFDEVICE Device, + _In_ WDFCMRESLIST ResourcesRaw, + _In_ WDFCMRESLIST ResourcesTranslated + ) + +/*++ + +Routine Description: + + This routine is called by the framework when the PnP manager sends an + IRP_MN_START_DEVICE request to the driver stack. + +Arguments: + + Device - Supplies a handle to a framework device object. + + ResourcesRaw - Supplies a handle to a collection of framework resource + objects. This collection identifies the raw (bus-relative) hardware + resources that have been assigned to the device. + + ResourcesTranslated - Supplies a handle to a collection of framework + resource objects. This collection identifies the translated + (system-physical) hardware resources that have been assigned to the + device. The resources appear from the CPU's point of view. + +Return Value: + + NT status code. + +--*/ + +{ + + PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor; + PSAMPLE_DRV_DEVICE_EXTENSION SampleDrvExtension; + ULONG Index; + ULONG ResourceCount; + NTSTATUS Status; + ULONG IoResourceIndex; + + UNREFERENCED_PARAMETER(Device); + UNREFERENCED_PARAMETER(ResourcesRaw); + + PAGED_CODE(); + + SampleDrvExtension = SampleDrvGetDeviceExtension(Device); + Status = STATUS_SUCCESS; + IoResourceIndex = 0; + + SampleDrvExtension->InterruptCount = 0; + + // + // Walk through the resource list and map all the resources. Only one + // memory resource and one interrupt is expected. + // + + ResourceCount = WdfCmResourceListGetCount(ResourcesTranslated); + for (Index = 0; Index < ResourceCount; Index += 1) { + Descriptor = WdfCmResourceListGetDescriptor(ResourcesTranslated, Index); + switch(Descriptor->Type) { + + // + // This memory resource supplies the base of the device registers. + // + + case CmResourceTypeConnection: + + // + // Check against expected connection type + // + + if ((Descriptor->u.Connection.Class == + CM_RESOURCE_CONNECTION_CLASS_GPIO) && + (Descriptor->u.Connection.Type == + CM_RESOURCE_CONNECTION_TYPE_GPIO_IO)) { + + SampleDrvExtension->ConnectionIds[IoResourceIndex].LowPart = + Descriptor->u.Connection.IdLowPart; + SampleDrvExtension->ConnectionIds[IoResourceIndex].HighPart = + Descriptor->u.Connection.IdHighPart; + IoResourceIndex++; + } else { + + Status = STATUS_UNSUCCESSFUL; + } + + break; + + // + // Interrupt resource + // + + case CmResourceTypeInterrupt: + SampleDrvExtension->InterruptCount++; + + default: + break; + } + + if (!NT_SUCCESS(Status)) { + goto DevicePrepareHardwareEnd; + } + } + + // + // Ensure that at least two interrupt resources are defined. One for DIRQL + // and another for the passive level ISR + // + + NT_ASSERT(SampleDrvExtension->InterruptCount > 0); + + if (SampleDrvExtension->InterruptCount < 1) { + Status = STATUS_UNSUCCESSFUL; + goto DevicePrepareHardwareEnd; + } + + // + // Store the number of GPIO IO connection strings + // + + SampleDrvExtension->IoResourceCount = IoResourceIndex; + +DevicePrepareHardwareEnd: + return Status; +} + +NTSTATUS +SampleDrvEvtDeviceD0Entry ( + _In_ WDFDEVICE Device, + _In_ WDF_POWER_DEVICE_STATE PreviousPowerState + ) + +/*++ + +Routine Description: + + This routine is invoked by the framework to program the device to goto + D0, which is the working state. The framework invokes callback every + time the hardware needs to be (re-)initialized. This includes after + IRP_MN_START_DEVICE, IRP_MN_CANCEL_STOP_DEVICE, IRP_MN_CANCEL_REMOVE_DEVICE, + and IRP_MN_SET_POWER-D0. + + N.B. This function is not marked pageable because this function is in + the device power up path. When a function is marked pagable and the + code section is paged out, it will generate a page fault which could + impact the fast resume behavior because the client driver will have + to wait until the system drivers can service this page fault. + +Arguments: + + Device - Supplies a handle to the framework device object. + + PreviousPowerState - WDF_POWER_DEVICE_STATE-typed enumerator that identifies + the device power state that the device was in before this transition + to D0. + +Return Value: + + NTSTATUS code. A failure here will indicate a fatal error and cause the + framework to tear down the stack. + +--*/ + +{ + + BYTE Data; + NTSTATUS Status; + WDFIOTARGET ReadTarget; + WDFIOTARGET WriteTarget; + PSAMPLE_DRV_DEVICE_EXTENSION SampleDrvExtension; + UNICODE_STRING ReadString; + WCHAR ReadStringBuffer[100]; + UNICODE_STRING WriteString; + WCHAR WriteStringBuffer[100]; + + UNREFERENCED_PARAMETER(PreviousPowerState); + + ReadTarget = NULL; + WriteTarget = NULL; + + SampleDrvExtension = SampleDrvGetDeviceExtension(Device); + + // + // For demonstration purporses, the sample device consumes two IO resources, + // the first of which will be used for input, and the second for output. + // + + RtlInitEmptyUnicodeString(&ReadString, + ReadStringBuffer, + sizeof(ReadStringBuffer)); + + RtlInitEmptyUnicodeString(&WriteString, + WriteStringBuffer, + sizeof(WriteStringBuffer)); + + + // + // Construct full-path string for GPIO read operation + // + + Status = RESOURCE_HUB_CREATE_PATH_FROM_ID(&ReadString, + SampleDrvExtension->ConnectionIds[0].LowPart, + SampleDrvExtension->ConnectionIds[0].HighPart); + + if (!NT_SUCCESS(Status)) { + goto Cleanup; + } + + // + // Construct full-path string for GPIO write operation + // + + Status = RESOURCE_HUB_CREATE_PATH_FROM_ID(&WriteString, + SampleDrvExtension->ConnectionIds[1].LowPart, + SampleDrvExtension->ConnectionIds[1].HighPart); + + if (!NT_SUCCESS(Status)) { + goto Cleanup; + } + + // + // Perform the read operation + // + + Data = 0x0; + Status = TestReadWrite(Device, &ReadString, TRUE, &Data, sizeof(Data), &ReadTarget); + if (!NT_SUCCESS(Status)) { + goto Cleanup; + } + + // + // Perform the write operation + // + + Status = TestReadWrite(Device, &WriteString, FALSE, &Data, sizeof(Data), &WriteTarget); + if (!NT_SUCCESS(Status)) { + goto Cleanup; + } + +Cleanup: + + if (ReadTarget != NULL) { + WdfIoTargetClose(ReadTarget); + WdfObjectDelete(ReadTarget); + } + + if (WriteTarget != NULL) { + WdfIoTargetClose(WriteTarget); + WdfObjectDelete(WriteTarget); + } + + return Status; +} + +NTSTATUS +TestReadWrite ( + _In_ WDFDEVICE Device, + _In_ PCUNICODE_STRING RequestString, + _In_ BOOLEAN ReadOperation, + _Inout_ PUCHAR Data, + _In_ _In_range_(>, 0) ULONG Size, + _Out_ WDFIOTARGET *IoTargetOut + ) + +/*++ + +Routine Description: + + This is a utility routine to test read or write on a set of GPIO pins. + +Arguments: + + Device - Supplies a handle to the framework device object. + + RequestString - Supplies a pointer to the unicode string to be opened. + + ReadOperation - Supplies a boolean that identifies whether read (TRUE) or + write (FALSE) should be performed. + + Data - Supplies a pointer containing the buffer that should be read from + or written to. + + Size - Supplies the size of the data buffer in bytes. + + IoTargetOut - Supplies a pointer that receives the IOTARGET created by + WDF. + +Return Value: + + None. + +--*/ + +{ + + WDF_OBJECT_ATTRIBUTES Attributes; + WDFREQUEST IoctlRequest; + WDFIOTARGET IoTarget; + ULONG DesiredAccess; + WDFMEMORY WdfMemory; + WDF_OBJECT_ATTRIBUTES RequestAttributes; + WDF_REQUEST_SEND_OPTIONS SendOptions; + NTSTATUS Status; + WDF_OBJECT_ATTRIBUTES ObjectAttributes; + WDF_IO_TARGET_OPEN_PARAMS OpenParams; + + WDF_OBJECT_ATTRIBUTES_INIT(&ObjectAttributes); + ObjectAttributes.ParentObject = Device; + + IoctlRequest = NULL; + IoTarget = NULL; + + if ((Data == NULL) || (Size == 0)) { + Status = STATUS_INVALID_PARAMETER; + goto TestReadWriteEnd; + } + + Status = WdfIoTargetCreate(Device, + &ObjectAttributes, + &IoTarget); + + if (!NT_SUCCESS(Status)) { + goto TestReadWriteEnd; + } + + // + // Specify desired file access + // + + if (ReadOperation != FALSE) { + DesiredAccess = FILE_GENERIC_READ; + + } else { + DesiredAccess = FILE_GENERIC_WRITE; + } + + WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME(&OpenParams, + RequestString, + DesiredAccess); + + // + // Open the IoTarget for I/O operation + // + + Status = WdfIoTargetOpen(IoTarget, &OpenParams); + if (!NT_SUCCESS(Status)) { + goto TestReadWriteEnd; + } + + WDF_OBJECT_ATTRIBUTES_INIT(&RequestAttributes); + Status = WdfRequestCreate(&RequestAttributes, IoTarget, &IoctlRequest); + if (!NT_SUCCESS(Status)) { + goto TestReadWriteEnd; + } + + // + // Set up a WDF memory object for the IOCTL request + // + + WDF_OBJECT_ATTRIBUTES_INIT(&Attributes); + Attributes.ParentObject = IoctlRequest; + Status = WdfMemoryCreatePreallocated(&Attributes, Data, Size, &WdfMemory); + if (!NT_SUCCESS(Status)) { + goto TestReadWriteEnd; + } + + // + // Format the request as read or write operation + // + + if (ReadOperation != FALSE) { + Status = WdfIoTargetFormatRequestForIoctl(IoTarget, + IoctlRequest, + IOCTL_GPIO_READ_PINS, + NULL, + 0, + WdfMemory, + 0); + + } else { + Status = WdfIoTargetFormatRequestForIoctl(IoTarget, + IoctlRequest, + IOCTL_GPIO_WRITE_PINS, + WdfMemory, + 0, + WdfMemory, + 0); + } + + if (!NT_SUCCESS(Status)) { + goto TestReadWriteEnd; + } + + // + // Send the request synchronously with an arbitrary timeout of 60 seconds + // + + WDF_REQUEST_SEND_OPTIONS_INIT(&SendOptions, + WDF_REQUEST_SEND_OPTION_SYNCHRONOUS); + + WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT(&SendOptions, + WDF_REL_TIMEOUT_IN_SEC(60)); + + Status = WdfRequestAllocateTimer(IoctlRequest); + if (!NT_SUCCESS(Status)) { + goto TestReadWriteEnd; + } + + if (!WdfRequestSend(IoctlRequest, IoTarget, &SendOptions)) { + Status = WdfRequestGetStatus(IoctlRequest); + } + + if (NT_SUCCESS(Status)) { + *IoTargetOut = IoTarget; + } + +TestReadWriteEnd: + if (IoctlRequest != NULL) { + WdfObjectDelete(IoctlRequest); + } + + if (!NT_SUCCESS(Status) && (IoTarget != NULL)) { + WdfIoTargetClose(IoTarget); + WdfObjectDelete(IoTarget); + } + + return Status; +} + + + diff --git a/gpio/samples/simdevice/simdevice.inx b/gpio/samples/simdevice/simdevice.inx new file mode 100644 index 00000000..34e65a68 --- /dev/null +++ b/gpio/samples/simdevice/simdevice.inx @@ -0,0 +1,76 @@ +;/*++ +; +;Copyright (c) Microsoft Corporation. All rights reserved. +; +;Module Name: +; +; SIMGPIO.INF +; +;Abstract: +; INF file for installing Simulated Peripheral Device Driver. +; +;--*/ + +[Version] +Signature="$WINDOWS NT$" +Class=System +ClassGuid={4d36e97d-e325-11ce-bfc1-08002be10318} +Provider=%MSFT% +DriverVer=05/07/2010 +CatalogFile=gpiosamples.cat + +[SourceDisksNames] +3426=windows cd + +[SourceDisksFiles] +Simdevice.sys = 3426 + +;12 == Windows\System32\Drivers +[DestinationDirs] +DefaultDestDir = 12 + +[ControlFlags] +ExcludeFromSelect=* + +;***************************************** +; SimDevice Install Section +;***************************************** + +[Manufacturer] +%MSFT%=Microsoft,NT$ARCH$ + +[Microsoft.NT$ARCH$] +%DeviceDesc%=DriverInstall,ACPI\TEST0003 + +[DriverInstall.NT] +CopyFiles=DriverInstall_Copy + +[DriverInstall.NT.Services] +AddService = simdevice,2,DriverInstall_Service + +[DriverInstall_Copy] +simdevice.sys,,,0x100 + +[DriverInstall_Service] +DisplayName = %SvcDesc% +ServiceType = %SERVICE_KERNEL_DRIVER% +StartType = %SERVICE_DEMAND_START% +ErrorControl = %SERVICE_ERROR_NORMAL% +ServiceBinary = %12%\Simdevice.sys + +[Strings] +;Localizable Strings +MSFT = "Microsoft" +Std = "(Standard system devices) Test device" +SvcDesc = "Test device service" +DeviceDesc = "Test device description" + +;Non-Localizable Strings +SERVICE_KERNEL_DRIVER = 1 +SERVICE_DEMAND_START = 3 +SERVICE_ERROR_NORMAL = 1 + + + + + diff --git a/gpio/samples/simdevice/simdevice.rc b/gpio/samples/simdevice/simdevice.rc new file mode 100644 index 00000000..3171260d --- /dev/null +++ b/gpio/samples/simdevice/simdevice.rc @@ -0,0 +1,12 @@ +#include <windows.h> + +#include <ntverp.h> + +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "Sample device driver" +#define VER_INTERNALNAME_STR "simdevice.sys" +#define VER_ORIGINALFILENAME_STR "simdevice.sys" + +#include "common.ver" + diff --git a/gpio/samples/simdevice/simdevice.vcxproj b/gpio/samples/simdevice/simdevice.vcxproj new file mode 100644 index 00000000..6d685fcf --- /dev/null +++ b/gpio/samples/simdevice/simdevice.vcxproj @@ -0,0 +1,189 @@ +<?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|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{57BFED37-06F0-435D-A4CA-610B56B8987F}</ProjectGuid> + <RootNamespace>$(MSBuildProjectName)</RootNamespace> + <KMDF_VERSION_MAJOR>1</KMDF_VERSION_MAJOR> + <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> + <Platform Condition="'$(Platform)' == ''">Win32</Platform> + <SampleGuid>{7E5C1E9A-1990-437C-B2AB-608C84382F8B}</SampleGuid> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType>KMDF</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType>KMDF</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType>KMDF</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType>KMDF</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <PropertyGroup> + <OutDir>$(IntDir)</OutDir> + </PropertyGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ItemGroup Label="WrappedTaskItems"> + <Inf Include=".\simdevice.inx"> + <DateStamp>*</DateStamp> + <SpecifyDriverVerDirectiveDate>true</SpecifyDriverVerDirectiveDate> + <Architecture>$(InfArch)</Architecture> + <SpecifyArchitecture>true</SpecifyArchitecture> + <CopyOutput>.\$(IntDir)\simdevice.inf</CopyOutput> + </Inf> + </ItemGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetName>simdevice</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetName>simdevice</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetName>simdevice</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetName>simdevice</TargetName> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\ksguid.lib;$(DDK_LIB_PATH)\ntstrsafe.lib</AdditionalDependencies> + </Link> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\..\inc</AdditionalIncludeDirectories> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\..\inc</AdditionalIncludeDirectories> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\..\inc</AdditionalIncludeDirectories> + </Midl> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\ksguid.lib;$(DDK_LIB_PATH)\ntstrsafe.lib</AdditionalDependencies> + </Link> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\..\inc</AdditionalIncludeDirectories> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\..\inc</AdditionalIncludeDirectories> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\..\inc</AdditionalIncludeDirectories> + </Midl> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\ksguid.lib;$(DDK_LIB_PATH)\ntstrsafe.lib</AdditionalDependencies> + </Link> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\..\inc</AdditionalIncludeDirectories> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\..\inc</AdditionalIncludeDirectories> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\..\inc</AdditionalIncludeDirectories> + </Midl> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\ksguid.lib;$(DDK_LIB_PATH)\ntstrsafe.lib</AdditionalDependencies> + </Link> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\..\inc</AdditionalIncludeDirectories> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\..\inc</AdditionalIncludeDirectories> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\..\inc</AdditionalIncludeDirectories> + </Midl> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="simdevice.c" /> + <ResourceCompile Include="simdevice.rc" /> + </ItemGroup> + <ItemGroup> + <Inf Exclude="@(Inf)" Include="*.inf" /> + <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" /> + <FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" /> + </ItemGroup> + <ItemGroup> + <None Exclude="@(None)" Include="*.txt;*.htm;*.html" /> + <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" /> + <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" /> + </ItemGroup> + <ItemGroup> + <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> +</Project>
\ No newline at end of file diff --git a/gpio/samples/simdevice/simdevice.vcxproj.Filters b/gpio/samples/simdevice/simdevice.vcxproj.Filters new file mode 100644 index 00000000..2cc912e8 --- /dev/null +++ b/gpio/samples/simdevice/simdevice.vcxproj.Filters @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> + <UniqueIdentifier>{C0D3EEB7-2138-4947-92CF-E7B13BB61AA2}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files"> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + <UniqueIdentifier>{70E36F5B-DD23-4371-98D3-59AB4042B0F1}</UniqueIdentifier> + </Filter> + <Filter Include="Resource Files"> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> + <UniqueIdentifier>{DCCD5E1C-EB33-41FC-81B3-52934D7E2A5A}</UniqueIdentifier> + </Filter> + <Filter Include="Driver Files"> + <Extensions>inf;inv;inx;mof;mc;</Extensions> + <UniqueIdentifier>{6B2886D3-87CA-46FD-B2A0-C624C0F69F92}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <FilesToPackage Include=".\Debug\\simdevice.inf"> + <Filter>Driver Files</Filter> + </FilesToPackage> + <Inf Include=".\simdevice.inx"> + <Filter>Driver Files</Filter> + </Inf> + </ItemGroup> + <ItemGroup> + <ClCompile Include="simdevice.c"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="simdevice.rc"> + <Filter>Resource Files</Filter> + </ResourceCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/gpio/samples/simdeviceumdf/GpioSampleUMDF.asl b/gpio/samples/simdeviceumdf/GpioSampleUMDF.asl new file mode 100644 index 00000000..7e52795f --- /dev/null +++ b/gpio/samples/simdeviceumdf/GpioSampleUMDF.asl @@ -0,0 +1,122 @@ + +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + GpioSample.asl + +Abstract: + + This sample ASL file describes a sample GPIO device and a sample peripheral + device which consumes IO and interrupt resources from the GPIO device. Please + note that: + + 1. The memory and IO descriptor under the GPIO device are simply examples of + what can be described. They are commented out to illustrate this point. Actual + values will vary according to the platform specifications (e.g. GIC and memory) + + 2. The sample ASL DSDT definition block defines only the components relevant + to demonstrate GPIO IO and interrupt resource usage. Rest of the DSDT will vary + according to platform specifications. + + 3. The PNP IDs for the GPIO and peripheral device are for demonstration purposes + only. Actual values need to reflect those chosen for the actual GPIO or + peripheral device. + +--*/ + + +DefinitionBlock ("DSDT.AML", "DSDT", 0x02, "MSFT", "SAMPLE", 0x1) { + + // + // System Bus + // + + Scope (\_SB) { + + // + // Sample GPIO device + // + + Device(GPIO) { + Name (_ADR, 0) + Name (_HID, "TEST0001") + Name (_CID, "TEST0001") + Name(_UID, 4) + + Method (_CRS, 0x0, NotSerialized) { + Name (RBUF, ResourceTemplate () { + + // + // Interrupt resource. In this example, banks 0 & 1 share the same + // interrupt to the parent controller and similarly banks 2 & 3. + // + // N.B. The definition below is chosen for an arbitrary + // test platform. It needs to be changed to reflect the hardware + // configuration of the actual platform. + // + + Interrupt(ResourceConsumer, Level, ActiveHigh, Shared, , , ) {50} + Interrupt(ResourceConsumer, Level, ActiveHigh, Shared, , , ) {50} + Interrupt(ResourceConsumer, Level, ActiveHigh, Shared, , , ) {51} + Interrupt(ResourceConsumer, Level, ActiveHigh, Shared, , , ) {51} + + // + // Memory resource. The definition below is chosen for an arbitrary + // test platform. It needs to be changed to reflect the hardware + // configuration of the actual platform. + // + + Memory32Fixed(ReadWrite, 0x00100000, 0x18) + }) + + Return (RBUF) + } + + Method (_STA, 0x0, NotSerialized) { + Return(0xf) + } + + // + // Sample peripheral device + // + + Device (TDEV) { + Name (_ADR, 0) + Name (_HID, "TEST0004") + Name (_CID, "TEST0004") + Name (_UID, 1) + + Method (_CRS, 0x0, NotSerialized) { + Name (RBUF, ResourceTemplate () { + + // + // GPIO Interrupt Resources + // + + GpioInt(Edge, ActiveHigh, Exclusive, PullUp, 0, "\\_SB.GPIO", 0, ResourceConsumer,, RawDataBuffer() {1}) {2} + + // + // GPIO IO Resources + // + + GpioIo(Exclusive, PullUp, 0, 0,, "\\_SB.GPIO",0, ResourceConsumer, , RawDataBuffer() {1}) {10} + }) + + Return (RBUF) + } + + Method (_STA, 0x0, NotSerialized) { + Return(0xf) + } + } + } + } + } diff --git a/gpio/samples/simdeviceumdf/SimdeviceUMDF.vcxproj b/gpio/samples/simdeviceumdf/SimdeviceUMDF.vcxproj new file mode 100644 index 00000000..05a8bfe6 --- /dev/null +++ b/gpio/samples/simdeviceumdf/SimdeviceUMDF.vcxproj @@ -0,0 +1,282 @@ +<?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|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{D768CF1C-AE06-4C12-B11D-21C5D70082F7}</ProjectGuid> + <RootNamespace>$(MSBuildProjectName)</RootNamespace> + <UMDF_VERSION_MAJOR>1</UMDF_VERSION_MAJOR> + <UMDF_VERSION_MINOR>11</UMDF_VERSION_MINOR> + <KMDF_VERSION_MAJOR>1</KMDF_VERSION_MAJOR> + <KMDF_VERSION_MINOR>11</KMDF_VERSION_MINOR> + <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> + <Platform Condition="'$(Platform)' == ''">Win32</Platform> + <SampleGuid>{DF2D116B-B9CA-4AE1-BAD4-416349DA768D}</SampleGuid> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType>UMDF</DriverType> + <PlatformToolset>WindowsUserModeDriver10.0</PlatformToolset> + <ConfigurationType>DynamicLibrary</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType>UMDF</DriverType> + <PlatformToolset>WindowsUserModeDriver10.0</PlatformToolset> + <ConfigurationType>DynamicLibrary</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType>UMDF</DriverType> + <PlatformToolset>WindowsUserModeDriver10.0</PlatformToolset> + <ConfigurationType>DynamicLibrary</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType>UMDF</DriverType> + <PlatformToolset>WindowsUserModeDriver10.0</PlatformToolset> + <ConfigurationType>DynamicLibrary</ConfigurationType> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <PropertyGroup> + <OutDir>$(IntDir)</OutDir> + </PropertyGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ItemGroup Label="WrappedTaskItems"> + <Inf Include="simdeviceUMDFDriver.inx"> + <Architecture>$(InfArch)</Architecture> + <SpecifyArchitecture>true</SpecifyArchitecture> + <CopyOutput>.\$(IntDir)\simdeviceUMDFDriver.inf</CopyOutput> + </Inf> + </ItemGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetName>SimdeviceUMDF</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetName>SimdeviceUMDF</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetName>SimdeviceUMDF</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetName>SimdeviceUMDF</TargetName> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_UNICODE;UNICODE</PreprocessorDefinitions> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_UNICODE;UNICODE</PreprocessorDefinitions> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_UNICODE;UNICODE</PreprocessorDefinitions> + </ResourceCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_UNICODE;UNICODE</PreprocessorDefinitions> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_UNICODE;UNICODE</PreprocessorDefinitions> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_UNICODE;UNICODE</PreprocessorDefinitions> + </ResourceCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_UNICODE;UNICODE</PreprocessorDefinitions> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_UNICODE;UNICODE</PreprocessorDefinitions> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_UNICODE;UNICODE</PreprocessorDefinitions> + </ResourceCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_UNICODE;UNICODE</PreprocessorDefinitions> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_UNICODE;UNICODE</PreprocessorDefinitions> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);_UNICODE;UNICODE</PreprocessorDefinitions> + </ResourceCompile> + </ItemDefinitionGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <WIN32_WINNT_VERSION>0x0A00</WIN32_WINNT_VERSION> + <NTDDI_VERSION>0x0A000000</NTDDI_VERSION> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <WIN32_WINNT_VERSION>0x0A00</WIN32_WINNT_VERSION> + <NTDDI_VERSION>0x0A000000</NTDDI_VERSION> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <WIN32_WINNT_VERSION>0x0A00</WIN32_WINNT_VERSION> + <NTDDI_VERSION>0x0A000000</NTDDI_VERSION> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <WIN32_WINNT_VERSION>0x0A00</WIN32_WINNT_VERSION> + <NTDDI_VERSION>0x0A000000</NTDDI_VERSION> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Link> + <EntryPointSymbol Condition="'$(Platform)'=='win32'">_DllMainCRTStartup@12</EntryPointSymbol> + <EntryPointSymbol Condition="'$(Platform)'!='win32'">_DllMainCRTStartup</EntryPointSymbol> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Link> + <EntryPointSymbol Condition="'$(Platform)'=='win32'">_DllMainCRTStartup@12</EntryPointSymbol> + <EntryPointSymbol Condition="'$(Platform)'!='win32'">_DllMainCRTStartup</EntryPointSymbol> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Link> + <EntryPointSymbol Condition="'$(Platform)'=='win32'">_DllMainCRTStartup@12</EntryPointSymbol> + <EntryPointSymbol Condition="'$(Platform)'!='win32'">_DllMainCRTStartup</EntryPointSymbol> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Link> + <EntryPointSymbol Condition="'$(Platform)'=='win32'">_DllMainCRTStartup@12</EntryPointSymbol> + <EntryPointSymbol Condition="'$(Platform)'!='win32'">_DllMainCRTStartup</EntryPointSymbol> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\..\..\inc;$(DDK_INC_PATH)</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\mincore.lib;$(SDK_LIB_PATH)\advapi32.lib</AdditionalDependencies> + <ModuleDefinitionFile>exports.def</ModuleDefinitionFile> + </Link> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\..\..\inc;$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </ResourceCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\..\..\inc;$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </Midl> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\..\..\inc;$(DDK_INC_PATH)</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\mincore.lib;$(SDK_LIB_PATH)\advapi32.lib</AdditionalDependencies> + <ModuleDefinitionFile>exports.def</ModuleDefinitionFile> + </Link> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\..\..\inc;$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </ResourceCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\..\..\inc;$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </Midl> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\..\..\inc;$(DDK_INC_PATH)</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\mincore.lib;$(SDK_LIB_PATH)\advapi32.lib</AdditionalDependencies> + <ModuleDefinitionFile>exports.def</ModuleDefinitionFile> + </Link> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\..\..\inc;$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </ResourceCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\..\..\inc;$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </Midl> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\..\..\inc;$(DDK_INC_PATH)</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\mincore.lib;$(SDK_LIB_PATH)\advapi32.lib</AdditionalDependencies> + <ModuleDefinitionFile>exports.def</ModuleDefinitionFile> + </Link> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\..\..\inc;$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </ResourceCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\..\..\inc;$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </Midl> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="comsup.cpp" /> + <ClCompile Include="device.cpp" /> + <ClCompile Include="dllsup.cpp" /> + <ClCompile Include="driver.cpp" /> + <ClCompile Include="queue.cpp" /> + <ResourceCompile Include="SimdeviceUMDF.rc" /> + </ItemGroup> + <ItemGroup> + <Inf Exclude="@(Inf)" Include="*.inf" /> + <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" /> + <FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" /> + </ItemGroup> + <ItemGroup> + <None Exclude="@(None)" Include="*.txt;*.htm;*.html" /> + <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" /> + <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" /> + </ItemGroup> + <ItemGroup> + <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> +</Project>
\ No newline at end of file diff --git a/gpio/samples/simdeviceumdf/SimdeviceUMDF.vcxproj.Filters b/gpio/samples/simdeviceumdf/SimdeviceUMDF.vcxproj.Filters new file mode 100644 index 00000000..db8bda4d --- /dev/null +++ b/gpio/samples/simdeviceumdf/SimdeviceUMDF.vcxproj.Filters @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> + <UniqueIdentifier>{7B1D3759-D374-48F4-8ADD-07752C95369D}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files"> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + <UniqueIdentifier>{A065C827-3D09-4244-997C-14744BF574C4}</UniqueIdentifier> + </Filter> + <Filter Include="Resource Files"> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> + <UniqueIdentifier>{9EB984ED-A895-4F5D-97FD-C72ACA844DAD}</UniqueIdentifier> + </Filter> + <Filter Include="Driver Files"> + <Extensions>inf;inv;inx;mof;mc;</Extensions> + <UniqueIdentifier>{3940722F-AA7D-4F6A-8A19-3CD74FCBD352}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <FilesToPackage Include=".\Debug\\simdeviceUMDFDriver.inf"> + <Filter>Driver Files</Filter> + </FilesToPackage> + <Inf Include="simdeviceUMDFDriver.inx"> + <Filter>Driver Files</Filter> + </Inf> + </ItemGroup> + <ItemGroup> + <ClCompile Include="comsup.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="device.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="dllsup.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="driver.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="queue.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <None Include="exports.def"> + <Filter>Source Files</Filter> + </None> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="SimdeviceUMDF.rc"> + <Filter>Resource Files</Filter> + </ResourceCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/gpio/samples/simdeviceumdf/comsup.cpp b/gpio/samples/simdeviceumdf/comsup.cpp new file mode 100644 index 00000000..ccafeeaf --- /dev/null +++ b/gpio/samples/simdeviceumdf/comsup.cpp @@ -0,0 +1,344 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + ComSup.cpp + +Abstract: + + This module contains implementations for the functions and methods + used for providing COM support. + + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#include "internal.h" + +// +// Implementation of CUnknown methods. +// + +CUnknown::CUnknown( + VOID + ) : m_ReferenceCount(1) +/*++ + + Routine Description: + + Constructor for an instance of the CUnknown class. This simply initializes + the reference count of the object to 1. The caller is expected to + call Release() if it wants to delete the object once it has been allocated. + + Arguments: + + None + + Return Value: + + None + +--*/ +{ + // do nothing. +} + +HRESULT +STDMETHODCALLTYPE +CUnknown::QueryInterface( + _In_ REFIID InterfaceId, + _Out_ PVOID *Object + ) +/*++ + + Routine Description: + + This method provides the basic support for query interface on CUnknown. + If the interface requested is IUnknown it references the object and + returns an interface pointer. Otherwise it returns an error. + + Arguments: + + InterfaceId - the IID being requested + + Object - a location to store the interface pointer to return. + + Return Value: + + S_OK or E_NOINTERFACE + +--*/ +{ + if (IsEqualIID(InterfaceId, __uuidof(IUnknown))) + { + *Object = QueryIUnknown(); + return S_OK; + } + else + { + *Object = NULL; + return E_NOINTERFACE; + } +} + +IUnknown * +CUnknown::QueryIUnknown( + VOID + ) +/*++ + + Routine Description: + + This helper method references the object and returns a pointer to the + object's IUnknown interface. + + This allows other methods to convert a CUnknown pointer into an IUnknown + pointer without a typecast and without calling QueryInterface and dealing + with the return value. + + Arguments: + + None + + Return Value: + + A pointer to the object's IUnknown interface. + +--*/ +{ + AddRef(); + return static_cast<IUnknown *>(this); +} + +ULONG +STDMETHODCALLTYPE +CUnknown::AddRef( + VOID + ) +/*++ + + Routine Description: + + This method adds one to the object's reference count. + + Arguments: + + None + + Return Value: + + The new reference count. The caller should only use this for debugging + as the object's actual reference count can change while the caller + examines the return value. + +--*/ +{ + return InterlockedIncrement(&m_ReferenceCount); +} + +ULONG +STDMETHODCALLTYPE +CUnknown::Release( + VOID + ) +/*++ + + Routine Description: + + This method subtracts one to the object's reference count. If the count + goes to zero, this method deletes the object. + + Arguments: + + None + + Return Value: + + The new reference count. If the caller uses this value it should only be + to check for zero (i.e. this call caused or will cause deletion) or + non-zero (i.e. some other call may have caused deletion, but this one + didn't). + +--*/ +{ + ULONG count = InterlockedDecrement(&m_ReferenceCount); + + if (count == 0) + { + delete this; + } + return count; +} + +// +// Implementation of CClassFactory methods. +// + +// +// Define storage for the factory's static lock count variable. +// + +LONG CClassFactory::s_LockCount = 0; + +IClassFactory * +CClassFactory::QueryIClassFactory( + VOID + ) +/*++ + + Routine Description: + + This helper method references the object and returns a pointer to the + object's IClassFactory interface. + + This allows other methods to convert a CClassFactory pointer into an + IClassFactory pointer without a typecast and without dealing with the + return value QueryInterface. + + Arguments: + + None + + Return Value: + + A referenced pointer to the object's IClassFactory interface. + +--*/ +{ + AddRef(); + return static_cast<IClassFactory *>(this); +} + +HRESULT +CClassFactory::QueryInterface( + _In_ REFIID InterfaceId, + _Out_ PVOID *Object + ) +/*++ + + Routine Description: + + This method attempts to retrieve the requested interface from the object. + + If the interface is found then the reference count on that interface (and + thus the object itself) is incremented. + + Arguments: + + InterfaceId - the interface the caller is requesting. + + Object - a location to store the interface pointer. + + Return Value: + + S_OK or E_NOINTERFACE + +--*/ +{ + // + // This class only supports IClassFactory so check for that. + // + + if (IsEqualIID(InterfaceId, __uuidof(IClassFactory))) + { + *Object = QueryIClassFactory(); + return S_OK; + } + else + { + // + // See if the base class supports the interface. + // + + return CUnknown::QueryInterface(InterfaceId, Object); + } +} + +HRESULT +STDMETHODCALLTYPE +CClassFactory::CreateInstance( + _In_opt_ IUnknown * /* OuterObject */, + _In_ REFIID InterfaceId, + _Out_ PVOID *Object + ) +/*++ + + Routine Description: + + This COM method is the factory routine - it creates instances of the driver + callback class and returns the specified interface on them. + + Arguments: + + OuterObject - only used for aggregation, which our driver callback class + does not support. + + InterfaceId - the interface ID the caller would like to get from our + new object. + + Object - a location to store the referenced interface pointer to the new + object. + + Return Value: + + Status. + +--*/ +{ + HRESULT hr; + + PCSimdeviceDriver driver; + + *Object = NULL; + + hr = CSimdeviceDriver::CreateInstance(&driver); + + if (SUCCEEDED(hr)) + { + hr = driver->QueryInterface(InterfaceId, Object); + driver->Release(); + } + + return hr; +} + +HRESULT +STDMETHODCALLTYPE +CClassFactory::LockServer( + _In_ BOOL Lock + ) +/*++ + + Routine Description: + + This COM method can be used to keep the DLL in memory. However since the + driver's DllCanUnloadNow function always returns false, this has little + effect. Still it tracks the number of lock and unlock operations. + + Arguments: + + Lock - Whether the caller wants to lock or unlock the "server" + + Return Value: + + S_OK + +--*/ +{ + if (Lock) + { + InterlockedIncrement(&s_LockCount); + } + else + { + InterlockedDecrement(&s_LockCount); + } + return S_OK; +} + + diff --git a/gpio/samples/simdeviceumdf/comsup.h b/gpio/samples/simdeviceumdf/comsup.h new file mode 100644 index 00000000..550c04e0 --- /dev/null +++ b/gpio/samples/simdeviceumdf/comsup.h @@ -0,0 +1,217 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + ComSup.h + +Abstract: + + This module contains classes and functions use for providing COM support + code. + + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#pragma once + +// +// Forward type declarations. They are here rather than in internal.h as +// you only need them if you choose to use these support classes. +// + +typedef class CUnknown *PCUnknown; +typedef class CClassFactory *PCClassFactory; + +// +// Base class to implement IUnknown. You can choose to derive your COM +// classes from this class, or simply implement IUnknown in each of your +// classes. +// + +class CUnknown : public IUnknown +{ + +// +// Private data members and methods. These are only accessible by the methods +// of this class. +// +private: + + // + // The reference count for this object. Initialized to 1 in the + // constructor. + // + + LONG m_ReferenceCount; + +// +// Protected data members and methods. These are accessible by the subclasses +// but not by other classes. +// +protected: + + // + // The constructor and destructor are protected to ensure that only the + // subclasses of CUnknown can create and destroy instances. + // + + CUnknown( + VOID + ); + + // + // The destructor MUST be virtual. Since any instance of a CUnknown + // derived class should only be deleted from within CUnknown::Release, + // the destructor MUST be virtual or only CUnknown::~CUnknown will get + // invoked on deletion. + // + // If you see that your CMyDevice specific destructor is never being + // called, make sure you haven't deleted the virtual destructor here. + // + + virtual + ~CUnknown( + VOID + ) + { + // Do nothing + } + +// +// Public Methods. These are accessible by any class. +// +public: + + IUnknown * + QueryIUnknown( + VOID + ); + +// +// COM Methods. +// +public: + + // + // IUnknown methods + // + + virtual + ULONG + STDMETHODCALLTYPE + AddRef( + VOID + ); + + virtual + ULONG + STDMETHODCALLTYPE + Release( + VOID + ); + + virtual + HRESULT + STDMETHODCALLTYPE + QueryInterface( + _In_ REFIID InterfaceId, + _Out_ PVOID *Object + ); +}; + +// +// Class factory support class. Create an instance of this from your +// DllGetClassObject method and modify the implementation to create +// an instance of your driver event handler class. +// + +class CClassFactory : public CUnknown, public IClassFactory +{ +// +// Private data members and methods. These are only accessible by the methods +// of this class. +// +private: + + // + // The lock count. This is shared across all instances of IClassFactory + // and can be queried through the public IsLocked method. + // + + static LONG s_LockCount; + +// +// Public Methods. These are accessible by any class. +// +public: + + IClassFactory * + QueryIClassFactory( + VOID + ); + +// +// COM Methods. +// +public: + + // + // IUnknown methods + // + + virtual + ULONG + STDMETHODCALLTYPE + AddRef( + VOID + ) + { + return __super::AddRef(); + } + + __drv_arg(this, __drv_freesMem(object)) + virtual + ULONG + STDMETHODCALLTYPE + Release( + VOID + ) + { + return __super::Release(); + } + + virtual + HRESULT + STDMETHODCALLTYPE + QueryInterface( + _In_ REFIID InterfaceId, + _Out_ PVOID *Object + ); + + // + // IClassFactory methods. + // + + virtual + HRESULT + STDMETHODCALLTYPE + CreateInstance( + _In_opt_ IUnknown *OuterObject, + _In_ REFIID InterfaceId, + _Out_ PVOID *Object + ); + + virtual + HRESULT + STDMETHODCALLTYPE + LockServer( + _In_ BOOL Lock + ); +}; + diff --git a/gpio/samples/simdeviceumdf/device.cpp b/gpio/samples/simdeviceumdf/device.cpp new file mode 100644 index 00000000..0ced0bd1 --- /dev/null +++ b/gpio/samples/simdeviceumdf/device.cpp @@ -0,0 +1,1106 @@ +/*++ + +Copyright (c) 1990-2010 Microsoft Corporation + +Module Name: + + Device.cpp + +Abstract: + + This is a simple UMDF device driver that consumes GPIO pins for I/O and interrupt. + + +Environment: + + UMDF + +--*/ + +// +// Inlcude the below set of headers to get the CM_RESOURCE_ definitions +// + + +#include "internal.h" + +DEFINE_GUID (GUID_DEVINTERFACE_ECHO, + 0xcdc35b6e, 0xbe4, 0x4936, 0xbf, 0x5f, 0x55, 0x37, 0x38, 0xa, 0x7c, 0x1a); +// {CDC35B6E-0BE4-4936-BF5F-5537380A7C1A} + + +HRESULT +CSimdevice::CreateInstance( + _In_ IWDFDriver *FxDriver, + _In_ IWDFDeviceInitialize * FxDeviceInit, + _Out_ PCSimdevice *Device + ) +/*++ + + Routine Description: + + This method creates and initializs an instance of the driver's + device callback object. + + Arguments: + + FxDeviceInit - the settings for the device. + + Device - a location to store the referenced pointer to the device object. + + Return Value: + + Status + +--*/ +{ + PCSimdevice device; + HRESULT hr; + + // + // Allocate a new instance of the device class. + // + + device = new CSimdevice(); + + if (NULL == device) { + return E_OUTOFMEMORY; + } + + // + // Initialize the instance. + // + + hr = device->Initialize(FxDriver, FxDeviceInit); + + if (SUCCEEDED(hr)) { + *Device = device; + } + else { + device->Release(); + } + + return hr; +} + +HRESULT +CSimdevice::Initialize( + _In_ IWDFDriver * FxDriver, + _In_ IWDFDeviceInitialize * FxDeviceInit + ) + +/*++ + + Routine Description: + + This method initializes the device callback object and creates the + partner device object. + + The method should perform any device-specific configuration that: + * could fail (these can't be done in the constructor) + * must be done before the partner object is created -or- + * can be done after the partner object is created and which aren't + influenced by any device-level parameters the parent (the driver + in this case) might set. + + Arguments: + + FxDeviceInit - the settings for this device. + + Return Value: + + status. + +--*/ +{ + IWDFDevice *fxDevice; + IWDFDeviceInitialize2 *fxDeviceInit2; + HRESULT hr; + + // + // Configure things like the locking model before we go to create our + // partner device. + // + + // + // Set no locking unless you need an automatic callbacks synchronization + // + + FxDeviceInit->SetLockingConstraint(None); + + // + // Create a new FX device object and assign the new callback object to + // handle any device level events that occur. + // + + // + // Set retrieval mode to direct I/O. This needs to be done before the call + // to CreateDevice. + // + + FxDeviceInit->QueryInterface(IID_PPV_ARGS(&fxDeviceInit2)); + + if (fxDeviceInit2 == NULL) { + hr = E_FAIL; + return hr; + } + + fxDeviceInit2->SetIoTypePreference(WdfDeviceIoBufferRetrievalDeferred, + WdfDeviceIoDirect, + WdfDeviceIoDirect); + + SAFE_RELEASE(fxDeviceInit2); + + // + // QueryIUnknown references the IUnknown interface that it returns + // (which is the same as referencing the device). We pass that to + // CreateDevice, which takes its own reference if everything works. + // + + { + IUnknown *unknown = this->QueryIUnknown(); + + hr = FxDriver->CreateDevice(FxDeviceInit, unknown, &fxDevice); + + unknown->Release(); + } + + // + // If that succeeded then set our FxDevice member variable. + // + + if (SUCCEEDED(hr)) { + m_FxDevice = fxDevice; + + // + // Drop the reference we got from CreateDevice. Since this object + // is partnered with the framework object they have the same + // lifespan - there is no need for an additional reference. + // + + fxDevice->Release(); + } + + return hr; +} + +HRESULT +CSimdevice::Configure( + VOID + ) +/*++ + + Routine Description: + + This method is called after the device callback object has been initialized + and returned to the driver. It would setup the device's queues and their + corresponding callback objects. + + Arguments: + + FxDevice - the framework device object for which we're handling events. + + Return Value: + + status + +--*/ +{ + PCSimdeviceQueue defaultQueue; + + HRESULT hr; + + hr = CSimdeviceQueue::CreateInstance(m_FxDevice, &defaultQueue); + + if (FAILED(hr)) { + return hr; + } + + hr = defaultQueue->Configure(); + + if (SUCCEEDED(hr)) { + // + // In case of success store defaultQueue in our member + // The reference is transferred to m_DefaultQueue + // + + m_Queue = defaultQueue; + } + else { + // + // In case of failure release the reference + // + + defaultQueue->Release(); + } + + if (SUCCEEDED(hr)) { + hr = m_FxDevice->CreateDeviceInterface(&GUID_DEVINTERFACE_ECHO, + NULL); + } + + return hr; +} + +HRESULT +CSimdevice::QueryInterface( + _In_ REFIID InterfaceId, + _Out_ PVOID *Object + ) +/*++ + + Routine Description: + + This method is called to get a pointer to one of the object's callback + interfaces. + + Since the sample driver doesn't support any of the device events, this + method simply calls the base class's BaseQueryInterface. + + If the sample is extended to include device event interfaces then this + method must be changed to check the IID and return pointers to them as + appropriate. + + Arguments: + + InterfaceId - the interface being requested + + Object - a location to store the interface pointer if successful + + Return Value: + + S_OK or E_NOINTERFACE + +--*/ +{ + HRESULT hr; + + if (IsEqualIID(InterfaceId, __uuidof(IPnpCallbackSelfManagedIo))) { + *Object = QueryIPnpCallbackSelfManagedIo(); + hr = S_OK; + } else if (IsEqualIID(InterfaceId, __uuidof(IPnpCallbackHardware2))) { + *Object = QueryIPnpCallbackHardware2(); + hr = S_OK; + } else if (IsEqualIID(InterfaceId, __uuidof(IPnpCallback))) { + *Object = QueryIPnpCallback(); + hr = S_OK; + } else { + hr = CUnknown::QueryInterface(InterfaceId, Object); + } + + return hr; +} + +HRESULT +CSimdevice::OnSelfManagedIoInit( + _In_ IWDFDevice * pWdfDevice + ) +/*++ + + Routine Description: + + This method is called to allow driver to initialize any resources + that driver might need to process I/O. + + Echo driver needs a thread to process completions. We initialize + this thread here + + Arguments: + + pWdfDevice - framework device object for which to initialze resources + + Return Value: + + S_OK in case of success + HRESULT correponding to error returned by CreateThread, in case of failure + +--*/ +{ + HRESULT hr = S_OK; + + UNREFERENCED_PARAMETER(pWdfDevice); + + + m_ThreadHandle = CreateThread( NULL, // Default Security Attrib. + 0, // Initial Stack Size, + CSimdeviceQueue::CompletionThread, // Thread Func + (LPVOID)m_Queue, // Arg to Thread Func is Queue + 0, // Creation Flags + NULL ); // Don't need the Thread Id. + + if (m_ThreadHandle == NULL) { + hr = HRESULT_FROM_WIN32(GetLastError()); + } + + return hr; +} + +void +CSimdevice::OnSelfManagedIoCleanup( + _In_ IWDFDevice * pWdfDevice + ) +/*++ + + Routine Description: + + This method is called to allow driver to cleanup any resources + that driver allocated to process I/O. + + It is critical that, in this routine driver wait for all of the + threads which it created to exit. Otherwise those threads could + continue to execute when framework unloads the driver which + would lead to a crash. + + Echo driver created a thread to handle completions. We wait for + that thread to exit in this routine + + Arguments: + + pWdfDevice - framework device object for which to cleanup resources + + Return Value: + + None + +--*/ +{ + // + // Kill the thread and + // wait for the thread to die. + // + + UNREFERENCED_PARAMETER(pWdfDevice); + + if (m_ThreadHandle) { + + // + // Ask queue to set terminate flag which will make + // the thread exit + // + + m_Queue->SetExitThread(); + + // + // Wait for the thread to exit + // + + WaitForSingleObject(m_ThreadHandle, INFINITE); + + // + // Close the thread handle + // + + CloseHandle(m_ThreadHandle); + m_ThreadHandle = NULL; + } + + // + // Release the reference we took on the queue callback object + // to keep it alive until the thread exits + // + + SAFE_RELEASE(m_Queue); +} + + +HRESULT +CSimdevice::OnPrepareHardware( + _In_ IWDFDevice3 * pWdfDevice, + _In_ IWDFCmResourceList * pWdfResourcesRaw, + _In_ IWDFCmResourceList * pWdfResourcesTranslated + ) +/*++ + +Routine Description: + + This routine is called by WUDF to initialize hardware resources (e.g. interrupts, + IO resources) + +Arguments: + + pWdfDevice - pointer to an IWDFDevice object for the device + + pWdfResourcesRaw - Supplies a pointer to a collection of framework resource + objects. This collection identifies the raw (bus-relative) hardware + resources that have been assigned to the device. + + pWdfResourcesTranslated - Supplies a pointer to a collection of framework + resource objects. This collection identifies the translated + (system-physical) hardware resources that have been assigned to the + device. The resources appear from the CPU's point of view. + +Return Value: + + HRESULT + +--*/ +{ + + ULONG i; + HRESULT hr = S_OK; + ULONG ConnectionCount = 0; + BOOLEAN fInterruptFound = FALSE; + PCM_PARTIAL_RESOURCE_DESCRIPTOR DescriptorTranslated; + PCM_PARTIAL_RESOURCE_DESCRIPTOR DescriptorRaw; + + // + // Parse the resource list and save the resource information. + // + + for (i=0; i < pWdfResourcesTranslated->GetCount(); i++) { + + DescriptorTranslated = (PCM_PARTIAL_RESOURCE_DESCRIPTOR)pWdfResourcesTranslated->GetDescriptor(i); + DescriptorRaw = (PCM_PARTIAL_RESOURCE_DESCRIPTOR)pWdfResourcesRaw->GetDescriptor(i); + + if(DescriptorTranslated == NULL) { + return E_FAIL; + } + + switch (DescriptorTranslated->Type) { + + // + // One or more GPIO IO resources are expected. In this implementation of the sample + // driver, however, only the first IO resource will be used (see OnD0Entry). + // + + case CmResourceTypeConnection: + + // + // Check against expected connection type for a GPIO IO descriptor + // + + if ((DescriptorTranslated->u.Connection.Class == + CM_RESOURCE_CONNECTION_CLASS_GPIO) && + (DescriptorTranslated->u.Connection.Type == + CM_RESOURCE_CONNECTION_TYPE_GPIO_IO)) { + + if (ConnectionCount >= MAX_CONNECTIONS) { + break; + } + + // + // Store GPIO IO resource connection ID in the device extension + // + + this->GetDeviceExtension()->ConnectionId[ConnectionCount].LowPart = + DescriptorTranslated->u.Connection.IdLowPart; + + this->GetDeviceExtension()->ConnectionId[ConnectionCount].HighPart = + DescriptorTranslated->u.Connection.IdHighPart; + } + + break; + + case CmResourceTypeInterrupt: + + // + // Connect the first interrupt resource we find + // + + if (fInterruptFound == FALSE) { + hr = this->SimdeviceConnectInterrupt( + pWdfDevice, + DescriptorRaw, + DescriptorTranslated); + + if (SUCCEEDED(hr)) { + fInterruptFound = TRUE; + } + } + default: + + // + // Ignore all other descriptors + // + + break; + } + } + + return hr; + +} + +HRESULT +CSimdevice::OnReleaseHardware( + _In_ IWDFDevice3 * pWdfDevice, + _In_ IWDFCmResourceList * pWdfResourcesTranslated + ) +/*++ + + +Routine Description: + + This method is called by WUDF to uninitialize the hardware. + +Parameters: + + pWdfDevice - pointer to an IWDFDevice object for the device + + pWdfResourcesTranslated - pointer to the translated resource list + +Return Values: + status + +--*/ +{ + UNREFERENCED_PARAMETER(pWdfDevice); + UNREFERENCED_PARAMETER(pWdfResourcesTranslated); + + return S_OK; +} + +HRESULT +CSimdevice::OnD0Entry( + _In_ IWDFDevice* pWdfDevice, + _In_ WDF_POWER_DEVICE_STATE previousState + ) + +/*++ + +Routine Description: + +This method is called after a new device enters the system + +Parameters: + + pWdfDevice - pointer to a device object + + previousState - previous WDF power state + +Return Values: + status + +--*/ +{ + + PDEVICE_EXTENSION DevExt; + HRESULT hr = S_OK; + BYTE Data; + NTSTATUS Status; + NTSTATUS Status1; + WCHAR ReadStringBuffer[100]; + WCHAR WriteStringBuffer[100]; + + UNREFERENCED_PARAMETER(pWdfDevice); + UNREFERENCED_PARAMETER(previousState); + + DevExt = GetDeviceExtension(); + + Status = StringCbPrintfW(&ReadStringBuffer[0], + sizeof(ReadStringBuffer), + L"\\\\.\\RESOURCE_HUB\\%0*I64x", + (size_t)(sizeof(LARGE_INTEGER) * 2), + DevExt->ConnectionId[0].QuadPart); + + Status = StringCbPrintfW(&WriteStringBuffer[0], + sizeof(WriteStringBuffer), + L"\\\\.\\RESOURCE_HUB\\%0*I64x", + (size_t)(sizeof(LARGE_INTEGER) * 2), + DevExt->ConnectionId[0].QuadPart); + + // + // Connect the first GPIO IO descriptor we find (in OnPrepareHardware) for read and write operations + // + + Data = 0xDB; + Status = STATUS_SUCCESS; + Status1 = TestReadWrite(pWdfDevice, &WriteStringBuffer[0], FALSE, &Data, sizeof(Data), NULL); + if (!NT_SUCCESS(Status1)) { + Status = Status1; + } + + Data = 0; + Status1 = TestReadWrite(pWdfDevice, &ReadStringBuffer[0], TRUE, &Data, sizeof(Data), NULL); + if (!NT_SUCCESS(Status1)) { + Status = Status1; + } + + return hr; +} + +HRESULT +CSimdevice::OnD0Exit( + _In_ IWDFDevice* pWdfDevice, + _In_ WDF_POWER_DEVICE_STATE TargetState + ) + +/*++ + +Routine Description: + +This method is called when a device exit D0 + +Parameters: + + pWdfDevice pointer to a device object + + TargetState - target D-state + +Return Values: + status + +--*/ + +{ + + UNREFERENCED_PARAMETER(pWdfDevice); + UNREFERENCED_PARAMETER(TargetState); + + return S_OK; +} + +HRESULT +CSimdevice::TestReadWrite( + _In_ IWDFDevice* pWdfDevice, + _In_ PWSTR RequestString, + _In_ BOOLEAN ReadOperation, + _Inout_updates_bytes_(Size) UCHAR *Data, + _In_ ULONG Size, + _Inout_opt_ IWDFRemoteTarget *IoTargetOut + ) + +/*++ + +Routine Description: + + This is a utility routine to test read or write on a set of GPIO pins. + +Arguments: + + pWdfDevice - Supplies a pointer to the framework device object. + + RequestString - Supplies a pointer to the unicode string to be opened. + + ReadOperation - Supplies a boolean that identifies whether read (TRUE) or + write (FALSE) should be performed. + + Data - Supplies a pointer containing the buffer that should be read from + or written to. + + Size - Supplies the size of the data buffer in bytes. + + IoTargetOut - Supplies a pointer that receives the IOTARGET created by + UMDF. + +Return Value: + + HRESULT + +--*/ + +{ + + IWDFMemory *pOutputMemory = NULL; + IWDFMemory *pInputMemory = NULL; + IRequestCallbackRequestCompletion *pICallback = NULL; + IWDFIoRequest *pIoRequest = NULL; + IWDFRemoteTarget *pRemoteTarget = NULL; + UMDF_IO_TARGET_OPEN_PARAMS OpenParams; + DWORD DesiredAccess; + HRESULT hr = S_OK; + IWDFRequestCompletionParams * FxComplParams = NULL; + IWDFDriver *pWdfDriver = NULL; + + UNREFERENCED_PARAMETER(IoTargetOut); + + if (ReadOperation != FALSE) { + DesiredAccess = FILE_GENERIC_READ; + + } else { + DesiredAccess = FILE_GENERIC_WRITE; + } + + // + // QueryIUnknown references the IUnknown interface that it returns + // (which is the same as referencing the CMyRemoteTarget). We pass that + // to the various Create* calls, which take their own reference if + // everything works. + // + + IUnknown * unknown = this->QueryIUnknown(); + + + IWDFDevice2 *pWdfDevice2 = NULL; + hr = pWdfDevice->QueryInterface(IID_PPV_ARGS(&pWdfDevice2)); + + // Create the IoTarget + + if (SUCCEEDED(hr)) { + hr = pWdfDevice2->CreateRemoteTarget( + unknown, + NULL, + &pRemoteTarget); + } + + // + // Determine whether the request is a read or write + // + + OpenParams.dwCreationDisposition = OPEN_EXISTING; + OpenParams.dwFlagsAndAttributes = FILE_FLAG_OVERLAPPED; + OpenParams.dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; + + + if (SUCCEEDED(hr)) { + hr = pRemoteTarget->OpenFileByName(RequestString, + DesiredAccess, + &OpenParams); + } + + // + //Create a new IO request + // + + if (SUCCEEDED(hr)) { + hr = pWdfDevice->CreateRequest(NULL, pWdfDevice, &pIoRequest); + } + + if (SUCCEEDED(hr)) { + hr = this->QueryInterface(__uuidof(IRequestCallbackRequestCompletion), (PVOID*)&pICallback); + + // + //Set completion callback + // + + if (SUCCEEDED(hr)){ + pIoRequest->SetCompletionCallback(pICallback, NULL); + } + + pWdfDevice->GetDriver(&pWdfDriver); + + hr = pWdfDriver->CreatePreallocatedWdfMemory(Data, + Size, + NULL, // no object event callback + pIoRequest, // request object as parent + &pInputMemory); + + hr = pWdfDriver->CreatePreallocatedWdfMemory(Data, + Size, + NULL, // no object event callback + pIoRequest, // request object as parent + &pOutputMemory); + + // + //Format IO request + // + + if (ReadOperation != FALSE){ + if (SUCCEEDED(hr)) { + hr = pRemoteTarget->FormatRequestForIoctl(pIoRequest, + IOCTL_GPIO_READ_PINS, + NULL, + NULL, + NULL, + pOutputMemory, + NULL); + } + } + else { + if (SUCCEEDED(hr)) { + hr = pRemoteTarget->FormatRequestForIoctl(pIoRequest, + IOCTL_GPIO_WRITE_PINS, + NULL, + pInputMemory, + NULL, + pOutputMemory, + NULL); + } + } + + // + //Send down the request + // + if (SUCCEEDED(hr)) { + hr = pIoRequest->Send(pRemoteTarget, + WDF_REQUEST_SEND_OPTION_SYNCHRONOUS, + 0); //No timeout + } + + if (SUCCEEDED(hr)) { + + // + //Get IWDFRequestCompletionParams interface and then get completion status. + // + + pIoRequest->GetCompletionParams(&FxComplParams); + hr = FxComplParams->GetCompletionStatus(); + } + + if(FAILED(hr)) { + pIoRequest->DeleteWdfObject(); + pIoRequest = NULL; + } + } + + // + // Clean-up + // + + if (pWdfDevice2 != NULL) { + SAFE_RELEASE(pWdfDevice2); + } + + if (pWdfDriver != NULL) { + SAFE_RELEASE(pWdfDriver); + } + + if (pRemoteTarget != NULL) { + pRemoteTarget->Close(); + } + + return hr; +} + + +void +CSimdevice::OnSurpriseRemoval( + _In_ IWDFDevice* pWdfDevice + ) + +/*++ + +Routine Description: + +This method is called when a device is surprise removed + +Parameters: + + pWdfDevice pointer to a device object + +Return Values: + None + +--*/ + +{ + UNREFERENCED_PARAMETER(pWdfDevice); +} + + +HRESULT +CSimdevice::OnQueryRemove( + _In_ IWDFDevice* pWdfDevice + ) +/*++ + +Routine Description: + +This method is called when a device processes the query remove IRP + +Parameters: + + pWdfDevice pointer to a device object + +Return Values: + HRESULT + +--*/ + + +{ + UNREFERENCED_PARAMETER(pWdfDevice); + return S_OK; +} + +HRESULT +CSimdevice::OnQueryStop( + _In_ IWDFDevice* pWdfDevice + ) +/*++ + +Routine Description: + +This method is called when a device processes the query stop IRP + +Parameters: + + pWdfDevice pointer to a device object + +Return Values: + HRESULT + +--*/ +{ + UNREFERENCED_PARAMETER(pWdfDevice); + return S_OK; +} + +HRESULT +CSimdevice::SimdeviceConnectInterrupt( + _In_ IWDFDevice* pWdfDevice, + _In_opt_ PCM_PARTIAL_RESOURCE_DESCRIPTOR RawResource, + _In_opt_ PCM_PARTIAL_RESOURCE_DESCRIPTOR TranslatedResource + ) + +/*++ + +Routine Description: + + This is a utility routine to create an interrupt descriptor from the interrupt resource + acquired from OnPrepareHardware and connect the device ISR routine. +Arguments: + + pWdfDevice - Supplies a pointer to the framework device object. + + pWdfResourcesRaw - Supplies a pointer to a collection of framework resource + objects. This collection identifies the raw (bus-relative) hardware + resources that have been assigned to the device. + + pWdfResourcesTranslated - Supplies a pointer to a collection of framework + resource objects. This collection identifies the translated + (system-physical) hardware resources that have been assigned to the + device. The resources appear from the CPU's point of view. + +Return Value: + + None. + +--*/ + +{ + + IWDFDevice3 * pIWDFDevice3 = NULL; + IWDFInterrupt * spInterrupt = NULL; + HRESULT hr = S_OK; + + if (pWdfDevice == NULL) { + hr = E_INVALIDARG; + } + + if (SUCCEEDED(hr)) { + hr = pWdfDevice->QueryInterface(IID_PPV_ARGS(&pIWDFDevice3)); + } + + if (SUCCEEDED(hr)) { + + // + // Create interrupt + // + + WUDF_INTERRUPT_CONFIG config; + WUDF_INTERRUPT_CONFIG_INIT( + &config, + &CSimdevice::OnInterruptIsr, + &CSimdevice::OnInterruptWorkItem); + + config.InterruptRaw = RawResource; + config.InterruptTranslated = TranslatedResource; + + hr = pIWDFDevice3->CreateInterrupt(&config, &spInterrupt); + } + + // + // Associate the device context with the interrupt + // + + if (SUCCEEDED(hr)) { + hr = spInterrupt->AssignContext(NULL, (void*)this); + } + + return hr; +} + +BOOLEAN +CSimdevice::OnInterruptIsr( + _In_ IWDFInterrupt* pInterrupt, + _In_ ULONG MessageID, + _In_ ULONG Reserved + ) + +/* ++ + +Routine Description: + + This method is called when an interrupt occurs. It determines if the + driver owns the interrupt and queues a work item to defer processing + of the data. + + Arguments: + + pInterrupt - pointer to the interrupt object + MessageID - interrupt message ID + Reserved - + +Return Values: + TRUE if interrupt recognized, else FALSE. + +-- */ + +{ + + UNREFERENCED_PARAMETER(MessageID); + UNREFERENCED_PARAMETER(Reserved); + + IWDFDevice* pWdfDevice = NULL; + CSimdevice* pMyDevice = NULL; + HRESULT hr; + + hr = pInterrupt->RetrieveContext((void**)&pMyDevice); + + if (FAILED(hr)) { + pWdfDevice = pInterrupt->GetDevice(); + pWdfDevice->SetPnpState(WdfPnpStateFailed, WdfTrue); + pWdfDevice->CommitPnpState(); + } + + // + // The sample driver always returns TRUE (e.g. claiming the interrupt) + // from its ISR. In reality, the driver needs to do whatever necessary to + // quiesce the interrupt before claiming the interrupt. + // If additional work needs to be done at , schedule a work item (as we do here). + // + + pInterrupt->QueueWorkItemForIsr(); + + return TRUE; +} + +VOID +CSimdevice::OnInterruptWorkItem( + _In_ IWDFInterrupt* pInterrupt, + _In_ IWDFObject* AssociatedObject + ) + +/* ++ + +Routine Description: + + This method is called on behalf of an interrupt to defer processing. + It retrieves latest data and posts it. + +Arguments: + pInterrupt - pointer to the interrupt object + AssociatedObject - pointer to the associated object + +Return Values: + None. + +--*/ +{ + + UNREFERENCED_PARAMETER(AssociatedObject); + + IWDFDevice* pWdfDevice = NULL; + CSimdevice* pMyDevice; + HRESULT hr; + + hr = pInterrupt->RetrieveContext((void**)&pMyDevice); + + if (FAILED(hr)) { + pWdfDevice = pInterrupt->GetDevice(); + pWdfDevice->SetPnpState(WdfPnpStateFailed, WdfTrue); + pWdfDevice->CommitPnpState(); + } + + + // + // The sample driver does nothing in the work item routine. The real driver + // can add code here to accomplish all that is required to complete the serving + // of the interrupt + // + + return; +} + + diff --git a/gpio/samples/simdeviceumdf/device.h b/gpio/samples/simdeviceumdf/device.h new file mode 100644 index 00000000..e0ceb68c --- /dev/null +++ b/gpio/samples/simdeviceumdf/device.h @@ -0,0 +1,365 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + Device.h + +Abstract: + + This module contains the type definitions for the UMDF Echo sample + driver's device callback class. + + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#pragma once + +#include "queue.h" + +#define MAX_CONNECTIONS (4) +#define MAX_RESOURCES (10) + + +// +// The device extension for the device object +// + +typedef struct _DEVICE_EXTENSION { + + CSimdevice* MyDevice; + + IWDFDevice* Device; + IWDFDevice3* Device3; + + IWDFCmResourceList* CmResourceList; + + LARGE_INTEGER ConnectionId[MAX_CONNECTIONS]; + +} DEVICE_EXTENSION, *PDEVICE_EXTENSION; + +// +// Class for the simdevice driver. +// + +class CSimdevice : + public CUnknown, + public IPnpCallbackSelfManagedIo, + public IPnpCallbackHardware2, + public IPnpCallback +{ + +// +// Private data members. +// +private: + + IWDFDevice *m_FxDevice; + + // + // Completion Thread handle used by queue callback object + // + HANDLE m_ThreadHandle; + + // + // Our queue callback object + // Strong reference - since we pass it to the thread we create + // + CSimdeviceQueue *m_Queue; + + // + // device data + // + DEVICE_EXTENSION m_DevExtension; + +// +// Private methods. +// + +private: + + CSimdevice( + VOID + ) + { + m_FxDevice = NULL; + ZeroMemory(&m_DevExtension, sizeof(DEVICE_EXTENSION)); + } + + HRESULT + Initialize( + _In_ IWDFDriver *FxDriver, + _In_ IWDFDeviceInitialize *FxDeviceInit + ); + + IPnpCallbackSelfManagedIo * + QueryIPnpCallbackSelfManagedIo( + VOID + ) + { + AddRef(); + return static_cast<IPnpCallbackSelfManagedIo *>(this); + } + + IPnpCallback * + QueryIPnpCallback( + VOID + ) + { + AddRef(); + return static_cast<IPnpCallback *>(this); + } + + IPnpCallbackHardware2 * + QueryIPnpCallbackHardware2( + VOID + ) + { + AddRef(); + return static_cast<IPnpCallbackHardware2 *>(this); + } + + +// +// Public methods +// +public: + + // + // The factory method used to create an instance of this driver. + // + + static + HRESULT + CreateInstance( + _In_ IWDFDriver *FxDriver, + _In_ IWDFDeviceInitialize *FxDeviceInit, + _Out_ PCSimdevice *Device + ); + + HRESULT + Configure( + VOID + ); + +// +// COM methods +// +public: + + // + // IUnknown methods. + // + + virtual + ULONG + STDMETHODCALLTYPE + AddRef( + VOID + ) + { + return __super::AddRef(); + } + + __drv_arg(this, __drv_freesMem(object)) + virtual + ULONG + STDMETHODCALLTYPE + Release( + VOID + ) + { + return __super::Release(); + } + + virtual + HRESULT + STDMETHODCALLTYPE + QueryInterface( + _In_ REFIID InterfaceId, + _Out_ PVOID *Object + ); + + // + // IPnpCallbackSelfManagedIo methods + // + + // + // We implement this interface to create and tear down + // our completion thread + // + // It is critical that we wait for all the threads we create + // to exit during OnSelfManagedIoCleanup, otherwise thread + // may continue to execute when framework unloads the driver, + // leading to a crash + // + // We don't manage any I/O separate from the queue, so apart + // from OnSelfManagedIoInit and OnSelfManagedIoCleanup, other + // methods have token implementations + // + + virtual + void + STDMETHODCALLTYPE + OnSelfManagedIoCleanup( + _In_ IWDFDevice * pWdfDevice + ); + + virtual + void + STDMETHODCALLTYPE + OnSelfManagedIoFlush( + _In_ IWDFDevice * pWdfDevice + ) + { + UNREFERENCED_PARAMETER( pWdfDevice ); + } + + virtual + HRESULT + STDMETHODCALLTYPE + OnSelfManagedIoInit( + _In_ IWDFDevice * pWdfDevice + ); + + virtual + HRESULT + STDMETHODCALLTYPE + OnSelfManagedIoSuspend( + _In_ IWDFDevice * pWdfDevice + ) + { + UNREFERENCED_PARAMETER( pWdfDevice ); + + return S_OK; + } + + virtual + HRESULT + STDMETHODCALLTYPE + OnSelfManagedIoRestart( + _In_ IWDFDevice * pWdfDevice + ) + { + UNREFERENCED_PARAMETER( pWdfDevice ); + + return S_OK; + } + + virtual + HRESULT + STDMETHODCALLTYPE + OnSelfManagedIoStop( + _In_ IWDFDevice * pWdfDevice + ) + { + UNREFERENCED_PARAMETER( pWdfDevice ); + + return S_OK; + } + + // + // IPnpCallback + // + + virtual + HRESULT + STDMETHODCALLTYPE + OnD0Entry( + _In_ IWDFDevice* pWdfDevice, + _In_ WDF_POWER_DEVICE_STATE previousState + ); + + virtual + HRESULT + STDMETHODCALLTYPE + OnD0Exit( + _In_ IWDFDevice* pWdfDevice, + _In_ WDF_POWER_DEVICE_STATE previousState + ); + + virtual + void + STDMETHODCALLTYPE + OnSurpriseRemoval( + _In_ IWDFDevice* pWdfDevice + ); + + virtual + HRESULT + STDMETHODCALLTYPE + OnQueryRemove( + _In_ IWDFDevice* pWdfDevice + ); + + virtual + HRESULT + STDMETHODCALLTYPE + OnQueryStop( + _In_ IWDFDevice* pWdfDevice + ); + + // + // IPnpCallbackHardware2 + // + + virtual + HRESULT + STDMETHODCALLTYPE + OnPrepareHardware( + _In_ IWDFDevice3 * pWdfDevice, + _In_ IWDFCmResourceList * pWdfResourcesRaw, + _In_ IWDFCmResourceList * pWdfResourcesTranslated + ); + + virtual + HRESULT + STDMETHODCALLTYPE + OnReleaseHardware( + _In_ IWDFDevice3 * pWdfDevice, + _In_ IWDFCmResourceList * pWdfResourcesTranslated + ); + + HRESULT + SimdeviceConnectInterrupt( + _In_ IWDFDevice* pWdfDevice, + _In_opt_ PCM_PARTIAL_RESOURCE_DESCRIPTOR RawResource, + _In_opt_ PCM_PARTIAL_RESOURCE_DESCRIPTOR TranslatedResource + ); + + static WUDF_INTERRUPT_ISR OnInterruptIsr; + static WUDF_INTERRUPT_WORKITEM OnInterruptWorkItem; + + IWDFDevice * + GetFxDevice( + VOID + ) + { + return m_FxDevice; + } + + PDEVICE_EXTENSION + GetDeviceExtension( + VOID + ) + { + return &m_DevExtension; + } + + HRESULT + TestReadWrite( + _In_ IWDFDevice* pWdfDevice, + _In_ PWSTR RequestString, + _In_ BOOLEAN ReadOperation, + _Inout_updates_bytes_(Size) UCHAR *Data, + _In_ ULONG Size, + _Inout_opt_ IWDFRemoteTarget *IoTargetOut + ); + +}; + diff --git a/gpio/samples/simdeviceumdf/dllsup.cpp b/gpio/samples/simdeviceumdf/dllsup.cpp new file mode 100644 index 00000000..7b0ea74d --- /dev/null +++ b/gpio/samples/simdeviceumdf/dllsup.cpp @@ -0,0 +1,151 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved. + +Module Name: + + dllsup.cpp + +Abstract: + + This module contains the implementation of the sample UMDF Driver's + entry point and its exported functions for providing COM support. + + This module can be copied without modification to a new UMDF driver. It + depends on some of the code in comsup.cpp & comsup.h to handle DLL + registration and creating the first class factory. + + This module is dependent on the following defines: + + MYDRIVER_CLASS_ID - A GUID encoded in struct format used to + initialize the driver's ClassID. + + These are defined in internal.h for the sample. If you choose + to use a different primary include file, you should ensure they are + defined there as well. + + +Environment: + + WDF User-Mode Driver Framework (WDF:UMDF) + +--*/ + +#include "internal.h" + +const GUID CLSID_MyDriverCoClass = MYDRIVER_CLASS_ID; + +BOOL +WINAPI +DllMain( + HINSTANCE ModuleHandle, + DWORD Reason, + PVOID /* Reserved */ + ) +/*++ + + Routine Description: + + This is the entry point and exit point for the I/O trace driver. This + does very little as the I/O trace driver has minimal global data. + + This method initializes tracing. + + Arguments: + + ModuleHandle - the DLL handle for this module. + + Reason - the reason this entry point was called. + + Reserved - unused + + Return Value: + + TRUE + +--*/ +{ + + UNREFERENCED_PARAMETER(ModuleHandle); + UNREFERENCED_PARAMETER(Reason); + + return TRUE; +} + +_Check_return_ +STDAPI +DllGetClassObject( + _In_ REFCLSID ClassId, + _In_ REFIID InterfaceId, + _Outptr_ LPVOID *Interface + ) +/*++ + + Routine Description: + + This routine is called by COM in order to instantiate the + driver callback object and do an initial query interface on it. + + This method only creates an instance of the driver's class factory, as this + is the minimum required to support UMDF. + + Arguments: + + ClassId - the CLSID of the object being "gotten" + + InterfaceId - the interface the caller wants from that object. + + Interface - a location to store the referenced interface pointer + + Return Value: + + S_OK if the function succeeds or error indicating the cause of the + failure. + +--*/ +{ + PCClassFactory factory; + + HRESULT hr = S_OK; + + *Interface = NULL; + + // + // If the CLSID doesn't match that of our "coclass" (defined in the IDL + // file) then we can't create the object the caller wants. This may + // indicate that the COM registration is incorrect, and another CLSID + // is referencing this drvier. + // + + if (IsEqualCLSID(ClassId, CLSID_MyDriverCoClass) == false) + { + return CLASS_E_CLASSNOTAVAILABLE; + } + + // + // Create an instance of the class factory for the caller. + // + + factory = new CClassFactory(); + + if (NULL == factory) + { + hr = E_OUTOFMEMORY; + } + + // + // Query the object we created for the interface the caller wants. After + // that we release the object. This will drive the reference count to + // 1 (if the QI succeeded an referenced the object) or 0 (if the QI failed). + // In the later case the object is automatically deleted. + // + + if (SUCCEEDED(hr)) + { + hr = factory->QueryInterface(InterfaceId, Interface); + factory->Release(); + } + + return hr; +} + diff --git a/gpio/samples/simdeviceumdf/driver.cpp b/gpio/samples/simdeviceumdf/driver.cpp new file mode 100644 index 00000000..09f47062 --- /dev/null +++ b/gpio/samples/simdeviceumdf/driver.cpp @@ -0,0 +1,222 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved. + +Module Name: + + Driver.cpp + +Abstract: + + This module contains the implementation of the UMDF Sample's + core driver callback object. + + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#include "internal.h" +//#include "driver.tmh" + +HRESULT +CSimdeviceDriver::CreateInstance( + _Out_ PCSimdeviceDriver *Driver + ) +/*++ + + Routine Description: + + This static method is invoked in order to create and initialize a new + instance of the driver class. The caller should arrange for the object + to be released when it is no longer in use. + + Arguments: + + Driver - a location to store a referenced pointer to the new instance + + Return Value: + + S_OK if successful, or error otherwise. + +--*/ +{ + PCSimdeviceDriver driver; + HRESULT hr; + + // + // Allocate the callback object. + // + + driver = new CSimdeviceDriver(); + + if (NULL == driver) + { + return E_OUTOFMEMORY; + } + + // + // Initialize the callback object. + // + + hr = driver->Initialize(); + + if (SUCCEEDED(hr)) + { + // + // Store a pointer to the new, initialized object in the output + // parameter. + // + + *Driver = driver; + } + else + { + + // + // Release the reference on the driver object to get it to delete + // itself. + // + + driver->Release(); + } + + return hr; +} + +HRESULT +CSimdeviceDriver::Initialize( + VOID + ) +/*++ + + Routine Description: + + This method is called to initialize a newly created driver callback object + before it is returned to the creator. Unlike the constructor, the + Initialize method contains operations which could potentially fail. + + Arguments: + + None + + Return Value: + + None + +--*/ +{ + return S_OK; +} + +HRESULT +CSimdeviceDriver::QueryInterface( + _In_ REFIID InterfaceId, + _Out_ PVOID *Interface + ) +/*++ + + Routine Description: + + This method returns a pointer to the requested interface on the callback + object.. + + Arguments: + + InterfaceId - the IID of the interface to query/reference + + Interface - a location to store the interface pointer. + + Return Value: + + S_OK if the interface is supported. + E_NOINTERFACE if it is not supported. + +--*/ +{ + if (IsEqualIID(InterfaceId, __uuidof(IDriverEntry))) + { + *Interface = QueryIDriverEntry(); + return S_OK; + } + else + { + return CUnknown::QueryInterface(InterfaceId, Interface); + } +} + +HRESULT +CSimdeviceDriver::OnDeviceAdd( + _In_ IWDFDriver *FxWdfDriver, + _In_ IWDFDeviceInitialize *FxDeviceInit + ) +/*++ + + Routine Description: + + The FX invokes this method when it wants to install our driver on a device + stack. This method creates a device callback object, then calls the Fx + to create an Fx device object and associate the new callback object with + it. + + Arguments: + + FxWdfDriver - the Fx driver object. + + FxDeviceInit - the initialization information for the device. + + Return Value: + + status + +--*/ +{ + HRESULT hr; + + PCSimdevice device = NULL; + + // + // TODO: Do any per-device initialization (reading settings from the + // registry for example) that's necessary before creating your + // device callback object here. Otherwise you can leave such + // initialization to the initialization of the device event + // handler. + // + + // + // Create a new instance of our device callback object + // + + hr = CSimdevice::CreateInstance(FxWdfDriver, FxDeviceInit, &device); + + // + // TODO: Change any per-device settings that the object exposes before + // calling Configure to let it complete its initialization. + // + + // + // If that succeeded then call the device's construct method. This + // allows the device to create any queues or other structures that it + // needs now that the corresponding fx device object has been created. + // + + if (SUCCEEDED(hr)) + { + hr = device->Configure(); + } + + // + // Release the reference on the device callback object now that it's been + // associated with an fx device object. + // + + if (NULL != device) + { + device->Release(); + } + + return hr; +} + diff --git a/gpio/samples/simdeviceumdf/driver.h b/gpio/samples/simdeviceumdf/driver.h new file mode 100644 index 00000000..a8f464be --- /dev/null +++ b/gpio/samples/simdeviceumdf/driver.h @@ -0,0 +1,151 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + Driver.h + +Abstract: + + This module contains the type definitions for the UMDF sample's + driver callback class. + + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#pragma once + +// +// This class handles driver events for the sample. In particular +// it supports the OnDeviceAdd event, which occurs when the driver is called +// to setup per-device handlers for a new device stack. +// + +class CSimdeviceDriver : public CUnknown, public IDriverEntry +{ +// +// Private data members. +// +private: + +// +// Private methods. +// +private: + + // + // Returns a refernced pointer to the IDriverEntry interface. + // + + IDriverEntry * + QueryIDriverEntry( + VOID + ) + { + AddRef(); + return static_cast<IDriverEntry*>(this); + } + + HRESULT + Initialize( + VOID + ); + +// +// Public methods +// +public: + + // + // The factory method used to create an instance of this driver. + // + + static + HRESULT + CreateInstance( + _Out_ PCSimdeviceDriver *Driver + ); + +// +// COM methods +// +public: + + // + // IDriverEntry methods + // + + virtual + HRESULT + STDMETHODCALLTYPE + OnInitialize( + _In_ IWDFDriver *FxWdfDriver + ) + { + UNREFERENCED_PARAMETER( FxWdfDriver ); + + return S_OK; + } + + virtual + HRESULT + STDMETHODCALLTYPE + OnDeviceAdd( + _In_ IWDFDriver *FxWdfDriver, + _In_ IWDFDeviceInitialize *FxDeviceInit + ); + + virtual + VOID + STDMETHODCALLTYPE + OnDeinitialize( + _In_ IWDFDriver *FxWdfDriver + ) + { + UNREFERENCED_PARAMETER( FxWdfDriver ); + + return; + } + + // + // IUnknown methods. + // + // We have to implement basic ones here that redirect to the + // base class becuase of the multiple inheritance. + // + + virtual + ULONG + STDMETHODCALLTYPE + AddRef( + VOID + ) + { + return __super::AddRef(); + } + + __drv_arg(this, __drv_freesMem(object)) + virtual + ULONG + STDMETHODCALLTYPE + Release( + VOID + ) + { + return __super::Release(); + } + + virtual + HRESULT + STDMETHODCALLTYPE + QueryInterface( + _In_ REFIID InterfaceId, + _Out_ PVOID *Object + ); +}; + diff --git a/gpio/samples/simdeviceumdf/exports.def b/gpio/samples/simdeviceumdf/exports.def new file mode 100644 index 00000000..45a4fb2b --- /dev/null +++ b/gpio/samples/simdeviceumdf/exports.def @@ -0,0 +1,10 @@ +; Echo.def : Declares the module parameters. + +; +; TODO: Change the library name here to match your binary name. +; + +LIBRARY "SimdeviceUMDF.DLL" + +EXPORTS + DllGetClassObject PRIVATE diff --git a/gpio/samples/simdeviceumdf/internal.h b/gpio/samples/simdeviceumdf/internal.h new file mode 100644 index 00000000..4cc79b71 --- /dev/null +++ b/gpio/samples/simdeviceumdf/internal.h @@ -0,0 +1,96 @@ +/*++ + +Copyright (C) Microsoft Corporation, All Rights Reserved + +Module Name: + + Internal.h + +Abstract: + + This module contains the local type definitions for the UMDF Echo + driver sample. + + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ +#ifndef _INTERNAL_H_ +#define _INTERNAL_H_ + +#pragma once + +#define UMDF_USING_NTSTATUS + +#include <windows.h> +#include <winternl.h> +#include <ntstatus.h> +#include <strsafe.h> +#include <gpio.h> + +_Analysis_mode_(_Analysis_code_type_user_driver_); // Macro letting the compiler know this is not a kernel driver (this will help surpress needless warnings) + +// Common WPD, UMDF, and WDM headers +#include <devioctl.h> +#include <initguid.h> + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) +#endif + +// +// Include the WUDF DDI +// +#include "wudfddi.h" + +// +// Use specstrings for in/out annotation of function parameters. +// + +#include "specstrings.h" + +// +// Forward definitions of classes in the other header files. +// + +typedef class CSimdeviceDriver *PCSimdeviceDriver; +typedef class CSimdevice *PCSimdevice; +typedef class CSimdeviceQueue *PCSimdeviceQueue; + +// +// Include the type specific headers. +// + +#include "comsup.h" +#include "driver.h" +#include "device.h" +#include "queue.h" + +__forceinline +#ifdef _PREFAST_ +__declspec(noreturn) +#endif +VOID +WdfTestNoReturn( + VOID + ) +{ + // do nothing. +} + +#define WUDF_TEST_DRIVER_ASSERT(p) \ +{ \ + if ( !(p) ) \ + { \ + DebugBreak(); \ + WdfTestNoReturn(); \ + } \ +} + +#define MYDRIVER_CLASS_ID {0x7ab7dcf5, 0xd1d4, 0x4085, {0x95, 0x47, 0x1d, 0xb9, 0x68, 0xcc, 0xa7, 0x20}} + + +#define SAFE_RELEASE(p) {if ((p)) { (p)->Release(); (p) = NULL; }} +#endif diff --git a/gpio/samples/simdeviceumdf/queue.cpp b/gpio/samples/simdeviceumdf/queue.cpp new file mode 100644 index 00000000..4b66a25f --- /dev/null +++ b/gpio/samples/simdeviceumdf/queue.cpp @@ -0,0 +1,529 @@ +/*++ + +Copyright (c) Microsoft Corporation, All Rights Reserved + +Module Name: + + queue.cpp + +Abstract: + + This file implements the I/O queue interface and performs + the read/write/ioctl operations. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + + +#include "internal.h" + +// +// IUnknown implementation +// + +// +// Queue destructor. +// Free up the buffer, wait for thread to terminate and +// delete critical section. +// + + +CSimdeviceQueue::~CSimdeviceQueue( + VOID + ) +/*++ + +Routine Description: + + + IUnknown implementation of Release + +Arguments: + + +Return Value: + + ULONG (reference count after Release) + +--*/ +{ + if (m_Buffer) { + delete [] m_Buffer; + } + + if (m_InitCritSec) { + ::DeleteCriticalSection(&m_Crit); + } +} + + +// +// Initialize +HRESULT +CSimdeviceQueue::CreateInstance( + _In_ IWDFDevice *FxDevice, + _Out_ PCSimdeviceQueue *Queue + ) +/*++ + +Routine Description: + + + CreateInstance creates an instance of the queue object. + +Arguments: + + ppUkwn - OUT parameter is an IUnknown interface to the queue object + +Return Value: + + HRESULT indicating success or failure + +--*/ +{ + CSimdeviceQueue *pMyQueue = new CSimdeviceQueue; + HRESULT hr; + + if (pMyQueue == NULL) { + return E_OUTOFMEMORY; + } + + hr = pMyQueue->Initialize(FxDevice); + + if (SUCCEEDED(hr)) + { + *Queue = pMyQueue; + } + else + { + pMyQueue->Release(); + } + return hr; +} + +HRESULT +CSimdeviceQueue::Initialize( + _In_ IWDFDevice *FxDevice + ) +{ + IWDFIoQueue *fxQueue; + HRESULT hr; + + // + // Initialize the critical section before we continue + // + + if (!InitializeCriticalSectionAndSpinCount(&m_Crit,0x80000400)) { + hr = HRESULT_FROM_WIN32(GetLastError()); + goto Exit; + } + m_InitCritSec = TRUE; + + // + // Create the framework queue + // + + { + IUnknown *unknown = QueryIUnknown(); + hr = FxDevice->CreateIoQueue(unknown, + TRUE, + WdfIoQueueDispatchSequential, + TRUE, + FALSE, + &fxQueue); + unknown->Release(); + } + + if (FAILED(hr)) { + goto Exit; + } + + m_FxQueue = fxQueue; + + fxQueue->Release(); + +Exit: + return hr; +} + +HRESULT +STDMETHODCALLTYPE +CSimdeviceQueue::QueryInterface( + _In_ REFIID InterfaceId, + _Out_ PVOID *Object + ) +/*++ + +Routine Description: + + + Query Interface + +Arguments: + + Follows COM specifications + +Return Value: + + HRESULT indicating success or failure + +--*/ +{ + HRESULT hr; + + + if (IsEqualIID(InterfaceId, __uuidof(IQueueCallbackWrite))) { + *Object = QueryIQueueCallbackWrite(); + hr = S_OK; + } else if (IsEqualIID(InterfaceId, __uuidof(IQueueCallbackRead))) { + *Object = QueryIQueueCallbackRead(); + hr = S_OK; + } else if (IsEqualIID(InterfaceId, __uuidof(IQueueCallbackDeviceIoControl))) { + *Object = QueryIQueueCallbackDeviceIoControl(); + hr = S_OK; + } else { + hr = CUnknown::QueryInterface(InterfaceId, Object); + } + + return hr; +} + +VOID +STDMETHODCALLTYPE +CSimdeviceQueue::OnDeviceIoControl( + _In_ IWDFIoQueue *pWdfQueue, + _In_ IWDFIoRequest *pWdfRequest, + _In_ ULONG ControlCode, + _In_ SIZE_T InputBufferSizeInBytes, + _In_ SIZE_T OutputBufferSizeInBytes + ) +/*++ + +Routine Description: + + + DeviceIoControl dispatch routine + +Arguments: + + pWdfQueue - Framework Queue instance + pWdfRequest - Framework Request instance + ControlCode - IO Control Code + InputBufferSizeInBytes - Length of input buffer + OutputBufferSizeInBytes - Length of output buffer + + Always succeeds DeviceIoIoctl +Return Value: + + VOID + +--*/ +{ + + UNREFERENCED_PARAMETER(pWdfQueue); + UNREFERENCED_PARAMETER(ControlCode); + UNREFERENCED_PARAMETER(InputBufferSizeInBytes); + UNREFERENCED_PARAMETER(OutputBufferSizeInBytes); + + pWdfRequest->Complete(S_OK); + return; +} + +VOID +STDMETHODCALLTYPE +CSimdeviceQueue::OnWrite( + _In_ IWDFIoQueue *pWdfQueue, + _In_ IWDFIoRequest *pWdfRequest, + _In_ SIZE_T BytesToWrite + ) +/*++ + +Routine Description: + + + Write dispatch routine + IQueueCallbackWrite + +Arguments: + + pWdfQueue - Framework Queue instance + pWdfRequest - Framework Request instance + BytesToWrite - Length of bytes in the write buffer + + Allocate and copy data to local buffer +Return Value: + + VOID + +--*/ +{ + + HRESULT hr; + IWDFMemory* pRequestMemory = NULL; + IWDFIoRequest2 * pWdfRequest2 = NULL; + + UNREFERENCED_PARAMETER(pWdfQueue); + + // + // Handle Zero length writes. + // + + if (!BytesToWrite) { + pWdfRequest->CompleteWithInformation(S_OK, 0); + return; + } + + if( BytesToWrite > MAX_WRITE_LENGTH ) { + + pWdfRequest->CompleteWithInformation(HRESULT_FROM_WIN32(ERROR_MORE_DATA), 0); + return; + } + + // Get memory object + hr = pWdfRequest->QueryInterface(IID_PPV_ARGS(&pWdfRequest2)); + + if (FAILED(hr)) { + pWdfRequest->Complete(hr); + return; + } + + // Release previous buffer if set + + if( m_Buffer != NULL ) { + delete [] m_Buffer; + m_Buffer = NULL; + m_Length = 0L; + } + + // Allocate Buffer + + m_Buffer = new UCHAR[BytesToWrite]; + if (m_Buffer == NULL) { + pWdfRequest->Complete(E_OUTOFMEMORY); + m_Length = 0L; + return; + } + + hr = pWdfRequest2->RetrieveInputMemory(&pRequestMemory); + + if (FAILED(hr)) { + goto Exit; + } + + // Copy from memory object to our buffer + + hr = pRequestMemory->CopyToBuffer(0, m_Buffer, BytesToWrite); + + if (FAILED(hr)) { + goto Exit; + } + + + // + // Save the information so that we can use it + // to complete the request later. + // + + Lock(); + + m_Length = (ULONG) BytesToWrite; + m_XferredBytes = m_Length; + m_CurrentRequest = pWdfRequest2; + pWdfRequest2 = NULL; + + Unlock(); + +Exit: + + if (FAILED(hr)) { + pWdfRequest2->CompleteWithInformation(hr, 0); + delete [] m_Buffer; + m_Buffer = NULL; + } + + SAFE_RELEASE(pRequestMemory); + SAFE_RELEASE(pWdfRequest2); + + return; +} + +VOID +STDMETHODCALLTYPE +CSimdeviceQueue::OnRead( + _In_ IWDFIoQueue *pWdfQueue, + _In_ IWDFIoRequest *pWdfRequest, + _In_ SIZE_T SizeInBytes + ) +/*++ + +Routine Description: + + + Read dispatch routine + IQueueCallbackRead + +Arguments: + + pWdfQueue - Framework Queue instance + pWdfRequest - Framework Request instance + SizeInBytes - Length of bytes in the read buffer + + Copy available data into the read buffer +Return Value: + + VOID + +--*/ +{ + IWDFMemory* pRequestMemory = NULL; + IWDFIoRequest2 * pWdfRequest2 = NULL; + HRESULT hr; + + UNREFERENCED_PARAMETER(pWdfQueue); + + // + // Handle Zero length reads. + // + + if (!SizeInBytes) { + pWdfRequest->CompleteWithInformation(S_OK, 0); + return; + } + + if (m_Buffer == NULL) { + pWdfRequest->CompleteWithInformation(HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER), SizeInBytes); + return; + } + + if (m_Length < SizeInBytes) { + SizeInBytes = m_Length; + } + + // + // Get memory object + // + + hr = pWdfRequest->QueryInterface(IID_PPV_ARGS(&pWdfRequest2)); + + if (FAILED(hr)) { + pWdfRequest->Complete(hr); + return; + } + + + hr = pWdfRequest2->RetrieveOutputMemory(&pRequestMemory ); + + if (FAILED(hr)) { + goto Exit; + } + + // Copy from buffer to memory object + + hr = pRequestMemory->CopyFromBuffer(0, m_Buffer, SizeInBytes); + + if (FAILED(hr)) { + goto Exit; + } + + + // + // Save the information so that we can use it + // to complete the request later. + // + + Lock(); + + m_CurrentRequest = pWdfRequest2; + m_XferredBytes = SizeInBytes; + pWdfRequest2 = NULL; + + Unlock(); + +Exit: + + if (FAILED(hr)) { + pWdfRequest2->CompleteWithInformation(hr, 0); + } + + SAFE_RELEASE(pRequestMemory); + SAFE_RELEASE(pWdfRequest2); + return; +} + +DWORD +CSimdeviceQueue::CompletionThread( + PVOID ThreadParameter + ) +/*++ + +Routine Description: + + + This routine is called from the thread started to complete + I/O requests. It sleeps for TIMER_PERIOD and then completes + the current request. Note that it has to release the lock + before it calls the request complete method. + +Arguments: + + ThreadParameter - This is a pointer to the Queue object. + +Return Value: + + VOID + +--*/ +{ + CSimdeviceQueue *pQueue = (CSimdeviceQueue *)ThreadParameter; + IWDFIoRequest2 *request; + SIZE_T bytesXferred = 0; + + for (;;) { + + // + // Block for a fixed time and then complete the request. + // + + Sleep(TIMER_PERIOD); + + pQueue->Lock(); + + // + // Process the current request. + // + + request = pQueue->m_CurrentRequest; + + if (request) { + bytesXferred = pQueue->m_XferredBytes; + } + + // + // Reset values. + // + + pQueue->m_CurrentRequest = NULL; + pQueue->m_XferredBytes = 0; + + + pQueue->Unlock(); + + if (request) { + request->CompleteWithInformation(S_OK, bytesXferred); + SAFE_RELEASE(request); + } + + // + // If thread needs to be terminated + // + + if (pQueue->m_ExitThread) { + ExitThread(0); + } + + } + +} diff --git a/gpio/samples/simdeviceumdf/queue.h b/gpio/samples/simdeviceumdf/queue.h new file mode 100644 index 00000000..108299a4 --- /dev/null +++ b/gpio/samples/simdeviceumdf/queue.h @@ -0,0 +1,211 @@ +/*++ + +Copyright (c) Microsoft Corporation, All Rights Reserved + +Module Name: + + queue.h + +Abstract: + + This file defines the queue callback interface. + +Environment: + + Windows User-Mode Driver Framework (WUDF) + +--*/ + +#pragma once + +// Set max write length for testing +#define MAX_WRITE_LENGTH (40*1024) + +// Set timer period in ms +#define TIMER_PERIOD 100 + +// +// Queue Callback Object. +// + +class CSimdeviceQueue : + public IQueueCallbackDeviceIoControl, + public IQueueCallbackRead, + public IQueueCallbackWrite, + public CUnknown +{ + PVOID m_Buffer; // Current buffer + ULONG m_Length; // Length of the buffer + SIZE_T m_XferredBytes; // Amount of bytes transferred for the current request + IWDFIoRequest2 *m_CurrentRequest; // Current request + CRITICAL_SECTION m_Crit; // Lock to protect updates to CSimdeviceQueue fields + BOOLEAN m_ExitThread; // If TRUE Terminate thread. + BOOLEAN m_InitCritSec; // If TRUE lock initialized + + IWDFIoQueue *m_FxQueue; + + CSimdeviceQueue() : + m_Buffer(NULL), + m_Length (0), + m_CurrentRequest(NULL), + m_XferredBytes(0), + m_ExitThread(FALSE), + m_InitCritSec(FALSE), + m_FxQueue(NULL) + { + } + + virtual ~CSimdeviceQueue(); + + __inline + void + Lock( + ) + { + ::EnterCriticalSection(&m_Crit); + } + + __inline + void + Unlock( + ) + { + ::LeaveCriticalSection(&m_Crit); + } + + HRESULT + Initialize( + _In_ IWDFDevice *FxDevice + ); + +public: + + // + // Completion thread routine. + // + + static DWORD CompletionThread( PVOID ThreadParameter); + + // + // Sets the flag to make thread exit + // + + void + SetExitThread() + { + m_ExitThread = TRUE; + } + + static + HRESULT + CreateInstance( + _In_ IWDFDevice *FxDevice, + _Out_ PCSimdeviceQueue *Queue + ); + + HRESULT + Configure( + VOID + ) + { + return S_OK; + } + + + IQueueCallbackDeviceIoControl * + QueryIQueueCallbackDeviceIoControl( + VOID + ) + { + AddRef(); + return static_cast<IQueueCallbackDeviceIoControl *>(this); + } + + IQueueCallbackRead * + QueryIQueueCallbackRead( + VOID + ) + { + AddRef(); + return static_cast<IQueueCallbackRead *>(this); + } + + IQueueCallbackWrite * + QueryIQueueCallbackWrite( + VOID + ) + { + AddRef(); + return static_cast<IQueueCallbackWrite *>(this); + } + + // + // IUnknown + // + + virtual + ULONG + STDMETHODCALLTYPE + AddRef( + VOID + ) { + return CUnknown::AddRef(); + } + + __drv_arg(this, __drv_freesMem(object)) + virtual + ULONG + STDMETHODCALLTYPE + Release( + VOID + ) { + return CUnknown::Release(); + } + + virtual + HRESULT + STDMETHODCALLTYPE + QueryInterface( + _In_ REFIID InterfaceId, + _Out_ PVOID *Object + ); + + // + // Wdf Callbacks + // + + // IQueueCallbackDeviceIoControl + // + virtual + VOID + STDMETHODCALLTYPE + OnDeviceIoControl( + _In_ IWDFIoQueue *pWdfQueue, + _In_ IWDFIoRequest *pWdfRequest, + _In_ ULONG ControlCode, + _In_ SIZE_T InputBufferSizeInBytes, + _In_ SIZE_T OutputBufferSizeInBytes + ); + + // IQueueCallbackWrite + // + virtual + VOID + STDMETHODCALLTYPE + OnWrite( + _In_ IWDFIoQueue *pWdfQueue, + _In_ IWDFIoRequest *pWdfRequest, + _In_ SIZE_T NumOfBytesToWrite + ); + + // IQueueCallbackRead + // + virtual + VOID + STDMETHODCALLTYPE + OnRead( + _In_ IWDFIoQueue *pWdfQueue, + _In_ IWDFIoRequest *pWdfRequest, + _In_ SIZE_T NumOfBytesToRead + ); +}; diff --git a/gpio/samples/simdeviceumdf/simdeviceumdf.rc b/gpio/samples/simdeviceumdf/simdeviceumdf.rc new file mode 100644 index 00000000..8465960f --- /dev/null +++ b/gpio/samples/simdeviceumdf/simdeviceumdf.rc @@ -0,0 +1,21 @@ +//--------------------------------------------------------------------------- +// SimdeviceUMDF.rc +// +// Copyright (c) Microsoft Corporation, All Rights Reserved +//--------------------------------------------------------------------------- + + +#include <windows.h> +#include <ntverp.h> + +// +// TODO: Change the file description and file names to match your binary. +// + +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT_UNKNOWN +#define VER_FILEDESCRIPTION_STR "WDF:UMDF Simdevice User-Mode Driver Sample" +#define VER_INTERNALNAME_STR "SimdeviceUMDF" +#define VER_ORIGINALFILENAME_STR "SimdeviceUMDF.dll" + +#include "common.ver" diff --git a/gpio/samples/simdeviceumdf/simdeviceumdfdriver.ctl b/gpio/samples/simdeviceumdf/simdeviceumdfdriver.ctl new file mode 100644 index 00000000..a0ce2089 --- /dev/null +++ b/gpio/samples/simdeviceumdf/simdeviceumdfdriver.ctl @@ -0,0 +1 @@ +d93fb470-afb1-4af8-860e-75f726c66f6b WudfEchoDriverTraceGuid diff --git a/gpio/samples/simdeviceumdf/simdeviceumdfdriver.inx b/gpio/samples/simdeviceumdf/simdeviceumdfdriver.inx new file mode 100644 index 00000000..fa17b2bb --- /dev/null +++ b/gpio/samples/simdeviceumdf/simdeviceumdfdriver.inx @@ -0,0 +1,78 @@ +; +; SimdeviceUMDFDriver.inf +; + +[Version] +Signature="$WINDOWS NT$" +Class=Sample +ClassGuid={78A1C341-4539-11d3-B88D-00C04FAD5171} +Provider=%MSFTWUDF% +CatalogFile=GpioSamples.cat +DriverVer=03/20/2003,5.00.3788 + +[Manufacturer] +%MSFTWUDF%=Microsoft,NT$ARCH$ + +[Microsoft.NT$ARCH$] +%SimdeviceUMDFName%=SimdeviceUMDF_Install,ACPI\TEST0004 + +[ClassInstall32] +AddReg=SampleClass_RegistryAdd + +[SampleClass_RegistryAdd] +HKR,,,,%ClassName% +HKR,,Icon,,"-10" + +[SourceDisksFiles] +SimdeviceUMDF.dll=1 + +[SourceDisksNames] +1 = %MediaDescription% + +; =================== Simdevice Sample UMDF Driver ================================== + +[SimdeviceUMDF_Install.NT] +CopyFiles=UMDriverCopy + +[SimdeviceUMDF_Install.NT.hw] + +[SimdeviceUMDF_Install.NT.Services] +AddService=SimdeviceUMDF,0x000001fa,SimdeviceUMDF_ServiceInstall + +[SimdeviceUMDF_Install.NT.Wdf] +UmdfService=SimdeviceUMDF,SimdeviceUMDF_Install +UmdfServiceOrder=SimdeviceUMDF + +; this enables direct hardware access from UMDF driver +UmdfDirectHardwareAccess=AllowDirectHardwareAccess + +; direct i/o devices cannot run in default pool. +UmdfHostProcessSharing=ProcessSharingDisabled + +[SimdeviceUMDF_Install] +UmdfLibraryVersion=$UMDFVERSION$ +DriverCLSID={7AB7DCF5-D1D4-4085-9547-1DB968CCA720} +ServiceBinary=%12%\UMDF\SimdeviceUMDF.dll + +[SimdeviceUMDF_ServiceInstall] +DisplayName = %SimdeviceUMDFDisplayName% +ServiceType = 1 +StartType = 3 +ErrorControl = 1 +ServiceBinary = %12%\WUDFRd.sys + +[DestinationDirs] +UMDriverCopy=12,UMDF ; copy to drivers\UMDF +CoInstallers_CopyFiles=11 + +[UMDriverCopy] +SimdeviceUMDF.dll + +; =================== Generic ================================== + +[Strings] +MSFTWUDF="Microsoft Internal (WUDF)" +MediaDescription="Microsoft WUDF Sample Driver Installation Media" +ClassName="Sample Device" +SimdeviceUMDFDisplayName="Sample User-mode Driver Framework Reflector driver" +SimdeviceUMDFName="Sample device UMDF Driver" diff --git a/gpio/samples/simgpio/simgpio.c b/gpio/samples/simgpio/simgpio.c new file mode 100644 index 00000000..4696e20d --- /dev/null +++ b/gpio/samples/simgpio/simgpio.c @@ -0,0 +1,2540 @@ + +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + simgpio.c + +Abstract: + + This sample implements a GPIO client driver for simulated GPIO (SimGpio) + controller. + + Note: DIRQL in the comments below refers to device IRQL, which is any + IRQL > DISPATCH_LEVEL (and less than some IRQL reserved for OS use). + + +Environment: + + Kernel mode + +--*/ + + +// +// The SimGpio controller has 4 GPIO banks, with banks consisting of +// 32 pins each. Any pin can be configured for interrupt, input or output. +// Each bank has the the following registers. The register set for different +// banks are laid out adjacent to one another in the physical memory map. +// +// Register | Offset | Size +// ------------------------------------------------ +// ModeRegister | 0x0 | 4 bytes +// PolarityRegister | 0x4 | 8 bytes +// EnableRegister | 0xC | 4 bytes +// StatusRegister | 0x10 | 4 bytes +// DirectionRegister | 0x14 | 4 bytes +// LevelRegister | 0x18 | 4 bytes +// +// Mode Register - Interrupt mode (Level = 0x1 or Edge = 0x0) for pins +// enabled for interrupts. +// +// Polarity Register - ActiveLow (0x0) or ActiveHigh (0x1) for Level-triggered +// interrupts; Falling-edge (0x0), Rising-edge (0x1), or Both-Edges (0x3) +// for Edge-triggered interrupts. Note there are two bits per pin. +// +// Enable Register - A mask of which pins are enabled for interrupt. A bit +// is set if the pin is enabled for interrupt. +// +// Status Register - A register containing which pins are actively interrupting. +// If any of the bit is set, then the GPIO controller's interrupt fires. +// +// Direction Register - A register which indicates whether a given pin is +// configured for input (0x1) or output (0x0). +// +// LevelRegister - A bitmask which indicates holds the value for a given pin +// (i.e. whether the pin is high (0x1) or low (0x0)). +// + + +// +// ------------------------------------------------------------------- Includes +// + +#include <ntddk.h> +#include <wdf.h> +#include <gpioclx.h> + +// +// -------------------------------------------------------------------- Defines +// + +// +// Define total number of pins on the simulated GPIO controller. +// + +#define SIM_GPIO_TOTAL_PINS (4 * 32) +#define SIM_GPIO_PINS_PER_BANK (32) +#define SIM_GPIO_TOTAL_BANKS (4) + +// +// Pool tag for SimGpio allocations. +// + +#define SIM_GPIO_POOL_TAG 'GmiS' + +// +// Define that controls whether f-state based power management will be supported +// or not by the client driver. +// + +#define ENABLE_F_STATE_POWER_MGMT + +// +// Define F1 state residency (in sec), latency values (in sec) and nominal +// power. +// +// This sample demonstrates F-state management effected as part of a critical +// system transition (when the system enters connected-standby). To prevent a +// GPIO bank from being transitioned into a low power state as part of runtime +// idle power management (by mini-PEP), specify very high values for residency +// and transition latency. +// +// If GPIO banks need to be runtime power managed, then specify more reasonable +// values below. +// + +#ifdef ENABLE_F_STATE_POWER_MGMT + +#define SIM_GPIO_F1_NOMINAL_POWER (0) +#define SIM_GPIO_F1_RESIDENCY (8*60*60) // 8hrs +#define SIM_GPIO_F1_TRANSITION (1*60*60) // 1hr + +#endif + +// +// Macro for pointer arithmetic. +// + +#define Add2Ptr(Ptr, Value) ((PVOID)((PUCHAR)(Ptr) + (Value))) + +// +// Determine whether the given pin is reserved or not. Currently no pins are +// reserved on the simulated GPIO controller. +// + +__pragma(warning(disable: 4127)) // conditional expression is a constant + +// +// ---------------------------------------------------------------------- Types +// + +// +// Define the registers within the SimGPIO controller. There are 32 pins per +// controller. Note this is a logical device and thus may correspond to a +// physical bank or module if the GPIO controller in hardware has more than +// 32 pins. +// + + + +typedef struct _SIM_GPIO_REGISTERS { + ULONG ModeRegister; + ULONG PolarityRegister[2]; + ULONG EnableRegister; + ULONG StatusRegister; + ULONG DirectionRegister; + ULONG LevelRegister; +} SIM_GPIO_REGISTERS, *PSIM_GPIO_REGISTERS; + +typedef struct _SIM_GPIO_BANK { + LARGE_INTEGER PhysicalBaseAddress; + PSIM_GPIO_REGISTERS Registers; + ULONG Length; + SIM_GPIO_REGISTERS SavedContext; +} SIM_GPIO_BANK, *PSIM_GPIO_BANK; + +// +// The SimGPIO client driver device extension. +// + +typedef struct _SIM_GPIO_CONTEXT { + USHORT TotalPins; + LARGE_INTEGER PhysicalBaseAddress; + PSIM_GPIO_REGISTERS ControllerBase; + ULONG Length; + SIM_GPIO_BANK Banks[SIM_GPIO_TOTAL_BANKS]; +} SIM_GPIO_CONTEXT, *PSIM_GPIO_CONTEXT; + +SIM_GPIO_REGISTERS GlobalGpioRegisters[SIM_GPIO_TOTAL_BANKS] = {0}; + +// +// ----------------------------------------------------------------- Prototypes +// + +DRIVER_INITIALIZE DriverEntry; + +EVT_WDF_DRIVER_UNLOAD SimGpioEvtDriverUnload; +EVT_WDF_DRIVER_DEVICE_ADD SimGpioEvtDeviceAdd; + +// +// General interfaces. +// + +GPIO_CLIENT_PREPARE_CONTROLLER SimGpioPrepareController; +GPIO_CLIENT_RELEASE_CONTROLLER SimGpioReleaseController; +GPIO_CLIENT_QUERY_CONTROLLER_BASIC_INFORMATION + SimGpioQueryControllerBasicInformation; + +GPIO_CLIENT_QUERY_SET_CONTROLLER_INFORMATION + SimGpioQuerySetControllerInformation; + +GPIO_CLIENT_START_CONTROLLER SimGpioStartController; +GPIO_CLIENT_STOP_CONTROLLER SimGpioStopController; + +// +// Interrupt enable, disable, mask and unmask handlers. +// + +GPIO_CLIENT_ENABLE_INTERRUPT SimGpioEnableInterrupt; +GPIO_CLIENT_DISABLE_INTERRUPT SimGpioDisableInterrupt; +GPIO_CLIENT_MASK_INTERRUPTS SimGpioMaskInterrupts; +GPIO_CLIENT_UNMASK_INTERRUPT SimGpioUnmaskInterrupt; +GPIO_CLIENT_RECONFIGURE_INTERRUPT SimGpioReconfigureInterrupt; + +// +// Handlers to query active/enabled interrupts and clear active interrupts. +// + +GPIO_CLIENT_QUERY_ACTIVE_INTERRUPTS SimGpioQueryActiveInterrupts; +GPIO_CLIENT_CLEAR_ACTIVE_INTERRUPTS SimGpioClearActiveInterrupts; +GPIO_CLIENT_QUERY_ENABLED_INTERRUPTS SimGpioQueryEnabledInterrupts; + +// +// Handlers for GPIO I/O operations. +// + +GPIO_CLIENT_CONNECT_IO_PINS SimGpioConnectIoPins; +GPIO_CLIENT_DISCONNECT_IO_PINS SimGpioDisconnectIoPins; +GPIO_CLIENT_READ_PINS_MASK SimGpioReadGpioPins; +GPIO_CLIENT_WRITE_PINS_MASK SimGpioWriteGpioPins; + +// +// Handlers for save and restore hardware context callbacks. +// + +GPIO_CLIENT_SAVE_BANK_HARDWARE_CONTEXT SimGpioSaveBankHardwareContext; +GPIO_CLIENT_RESTORE_BANK_HARDWARE_CONTEXT SimGpioRestoreBankHardwareContext; + +// +// -------------------------------------------------------------------- Pragmas +// + +#pragma alloc_text(INIT, DriverEntry) +#pragma alloc_text(PAGE, SimGpioEvtDriverUnload) +#pragma alloc_text(PAGE, SimGpioEvtDeviceAdd) + +// +// ------------------------------------------------------------------ Functions +// + +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + This routine is the driver initialization entry point. + +Arguments: + + DriverObject - Pointer to the driver object created by the I/O manager. + + RegistryPath - Pointer to the driver specific registry key. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + WDFDRIVER Driver; + WDF_DRIVER_CONFIG DriverConfig; + GPIO_CLIENT_REGISTRATION_PACKET RegistrationPacket; + NTSTATUS Status; + + // + // Initialize the driver configuration structure. + // + + WDF_DRIVER_CONFIG_INIT(&DriverConfig, SimGpioEvtDeviceAdd); + DriverConfig.EvtDriverUnload = SimGpioEvtDriverUnload; + + // + // Create a framework driver object to represent our driver. + // + + Status = WdfDriverCreate(DriverObject, + RegistryPath, + WDF_NO_OBJECT_ATTRIBUTES, + &DriverConfig, + &Driver); + + if (!NT_SUCCESS(Status)) { + goto DriverEntryEnd; + } + + // + // Initialize the client driver registration packet. + // + + RtlZeroMemory(&RegistrationPacket, sizeof(GPIO_CLIENT_REGISTRATION_PACKET)); + RegistrationPacket.Version = GPIO_CLIENT_VERSION; + RegistrationPacket.Size = sizeof(GPIO_CLIENT_REGISTRATION_PACKET); + + // + // Initialize the device context size. + // + + RegistrationPacket.ControllerContextSize = sizeof(SIM_GPIO_CONTEXT); + + // + // General interfaces. + // + + RegistrationPacket.CLIENT_PrepareController = SimGpioPrepareController; + RegistrationPacket.CLIENT_QueryControllerBasicInformation = + SimGpioQueryControllerBasicInformation; + + // + // The query/set handler is required in this sample only if F-state + // power management is enabled to indicate which banks support f-state + // based power maanagement. + // + +#ifdef ENABLE_F_STATE_POWER_MGMT + + RegistrationPacket.CLIENT_QuerySetControllerInformation = + SimGpioQuerySetControllerInformation; + +#endif + + RegistrationPacket.CLIENT_StartController = SimGpioStartController; + RegistrationPacket.CLIENT_StopController = SimGpioStopController; + RegistrationPacket.CLIENT_ReleaseController = SimGpioReleaseController; + + // + // Interrupt enable and disable handlers. + // + + RegistrationPacket.CLIENT_DisableInterrupt = SimGpioDisableInterrupt; + RegistrationPacket.CLIENT_EnableInterrupt = SimGpioEnableInterrupt; + + // + // Interrupt mask, unmask and reconfigure interrupt handlers. + // + + RegistrationPacket.CLIENT_MaskInterrupts = SimGpioMaskInterrupts; + RegistrationPacket.CLIENT_UnmaskInterrupt = SimGpioUnmaskInterrupt; + RegistrationPacket.CLIENT_ReconfigureInterrupt = SimGpioReconfigureInterrupt; + + // + // Handlers to query active/enabled interrupts and clear active interrupts. + // + + RegistrationPacket.CLIENT_ClearActiveInterrupts = SimGpioClearActiveInterrupts; + RegistrationPacket.CLIENT_QueryActiveInterrupts = SimGpioQueryActiveInterrupts; + RegistrationPacket.CLIENT_QueryEnabledInterrupts = SimGpioQueryEnabledInterrupts; + + // + // Handlers for GPIO I/O operations. + // + + RegistrationPacket.CLIENT_ConnectIoPins = SimGpioConnectIoPins; + RegistrationPacket.CLIENT_DisconnectIoPins = SimGpioDisconnectIoPins; + RegistrationPacket.CLIENT_ReadGpioPinsUsingMask = SimGpioReadGpioPins; + RegistrationPacket.CLIENT_WriteGpioPinsUsingMask = SimGpioWriteGpioPins; + + // + // Handlers for GPIO save and restore context (if F-state power mgmt is + // supported). + // + +#ifdef ENABLE_F_STATE_POWER_MGMT + + RegistrationPacket.CLIENT_SaveBankHardwareContext = SimGpioSaveBankHardwareContext; + RegistrationPacket.CLIENT_RestoreBankHardwareContext = SimGpioRestoreBankHardwareContext; + +#endif + + // + // Register the SimGPIO client driver with the GPIO class extension. + // + + Status = GPIO_CLX_RegisterClient(Driver, &RegistrationPacket, RegistryPath); + +DriverEntryEnd: + return Status; +} + +VOID +SimGpioEvtDriverUnload ( + _In_ WDFDRIVER Driver + ) + +/*++ + +Routine Description: + + This routine is called by WDF to allow final cleanup prior to unloading the + SimGPIO client driver. This routine unregisters the client driver from the + class extension. + +Arguments: + + Driver - Supplies a handle to a framework driver object. + +Return Value: + + None. + +--*/ + +{ + + NTSTATUS Status; + + PAGED_CODE(); + + Status = GPIO_CLX_UnregisterClient(Driver); + NT_ASSERT(NT_SUCCESS(Status)); +} + +NTSTATUS +SimGpioEvtDeviceAdd ( + _In_ WDFDRIVER Driver, + _Inout_ PWDFDEVICE_INIT DeviceInit + ) + +/*++ + +Routine Description: + + This routine is the AddDevice entry point for the client driver. This + routine is called by the framework in response to AddDevice call from the + PnP manager. It will create and initialize the device object to represent + a new instance of the simulated GPIO controller. + +Arguments: + + Driver - Supplies a handle to the driver object created in DriverEntry. + + DeviceInit - Supplies a pointer to a framework-allocated WDFDEVICE_INIT + structure. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + WDFDEVICE Device; + WDF_OBJECT_ATTRIBUTES FdoAttributes; + NTSTATUS Status; + + PAGED_CODE(); + + // + // Call the GPIO class extension's pre-device create interface. + // + + Status = GPIO_CLX_ProcessAddDevicePreDeviceCreate(Driver, + DeviceInit, + &FdoAttributes); + if (!NT_SUCCESS(Status)) { + goto EvtDeviceAddEnd; + } + + // + // Call the framework to create the device and attach it to the lower stack. + // + + Status = WdfDeviceCreate(&DeviceInit, &FdoAttributes, &Device); + if (!NT_SUCCESS(Status)) { + goto EvtDeviceAddEnd; + } + + // + // Call the GPIO class extension's post-device create interface. + // + + Status = GPIO_CLX_ProcessAddDevicePostDeviceCreate(Driver, Device); + if (!NT_SUCCESS(Status)) { + goto EvtDeviceAddEnd; + } + +EvtDeviceAddEnd: + return Status; +} + +_IRQL_requires_(PASSIVE_LEVEL) +VOID +SimGpiopUnmapControllerBase ( + _In_ PSIM_GPIO_CONTEXT GpioContext + ) + +/*++ + +Routine Description: + + This routine releases the memory mapping for the GPIO controller's + registers, if one has been established. + + N.B. This function is not marked pageable because this function is in + the device power down path. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + if ((GpioContext->ControllerBase != NULL) && + (GpioContext->ControllerBase != GlobalGpioRegisters)) { + MmUnmapIoSpace(GpioContext->ControllerBase, GpioContext->Length); + GpioContext->ControllerBase = NULL; + } +} + +// +// ---------------------------------------------------------- General intefaces +// + +_Must_inspect_result_ +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SimGpioPrepareController ( + _In_ WDFDEVICE Device, + _In_ PVOID Context, + _In_ WDFCMRESLIST ResourcesRaw, + _In_ WDFCMRESLIST ResourcesTranslated + ) + +/*++ + +Routine Description: + + This routine is called by the GPIO class extension to prepare the + simulated GPIO controller for use. + + N.B. This function is not marked pageable because this function is in + the device power up path. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + ResourcesRaw - Supplies a handle to a collection of framework resource + objects. This collection identifies the raw (bus-relative) hardware + resources that have been assigned to the device. + + ResourcesTranslated - Supplies a handle to a collection of framework + resource objects. This collection identifies the translated + (system-physical) hardware resources that have been assigned to the + device. The resources appear from the CPU's point of view. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + BANK_ID BankId; + PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor; + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + ULONG Index; + ULONG InterruptResourceCount; + ULONG MemoryResourceCount; + ULONG RequiredSize; + ULONG Offset; + ULONG ResourceCount; + NTSTATUS Status; + + UNREFERENCED_PARAMETER(Device); + UNREFERENCED_PARAMETER(ResourcesRaw); + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + RtlZeroMemory(GpioContext, sizeof(SIM_GPIO_CONTEXT)); + GpioContext->TotalPins = SIM_GPIO_TOTAL_PINS; + + // + // Walk through the resource list and map all the resources. Atleast one + // memory resource and one interrupt resource is expected. The resources + // are described in the ACPI namespace. + // + + InterruptResourceCount = 0; + MemoryResourceCount = 0; + ResourceCount = WdfCmResourceListGetCount(ResourcesTranslated); + Status = STATUS_SUCCESS; + for (Index = 0; Index < ResourceCount; Index += 1) { + Descriptor = WdfCmResourceListGetDescriptor(ResourcesTranslated, Index); + switch(Descriptor->Type) { + + // + // The memory resource supplies the physical register base for the GPIO + // controller. Map it virtually as non-cached. + // + + case CmResourceTypeMemory: + if (MemoryResourceCount == 0) { + RequiredSize = SIM_GPIO_TOTAL_BANKS * sizeof(SIM_GPIO_REGISTERS); + + NT_ASSERT(Descriptor->u.Memory.Length >= RequiredSize); + + if (Descriptor->u.Memory.Length < RequiredSize) { + Status = STATUS_UNSUCCESSFUL; + break; + } + + GpioContext->PhysicalBaseAddress = Descriptor->u.Memory.Start; + GpioContext->ControllerBase = + (PSIM_GPIO_REGISTERS)MmMapIoSpaceEx( + Descriptor->u.Memory.Start, + RequiredSize, + PAGE_NOCACHE | PAGE_READWRITE); + + // + // Fail initialization if mapping of the memory region failed. + // + + if (GpioContext->ControllerBase == NULL) { + Status = STATUS_UNSUCCESSFUL; + } + + GpioContext->Length = RequiredSize; + } + + MemoryResourceCount += 1; + break; + + // + // IO port resources are unexpected, fail initialization. + // + + case CmResourceTypePort: + Status = STATUS_UNSUCCESSFUL; + break; + + // + // Interrupt resource which supplies the GPIO controller interrupt + // (that connects to the GIC). + // + // N.B. Connecting of the interrupt is handled by the GPIO class + // extension. Only ensure that appropriate number of interrupts + // were described. + // + + case CmResourceTypeInterrupt: + InterruptResourceCount += 1; + break; + + // + // This could be device-private type added by the underlying bus + // driver. Do not filter or alter this resource information. + // + + default: + break; + } + + if (!NT_SUCCESS(Status)) { + goto PrepareControllerEnd; + } + } + + // + // NOTE: As SimGPIO is not a real hardware device, it doesn't really have + // any physical registers. For test purposes, fake the GPIO registers + // using a software defined global structure. This should NOT be done + // for a real GPIO controller + // + // BEGIN: SIMGPIO HACK. + // + + if (MemoryResourceCount == 0) { + GpioContext->ControllerBase = GlobalGpioRegisters; + MemoryResourceCount = 1; + } + + // + // END: SIMGPIO HACK. + // + + // + // Fail initialization if minimum number of interrupt + // and memory resources were not described correctly. + // + + if ((InterruptResourceCount < SIM_GPIO_TOTAL_BANKS) || + (MemoryResourceCount < 1)) { + Status = STATUS_UNSUCCESSFUL; + goto PrepareControllerEnd; + } + + // + // Initialize the base address of registers per bank. + // + + for (BankId = 0; BankId < SIM_GPIO_TOTAL_BANKS; BankId += 1) { + GpioBank = &GpioContext->Banks[BankId]; + Offset = BankId * sizeof(SIM_GPIO_REGISTERS); + GpioBank->Registers = Add2Ptr(GpioContext->ControllerBase, Offset); + GpioBank->Length = sizeof(SIM_GPIO_REGISTERS); + } + +PrepareControllerEnd: + if (!NT_SUCCESS(Status)) { + SimGpiopUnmapControllerBase(GpioContext); + } + + return Status; +} + +_Must_inspect_result_ +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SimGpioReleaseController ( + _In_ WDFDEVICE Device, + _In_ PVOID Context + ) + +/*++ + +Routine Description: + + This routine is called by the GPIO class extension to uninitialize the GPIO + controller. + + N.B. This function is not marked pageable because this function is in + the device power down path. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + UNREFERENCED_PARAMETER(Device); + UNREFERENCED_PARAMETER(Context); + + // + // Release the mappings established in the initialize callback. + // + // N.B. Disconnecting of the interrupt is handled by the GPIO class + // extension. + // + + SimGpiopUnmapControllerBase((PSIM_GPIO_CONTEXT)Context); + return STATUS_SUCCESS; +} + +_Must_inspect_result_ +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SimGpioQueryControllerBasicInformation ( + _In_ PVOID Context, + _Out_ PCLIENT_CONTROLLER_BASIC_INFORMATION ControllerInformation + ) + +/*++ + +Routine Description: + + This routine returns the GPIO controller's attributes to the class extension. + + N.B. This function is not marked pageable because this function is in + the device power up path. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + ControllerInformation - Supplies a pointer to a buffer that receives + controller's information. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + PSIM_GPIO_CONTEXT GpioContext; + + ControllerInformation->Version = GPIO_CONTROLLER_BASIC_INFORMATION_VERSION; + ControllerInformation->Size = sizeof(CLIENT_CONTROLLER_BASIC_INFORMATION); + + // + // Specify the number of pins on the SimGPIO controller. + // + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + ControllerInformation->TotalPins = GpioContext->TotalPins; + ControllerInformation->NumberOfPinsPerBank = SIM_GPIO_PINS_PER_BANK; + + // + // Indicate that the GPIO controller is memory-mapped and thus can be + // manipulated at DIRQL. + // + // N.B. If the GPIO controller is off-SOC behind some serial bus like + // I2C or SPI, then this field must be set to FALSE. + // + + ControllerInformation->Flags.MemoryMappedController = TRUE; + + // + // Indicate that status register must be cleared explicitly. + // + + ControllerInformation->Flags.ActiveInterruptsAutoClearOnRead = 0; + + // + // Indicate that the client driver would like to receive IO requests as a + // set of bitmasks as that maps directly to the register operations. + // + + ControllerInformation->Flags.FormatIoRequestsAsMasks = 1; + + // + // Indicate that the GPIO controller does not support controller-level + // D-state power management. + // + + ControllerInformation->Flags.DeviceIdlePowerMgmtSupported = FALSE; + + // + // Note if bank-level F-state power management is supported, then specify + // as such. Note the QuerySetControllerInformation() handler also needs to + // be implemented in this case. + // + +#ifdef ENABLE_F_STATE_POWER_MGMT + + ControllerInformation->Flags.BankIdlePowerMgmtSupported = TRUE; + +#else + + ControllerInformation->Flags.BankIdlePowerMgmtSupported = FALSE; + +#endif + + // + // Note the IdleTimeout parameter does not need to be initialized if + // D-state power management is not supported. + // + // ControllerInformation->IdleTimeout = IdleTimeoutDefaultValue; + // + + // + // Note if the GPIO controller does not support hardware debouncing and + // software-debouncing should be used instead, set the EmulateDebouncing + // flag. + // + // ControllerInformation->Flags.EmulateDebouncing = TRUE; + // + + // + // Indicate that the client driver prefers GPIO class extension ActiveBoth + // emulation. + // + + ControllerInformation->Flags.EmulateActiveBoth = TRUE; + + return STATUS_SUCCESS; +} + +_Must_inspect_result_ +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SimGpioQuerySetControllerInformation ( + _In_ PVOID Context, + _In_ PCLIENT_CONTROLLER_QUERY_SET_INFORMATION_INPUT InputBuffer, + _Out_opt_ PCLIENT_CONTROLLER_QUERY_SET_INFORMATION_OUTPUT OutputBuffer + ) + +/*++ + +Routine Description: + + This routine is the generic GPIO query/set handler. Currently it only + supports returning bank power information. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + InputBuffer - Supplies a pointer to a buffer that receives the parameters + for the query or set operation. + + OutputBuffer - Supplies a pointer to the GPIO class extension allocated + buffer to return the output values. Note on entry, the + OutputBuffer->Size indicates how big the output buffer is. On exit, the + OutputBuffer->Size indicates the filled-in size or required size. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + PPO_FX_COMPONENT_IDLE_STATE F1Parameters; + NTSTATUS Status; + + UNREFERENCED_PARAMETER(Context); + + if((InputBuffer == NULL) || (OutputBuffer == NULL)) { + Status = STATUS_NOT_SUPPORTED; + goto QuerySetControllerInformationEnd; + } + + if (InputBuffer->RequestType != QueryBankPowerInformation) { + Status = STATUS_NOT_SUPPORTED; + goto QuerySetControllerInformationEnd; + } + + // + // Set the version and size of the output buffer. + // + + OutputBuffer->Version = GPIO_BANK_POWER_INFORMATION_OUTPUT_VERSION; + OutputBuffer->Size = sizeof(CLIENT_CONTROLLER_QUERY_SET_INFORMATION_OUTPUT); + + // + // Mark the given bank (InputBuffer->BankPowerInformation.BankId) as + // supporting F1 state. Since all banks support it, the BankId is not + // checked. + // + + OutputBuffer->BankPowerInformation.F1StateSupported = TRUE; + + // + // Supply the attributes for the F1 power state. + // + + F1Parameters = &OutputBuffer->BankPowerInformation.F1IdleStateParameters; + F1Parameters->NominalPower = SIM_GPIO_F1_NOMINAL_POWER; + F1Parameters->ResidencyRequirement = + WDF_ABS_TIMEOUT_IN_SEC(SIM_GPIO_F1_RESIDENCY); + + F1Parameters->TransitionLatency = + WDF_ABS_TIMEOUT_IN_SEC(SIM_GPIO_F1_TRANSITION); + + Status = STATUS_SUCCESS; + +QuerySetControllerInformationEnd: + return Status; +} + + +_Must_inspect_result_ +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SimGpioStartController ( + _In_ PVOID Context, + _In_ BOOLEAN RestoreContext, + _In_ WDF_POWER_DEVICE_STATE PreviousPowerState + ) + +/*++ + +Routine Description: + + This routine starts the simulated GPIO controller. This routine is + responsible for configuring all the pins to their default modes. + + N.B. This function is not marked pageable because this function is in + the device power up path. It is called at PASSIVE_IRQL though. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + RestoreContext - Supplies a flag that indicates whether the client driver + should restore the GPIO controller state to a previously saved state + or not. + + PreviousPowerState - Supplies the device power state that the device was in + before this transition to D0. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + BANK_ID BankId; + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + ULONG PinValue; + GPIO_SAVE_RESTORE_BANK_HARDWARE_CONTEXT_PARAMETERS RestoreParameters; + PSIM_GPIO_REGISTERS SimGpioRegisters; + + UNREFERENCED_PARAMETER(PreviousPowerState); + + // + // Perform all the steps necessary to start the device. + // + + // + // If restore context is FALSE, then this is initial transition into D0 + // power state for this controller. In such case, disable any interrupts + // that may have been left enabled (e.g. perhaps by FW, previous D0 -> Dx + // transition etc.) Otherwise, such interrupts could trigger an interrupt + // storm if they were to assert without any driver being registered to + // handle such interrupts. + // + // If restore context is TRUE, then this is a transition into D0 power + // state from a lower power Dx state. In such case, restore the context + // that was present before the controller transitioned into the lower + // power state. + // + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + if (RestoreContext == FALSE) { + for (BankId = 0; BankId < SIM_GPIO_TOTAL_BANKS; BankId += 1) { + GpioBank = &GpioContext->Banks[BankId]; + SimGpioRegisters = GpioBank->Registers; + + // + // Disable all interrupts on this bank by clearing the enable + // register. + // + + PinValue = READ_REGISTER_ULONG(&SimGpioRegisters->EnableRegister); + if (PinValue > 0) { + PinValue = 0; + WRITE_REGISTER_ULONG(&SimGpioRegisters->EnableRegister, + PinValue); + } + } + + } else { + + // + // Restoring the controller state involves restoring the state of + // each SimGPIO bank. + // + + for (BankId = 0; BankId < SIM_GPIO_TOTAL_BANKS; BankId += 1) { + RestoreParameters.BankId = BankId; + RestoreParameters.State = PreviousPowerState; + SimGpioRestoreBankHardwareContext(Context, &RestoreParameters); + } + } + + return STATUS_SUCCESS; +} + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SimGpioStopController ( + _In_ PVOID Context, + _In_ BOOLEAN SaveContext, + _In_ WDF_POWER_DEVICE_STATE TargetState + ) + +/*++ + +Routine Description: + + This routine stops the GPIO controller. This routine is responsible for + resetting all the pins to their default modes. + + N.B. This function is not marked pageable because this function is in + the device power down path. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + SaveContext - Supplies a flag that indicates whether the client driver + should save the GPIO controller state or not. The state may need + to be restored when the controller is restarted. + + TargetState - Supplies the device power state which the device will be put + in once the callback is complete. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + + BANK_ID BankId; + PSIM_GPIO_CONTEXT GpioContext; + GPIO_SAVE_RESTORE_BANK_HARDWARE_CONTEXT_PARAMETERS SaveParameters; + + UNREFERENCED_PARAMETER(TargetState); + + // + // Perform all the steps necessary to stop the device. + // + + // + // If save context is FALSE, then this is a final transition into D3/off + // power state. Hence saving of context is not necessary. + // + // If save context is TRUE, then this is a transition into a lower power + // Dx state. In such case, save the context as it will need to be + // restored when the device is brought back to D0 (i.e. ON) power state. + // + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + if (SaveContext == TRUE) { + for (BankId = 0; BankId < SIM_GPIO_TOTAL_BANKS; BankId += 1) { + SaveParameters.BankId = BankId; + SaveParameters.State = TargetState; + SimGpioSaveBankHardwareContext(Context, &SaveParameters); + } + } + + return STATUS_SUCCESS; +} + +// +// --------------------------------------------------------- Interrupt Handlers +// + +_Must_inspect_result_ +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SimGpioEnableInterrupt ( + _In_ PVOID Context, + _In_ PGPIO_ENABLE_INTERRUPT_PARAMETERS EnableParameters + ) + +/*++ + +Routine Description: + + This routine configures the supplied pin for interrupt. + + N.B. This routine is called from within a regular thread context (i.e., + non-interrupt context) by the class extension. Thus the interrupt lock + needs to be explicitly acquired for memory-mapped GPIO controllers + prior to manipulating any device state that is also affected from a + routine called within the interrupt context. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + EnableParameters - Supplies a pointer to a structure containing enable + operation parameters. Fields are: + + BankId - Supplies the ID for the GPIO bank. + + PinNumber - Supplies the interrupt line that should be enabled. The pin + number is relative to the bank. + + Flags - Supplies flags controlling the enable operation. Currently + no flags are defined. + + InterruptMode - Supplies the trigger mode (edge or level) configured for + this interrupt when it was enabled. + + Polarity - Supplies the polarity (active low or active high) configured + for this interrupt when it was enabled. + Note: For edge-triggered interrupts, ActiveLow corresponds to the + falling edge; ActiveHigh corresponds to the rising edge. + + PullConfiguration - Supplies the pin pull-up/pull-down configuration. + + DebouceTimeout - Supplies the debounce timeout to be applied. The + field is in 100th of milli-seconds (i.e., 5.84ms will be supplied + as 584). Default value is zero, which implies, no debounce. + + VendorData - Supplies an optional pointer to a buffer containing the + vendor data supplied in the GPIO descriptor. This field will be + NULL if no vendor data was supplied. This buffer is read-only. + + VendorDataLength - Supplies the length of the vendor data buffer. + +Return Value: + + NTSTATUS code. + +Environment: + + Entry IRQL: PASSIVE_LEVEL. + + Synchronization: The GPIO class extension will synchronize this call + against other passive-level interrupt callbacks (e.g. enable/disable/ + unmask) and IO callbacks. + +--*/ + +{ + + BANK_ID BankId; + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + ULONG Index; + PIN_NUMBER PinNumber; + ULONG PinValue; + PIN_NUMBER ShiftBits; + PSIM_GPIO_REGISTERS SimGpioRegisters; + NTSTATUS Status; + + // + // If the polarity is not supported, then bail out. Note the interrupt + // polarity cannot be InterruptActiveBoth as this sample uses ActiveBoth + // emulation. + // + + if ((EnableParameters->Polarity != InterruptActiveHigh) && + (EnableParameters->Polarity != InterruptActiveLow)) { + + Status = STATUS_NOT_SUPPORTED; + goto EnableInterruptEnd; + } + + BankId = EnableParameters->BankId; + PinNumber = EnableParameters->PinNumber; + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[BankId]; + SimGpioRegisters = GpioBank->Registers; + Status = STATUS_SUCCESS; + + // + // The interrupt enable register/bitmap may be manipulated from within the + // interrupt context. Hence updates to it must be synchronized using the + // interrupt lock. + // + + GPIO_CLX_AcquireInterruptLock(Context, BankId); + + // + // Set the mode register. If the interrupt is Level then set the bit; + // otherwise, clear it (edge-triggered). + // + + PinValue = READ_REGISTER_ULONG(&SimGpioRegisters->ModeRegister); + if (EnableParameters->InterruptMode == LevelSensitive) { + PinValue |= (1 << PinNumber); + } else { + PinValue &= ~(1 << PinNumber); + } + + WRITE_REGISTER_ULONG(&SimGpioRegisters->ModeRegister, PinValue); + + // + // Set the polarity register. There are two bits for each pin. If the + // interrupt is ActiveHigh (or Rising-edge) then set it to 0x1. + // + + Index = PinNumber / 16; + ShiftBits = (PinNumber % 16) * 2; + PinValue = READ_REGISTER_ULONG(&SimGpioRegisters->PolarityRegister[Index]); + switch (EnableParameters->Polarity) { + + case InterruptActiveHigh: + PinValue &= ~(0x3 << ShiftBits); + PinValue |= (1 << ShiftBits); + break; + + case InterruptActiveLow: + PinValue &= ~(0x3 << ShiftBits); + break; + } + + WRITE_REGISTER_ULONG(&SimGpioRegisters->PolarityRegister[Index], PinValue); + + // + // NOTE: If the GPIO controller supports a separate set of mask registers, + // then any stale value must be cleared here. SimGPIO controller + // doesn't and hence this step is skipped here. + // + + // + // Clear the corresponding status bit first to ignore any stale value. + // + + PinValue = READ_REGISTER_ULONG(&SimGpioRegisters->StatusRegister); + PinValue &= ~(1 << PinNumber); + WRITE_REGISTER_ULONG(&SimGpioRegisters->StatusRegister, PinValue); + + // + // Enable the interrupt by setting the bit in the interrupt enable register. + // + + PinValue = READ_REGISTER_ULONG(&SimGpioRegisters->EnableRegister); + PinValue |= (1 << PinNumber); + WRITE_REGISTER_ULONG(&SimGpioRegisters->EnableRegister, PinValue); + + // + // Release the interrupt lock. + // + + GPIO_CLX_ReleaseInterruptLock(Context, BankId); + + Status = STATUS_SUCCESS; + +EnableInterruptEnd: + return Status; +} + +_Must_inspect_result_ +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SimGpioDisableInterrupt ( + _In_ PVOID Context, + _In_ PGPIO_DISABLE_INTERRUPT_PARAMETERS DisableParameters + ) + +/*++ + +Routine Description: + + This routine disables the supplied pin from interrupting. + + This routine is not marked PAGED as it may be called before/after + the boot device is in D0/D3 if boot device has GPIO dependencies. + + N.B. This routine is called from within a regular thread context (i.e., + non-interrupt context) by the class extension. Thus the interrupt lock + needs to be explicitly acquired prior to manipulating the device state. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + DisableParameters - Supplies a pointer to a structure supplying the + parameters for disabling the interrupt. Fields are: + + BankId - Supplies the ID for the GPIO bank. + + PinNumber - Supplies the interrupt line that should be disabled. The pin + number is relative to the bank. + + Flags - Supplies flags controlling the disable operation. Currently + no flags are defined. + +Return Value: + + NTSTATUS code (STATUS_SUCCESS always for memory-mapped GPIO controllers). + +Environment: + + Entry IRQL: PASSIVE_LEVEL. + + Synchronization: The GPIO class extension will synchronize this call + against other passive-level interrupt callbacks (e.g. enable/disable/ + unmask) and IO callbacks. + +--*/ + +{ + + BANK_ID BankId; + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + ULONG PinValue; + PSIM_GPIO_REGISTERS SimGpioRegisters; + + BankId = DisableParameters->BankId; + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[BankId]; + SimGpioRegisters = GpioBank->Registers; + + // + // The interrupt enable register may be manipulated from within the + // interrupt context. Hence updates to it must be synchronized using the + // interrupt lock. + // + + GPIO_CLX_AcquireInterruptLock(Context, BankId); + + // + // Disable the interrupt by clearing the bit in the interrupt enable + // register. + // + + PinValue = READ_REGISTER_ULONG(&SimGpioRegisters->EnableRegister); + PinValue &= ~(1 << DisableParameters->PinNumber); + WRITE_REGISTER_ULONG(&SimGpioRegisters->EnableRegister, PinValue); + + GPIO_CLX_ReleaseInterruptLock(Context, BankId); + + return STATUS_SUCCESS; +} + +_Must_inspect_result_ +_IRQL_requires_same_ +NTSTATUS +SimGpioMaskInterrupts ( + _In_ PVOID Context, + _In_ PGPIO_MASK_INTERRUPT_PARAMETERS MaskParameters + ) + +/*++ + +Routine Description: + + This routine invokes masks the supplied pin from interrupting. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + MaskParameters - Supplies a pointer to a structure containing mask + operation parameters. Fields are: + + BankId - Supplies the ID for the GPIO bank. + + PinMask - Supplies a bitmask of pins which should be masked. If a pin + should be masked, then the corresponding bit is set in the bitmask. + + FailedMask - Supplies a bitmask of pins that failed to be masked. If + a pin could not be masked, the bit should be set in this field. + + N.B. This should only be done if for non memory-mapped controllers. + Memory-mapped controllers are never expected to fail this + operation. + +Return Value: + + NTSTATUS code (STATUS_SUCCESS always for memory-mapped GPIO controllers). + +Environment: + + Entry IRQL: DIRQL if the GPIO controller is memory-mapped; PASSIVE_LEVEL + if the controller is behind some serial-bus. + + N.B. For memory-mapped controllers, this routine is called from within + the interrupt context with the interrupt lock acquired by the class + extension. Hence the lock is not re-acquired here. + + Synchronization: The GPIO class extension will synchronize this call + against other query/clear active and enabled interrupts. + Memory-mapped GPIO controllers: + Callbacks invoked at PASSIVE_LEVEL IRQL (e.g. interrupt + enable/disable/unmask or IO operations) may be active. Those + routines should acquire the interrupt lock prior to manipulating + any state accessed from within this routine. + + Serial-accessible GPIO controllers: + This call is synchronized with all other interrupt and IO callbacks. + +--*/ + +{ + + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + ULONG PinValue; + PSIM_GPIO_REGISTERS SimGpioRegisters; + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[MaskParameters->BankId]; + SimGpioRegisters = GpioBank->Registers; + + // + // Mask is essentially same as disable for SimGPIO controller. The + // difference between the routines is that mask callback is called at DIRQL + // and automatically synchronized with other DIRQL interrupts callbacks. + // + + PinValue = READ_REGISTER_ULONG(&SimGpioRegisters->EnableRegister); + PinValue &= ~(ULONG)(MaskParameters->PinMask); + WRITE_REGISTER_ULONG(&SimGpioRegisters->EnableRegister, PinValue); + + // + // Set the bitmask of pins that could not be successfully masked. + // Since this is a memory-mapped controller, the mask operation always + // succeeds. + // + + MaskParameters->FailedMask = 0x0; + return STATUS_SUCCESS; +} + +NTSTATUS +SimGpioUnmaskInterrupt ( + _In_ PVOID Context, + _In_ PGPIO_ENABLE_INTERRUPT_PARAMETERS UnmaskParameters + ) + +/*++ + +Routine Description: + + This routine invokes unmasks the supplied interrupt pin. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + UnmaskParameters - Supplies a pointer to a structure containing parameters + for unmasking the interrupt. Fields are: + + BankId - Supplies the ID for the GPIO bank. + + PinNumber - Supplies the interrupt line that should be unmasked. The pin + number is relative to the bank. + + InterruptMode - Supplies the trigger mode (edge or level) configured for + this interrupt when it was enabled. + + Polarity - Supplies the polarity (active low or active high) configured + for this interrupt when it was enabled. + Note: For edge-triggered interrupts, ActiveLow corresponds to the + falling edge; ActiveHigh corresponds to the rising edge. + + PullConfiguration - Supplies the pin pull-up/pull-down configuration. + + DebouceTimeout - Supplies the debounce timeout to be applied. The + field is in 100th of milli-seconds (i.e., 5.84ms will be supplied + as 584). Default value is zero, which implies, no debounce. + + VendorData - NULL. + + VendorDataLength - 0. + + N.B. The VendorData and VendorDataLength are not supplied for unmask + operation (i.e., both fields are zero). + +Return Value: + + NTSTATUS code (STATUS_SUCCESS always for memory-mapped GPIO controllers). + +Environment: + + Entry IRQL: DIRQL if the GPIO controller is memory-mapped; PASSIVE_LEVEL + if the controller is behind some serial-bus. + + N.B. For memory-mapped controllers, this routine is called from within + the interrupt context with the interrupt lock acquired by the class + extension. Hence the lock is not re-acquired here. + + Synchronization: The GPIO class extension will synchronize this call + against other query/clear active and enabled interrupts. + Memory-mapped GPIO controllers: + Callbacks invoked at PASSIVE_LEVEL IRQL (e.g. interrupt + enable/disable/unmask or IO operations) may be active. Those + routines should acquire the interrupt lock prior to manipulating + any state accessed from within this routine. + + Serial-accessible GPIO controllers: + This call is synchronized with all other interrupt and IO callbacks. + +--*/ + +{ + + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + ULONG PinValue; + PSIM_GPIO_REGISTERS SimGpioRegisters; + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[UnmaskParameters->BankId]; + SimGpioRegisters = GpioBank->Registers; + + // + // Unmask is same as enable on this GPIO controller. The difference between + // this routine and the enable routine is that unmask callback is called at + // DIRQL and automatically synchronized with other DIRQL-level callbacks. + // + + PinValue = READ_REGISTER_ULONG(&SimGpioRegisters->EnableRegister); + PinValue |= (1 << UnmaskParameters->PinNumber); + WRITE_REGISTER_ULONG(&SimGpioRegisters->EnableRegister, PinValue); + + return STATUS_SUCCESS; +} + +_Must_inspect_result_ +_IRQL_requires_same_ +NTSTATUS +SimGpioQueryActiveInterrupts ( + _In_ PVOID Context, + _In_ PGPIO_QUERY_ACTIVE_INTERRUPTS_PARAMETERS QueryActiveParameters + ) + +/*++ + +Routine Description: + + This routine returns the current set of active interrupts. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + QueryActiveParameters - Supplies a pointer to a structure containing query + parameters. Fields are: + + BankId - Supplies the ID for the GPIO bank. + + EnabledMask - Supplies a bitmask of pins enabled for interrupts + on the specified GPIO bank. + + ActiveMask - Supplies a bitmask that receives the active interrupt + mask. If a pin is interrupting and set in EnabledMask, then the + corresponding bit is set in the bitmask. + +Return Value: + + NTSTATUS code (STATUS_SUCCESS always for memory-mapped GPIO controllers). + +Environment: + + Entry IRQL: DIRQL if the GPIO controller is memory-mapped; PASSIVE_LEVEL + if the controller is behind some serial-bus. + + N.B. For memory-mapped controllers, this routine is called from within + the interrupt context with the interrupt lock acquired by the class + extension. + + Synchronization: The GPIO class extension will synchronize this call + against other query/clear active and enabled interrupts. + Memory-mapped GPIO controllers: + Callbacks invoked at PASSIVE_LEVEL IRQL (e.g. interrupt + enable/disable/unmask or IO operations) may be active. Those + routines should acquire the interrupt lock prior to manipulating + any state accessed from within this routine. + + Serial-accessible GPIO controllers: + This call is synchronized with all other interrupt and IO callbacks. + +--*/ + +{ + + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + ULONG PinValue; + PSIM_GPIO_REGISTERS SimGpioRegisters; + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[QueryActiveParameters->BankId]; + SimGpioRegisters = GpioBank->Registers; + + // + // NOTE: As SimGPIO is not a real hardware device, no interrupt will ever + // fire. Thus the status register value will never change. To pretend + // as if a real interrupt happened, it marks all currently enabled + // interrupts as asserting. Copy the enable interrupt value into + // the status register. + // + // This should NOT be done for a real GPIO controller. + // + + // + // BEGIN: SIMGPIO HACK. + // + + PinValue = READ_REGISTER_ULONG(&SimGpioRegisters->EnableRegister); + WRITE_REGISTER_ULONG(&SimGpioRegisters->StatusRegister, PinValue); + + // + // END: SIMGPIO HACK. + // + + // + // Return the current value of the interrupt status register into the + // ActiveMask parameter. + // + + PinValue = READ_REGISTER_ULONG(&SimGpioRegisters->StatusRegister); + QueryActiveParameters->ActiveMask = (ULONG64)PinValue; + return STATUS_SUCCESS; +} + +_Must_inspect_result_ +_IRQL_requires_same_ +NTSTATUS +SimGpioQueryEnabledInterrupts ( + _In_ PVOID Context, + _In_ PGPIO_QUERY_ENABLED_INTERRUPTS_PARAMETERS QueryEnabledParameters + ) + +/*++ + +Routine Description: + + This routine returns the current set of enabled interrupts. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + QueryEnabledParameters - Supplies a pointer to a structure containing query + parameters. Fields are: + + BankId - Supplies the ID for the GPIO bank. + + EnabledMask - Supplies a bitmask that receives the enabled interrupt + mask. If a pin is enabled, then the corresponding bit is set in the + mask. + +Return Value: + + NTSTATUS code (STATUS_SUCCESS always for memory-mapped GPIO controllers). + +Environment: + + Entry IRQL: DIRQL if the GPIO controller is memory-mapped; PASSIVE_LEVEL + if the controller is behind some serial-bus. + + N.B. For memory-mapped controllers, this routine is called with the + interrupt lock acquired by the class extension, but not always + from within the interrupt context. + + Synchronization: The GPIO class extension will synchronize this call + against other query/clear active and enabled interrupts. + Memory-mapped GPIO controllers: + Callbacks invoked at PASSIVE_LEVEL IRQL (e.g. interrupt + enable/disable/unmask or IO operations) may be active. Those + routines should acquire the interrupt lock prior to manipulating + any state accessed from within this routine. + + Serial-accessible GPIO controllers: + This call is synchronized with all other interrupt and IO callbacks. + +--*/ + +{ + + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + ULONG PinValue; + PSIM_GPIO_REGISTERS SimGpioRegisters; + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[QueryEnabledParameters->BankId]; + SimGpioRegisters = GpioBank->Registers; + + // + // Return the current value of the interrupt enable register into the + // EnabledMask parameter. It is strongly preferred that the true state of + // the hardware is returned, rather than a software-cached variable, since + // CLIENT_QueryEnabledInterrupts is used by the class extension to detect + // interrupt storms. + // + + PinValue = READ_REGISTER_ULONG(&SimGpioRegisters->EnableRegister); + QueryEnabledParameters->EnabledMask = (ULONG64)PinValue; + return STATUS_SUCCESS; +} + +_Must_inspect_result_ +_IRQL_requires_same_ +NTSTATUS +SimGpioClearActiveInterrupts ( + _In_ PVOID Context, + _In_ PGPIO_CLEAR_ACTIVE_INTERRUPTS_PARAMETERS ClearParameters + ) + +/*++ + +Routine Description: + + This routine clears the GPIO controller's active set of interrupts. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + ClearParameters - Supplies a pointer to a structure containing clear + operation parameters. Fields are: + + BankId - Supplies the ID for the GPIO bank. + + ClearActiveMask - Supplies a mask of pins which should be marked as + inactive. If a pin should be cleared, then the corresponding bit is + set in the mask. + + FailedMask - Supplies a bitmask of pins that failed to be cleared. If + a pin could not be cleared, the bit should be set in this field. + + N.B. This should only be done if for non memory-mapped controllers. + Memory-mapped controllers are never expected to fail this + operation. + +Return Value: + + NTSTATUS code (STATUS_SUCCESS always for memory-mapped GPIO controllers). + +Environment: + + Entry IRQL: DIRQL if the GPIO controller is memory-mapped; PASSIVE_LEVEL + if the controller is behind some serial-bus. + + N.B. For memory-mapped controllers, this routine is called from within + the interrupt context with the interrupt lock acquired by the class + extension. + + Synchronization: The GPIO class extension will synchronize this call + against other query/clear active and enabled interrupts. + Memory-mapped GPIO controllers: + Callbacks invoked at PASSIVE_LEVEL IRQL (e.g. interrupt + enable/disable/unmask or IO operations) may be active. Those + routines should acquire the interrupt lock prior to manipulating + any state accessed from within this routine. + + Serial-accessible GPIO controllers: + This call is synchronized with all other interrupt and IO callbacks. + +--*/ + +{ + + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + ULONG PinValue; + PSIM_GPIO_REGISTERS SimGpioRegisters; + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[ClearParameters->BankId]; + SimGpioRegisters = GpioBank->Registers; + + // + // Clear the bits that are set in the ClearActiveMask parameter. + // + + PinValue = READ_REGISTER_ULONG(&SimGpioRegisters->StatusRegister); + PinValue &= ~((ULONG)ClearParameters->ClearActiveMask); + WRITE_REGISTER_ULONG(&SimGpioRegisters->StatusRegister, PinValue); + + // + // Set the bitmask of pins that could not be successfully cleared. + // Since this is a memory-mapped controller, the clear operation always + // succeeds. + // + + ClearParameters->FailedClearMask = 0x0; + return STATUS_SUCCESS; +} + +NTSTATUS +SimGpioReconfigureInterrupt ( + _In_ PVOID Context, + _In_ PGPIO_RECONFIGURE_INTERRUPTS_PARAMETERS ReconfigureParameters + ) + +/*++ + +Routine Description: + + This routine reconfigures the interrupt in the specified mode. + + N.B. This routine is called with the interrupt lock acquired by the + class extension. Hence the lock is not re-acquired here. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + ReconfigureParameters - Supplies a pointer to a structure containing + parameters for reconfiguring the interrupt. Fields are: + + BankId - Supplies the ID for the GPIO bank. + + PinNumber - Supplies the interrupt line that should be reconfigured. + The pin number is relative to the bank. + + InterruptMode - Supplies the trigger mode (edge or level) for the new + configuration. + + Polarity - Supplies the polarity (active low or active high) for the + new configuration. + Note: For edge-triggered interrupts, ActiveLow corresponds to the + falling edge; ActiveHigh corresponds to the rising edge. + +Return Value: + + NTSTATUS code. + +Environment: + + Entry IRQL: DIRQL if the GPIO controller is memory-mapped; PASSIVE_LEVEL + if the controller is behind some serial-bus. + + N.B. For memory-mapped controllers, this routine is called from within + the interrupt context with the interrupt lock acquired by the class + extension. Hence the lock is not re-acquired here. + + Synchronization: The GPIO class extension will synchronize this call + against other query/clear active and enabled interrupts. + Memory-mapped GPIO controllers: + Callbacks invoked at PASSIVE_LEVEL IRQL (e.g. interrupt + enable/disable/unmask or IO operations) may be active. Those + routines should acquire the interrupt lock prior to manipulating + any state accessed from within this routine. + + Serial-accessible GPIO controllers: + This call is synchronized with all other interrupt and IO callbacks. + +--*/ + +{ + + BANK_ID BankId; + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + ULONG Index; + PIN_NUMBER PinNumber; + ULONG PinValue; + PIN_NUMBER ShiftBits; + PSIM_GPIO_REGISTERS SimGpioRegisters; + NTSTATUS Status; + + BankId = ReconfigureParameters->BankId; + PinNumber = ReconfigureParameters->PinNumber; + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[BankId]; + SimGpioRegisters = GpioBank->Registers; + Status = STATUS_SUCCESS; + + // + // Clear any stale status bits from the previous configuration. + // + + PinValue = READ_REGISTER_ULONG(&SimGpioRegisters->StatusRegister); + PinValue &= ~(1 << PinNumber); + WRITE_REGISTER_ULONG(&SimGpioRegisters->StatusRegister, PinValue); + + // + // Set the mode register. If the interrupt is Level then set the bit; + // otherwise, clear it (edge-triggered). + // + + PinValue = READ_REGISTER_ULONG(&SimGpioRegisters->ModeRegister); + if (ReconfigureParameters->InterruptMode == LevelSensitive) { + PinValue |= (1 << PinNumber); + } else { + PinValue &= ~(1 << PinNumber); + } + + WRITE_REGISTER_ULONG(&SimGpioRegisters->ModeRegister, PinValue); + + // + // Set the polarity register. There are two bits for each pin. If the + // interrupt is ActiveHigh (or Rising-edge) then set it to 0x1. + // + + Index = PinNumber / 16; + ShiftBits = (PinNumber % 16) * 2; + PinValue = READ_REGISTER_ULONG(&SimGpioRegisters->PolarityRegister[Index]); + switch (ReconfigureParameters->Polarity) { + + case InterruptActiveHigh: + PinValue &= ~(0x3 << ShiftBits); + PinValue |= (1 << ShiftBits); + break; + + case InterruptActiveLow: + PinValue &= ~(0x3 << ShiftBits); + break; + + default: + + NT_ASSERT(FALSE); + } + + WRITE_REGISTER_ULONG(&SimGpioRegisters->PolarityRegister[Index], PinValue); + return STATUS_SUCCESS; +} + +// +// --------------------------------------------------------------- I/O Handlers +// + +_Must_inspect_result_ +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SimGpioConnectIoPins ( + _In_ PVOID Context, + _In_ PGPIO_CONNECT_IO_PINS_PARAMETERS ConnectParameters + ) + +/*++ + +Routine Description: + + This routine invokes connects the specified pins for IO. The pins can + be read from if connected for input, or written to if connected for + output. + + N.B. This routine is called at PASSIVE_LEVEL but is not marked as + PAGED_CODE as it could be executed late in the hibernate or + early in resume sequence (or the deep-idle sequence). + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + ConnectParameters - Supplies a pointer to a structure supplying the + parameters for connecting the IO pins. Fields description: + + BankId - Supplies the ID for the GPIO bank. + + PinNumberTable - Supplies an array of pins to be connected for IO. The + pin numbers are 0-based and relative to the GPIO bank. + + PinCount - Supplies the number of pins in the pin number table. + + ConnectMode - Supplies the mode in which the pins should be configured + (viz. input or output). + + ConnectFlags - Supplies the flags controlling the IO setup. Currently + no flags are defined. + + PullConfiguration - Supplies the pin pull-up/pull-down configuration. + + DebouceTimeout - Supplies the debounce timeout to be applied. The + field is in 100th of milli-seconds (i.e., 5.84ms will be supplied + as 584). Default value is zero, which implies, no debounce. + + DriveStrength - Supplies the drive strength to be applied. The value + is in 100th of mA (i.e., 1.21mA will be supplied as 121mA). + + VendorData - Supplies an optional pointer to a buffer containing the + vendor data supplied in the GPIO descriptor. This field will be + NULL if no vendor data was supplied. This buffer is read-only. + + VendorDataLength - Supplies the length of the vendor data buffer. + + ConnectFlags - Supplies the flag to be used for connect operation. + Currently no flags are defined. + +Return Value: + + NT status code. + +Environment: + + Entry IRQL: PASSIVE_LEVEL. + + Synchronization: The GPIO class extension will synchronize this call + against other passive-level interrupt callbacks (e.g. enable/disable/ + unmask) and IO callbacks. + +--*/ + +{ + + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + ULONG Index; + PIN_NUMBER PinNumber; + PPIN_NUMBER PinNumberTable; + ULONG PinValue; + PSIM_GPIO_REGISTERS SimGpioRegisters; + NTSTATUS Status; + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[ConnectParameters->BankId]; + SimGpioRegisters = GpioBank->Registers; + Status = STATUS_SUCCESS; + + // + // Read the current direction register value. + // + + PinValue = READ_REGISTER_ULONG(&SimGpioRegisters->DirectionRegister); + + // + // Walk through all the supplied pins and connect them in the specified + // mode (input or output). + // + + PinNumberTable = ConnectParameters->PinNumberTable; + for (Index = 0; Index < ConnectParameters->PinCount; Index += 1) { + PinNumber = PinNumberTable[Index]; + + // + // If the pins are being connected for input, then set the bit. + // Otherwise clear the bit. + // + + if (ConnectParameters->ConnectMode == ConnectModeInput) { + PinValue |= (1 << PinNumber); + + } else if (ConnectParameters->ConnectMode == ConnectModeOutput) { + PinValue &= ~(1 << PinNumber); + } + } + + WRITE_REGISTER_ULONG(&SimGpioRegisters->DirectionRegister, PinValue); + return Status; +} + +_Must_inspect_result_ +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SimGpioDisconnectIoPins ( + _In_ PVOID Context, + _In_ PGPIO_DISCONNECT_IO_PINS_PARAMETERS DisconnectParameters + ) + +/*++ + +Routine Description: + + This routine invokes disconnects the specified IO pins. The pins are + put back in their original mode. + + N.B. This routine is called at PASSIVE_LEVEL but is not marked as + PAGED_CODE as it could be executed late in the hibernate or + early in resume sequence (or the deep-idle sequence). + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + DisconnectParameters - Supplies a pointer to a structure containing + disconnect operation parameters. Fields are: + + BankId - Supplies the ID for the GPIO bank. + + PinNumberTable - Supplies an array of pins to be disconnected. The pin + numbers are relative to the GPIO bank. + + PinCount - Supplies the number of pins in the pin number table. + + DisconnectMode - Supplies the mode in which the pins are currently + configured (viz. input or output). + + DisconnectFlags - Supplies the flags controlling the IO setup. Currently + no flags are defined. + +Return Value: + + NTSTATUS code (STATUS_SUCCESS always for memory-mapped GPIO controllers). + +Environment: + + Entry IRQL: PASSIVE_LEVEL. + + Synchronization: The GPIO class extension will synchronize this call + against other passive-level interrupt callbacks (e.g. enable/disable/ + unmask) and IO callbacks. + +--*/ + +{ + + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + ULONG Index; + PIN_NUMBER PinNumber; + PPIN_NUMBER PinNumberTable; + ULONG PinValue; + PSIM_GPIO_REGISTERS SimGpioRegisters; + + // + // If the pin configuration should be preserved post disconnect, then + // there is nothing left to do. + // + + if (DisconnectParameters->DisconnectFlags.PreserveConfiguration == 1) { + return STATUS_SUCCESS; + } + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[DisconnectParameters->BankId]; + SimGpioRegisters = GpioBank->Registers; + + // + // Read the current direction register value. + // + + PinValue = READ_REGISTER_ULONG(&SimGpioRegisters->DirectionRegister); + + // + // Walk through all the supplied pins and disconnect them. On SimGPIO + // controller, all pins are reset to the default mode (output). + // + + PinNumberTable = DisconnectParameters->PinNumberTable; + for (Index = 0; Index < DisconnectParameters->PinCount; Index += 1) { + PinNumber = PinNumberTable[Index]; + PinValue &= ~(1 << PinNumber); + } + + WRITE_REGISTER_ULONG(&SimGpioRegisters->DirectionRegister, PinValue); + + return STATUS_SUCCESS; +} + +_Must_inspect_result_ +NTSTATUS +SimGpioReadGpioPins ( + _In_ PVOID Context, + _In_ PGPIO_READ_PINS_MASK_PARAMETERS ReadParameters + ) + +/*++ + +Routine Description: + + This routine reads the current values for all the pins. + + As the FormatIoRequestsAsMasks bit was set inside + SimGpioQueryControllerInformation(), all this routine needs to do is read + the level register value and return to the GPIO class extension. It will + return the right set of bits to the caller. + + N.B. This routine is called at DIRQL for memory-mapped GPIOs and thus not + marked as PAGED. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + ReadParameters - Supplies a pointer to a structure containing read + operation parameters. Fields are: + + BankId - Supplies the ID for the GPIO bank. + + PinValues - Supplies a pointer to a variable that receives the current + pin values. + + Flags - Supplies the flag to be used for read operation. Currently + defined flags are: + + WriteConfiguredPins: If set, the read is being done on a set of + pin that were configured for write. In such cases, the + GPIO client driver is expected to read and return the + output register value. + +Return Value: + + NTSTATUS code (STATUS_SUCCESS always for memory-mapped GPIO controllers). + +Environment: + + Entry IRQL: DIRQL if the GPIO controller is memory-mapped; + PASSIVE_LEVEL if the controller is behind some serial-bus. + + Synchronization: The GPIO class extension will synchronize this call + against other passive-level interrupt callbacks (e.g. enable/disable) + and IO callbacks (connect/disconnect). + +--*/ + +{ + + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + ULONG PinValue; + PSIM_GPIO_REGISTERS SimGpioRegisters; + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[ReadParameters->BankId]; + SimGpioRegisters = GpioBank->Registers; + + // + // Read the current level register value. Note the GPIO class may invoke + // the read routine on write-configured pins. In such case the output + // register values should be read. + // + // N.B. In case of SimGPIO, the LevelRegister holds the value for input + // as well as output pins. Thus the same register is read in either + // case. + // + + if (ReadParameters->Flags.WriteConfiguredPins == FALSE) { + PinValue = READ_REGISTER_ULONG(&SimGpioRegisters->LevelRegister); + + } else { + PinValue = READ_REGISTER_ULONG(&SimGpioRegisters->LevelRegister); + } + + *ReadParameters->PinValues = PinValue; + return STATUS_SUCCESS; +} + +_Must_inspect_result_ +NTSTATUS +SimGpioWriteGpioPins ( + _In_ PVOID Context, + _In_ PGPIO_WRITE_PINS_MASK_PARAMETERS WriteParameters + ) + +/*++ + +Routine Description: + + This routine sets the current values for the specified pins. This call is + synchronized with the write and connect/disconnect IO calls. + + N.B. This routine is called at DIRQL for memory-mapped GPIOs and thus not + marked as PAGED. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + WriteParameters - Supplies a pointer to a structure containing write + operation parameters. Fields are: + + BankId - Supplies the ID for the GPIO bank. + + SetMask - Supplies a mask of pins which should be set (0x1). If a pin + should be set, then the corresponding bit is set in the mask. + All bits that are clear in the mask should be left intact. + + ClearMask - Supplies a mask of pins which should be cleared (0x0). If + a pin should be cleared, then the bit is set in the bitmask. All + bits that are clear in the mask should be left intact. + + Flags - Supplies the flag controlling the write operation. Currently + no flags are defined. + +Return Value: + + NTSTATUS code (STATUS_SUCCESS always for memory-mapped GPIO controllers). + +Environment: + + Entry IRQL: DIRQL if the GPIO controller is memory-mapped; + PASSIVE_LEVEL if the controller is behind some serial-bus. + + Synchronization: The GPIO class extension will synchronize this call + against other passive-level interrupt callbacks (e.g. enable/disable) + and IO callbacks (connect/disconnect). + +--*/ + +{ + + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + ULONG PinValue; + PSIM_GPIO_REGISTERS SimGpioRegisters; + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[WriteParameters->BankId]; + SimGpioRegisters = GpioBank->Registers; + + // + // Read the current level register value. + // + + PinValue = READ_REGISTER_ULONG(&SimGpioRegisters->LevelRegister); + + // + // Set the bits specified in the set mask and clear the ones specified + // in the clear mask. + // + + PinValue |= WriteParameters->SetMask; + PinValue &= ~WriteParameters->ClearMask; + + // + // Write the updated value to the register. + // + + WRITE_REGISTER_ULONG(&SimGpioRegisters->LevelRegister, PinValue); + + return STATUS_SUCCESS; +} + +// +// ------------------------------------------------------- Power mgmt handlers +// + +VOID +SimGpioSaveBankHardwareContext ( + _In_ PVOID Context, + _In_ PGPIO_SAVE_RESTORE_BANK_HARDWARE_CONTEXT_PARAMETERS SaveParameters + ) + +/*++ + +Routine Description: + + This routine saves the hardware context for the GPIO controller. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + SaveRestoreParameters - Supplies a pointer to a structure containing + parameters for the save operation: + + BankId - Supplies the ID for the GPIO bank. + + State - Target F-state the bank will be transitioned into. + + Flags - Supplies flags for the save operation: + CriticalTransition - TRUE if this is due to a critical transition. + +Return Value: + + None. + +--*/ + +{ + + PULONG DestinationAddress; + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + ULONG Index; + ULONG RegisterCount; + PSIM_GPIO_REGISTERS SimGpioRegisters; + PULONG SourceAddress; + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[SaveParameters->BankId]; + SimGpioRegisters = GpioBank->Registers; + + // + // Copy the contents of the registers into memory. + // + + SourceAddress = &SimGpioRegisters->ModeRegister; + DestinationAddress = &GpioBank->SavedContext.ModeRegister; + RegisterCount = sizeof(SIM_GPIO_REGISTERS) / sizeof(ULONG); + for (Index = 0; Index < RegisterCount; Index += 1) { + *DestinationAddress = READ_REGISTER_ULONG(SourceAddress); + SourceAddress += 1; + DestinationAddress += 1; + } + + return; +} + +VOID +SimGpioRestoreBankHardwareContext ( + _In_ PVOID Context, + _In_ PGPIO_SAVE_RESTORE_BANK_HARDWARE_CONTEXT_PARAMETERS RestoreParameters + ) + +/*++ + +Routine Description: + + This routine saves the hardware context for the GPIO controller. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + SaveRestoreParameters - Supplies a pointer to a structure containing + parameters for the restore operation: + + BankId - Supplies the ID for the GPIO bank. + + State - Target F-state the bank will be transitioned into. + + Flags - Supplies flags for the save operation: + CriticalTransition - TRUE if this is due to a critical transition. + +Return Value: + + None. + +--*/ + +{ + + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + ULONG PinValue; + PSIM_GPIO_REGISTERS SimGpioRegisters; + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[RestoreParameters->BankId]; + SimGpioRegisters = GpioBank->Registers; + + // + // Restore the level register. + // + + PinValue = GpioBank->SavedContext.LevelRegister; + WRITE_REGISTER_ULONG(&SimGpioRegisters->LevelRegister, PinValue); + + // + // Restore the mode, polarity and enable registers. + // + + PinValue = GpioBank->SavedContext.ModeRegister; + WRITE_REGISTER_ULONG(&SimGpioRegisters->ModeRegister, PinValue); + + PinValue = GpioBank->SavedContext.PolarityRegister[0]; + WRITE_REGISTER_ULONG(&SimGpioRegisters->PolarityRegister[0], PinValue); + + PinValue = GpioBank->SavedContext.PolarityRegister[1]; + WRITE_REGISTER_ULONG(&SimGpioRegisters->PolarityRegister[1], PinValue); + + PinValue = GpioBank->SavedContext.StatusRegister; + WRITE_REGISTER_ULONG(&SimGpioRegisters->StatusRegister, PinValue); + + // + // Restore the direction register. + // + + PinValue = GpioBank->SavedContext.DirectionRegister; + WRITE_REGISTER_ULONG(&SimGpioRegisters->DirectionRegister, PinValue); + + // + // Take care to restore the enable register only after restoring the + // mode and polarity registers. Otherwise, the interrupt line will get + // sampled when the enable register gets written to with the mode and + // polarity at that point in time (and could cause a spurious interrupt). + // + + PinValue = GpioBank->SavedContext.EnableRegister; + WRITE_REGISTER_ULONG(&SimGpioRegisters->EnableRegister, PinValue); + + return; +} + +__pragma(warning(default: 4127)) // conditional expression is a constant + + diff --git a/gpio/samples/simgpio/simgpio.inx b/gpio/samples/simgpio/simgpio.inx Binary files differnew file mode 100644 index 00000000..0a00ec79 --- /dev/null +++ b/gpio/samples/simgpio/simgpio.inx diff --git a/gpio/samples/simgpio/simgpio.rc b/gpio/samples/simgpio/simgpio.rc new file mode 100644 index 00000000..1573b89c --- /dev/null +++ b/gpio/samples/simgpio/simgpio.rc @@ -0,0 +1,12 @@ +#include <windows.h> + +#include <ntverp.h> + +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "Simulated GPIO Client" +#define VER_INTERNALNAME_STR "simgpio.sys" +#define VER_ORIGINALFILENAME_STR "simgpio.sys" + +#include "common.ver" + diff --git a/gpio/samples/simgpio/simgpio.vcxproj b/gpio/samples/simgpio/simgpio.vcxproj new file mode 100644 index 00000000..f9e47196 --- /dev/null +++ b/gpio/samples/simgpio/simgpio.vcxproj @@ -0,0 +1,161 @@ +<?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|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{BCCD1049-70D2-4C07-BE8C-E9B609BD1714}</ProjectGuid> + <RootNamespace>$(MSBuildProjectName)</RootNamespace> + <KMDF_VERSION_MAJOR>1</KMDF_VERSION_MAJOR> + <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> + <Platform Condition="'$(Platform)' == ''">Win32</Platform> + <SampleGuid>{1C686E11-FC59-4029-AFA6-6083113CBA26}</SampleGuid> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType>KMDF</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType>KMDF</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType>KMDF</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType>KMDF</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <PropertyGroup> + <OutDir>$(IntDir)</OutDir> + </PropertyGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ItemGroup Label="WrappedTaskItems"> + <Inf Include=".\simgpio.inx"> + <DateStamp>*</DateStamp> + <SpecifyDriverVerDirectiveDate>true</SpecifyDriverVerDirectiveDate> + <Architecture>$(InfArch)</Architecture> + <SpecifyArchitecture>true</SpecifyArchitecture> + <CopyOutput>.\$(IntDir)\simgpio.inf</CopyOutput> + </Inf> + </ItemGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetName>simgpio</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetName>simgpio</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetName>simgpio</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetName>simgpio</TargetName> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\ksguid.lib;$(DDK_LIB_PATH)\ntstrsafe.lib;$(DDK_LIB_PATH)\msgpioclxstub.lib</AdditionalDependencies> + </Link> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\ksguid.lib;$(DDK_LIB_PATH)\ntstrsafe.lib;$(DDK_LIB_PATH)\msgpioclxstub.lib</AdditionalDependencies> + </Link> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\ksguid.lib;$(DDK_LIB_PATH)\ntstrsafe.lib;$(DDK_LIB_PATH)\msgpioclxstub.lib</AdditionalDependencies> + </Link> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\ksguid.lib;$(DDK_LIB_PATH)\ntstrsafe.lib;$(DDK_LIB_PATH)\msgpioclxstub.lib</AdditionalDependencies> + </Link> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="simgpio.c" /> + <ResourceCompile Include="simgpio.rc" /> + </ItemGroup> + <ItemGroup> + <Inf Exclude="@(Inf)" Include="*.inf" /> + <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" /> + <FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" /> + </ItemGroup> + <ItemGroup> + <None Exclude="@(None)" Include="*.txt;*.htm;*.html" /> + <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" /> + <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" /> + </ItemGroup> + <ItemGroup> + <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> +</Project>
\ No newline at end of file diff --git a/gpio/samples/simgpio/simgpio.vcxproj.Filters b/gpio/samples/simgpio/simgpio.vcxproj.Filters new file mode 100644 index 00000000..21ab66e9 --- /dev/null +++ b/gpio/samples/simgpio/simgpio.vcxproj.Filters @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> + <UniqueIdentifier>{BFF3D918-3BBB-4AF1-B1A2-C2A4FD8E82E8}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files"> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + <UniqueIdentifier>{3BC12FA6-2491-4DA4-86C0-8F10AA4DB124}</UniqueIdentifier> + </Filter> + <Filter Include="Resource Files"> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> + <UniqueIdentifier>{004102F6-6CB6-4E30-A1D8-B663D4B8CE08}</UniqueIdentifier> + </Filter> + <Filter Include="Driver Files"> + <Extensions>inf;inv;inx;mof;mc;</Extensions> + <UniqueIdentifier>{38B34A48-263B-481A-81DE-81BF490980CD}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <FilesToPackage Include=".\Debug\\simgpio.inf"> + <Filter>Driver Files</Filter> + </FilesToPackage> + <Inf Include=".\simgpio.inx"> + <Filter>Driver Files</Filter> + </Inf> + </ItemGroup> + <ItemGroup> + <ClCompile Include="simgpio.c"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="simgpio.rc"> + <Filter>Resource Files</Filter> + </ResourceCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/gpio/samples/simgpio_i2c/simgpio_i2c.c b/gpio/samples/simgpio_i2c/simgpio_i2c.c new file mode 100644 index 00000000..9cdaee69 --- /dev/null +++ b/gpio/samples/simgpio_i2c/simgpio_i2c.c @@ -0,0 +1,2692 @@ + +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + simgpio_i2c.c + +Abstract: + + This sample implements a GPIO client driver for simulated GPIO (SimGpio) + controller. + + Note: DIRQL in the comments below refers to device IRQL, which is any + IRQL > DISPATCH_LEVEL (and less than some IRQL reserved for OS use). + + +Environment: + + Kernel mode + +--*/ + +// +// The SimGpio_I2C controller has 2 GPIO banks, with banks consisting of +// 8 pins each. Any pin can be configured for interrupt, input or output. +// Each bank has the the following registers. There are two sets of registers: +// the first set for pins 0...7 and second for pins 8...15. +// +// Register | Address | Offset | Size +// ---------------------------------------------------------------- +// +// (Register set for pins 0...7) +// +// ModeRegister1 | 0x0 | 0x0 | 1 byte +// PolarityRegister1 | 0x1 | 0x1 | 1 byte +// EnableRegister1 | 0x2 | 0x2 | 1 byte +// StatusRegister1 | 0x3 | 0x3 | 1 byte +// DirectionRegister1 | 0x4 | 0x4 | 1 byte +// LevelRegister1 | 0x5 | 0x5 | 1 byte +// +// (Register set for pins 8...15) +// +// ModeRegister2 | 0x6 | 0x6 | 1 byte +// PolarityRegister2 | 0x7 | 0x7 | 1 byte +// EnableRegister2 | 0x8 | 0x8 | 1 byte +// StatusRegister2 | 0x9 | 0x9 | 1 byte +// DirectionRegister2 | 0xA | 0xA | 1 byte +// LevelRegister2 | 0xB | 0xB | 1 byte +// +// Mode Register - Interrupt mode (Level = 0x1 or Edge = 0x0) for pins +// enabled for interrupts. +// +// Polarity Register - ActiveLow (0x0) or ActiveHigh (0x1) for Level-triggered +// interrupts; Falling-edge (0x0), Rising-edge (0x1). Note this controller +// does not support ActiveBoth. +// +// Enable Register - A mask of which pins are enabled for interrupt. A bit +// is set if the pin is enabled for interrupt. +// +// Status Register - A register containing which pins are actively interrupting. +// If any of the bit is set, then the GPIO controller's interrupt fires. +// +// Direction Register - A register which indicates whether a given pin is +// configured for input (0x1) or output (0x0). +// +// LevelRegister - A bitmask which indicates holds the value for a given pin +// (i.e. whether the pin is high (0x1) or low (0x0)). +// + +// +// ------------------------------------------------------------------- Includes +// + +#include <ntddk.h> +#include <wdf.h> +#include <gpioclx.h> +#include "simgpio_i2c.h" +#include "trace.h" +#include "simgpio_i2c.tmh" + +// +// -------------------------------------------------------------------- Defines +// + +// +// Determine whether the given pin is reserved or not. Currently no pins are +// reserved on the simulated GPIO controller. +// + +__pragma(warning(disable: 4127)) // conditional expression is a constant + +// +// ----------------------------------------------------------------- Prototypes +// + +DRIVER_INITIALIZE DriverEntry; + +EVT_WDF_DRIVER_DEVICE_ADD SimGpioEvtDeviceAdd; +EVT_WDF_DRIVER_UNLOAD SimGpioEvtDriverUnload; + +// +// General interfaces. +// + +GPIO_CLIENT_PREPARE_CONTROLLER SimGpioPrepareController; +GPIO_CLIENT_RELEASE_CONTROLLER SimGpioReleaseController; +GPIO_CLIENT_QUERY_CONTROLLER_BASIC_INFORMATION + SimGpioQueryControllerBasicInformation; + +GPIO_CLIENT_START_CONTROLLER SimGpioStartController; +GPIO_CLIENT_STOP_CONTROLLER SimGpioStopController; + +// +// Interrupt enable, disable, mask and unmask handlers. +// + +GPIO_CLIENT_ENABLE_INTERRUPT SimGpioEnableInterrupt; +GPIO_CLIENT_DISABLE_INTERRUPT SimGpioDisableInterrupt; +GPIO_CLIENT_MASK_INTERRUPTS SimGpioMaskInterrupts; +GPIO_CLIENT_UNMASK_INTERRUPT SimGpioUnmaskInterrupt; +GPIO_CLIENT_RECONFIGURE_INTERRUPT SimGpioReconfigureInterrupt; + +// +// Handlers to query active/enabled interrupts and clear active interrupts. +// + +GPIO_CLIENT_QUERY_ACTIVE_INTERRUPTS SimGpioQueryActiveInterrupts; +GPIO_CLIENT_CLEAR_ACTIVE_INTERRUPTS SimGpioClearActiveInterrupts; +GPIO_CLIENT_QUERY_ENABLED_INTERRUPTS SimGpioQueryEnabledInterrupts; + +// +// Handlers for GPIO I/O operations. +// + +GPIO_CLIENT_CONNECT_IO_PINS SimGpioConnectIoPins; +GPIO_CLIENT_DISCONNECT_IO_PINS SimGpioDisconnectIoPins; +GPIO_CLIENT_READ_PINS_MASK SimGpioReadGpioPins; +GPIO_CLIENT_WRITE_PINS_MASK SimGpioWriteGpioPins; + +// +// -------------------------------------------------------------------- Pragmas +// + +#pragma alloc_text(INIT, DriverEntry) +#pragma alloc_text(PAGE, SimGpioEvtDeviceAdd) +#pragma alloc_text(PAGE, SimGpioEvtDriverUnload) + +// +// ------------------------------------------------------------------ Functions +// + +NTSTATUS +DriverEntry ( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + This routine is the driver initialization entry point. + +Arguments: + + DriverObject - Pointer to the driver object created by the I/O manager. + + RegistryPath - Pointer to the driver specific registry key. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + WDFDRIVER Driver; + WDF_DRIVER_CONFIG DriverConfig; + GPIO_CLIENT_REGISTRATION_PACKET RegistrationPacket; + NTSTATUS Status; + + WPP_INIT_TRACING(DriverObject, RegistryPath); + + // + // Initialize the driver configuration structure. + // + + WDF_DRIVER_CONFIG_INIT(&DriverConfig, SimGpioEvtDeviceAdd); + DriverConfig.EvtDriverUnload = SimGpioEvtDriverUnload; + + // + // Create a framework driver object to represent our driver. + // + + Status = WdfDriverCreate(DriverObject, + RegistryPath, + WDF_NO_OBJECT_ATTRIBUTES, + &DriverConfig, + &Driver); + + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: WdfDriverCreate failed status:%#x\n", + __FUNCTION__, + Status); + + WPP_CLEANUP(DriverObject); + goto DriverEntryEnd; + } + + // + // Initialize the client driver registration packet. + // + + RtlZeroMemory(&RegistrationPacket, sizeof(GPIO_CLIENT_REGISTRATION_PACKET)); + RegistrationPacket.Version = GPIO_CLIENT_VERSION; + RegistrationPacket.Size = sizeof(GPIO_CLIENT_REGISTRATION_PACKET); + + // + // Initialize the device context size. + // + + RegistrationPacket.ControllerContextSize = sizeof(SIM_GPIO_CONTEXT); + + // + // General interfaces. + // + + RegistrationPacket.CLIENT_PrepareController = SimGpioPrepareController; + RegistrationPacket.CLIENT_QueryControllerBasicInformation = + SimGpioQueryControllerBasicInformation; + + RegistrationPacket.CLIENT_StartController = SimGpioStartController; + RegistrationPacket.CLIENT_StopController = SimGpioStopController; + RegistrationPacket.CLIENT_ReleaseController = SimGpioReleaseController; + + // + // Interrupt enable and disable handlers. + // + + RegistrationPacket.CLIENT_DisableInterrupt = SimGpioDisableInterrupt; + RegistrationPacket.CLIENT_EnableInterrupt = SimGpioEnableInterrupt; + + // + // Interrupt mask, unmask and reconfigure interrupt handlers. + // + + RegistrationPacket.CLIENT_MaskInterrupts = SimGpioMaskInterrupts; + RegistrationPacket.CLIENT_UnmaskInterrupt = SimGpioUnmaskInterrupt; + RegistrationPacket.CLIENT_ReconfigureInterrupt = SimGpioReconfigureInterrupt; + + // + // Handlers to query active/enabled interrupts and clear active interrupts. + // + + RegistrationPacket.CLIENT_ClearActiveInterrupts = SimGpioClearActiveInterrupts; + RegistrationPacket.CLIENT_QueryActiveInterrupts = SimGpioQueryActiveInterrupts; + RegistrationPacket.CLIENT_QueryEnabledInterrupts = SimGpioQueryEnabledInterrupts; + + // + // Handlers for GPIO I/O operations. + // + + RegistrationPacket.CLIENT_ConnectIoPins = SimGpioConnectIoPins; + RegistrationPacket.CLIENT_DisconnectIoPins = SimGpioDisconnectIoPins; + RegistrationPacket.CLIENT_ReadGpioPinsUsingMask = SimGpioReadGpioPins; + RegistrationPacket.CLIENT_WriteGpioPinsUsingMask = SimGpioWriteGpioPins; + + // + // Register the SimGPIO client driver with the GPIO class extension. + // + + Status = GPIO_CLX_RegisterClient(Driver, &RegistrationPacket, RegistryPath); + +DriverEntryEnd: + return Status; +} + +NTSTATUS +SimGpioEvtDeviceAdd ( + _In_ WDFDRIVER Driver, + _Inout_ PWDFDEVICE_INIT DeviceInit + ) + +/*++ + +Routine Description: + + This routine is the AddDevice entry point for the client driver. This + routine is called by the framework in response to AddDevice call from the + PnP manager. It will create and initialize the device object to represent + a new instance of the simulated GPIO controller. + +Arguments: + + Driver - Supplies a handle to the driver object created in DriverEntry. + + DeviceInit - Supplies a pointer to a framework-allocated WDFDEVICE_INIT + structure. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + WDFDEVICE Device; + WDF_OBJECT_ATTRIBUTES FdoAttributes; + NTSTATUS Status; + + PAGED_CODE(); + + // + // Call the GPIO class extension's pre-device create interface. + // + + Status = GPIO_CLX_ProcessAddDevicePreDeviceCreate(Driver, + DeviceInit, + &FdoAttributes); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: GPIO_CLX_ProcessAddDevicePreDeviceCreate() failed! " + "Status = %#x\n", + __FUNCTION__, + Status); + + goto EvtDeviceAddEnd; + } + + // + // Call the framework to create the device and attach it to the lower stack. + // + + Status = WdfDeviceCreate(&DeviceInit, &FdoAttributes, &Device); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: WdfDeviceCreate() failed! Status = %#x\n", + __FUNCTION__, + Status); + + goto EvtDeviceAddEnd; + } + + // + // Call the GPIO class extension's post-device create interface. + // + + Status = GPIO_CLX_ProcessAddDevicePostDeviceCreate(Driver, Device); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: GPIO_CLX_ProcessAddDevicePostDeviceCreate() failed! " + "Status = %#x\n", + __FUNCTION__, + Status); + + goto EvtDeviceAddEnd; + } + +EvtDeviceAddEnd: + return Status; +} + +// +// ---------------------------------------------------------- General intefaces +// + +_Must_inspect_result_ +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SimGpioPrepareController ( + _In_ WDFDEVICE Device, + _In_ PVOID Context, + _In_ WDFCMRESLIST ResourcesRaw, + _In_ WDFCMRESLIST ResourcesTranslated + ) + +/*++ + +Routine Description: + + This routine is called by the GPIO class extension to prepare the + simulated GPIO controller for use. + + N.B. This function is not marked pageable because this function is in + the device power up path. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + ResourcesRaw - Supplies a handle to a collection of framework resource + objects. This collection identifies the raw (bus-relative) hardware + resources that have been assigned to the device. + + ResourcesTranslated - Supplies a handle to a collection of framework + resource objects. This collection identifies the translated + (system-physical) hardware resources that have been assigned to the + device. The resources appear from the CPU's point of view. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + UCHAR ConnectionClass; + UCHAR ConnectionType; + PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor; + PSIM_GPIO_CONTEXT GpioContext; + ULONG Index; + BOOLEAN I2ResourceFound; + ULONG InterruptResourceCount; + ULONG ResourceCount; + NTSTATUS Status; + + UNREFERENCED_PARAMETER(Device); + UNREFERENCED_PARAMETER(ResourcesRaw); + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + RtlZeroMemory(GpioContext, sizeof(SIM_GPIO_CONTEXT)); + GpioContext->TotalPins = SIM_GPIO_TOTAL_PINS; + + // + // Walk through the resource list and map all the resources. Atleast one + // memory resource and one interrupt resource is expected. The resources + // are described in the ACPI namespace. + // + + I2ResourceFound = FALSE; + InterruptResourceCount = 0; + ResourceCount = WdfCmResourceListGetCount(ResourcesTranslated); + Status = STATUS_SUCCESS; + for (Index = 0; Index < ResourceCount; Index += 1) { + Descriptor = WdfCmResourceListGetDescriptor(ResourcesTranslated, Index); + switch(Descriptor->Type) { + + // + // Look for a connection resource that supplies the connection ID + // required to open a handle to the I2C controller. + // + + case CmResourceTypeConnection: + ConnectionClass = Descriptor->u.Connection.Class; + ConnectionType = Descriptor->u.Connection.Type; + if ((ConnectionClass == CM_RESOURCE_CONNECTION_CLASS_SERIAL) && + (ConnectionType == CM_RESOURCE_CONNECTION_TYPE_SERIAL_I2C) && + (I2ResourceFound == FALSE)) { + + GpioContext->SpbConnectionId.LowPart = + Descriptor->u.Connection.IdLowPart; + + GpioContext->SpbConnectionId.HighPart = + Descriptor->u.Connection.IdHighPart; + + I2ResourceFound = TRUE; + } + + break; + + // + // Interrupt resource which supplies the GPIO controller interrupt + // (that connects to the GIC). + // + // N.B. Connecting of the interrupt is handled by the GPIO class + // extension. Only ensure that appropriate number of interrupts + // were described. + // + + case CmResourceTypeInterrupt: + InterruptResourceCount += 1; + break; + + // + // This could be device-private type added by the underlying bus + // driver. Do not filter or alter this resource information. + // + + default: + break; + } + + if (!NT_SUCCESS(Status)) { + goto PrepareControllerEnd; + } + } + + // + // Fail initialization if minimum number of interrupt and connection + // resources were not described correctly. + // + + if ((InterruptResourceCount != 1) || (I2ResourceFound == FALSE)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: Resource do not match expected values! " + "Interrupt = %d, I2C = %d\n", + __FUNCTION__, + InterruptResourceCount, + I2ResourceFound); + + Status = STATUS_UNSUCCESSFUL; + goto PrepareControllerEnd; + } + + GpioContext->TotalPins = SIM_GPIO_TOTAL_PINS; + GpioContext->TotalBanks = SIM_GPIO_TOTAL_BANKS; + GpioContext->Device = Device; + for (Index = 0; Index < GpioContext->TotalBanks; Index += 1) { + GpioContext->Banks[Index].GpioContext = GpioContext; + GpioContext->Banks[Index].AddressBase = + (USHORT)(Index * MaximumSimGpioAddress); + } + + // + // For SimGpio_i2c, the connection to the I2C controller needs to be + // established. + // + + Status = SimGpioSetupSpbConnection(GpioContext); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSetupSpbConnection() failed! Status = %#x\n", + __FUNCTION__, + Status); + + goto PrepareControllerEnd; + } + +PrepareControllerEnd: + return Status; +} + +_Must_inspect_result_ +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SimGpioReleaseController ( + _In_ WDFDEVICE Device, + _In_ PVOID Context + ) + +/*++ + +Routine Description: + + This routine is called by the GPIO class extension to uninitialize the GPIO + controller. + + N.B. This function is not marked pageable because this function is in + the device power down path. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + UNREFERENCED_PARAMETER(Device); + + // + // Release the mappings established in the initialize callback. + // + // N.B. Disconnecting of the interrupt is handled by the GPIO class + // extension. + // + + SimGpioDestroySpbConnection(Context); + + // + // Stop WPP Tracing. + // + // TODO: DO THIS in the cleanup callback! + // + + WPP_CLEANUP(NULL); + + return STATUS_SUCCESS; +} + +_Must_inspect_result_ +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SimGpioQueryControllerBasicInformation ( + _In_ PVOID Context, + _Out_ PCLIENT_CONTROLLER_BASIC_INFORMATION ControllerInformation + ) + +/*++ + +Routine Description: + + This routine returns the GPIO controller's attributes to the class extension. + + N.B. This function is not marked pageable because this function is in + the device power up path. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + ControllerInformation - Supplies a pointer to a buffer that receives + controller's information. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + PSIM_GPIO_CONTEXT GpioContext; + + ControllerInformation->Version = GPIO_CONTROLLER_BASIC_INFORMATION_VERSION; + ControllerInformation->Size = sizeof(CLIENT_CONTROLLER_BASIC_INFORMATION); + + // + // Specify the number of pins on the SimGPIO controller. + // + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + ControllerInformation->TotalPins = GpioContext->TotalPins; + ControllerInformation->NumberOfPinsPerBank = SIM_GPIO_PINS_PER_BANK; + + // + // Indicate that the GPIO controller is off-SoC (i.e. behind some I2C bus) + // and thus can only be serviced at PASSIVE_LEVEL IRQL. + // + + ControllerInformation->Flags.MemoryMappedController = FALSE; + + // + // Indicate that status register must be cleared explicitly. + // + + ControllerInformation->Flags.ActiveInterruptsAutoClearOnRead = FALSE; + + // + // Indicate that the client driver would like to receive IO requests as a + // set of bitmasks as that maps directly to the register operations. + // + + ControllerInformation->Flags.FormatIoRequestsAsMasks = TRUE; + + // + // Indicate that the GPIO controller does not support controller-level + // D-state power management or F-state power management. + // + // N.B. F-state management is only supported for on-SoC GPIO controllers. + // + + ControllerInformation->Flags.DeviceIdlePowerMgmtSupported = FALSE; + ControllerInformation->Flags.BankIdlePowerMgmtSupported = FALSE; + + // + // Note the IdleTimeout parameter does not need to be initialized if + // D-state power management is not supported. + // + // ControllerInformation->IdleTimeout = IdleTimeoutDefaultValue; + // + + // + // Indicate that the client driver prefers GPIO class extension ActiveBoth + // emulation. + // + + ControllerInformation->Flags.EmulateActiveBoth = TRUE; + + return STATUS_SUCCESS; +} + +_Must_inspect_result_ +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SimGpioStartController ( + _In_ PVOID Context, + _In_ BOOLEAN RestoreContext, + _In_ WDF_POWER_DEVICE_STATE PreviousPowerState + ) + +/*++ + +Routine Description: + + This routine starts the simulated GPIO controller. This routine is + responsible for configuring all the pins to their default modes. + + N.B. This function is not marked pageable because this function is in + the device power up path. It is called at PASSIVE_IRQL though. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + RestoreContext - Supplies a flag that indicates whether the client driver + should restore the GPIO controller state to a previously saved state + or not. + + PreviousPowerState - Supplies the device power state that the device was in + before this transition to D0. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + BANK_ID BankId; + UCHAR EnableValue; + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + NTSTATUS Status; + + UNREFERENCED_PARAMETER(PreviousPowerState); + + // + // Perform all the steps necessary to start the device. + // + + // + // If restore context is FALSE, then this is initial transition into D0 + // power state for this controller. In such case, disable any interrupts + // that may have been left enabled (e.g. perhaps by FW, previous D0 -> Dx + // transition etc.) Otherwise, such interrupts could trigger an interrupt + // storm if they were to assert without any driver being registered to + // handle such interrupts. + // + // If restore context is TRUE, then this is a transition into D0 power + // state from a lower power Dx state. In such case, restore the context + // that was present before the controller transitioned into the lower + // power state. + // + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + Status = STATUS_SUCCESS; + if (RestoreContext == FALSE) { + for (BankId = 0; BankId < GpioContext->TotalBanks; BankId += 1) { + GpioBank = &GpioContext->Banks[BankId]; + + // + // Read the current values of the interrupt enable register. + // + + EnableValue = 0; + Status = SimGpioSpbReadByte(GpioBank, EnableRegister, &EnableValue); + if (!NT_SUCCESS(Status)) { + TraceEvents(TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbReadByte(EnableRegister) failed! " + "Status = %#x\n", + __FUNCTION__, + Status); + + goto StartControllerEnd; + } + + // + // If no interrupts are enabled, then skip to the next bank. + // + + if (EnableValue == 0) { + continue; + } + + // + // Disable all interrupts on this bank by clearing the enable + // register. + // + + EnableValue = 0; + Status = SimGpioSpbWriteByte(GpioBank, EnableRegister, EnableValue); + if (!NT_SUCCESS(Status)) { + TraceEvents(TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbWriteByte(EnableRegister) failed! " + "Status = %#x\n", + __FUNCTION__, + Status); + + goto StartControllerEnd; + } + } + + } else { + + // + // Restoring the controller state involves restoring the state of + // each SimGPIO bank. + // + + for (BankId = 0; BankId < GpioContext->TotalBanks; BankId += 1) { + Status = SimGpioRestoreBankHardwareContext(Context, BankId); + if (!NT_SUCCESS(Status)) { + TraceEvents(TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioRestoreBankHardwareContext() failed! " + "Context = %p, Bank = %d, Status = %#x\n", + __FUNCTION__, + Context, + BankId, + Status); + + goto StartControllerEnd; + } + } + } + +StartControllerEnd: + return Status; +} + +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SimGpioStopController ( + _In_ PVOID Context, + _In_ BOOLEAN SaveContext, + _In_ WDF_POWER_DEVICE_STATE TargetState + ) + +/*++ + +Routine Description: + + This routine stops the GPIO controller. This routine is responsible for + resetting all the pins to their default modes. + + N.B. This function is not marked pageable because this function is in + the device power up path. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + SaveContext - Supplies a flag that indicates whether the client driver + should save the GPIO controller state or not. The state may need + to be restored when the controller is restarted. + + TargetState - Supplies the device power state which the device will be put + in once the callback is complete. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + BANK_ID BankId; + PSIM_GPIO_CONTEXT GpioContext; + NTSTATUS Status; + + UNREFERENCED_PARAMETER(TargetState); + + // + // Perform all the steps necessary to stop the device. + // + + // + // If save context is FALSE, then this is a final transition into D3/off + // power state. Hence saving of context is not necessary. + // + // If save context is TRUE, then this is a transition into a lower power + // Dx state. In such case, save the context as it will need to be + // restored when the device is brought back to D0 (i.e. ON) power state. + // + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + Status = STATUS_SUCCESS; + if (SaveContext == TRUE) { + for (BankId = 0; BankId < GpioContext->TotalBanks; BankId += 1) { + Status = SimGpioSaveBankHardwareContext(Context, BankId); + if (!NT_SUCCESS(Status)) { + TraceEvents(TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSaveBankHardwareContext() failed! " + "Context = %p, Bank = %d, Status = %#x\n", + __FUNCTION__, + Context, + BankId, + Status); + + goto StopControllerEnd; + } + } + } + +StopControllerEnd: + return Status; +} + +// +// --------------------------------------------------------- Interrupt Handlers +// + +_Must_inspect_result_ +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SimGpioEnableInterrupt ( + _In_ PVOID Context, + _In_ PGPIO_ENABLE_INTERRUPT_PARAMETERS EnableParameters + ) + +/*++ + +Routine Description: + + This routine configures the supplied pin for interrupt. + + N.B. For off-SOC GPIOs, the GPIO class extension synchronizes this callback + with other interrupt-related callbacks. Thus the locking is + automatically taken care of by the class extension. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + EnableParameters - Supplies a pointer to a structure containing enable + operation parameters. Fields are: + + BankId - Supplies the ID for the GPIO bank. + + PinNumber - Supplies the interrupt line that should be enabled. The pin + number is relative to the bank. + + Flags - Supplies flags controlling the enable operation. Currently + no flags are defined. + + InterruptMode - Supplies the trigger mode (edge or level) configured for + this interrupt when it was enabled. + + Polarity - Supplies the polarity (active low or active high) configured + for this interrupt when it was enabled. + Note: For edge-triggered interrupts, ActiveLow corresponds to the + falling edge; ActiveHigh corresponds to the rising edge. + + PullConfiguration - Supplies the pin pull-up/pull-down configuration. + + DebouceTimeout - Supplies the debounce timeout to be applied. The + field is in 100th of milli-seconds (i.e., 5.84ms will be supplied + as 584). Default value is zero, which implies, no debounce. + + VendorData - Supplies an optional pointer to a buffer containing the + vendor data supplied in the GPIO descriptor. This field will be + NULL if no vendor data was supplied. This buffer is read-only. + + VendorDataLength - Supplies the length of the vendor data buffer. + +Return Value: + + NTSTATUS code. + +Environment: + + Entry IRQL: PASSIVE_LEVEL. + + Synchronization: The GPIO class extension will synchronize this call + against other passive-level interrupt callbacks (e.g. enable/disable/ + unmask) and IO callbacks. + +--*/ + +{ + + BANK_ID BankId; + UCHAR EnableValue; + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + UCHAR ModeValue; + PIN_NUMBER PinNumber; + UCHAR PolarityValue; + NTSTATUS Status; + UCHAR StatusRegisterValue; + + // + // If the polarity is not supported, then bail out. Note the interrupt + // polarity cannot be InterruptActiveBoth as this sample uses ActiveBoth + // emulation. + // + + if ((EnableParameters->Polarity != InterruptActiveHigh) && + (EnableParameters->Polarity != InterruptActiveLow)) { + + Status = STATUS_NOT_SUPPORTED; + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: Polarity not supported! Polarity = %d, Status = %#x\n", + __FUNCTION__, + EnableParameters->Polarity, + Status); + + goto EnableInterruptEnd; + } + + BankId = EnableParameters->BankId; + PinNumber = EnableParameters->PinNumber; + Status = STATUS_SUCCESS; + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[BankId]; + + // + // Read the current values of the interrupt mode register, polarity + // register, enable register. + // + + Status = SimGpioSpbReadByte(GpioBank, ModeRegister, &ModeValue); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbReadByte(ModeRegister) failed! Status = %#x\n", + __FUNCTION__, + Status); + + goto EnableInterruptEnd; + } + + Status = SimGpioSpbReadByte(GpioBank, PolarityRegister, &PolarityValue); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbReadByte(PolarityRegister) failed! Status = %#x\n", + __FUNCTION__, + Status); + + goto EnableInterruptEnd; + } + + Status = SimGpioSpbReadByte(GpioBank, EnableRegister, &EnableValue); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbReadByte(EnableRegister) failed! Status = %#x\n", + __FUNCTION__, + Status); + + goto EnableInterruptEnd; + } + + // + // Determine the mode register value. If the interrupt is Level then set + // the bit; otherwise, clear it (edge-triggered). + // + + if (EnableParameters->InterruptMode == LevelSensitive) { + ModeValue |= (1 << PinNumber); + } else { + ModeValue &= ~(1 << PinNumber); + } + + // + // Determine the new polarity register value. If the interrupt is ActiveHigh + // (or Rising-edge) then set it to 0x1. Otherwise, clear it. + // + + switch (EnableParameters->Polarity) { + + case InterruptActiveHigh: + PolarityValue |= (1 << PinNumber); + break; + + case InterruptActiveLow: + PolarityValue &= ~(1 << PinNumber); + break; + } + + // + // Clear the corresponding status bit first to ignore any stale value. + // The status register is atomically modified on the controller. It takes + // a bitmask on the bits to be cleared. + // + + StatusRegisterValue = (1 << PinNumber); + + // + // Enable the interrupt by setting the bit in the interrupt enable register. + // + + EnableValue |= (1 << PinNumber); + + // + // Write the new values for the interrupt mode register, polarity + // register, status register and enable register. + // + + Status = SimGpioSpbWriteByte(GpioBank, ModeRegister, ModeValue); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbWriteByte(ModeRegister) failed! Status = %#x\n", + __FUNCTION__, + Status); + + goto EnableInterruptEnd; + } + + Status = SimGpioSpbWriteByte(GpioBank, PolarityRegister, PolarityValue); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbWriteByte(PolarityRegister) failed! Status = %#x\n", + __FUNCTION__, + Status); + + goto EnableInterruptEnd; + } + + Status = SimGpioSpbWriteByte(GpioBank, StatusRegister, StatusRegisterValue); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbWriteByte(StatusRegister) failed! Status = %#x\n", + __FUNCTION__, + Status); + + goto EnableInterruptEnd; + } + + // + // Enable the interrupt by setting the bit in the interrupt enable register. + // + + Status = SimGpioSpbWriteByte(GpioBank, EnableRegister, EnableValue); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbWriteByte(EnableRegister) failed! Status = %#x\n", + __FUNCTION__, + Status); + + + goto EnableInterruptEnd; + } + +EnableInterruptEnd: + return Status; +} + +_Must_inspect_result_ +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SimGpioDisableInterrupt ( + _In_ PVOID Context, + _In_ PGPIO_DISABLE_INTERRUPT_PARAMETERS DisableParameters + ) + +/*++ + +Routine Description: + + This routine disables the supplied pin from interrupting. + + This routine is not marked PAGED as it may be called before/after + the boot device is in D0/D3 if boot device has GPIO dependencies. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + DisableParameters - Supplies a pointer to a structure supplying the + parameters for disabling the interrupt. Fields are: + + BankId - Supplies the ID for the GPIO bank. + + PinNumber - Supplies the interrupt line that should be disabled. The pin + number is relative to the bank. + + Flags - Supplies flags controlling the disable operation. Currently + no flags are defined. + +Return Value: + + NTSTATUS code. + +Environment: + + Entry IRQL: PASSIVE_LEVEL. + + Synchronization: The GPIO class extension will synchronize this call + against other passive-level interrupt callbacks (e.g. enable/disable/ + unmask) and IO callbacks. + +--*/ + +{ + + UCHAR EnableValue; + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + NTSTATUS Status; + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[DisableParameters->BankId]; + + // + // Read the current value of the interrupt enable register. + // + + Status = SimGpioSpbReadByte(GpioBank, EnableRegister, &EnableValue); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbReadByte(EnableRegister) failed! Status = %#x\n", + __FUNCTION__, + Status); + + goto DisableInterruptEnd; + } + + // + // Disable the interrupt by clearing the bit in the interrupt enable + // register. + // + + EnableValue &= ~(1 << DisableParameters->PinNumber); + Status = SimGpioSpbWriteByte(GpioBank, EnableRegister, EnableValue); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbWriteByte(EnableRegister) failed! Status = %#x\n", + __FUNCTION__, + Status); + + goto DisableInterruptEnd; + } + +DisableInterruptEnd: + return Status; +} + +_Must_inspect_result_ +_IRQL_requires_same_ +NTSTATUS +SimGpioMaskInterrupts ( + _In_ PVOID Context, + _In_ PGPIO_MASK_INTERRUPT_PARAMETERS MaskParameters + ) + +/*++ + +Routine Description: + + This routine invokes masks the supplied pin from interrupting. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + MaskParameters - Supplies a pointer to a structure containing mask + operation parameters. Fields are: + + BankId - Supplies the ID for the GPIO bank. + + PinMask - Supplies a bitmask of pins which should be masked. If a pin + should be masked, then the corresponding bit is set in the bitmask. + + FailedMask - Supplies a bitmask of pins that failed to be masked. If + a pin could not be masked, the bit should be set in this field. + + N.B. This should only be done if for non memory-mapped controllers. + Memory-mapped controllers are never expected to fail this + operation. + +Return Value: + + NTSTATUS code. + +Environment: + + Entry IRQL: DIRQL if the GPIO controller is memory-mapped; PASSIVE_LEVEL + if the controller is behind some serial-bus. + + N.B. For memory-mapped controllers, this routine is called from within + the interrupt context with the interrupt lock acquired by the class + extension. Hence the lock is not re-acquired here. + + Synchronization: The GPIO class extension will synchronize this call + against other query/clear active and enabled interrupts. + Memory-mapped GPIO controllers: + Callbacks invoked at PASSIVE_LEVEL IRQL (e.g. interrupt + enable/disable/unmask or IO operations) may be active. Those + routines should acquire the interrupt lock prior to manipulating + any state accessed from within this routine. + + Serial-accessible GPIO controllers: + This call is synchronized with all other interrupt and IO callbacks. + +--*/ + +{ + + GPIO_DISABLE_INTERRUPT_PARAMETERS DisableParameters; + PIN_NUMBER PinIndex; + ULONG64 PinMask; + NTSTATUS Status; + + // + // Mask is essentially same as disable for SimGPIO controller. The primary + // difference is that mask callback supplies a bit-mask. + // + + Status = STATUS_SUCCESS; + PinMask = MaskParameters->PinMask; + RtlZeroMemory(&DisableParameters, sizeof(DisableParameters)); + DisableParameters.BankId = MaskParameters->BankId; + while (PinMask > 0) { + PinIndex = RtlFindLeastSignificantBit(PinMask); + if (PinIndex == 0xFF) { + + NT_ASSERT(FALSE); + + goto MaskInterruptsEnd; + } + + DisableParameters.PinNumber = PinIndex; + Status = SimGpioDisableInterrupt(Context, &DisableParameters); + if (!NT_SUCCESS(Status)) { + goto MaskInterruptsEnd; + } + + PinMask &= ~(1 << PinIndex); + } + + // + // Set the bitmask of pins that could not be successfully masked. + // + +MaskInterruptsEnd: + MaskParameters->FailedMask = PinMask; + return Status; +} + +NTSTATUS +SimGpioUnmaskInterrupt ( + _In_ PVOID Context, + _In_ PGPIO_ENABLE_INTERRUPT_PARAMETERS UnmaskParameters + ) + +/*++ + +Routine Description: + + This routine invokes unmasks the supplied interrupt pin. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + UnmaskParameters - Supplies a pointer to a structure containing parameters + for unmasking the interrupt. Fields are: + + BankId - Supplies the ID for the GPIO bank. + + PinNumber - Supplies the interrupt line that should be unmasked. The pin + number is relative to the bank. + + InterruptMode - Supplies the trigger mode (edge or level) configured for + this interrupt when it was enabled. + + Polarity - Supplies the polarity (active low or active high) configured + for this interrupt when it was enabled. + Note: For edge-triggered interrupts, ActiveLow corresponds to the + falling edge; ActiveHigh corresponds to the rising edge. + + PullConfiguration - Supplies the pin pull-up/pull-down configuration. + + DebouceTimeout - Supplies the debounce timeout to be applied. The + field is in 100th of milli-seconds (i.e., 5.84ms will be supplied + as 584). Default value is zero, which implies, no debounce. + + VendorData - NULL. + + VendorDataLength - 0. + + N.B. The VendorData and VendorDataLength are not supplied for unmask + operation (i.e., both fields are zero). + +Return Value: + + NTSTATUS code. + +Environment: + + Entry IRQL: DIRQL if the GPIO controller is memory-mapped; PASSIVE_LEVEL + if the controller is behind some serial-bus. + + N.B. For memory-mapped controllers, this routine is called from within + the interrupt context with the interrupt lock acquired by the class + extension. Hence the lock is not re-acquired here. + + Synchronization: The GPIO class extension will synchronize this call + against other query/clear active and enabled interrupts. + Memory-mapped GPIO controllers: + Callbacks invoked at PASSIVE_LEVEL IRQL (e.g. interrupt + enable/disable/unmask or IO operations) may be active. Those + routines should acquire the interrupt lock prior to manipulating + any state accessed from within this routine. + + Serial-accessible GPIO controllers: + This call is synchronized with all other interrupt and IO callbacks. + +--*/ + +{ + + UCHAR EnableValue; + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + NTSTATUS Status; + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[UnmaskParameters->BankId]; + + // + // Read the current value of the interrupt enable register. + // + + Status = SimGpioSpbReadByte(GpioBank, EnableRegister, &EnableValue); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbReadByte(EnableRegister) failed! Status = %#x\n", + __FUNCTION__, + Status); + + goto UnmaskInterruptEnd; + } + + // + // Unmask the interrupt by setting the bit in the interrupt enable + // register. + // + + EnableValue |= (1 << UnmaskParameters->PinNumber); + Status = SimGpioSpbWriteByte(GpioBank, EnableRegister, EnableValue); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbWriteByte(EnableRegister) failed! Status = %#x\n", + __FUNCTION__, + Status); + + goto UnmaskInterruptEnd; + } + +UnmaskInterruptEnd: + return Status; +} + +_Must_inspect_result_ +_IRQL_requires_same_ +NTSTATUS +SimGpioQueryActiveInterrupts ( + _In_ PVOID Context, + _In_ PGPIO_QUERY_ACTIVE_INTERRUPTS_PARAMETERS QueryActiveParameters + ) + +/*++ + +Routine Description: + + This routine returns the current set of active interrupts. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + QueryActiveParameters - Supplies a pointer to a structure containing query + parameters. Fields are: + + BankId - Supplies the ID for the GPIO bank. + + EnabledMask - Supplies a bitmask of pins enabled for interrupts + on the specified GPIO bank. + + ActiveMask - Supplies a bitmask that receives the active interrupt + mask. If a pin is interrupting and set in EnabledMask, then the + corresponding bit is set in the bitmask. + +Return Value: + + NTSTATUS code. + +Environment: + + Entry IRQL: DIRQL if the GPIO controller is memory-mapped; PASSIVE_LEVEL + if the controller is behind some serial-bus. + + N.B. For memory-mapped controllers, this routine is called from within + the interrupt context with the interrupt lock acquired by the class + extension. + + Synchronization: The GPIO class extension will synchronize this call + against other query/clear active and enabled interrupts. + Memory-mapped GPIO controllers: + Callbacks invoked at PASSIVE_LEVEL IRQL (e.g. interrupt + enable/disable/unmask or IO operations) may be active. Those + routines should acquire the interrupt lock prior to manipulating + any state accessed from within this routine. + + Serial-accessible GPIO controllers: + This call is synchronized with all other interrupt and IO callbacks. + +--*/ + +{ + + UCHAR EnableValue; + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + NTSTATUS ReturnStatus; + UCHAR StatusValue; + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[QueryActiveParameters->BankId]; + + // + // NOTE: As SimGPIO is not a real hardware device, no interrupt will ever + // fire. Thus the status register value will never change. To pretend + // as if a real interrupt happened, it marks all currently enabled + // interrupts as asserting. Copy the enable interrupt value into + // the status register. + // + // This should NOT be done for a real GPIO controller!! + // + + // + // BEGIN: SIMGPIO HACK. + // + + ReturnStatus = SimGpioSpbReadByte(GpioBank, EnableRegister, &EnableValue); + if (!NT_SUCCESS(ReturnStatus)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbReadByte(EnableRegister) failed! Status = %#x\n", + __FUNCTION__, + ReturnStatus); + + goto QueryActiveInterruptsEnd; + } + + ReturnStatus = SimGpioSpbWriteByte(GpioBank, StatusRegister, EnableValue); + if (!NT_SUCCESS(ReturnStatus)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbWriteByte(StatusRegister) failed! " + "Status = %#x\n", + __FUNCTION__, + ReturnStatus); + + goto QueryActiveInterruptsEnd; + } + + // + // END: SIMGPIO HACK. + // + + // + // Return the current value of the interrupt status register in the + // ActiveMask parameter. + // + + ReturnStatus = SimGpioSpbReadByte(GpioBank, StatusRegister, &StatusValue); + if (!NT_SUCCESS(ReturnStatus)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbWriteByte(EnableRegister) failed! " + "Status = %#x\n", + __FUNCTION__, + ReturnStatus); + + goto QueryActiveInterruptsEnd; + } + + QueryActiveParameters->ActiveMask = (ULONG64)StatusValue; + +QueryActiveInterruptsEnd: + return ReturnStatus; +} + +_Must_inspect_result_ +_IRQL_requires_same_ +NTSTATUS +SimGpioQueryEnabledInterrupts ( + _In_ PVOID Context, + _In_ PGPIO_QUERY_ENABLED_INTERRUPTS_PARAMETERS QueryEnabledParameters + ) + +/*++ + +Routine Description: + + This routine returns the current set of enabled interrupts. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + QueryEnabledParameters - Supplies a pointer to a structure containing query + parameters. Fields are: + + BankId - Supplies the ID for the GPIO bank. + + EnabledMask - Supplies a bitmask that receives the enabled interrupt + mask. If a pin is enabled, then the corresponding bit is set in the + mask. + +Return Value: + + NTSTATUS code. + +Environment: + + Entry IRQL: DIRQL if the GPIO controller is memory-mapped; PASSIVE_LEVEL + if the controller is behind some serial-bus. + + N.B. For memory-mapped controllers, this routine is called with the + interrupt lock acquired by the class extension, but not always + from within the interrupt context. + + Synchronization: The GPIO class extension will synchronize this call + against other query/clear active and enabled interrupts. + Memory-mapped GPIO controllers: + Callbacks invoked at PASSIVE_LEVEL IRQL (e.g. interrupt + enable/disable/unmask or IO operations) may be active. Those + routines should acquire the interrupt lock prior to manipulating + any state accessed from within this routine. + + Serial-accessible GPIO controllers: + This call is synchronized with all other interrupt and IO callbacks. + +--*/ + +{ + + UCHAR EnableValue; + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + NTSTATUS ReturnStatus; + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[QueryEnabledParameters->BankId]; + + // + // Return the current value of the interrupt enable register into the + // EnabledMask parameter. It is strongly preferred that the true state of + // the hardware is returned, rather than a software-cached variable, since + // CLIENT_QueryEnabledInterrupts is used by the class extension to detect + // interrupt storms. + // + + ReturnStatus = SimGpioSpbReadByte(GpioBank, EnableRegister, &EnableValue); + if (!NT_SUCCESS(ReturnStatus)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbReadByte(EnableRegister) failed! Status = %#x\n", + __FUNCTION__, + ReturnStatus); + + goto QueryEnabledInterruptsEnd; + } + + QueryEnabledParameters->EnabledMask = (ULONG64)EnableValue; + +QueryEnabledInterruptsEnd: + return ReturnStatus; +} +_Must_inspect_result_ +_IRQL_requires_same_ +NTSTATUS +SimGpioClearActiveInterrupts ( + _In_ PVOID Context, + _In_ PGPIO_CLEAR_ACTIVE_INTERRUPTS_PARAMETERS ClearParameters + ) + +/*++ + +Routine Description: + + This routine clears the GPIO controller's active set of interrupts. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + ClearParameters - Supplies a pointer to a structure containing clear + operation parameters. Fields are: + + BankId - Supplies the ID for the GPIO bank. + + ClearActiveMask - Supplies a mask of pins which should be marked as + inactive. If a pin should be cleared, then the corresponding bit is + set in the mask. + + FailedClearMask - Supplies a bitmask of pins that failed to be cleared. If + a pin could not be cleared, the bit should be set in this field. + + N.B. This should only be done if for non memory-mapped controllers. + Memory-mapped controllers are never expected to fail this + operation. + +Return Value: + + NTSTATUS code. + +Environment: + + Entry IRQL: DIRQL if the GPIO controller is memory-mapped; PASSIVE_LEVEL + if the controller is behind some serial-bus. + + N.B. For memory-mapped controllers, this routine is called from within + the interrupt context with the interrupt lock acquired by the class + extension. + + Synchronization: The GPIO class extension will synchronize this call + against other query/clear active and enabled interrupts. + Memory-mapped GPIO controllers: + Callbacks invoked at PASSIVE_LEVEL IRQL (e.g. interrupt + enable/disable/unmask or IO operations) may be active. Those + routines should acquire the interrupt lock prior to manipulating + any state accessed from within this routine. + + Serial-accessible GPIO controllers: + This call is synchronized with all other interrupt and IO callbacks. + +--*/ + +{ + + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + NTSTATUS ReturnStatus; + UCHAR StatusValue; + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[ClearParameters->BankId]; + + // + // Clear the bits that are set in the ClearActiveMask parameter. + // + + StatusValue = (UCHAR)ClearParameters->ClearActiveMask; + ReturnStatus = SimGpioSpbWriteByte(GpioBank, StatusRegister, StatusValue); + if (!NT_SUCCESS(ReturnStatus)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbWriteByte(StatusRegister) failed! Status = %#x\n", + __FUNCTION__, + ReturnStatus); + + goto ClearActiveInterruptsEnd; + } + + // + // If the operation succeeded, then all the bits were cleared. + // + + StatusValue = 0x0; + + // + // Set the bitmask of pins that could not be successfully cleared. + // Either all bits get written successfully or none does. + // + +ClearActiveInterruptsEnd: + ClearParameters->FailedClearMask = StatusValue; + return ReturnStatus; +} + +NTSTATUS +SimGpioReconfigureInterrupt ( + _In_ PVOID Context, + _In_ PGPIO_RECONFIGURE_INTERRUPTS_PARAMETERS ReconfigureParameters + ) + +/*++ + +Routine Description: + + This routine reconfigures the interrupt in the specified mode. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + ReconfigureParameters - Supplies a pointer to a structure containing + parameters for reconfiguring the interrupt. Fields are: + + BankId - Supplies the ID for the GPIO bank. + + PinNumber - Supplies the interrupt line that should be reconfigured. + The pin number is relative to the bank. + + InterruptMode - Supplies the trigger mode (edge or level) for the new + configuration. + + Polarity - Supplies the polarity (active low or active high) for the + new configuration. + Note: For edge-triggered interrupts, ActiveLow corresponds to the + falling edge; ActiveHigh corresponds to the rising edge. + +Return Value: + + NTSTATUS code. + +Environment: + + Entry IRQL: DIRQL if the GPIO controller is memory-mapped; PASSIVE_LEVEL + if the controller is behind some serial-bus. + + N.B. For memory-mapped controllers, this routine is called from within + the interrupt context with the interrupt lock acquired by the class + extension. Hence the lock is not re-acquired here. + + Synchronization: The GPIO class extension will synchronize this call + against other query/clear active and enabled interrupts. + Memory-mapped GPIO controllers: + Callbacks invoked at PASSIVE_LEVEL IRQL (e.g. interrupt + enable/disable/unmask or IO operations) may be active. Those + routines should acquire the interrupt lock prior to manipulating + any state accessed from within this routine. + + Serial-accessible GPIO controllers: + This call is synchronized with all other interrupt and IO callbacks. + +--*/ + +{ + + BANK_ID BankId; + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + UCHAR ModeValue; + PIN_NUMBER PinNumber; + UCHAR PolarityValue; + NTSTATUS Status; + UCHAR StatusRegisterValue; + + + BankId = ReconfigureParameters->BankId; + PinNumber = ReconfigureParameters->PinNumber; + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[BankId]; + + // + // Read the current values of the interrupt mode register, polarity + // register, enable register. + // + + Status = SimGpioSpbReadByte(GpioBank, ModeRegister, &ModeValue); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbReadByte(ModeRegister) failed! Status = %#x\n", + __FUNCTION__, + Status); + + goto ReconfigureInterruptEnd; + } + + Status = SimGpioSpbReadByte(GpioBank, PolarityRegister, &PolarityValue); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbReadByte(PolarityRegister) failed! Status = %#x\n", + __FUNCTION__, + Status); + + goto ReconfigureInterruptEnd; + } + + // + // Determine the mode register value. If the interrupt is Level then set + // the bit; otherwise, clear it (edge-triggered). + // + + if (ReconfigureParameters->InterruptMode == LevelSensitive) { + ModeValue |= (1 << PinNumber); + } else { + ModeValue &= ~(1 << PinNumber); + } + + // + // Determine the new polarity register value. If the interrupt is ActiveHigh + // (or Rising-edge) then set it to 0x1. Otherwise, clear it. + // + + switch (ReconfigureParameters->Polarity) { + + case InterruptActiveHigh: + PolarityValue |= (1 << PinNumber); + break; + + case InterruptActiveLow: + PolarityValue &= ~(1 << PinNumber); + break; + } + + // + // Clear any stale status bits from the previous configuration. + // + // The status register is atomically modified on the controller. It takes + // a bitmask on the bits to be cleared. + // + + StatusRegisterValue = (1 << PinNumber); + Status = SimGpioSpbWriteByte(GpioBank, StatusRegister, StatusRegisterValue); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbWriteByte(StatusRegister) failed! Status = %#x\n", + __FUNCTION__, + Status); + + goto ReconfigureInterruptEnd; + } + + // + // Write the new values for the interrupt mode register, polarity + // register, status register and enable register. + // + + Status = SimGpioSpbWriteByte(GpioBank, ModeRegister, ModeValue); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbWriteByte(ModeRegister) failed! Status = %#x\n", + __FUNCTION__, + Status); + + goto ReconfigureInterruptEnd; + } + + Status = SimGpioSpbWriteByte(GpioBank, PolarityRegister, PolarityValue); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbWriteByte(PolarityRegister) failed! Status = %#x\n", + __FUNCTION__, + Status); + + goto ReconfigureInterruptEnd; + } + +ReconfigureInterruptEnd: + return Status; +} + +// +// --------------------------------------------------------------- I/O Handlers +// + +_Must_inspect_result_ +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SimGpioConnectIoPins ( + _In_ PVOID Context, + _In_ PGPIO_CONNECT_IO_PINS_PARAMETERS ConnectParameters + ) + +/*++ + +Routine Description: + + This routine invokes connects the specified pins for IO. The pins can + be read from if connected for input, or written to if connected for + output. + + N.B. This routine is called at PASSIVE_LEVEL but is not marked as + PAGED_CODE as it could be executed late in the hibernate or + early in resume sequence (or the deep-idle sequence). + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + ConnectParameters - Supplies a pointer to a structure supplying the + parameters for connecting the IO pins. Fields description: + + BankId - Supplies the ID for the GPIO bank. + + PinNumberTable - Supplies an array of pins to be connected for IO. The + pin numbers are 0-based and relative to the GPIO bank. + + PinCount - Supplies the number of pins in the pin number table. + + ConnectMode - Supplies the mode in which the pins should be configured + (viz. input or output). + + ConnectFlags - Supplies the flags controlling the IO setup. Currently + no flags are defined. + + PullConfiguration - Supplies the pin pull-up/pull-down configuration. + + DebouceTimeout - Supplies the debounce timeout to be applied. The + field is in 100th of milli-seconds (i.e., 5.84ms will be supplied + as 584). Default value is zero, which implies, no debounce. + + DriveStrength - Supplies the drive strength to be applied. The value + is in 100th of mA (i.e., 1.21mA will be supplied as 121mA). + + VendorData - Supplies an optional pointer to a buffer containing the + vendor data supplied in the GPIO descriptor. This field will be + NULL if no vendor data was supplied. This buffer is read-only. + + VendorDataLength - Supplies the length of the vendor data buffer. + + ConnectFlags - Supplies the flag to be used for connect operation. + Currently no flags are defined. + +Return Value: + + NT status code. + +Environment: + + Entry IRQL: PASSIVE_LEVEL. + + Synchronization: The GPIO class extension will synchronize this call + against other passive-level interrupt callbacks (e.g. enable/disable/ + unmask) and IO callbacks. + +--*/ + +{ + + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + ULONG Index; + PIN_NUMBER PinNumber; + PPIN_NUMBER PinNumberTable; + UCHAR PinValue; + NTSTATUS Status; + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[ConnectParameters->BankId]; + + // + // Read the current direction register value. + // + + Status = SimGpioSpbReadByte(GpioBank, DirectionRegister, &PinValue); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbReadByte(DirectionRegister) failed! Status = %#x\n", + __FUNCTION__, + Status); + + goto ConnectIoPinsEnd; + } + + // + // Walk through all the supplied pins and connect them in the specified + // mode (input or output). + // + + PinNumberTable = ConnectParameters->PinNumberTable; + for (Index = 0; Index < ConnectParameters->PinCount; Index += 1) { + PinNumber = PinNumberTable[Index]; + + // + // If the pins are being connected for input, then set the bit. + // Otherwise clear the bit. + // + + if (ConnectParameters->ConnectMode == ConnectModeInput) { + PinValue |= (1 << PinNumber); + + } else if (ConnectParameters->ConnectMode == ConnectModeOutput) { + PinValue &= ~(1 << PinNumber); + } + } + + Status = SimGpioSpbWriteByte(GpioBank, DirectionRegister, PinValue); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbWriteByte(DirectionRegister) failed! Status = %#x\n", + __FUNCTION__, + Status); + + goto ConnectIoPinsEnd; + } + +ConnectIoPinsEnd: + return Status; +} + +_Must_inspect_result_ +_IRQL_requires_(PASSIVE_LEVEL) +NTSTATUS +SimGpioDisconnectIoPins ( + _In_ PVOID Context, + _In_ PGPIO_DISCONNECT_IO_PINS_PARAMETERS DisconnectParameters + ) + +/*++ + +Routine Description: + + This routine invokes disconnects the specified IO pins. The pins are + put back in their original mode. + + N.B. This routine is called at PASSIVE_LEVEL but is not marked as + PAGED_CODE as it could be executed late in the hibernate or + early in resume sequence (or the deep-idle sequence). + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + DisconnectParameters - Supplies a pointer to a structure containing + disconnect operation parameters. Fields are: + + BankId - Supplies the ID for the GPIO bank. + + PinNumberTable - Supplies an array of pins to be disconnected. The pin + numbers are relative to the GPIO bank. + + PinCount - Supplies the number of pins in the pin number table. + + DisconnectMode - Supplies the mode in which the pins are currently + configured (viz. input or output). + + DisconnectFlags - Supplies the flags controlling the IO setup. Currently + no flags are defined. + +Return Value: + + NT status code. + +Environment: + + Entry IRQL: PASSIVE_LEVEL. + + Synchronization: The GPIO class extension will synchronize this call + against other passive-level interrupt callbacks (e.g. enable/disable/ + unmask) and IO callbacks. + +--*/ + +{ + + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + ULONG Index; + PIN_NUMBER PinNumber; + PPIN_NUMBER PinNumberTable; + UCHAR PinValue; + NTSTATUS Status; + + // + // If the pin configuration should be preserved post disconnect, then + // there is nothing left to do. + // + + if (DisconnectParameters->DisconnectFlags.PreserveConfiguration == 1) { + Status = STATUS_SUCCESS; + goto DisconnectIoPinsEnd; + } + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[DisconnectParameters->BankId]; + + // + // Read the current direction register value. + // + + Status = SimGpioSpbReadByte(GpioBank, DirectionRegister, &PinValue); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbReadByte(DirectionRegister) failed! " + "Status = %#x\n", + __FUNCTION__, + Status); + + goto DisconnectIoPinsEnd; + } + + // + // Walk through all the supplied pins and disconnect them. On SimGPIO + // controller, all pins are reset to the default mode (output). + // + + PinNumberTable = DisconnectParameters->PinNumberTable; + for (Index = 0; Index < DisconnectParameters->PinCount; Index += 1) { + PinNumber = PinNumberTable[Index]; + PinValue &= ~(1 << PinNumber); + } + + Status = SimGpioSpbWriteByte(GpioBank, DirectionRegister, PinValue); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbWriteByte(DirectionRegister) failed! " + "Status = %#x\n", + __FUNCTION__, + Status); + + goto DisconnectIoPinsEnd; + } + +DisconnectIoPinsEnd: + return Status; +} + +_Must_inspect_result_ +NTSTATUS +SimGpioReadGpioPins ( + _In_ PVOID Context, + _In_ PGPIO_READ_PINS_MASK_PARAMETERS ReadParameters + ) + +/*++ + +Routine Description: + + This routine reads the current values for all the pins. + + As the FormatIoRequestsAsMasks bit was set inside + SimGpioQueryControllerInformation(), all this routine needs to do is read + the level register value and return to the GPIO class extension. It will + return the right set of bits to the caller. + + N.B. This routine is called at PASSIVE_LEVEL for off-SoC GPIOs but is not + marked as PAGED_CODE as it could be executed late in the hibernate or + early in resume sequence (or the deep-idle sequence). + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + ReadParameters - Supplies a pointer to a structure containing read + operation parameters. Fields are: + + BankId - Supplies the ID for the GPIO bank. + + PinValues - Supplies a pointer to a variable that receives the current + pin values. + + Flags - Supplies the flag to be used for read operation. Currently + defined flags are: + + WriteConfiguredPins: If set, the read is being done on a set of + pin that were configured for write. In such cases, the + GPIO client driver is expected to read and return the + output register value. + +Return Value: + + NT status code. + +Environment: + + Entry IRQL: PASSIVE_LEVEL. + + Synchronization: The GPIO class extension will synchronize this call + against other passive-level interrupt callbacks (e.g. enable/disable/ + unmask) and IO callbacks. + +--*/ + +{ + + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + UCHAR PinValue; + NTSTATUS Status; + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[ReadParameters->BankId]; + + // + // Read the current level register value. Note the GPIO class may invoke + // the read routine on write-configured pins. In such case the output + // register values should be read. + // + // N.B. In case of SimGPIO, the LevelRegister holds the value for input + // as well as output pins. Thus the same register is read in either + // case. + // + + if (ReadParameters->Flags.WriteConfiguredPins == FALSE) { + Status = SimGpioSpbReadByte(GpioBank, LevelRegister, &PinValue); + + } else { + Status = SimGpioSpbReadByte(GpioBank, LevelRegister, &PinValue); + } + + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbReadByte(DirectionRegister) failed! " + "Status = %#x\n", + __FUNCTION__, + Status); + + goto ReadGpioPinsEnd; + } + + *ReadParameters->PinValues = PinValue; + +ReadGpioPinsEnd: + return Status; +} + +_Must_inspect_result_ +NTSTATUS +SimGpioWriteGpioPins ( + _In_ PVOID Context, + _In_ PGPIO_WRITE_PINS_MASK_PARAMETERS WriteParameters + ) + +/*++ + +Routine Description: + + This routine sets the current values for the specified pins. This call is + synchronized with the write and connect/disconnect IO calls. + + N.B. This routine is called at PASSIVE_LEVEL for off-SoC GPIOs but is not + marked as PAGED_CODE as it could be executed late in the hibernate or + early in resume sequence (or the deep-idle sequence). + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + WriteParameters - Supplies a pointer to a structure containing write + operation parameters. Fields are: + + BankId - Supplies the ID for the GPIO bank. + + SetMask - Supplies a mask of pins which should be set (0x1). If a pin + should be set, then the corresponding bit is set in the mask. + All bits that are clear in the mask should be left intact. + + ClearMask - Supplies a mask of pins which should be cleared (0x0). If + a pin should be cleared, then the bit is set in the bitmask. All + bits that are clear in the mask should be left intact. + + Flags - Supplies the flag controlling the write operation. Currently + no flags are defined. + +Return Value: + + NT status code. + +--*/ + +{ + + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + UCHAR PinValue; + NTSTATUS Status; + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[WriteParameters->BankId]; + + // + // Read the current level register value. + // + + Status = SimGpioSpbReadByte(GpioBank, LevelRegister, &PinValue); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbReadByte(DirectionRegister) failed! " + "Status = %#x\n", + __FUNCTION__, + Status); + + goto WriteGpioPinsEnd; + } + + // + // Set the bits specified in the set mask and clear the ones specified + // in the clear mask. + // + + PinValue |= WriteParameters->SetMask; + PinValue &= ~WriteParameters->ClearMask; + + // + // Write the updated value to the register. + // + + Status = SimGpioSpbWriteByte(GpioBank, LevelRegister, PinValue); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbWriteByte(DirectionRegister) failed! " + "Status = %#x\n", + __FUNCTION__, + Status); + + goto WriteGpioPinsEnd; + } + +WriteGpioPinsEnd: + return Status; +} + +VOID +SimGpioEvtDriverUnload ( + _In_ WDFDRIVER Driver + ) + +/*++ + +Routine Description: + + This routine is called by WDF to allow final cleanup prior to unloading + the GPIO class extension. This routine destroys the class library and stops + tracing. + +Arguments: + + Driver - Supplies a handle to a framework driver object. + +Return Value: + + None. + +--*/ + +{ + + PDRIVER_OBJECT DriverObject; + NTSTATUS Status; + + PAGED_CODE(); + + Status = GPIO_CLX_UnregisterClient(Driver); + NT_ASSERT(NT_SUCCESS(Status)); + + DriverObject = WdfDriverWdmGetDriverObject(Driver); + + TraceEvents(TRACE_LEVEL_INFORMATION, + TRACE_FLAG_INIT, + "%s: Driver unloaded!\n", + __FUNCTION__); + + WPP_CLEANUP(DriverObject); + return; +} + +// +// ------------------------------------------------------- Power mgmt handlers +// + +NTSTATUS +SimGpioSaveBankHardwareContext ( + _In_ PVOID Context, + _In_ BANK_ID BankId + ) + +/*++ + +Routine Description: + + This routine saves the hardware context for the GPIO controller. + + N.B. This function is not marked pageable because this function is in + the device power up path. It is called at PASSIVE_IRQL though. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + BankId - Supplies the ID of the bank. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + PUCHAR DestinationAddress; + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + ULONG Index; + NTSTATUS Status; + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[BankId]; + Status = STATUS_SUCCESS; + + // + // Loop through all the registers and save them. The status register can + // only be written to be cleared and thus isn't saved. + // + + for (Index = 0; Index < MaximumSimGpioAddress; Index += 1) { + if (Index == StatusRegister) { + continue; + } + + DestinationAddress = &GpioBank->SavedRegisterContext[Index]; + Status = SimGpioSpbReadByte(GpioBank, Index, DestinationAddress); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbReadByte(Register = %d) failed! " + "Status = %#x\n", + __FUNCTION__, + Index, + Status); + + goto SaveBankHardwareContextEnd; + } + } + +SaveBankHardwareContextEnd: + return Status; +} + +NTSTATUS +SimGpioRestoreBankHardwareContext ( + _In_ PVOID Context, + _In_ BANK_ID BankId + ) + +/*++ + +Routine Description: + + This routine saves the hardware context for the GPIO controller. + + N.B. This function is not marked pageable because this function is in + the device power up path. + +Arguments: + + Context - Supplies a pointer to the GPIO client driver's device extension. + + BankId - Supplies the ID of the bank. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + PSIM_GPIO_BANK GpioBank; + PSIM_GPIO_CONTEXT GpioContext; + ULONG Index; + NTSTATUS Status; + UCHAR Value; + + GpioContext = (PSIM_GPIO_CONTEXT)Context; + GpioBank = &GpioContext->Banks[BankId]; + Status = STATUS_SUCCESS; + + // + // Loop through all the registers and write them. The order of restore is + // important. The enable and direction registers need to be programmed + // after the (mode, polarity) and level registers have been written to. + // Hence they are restored at the very end. + // + + for (Index = 0; Index < MaximumSimGpioAddress; Index += 1) { + if ((Index == EnableRegister) || + (Index == DirectionRegister) || + (Index == StatusRegister)) { + + continue; + } + + Value = GpioBank->SavedRegisterContext[Index]; + Status = SimGpioSpbWriteByte(GpioBank, Index, Value); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbWriteByte(Register = %d) failed! " + "Status = %#x\n", + __FUNCTION__, + Index, + Status); + + goto RestoreBankHardwareContextEnd; + } + } + + // + // Restore the direction register. + // + + Value = GpioBank->SavedRegisterContext[DirectionRegister]; + Status = SimGpioSpbWriteByte(GpioBank, DirectionRegister, Value); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbWriteByte(DirectionRegister) failed! Status = %#x\n", + __FUNCTION__, + Status); + + goto RestoreBankHardwareContextEnd; + } + + // + // Restore the interrupt enable register at the very end. + // + + Value = GpioBank->SavedRegisterContext[EnableRegister]; + Status = SimGpioSpbWriteByte(GpioBank, EnableRegister, Value); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: SimGpioSpbWriteByte(EnableRegister) failed! Status = %#x\n", + __FUNCTION__, + Status); + + goto RestoreBankHardwareContextEnd; + } + +RestoreBankHardwareContextEnd: + return Status; +} + +__pragma(warning(default: 4127)) // conditional expression is a constant + + diff --git a/gpio/samples/simgpio_i2c/simgpio_i2c.h b/gpio/samples/simgpio_i2c/simgpio_i2c.h new file mode 100644 index 00000000..a9c02af3 --- /dev/null +++ b/gpio/samples/simgpio_i2c/simgpio_i2c.h @@ -0,0 +1,176 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + simgpio_i2c.h + +Abstract: + + This sample implements a GPIO client driver for simulated GPIO (SimGpio) + controller. + + Note: DIRQL in the comments below refers to device IRQL, which is any + IRQL > DISPATCH_LEVEL (and less than some IRQL reserved for OS use). + + +Environment: + + Kernel mode + +--*/ + +#pragma once + +// +// -------------------------------------------------------------------- Defines +// + +// +// Define total number of pins on the simulated GPIO controller. +// + +#define SIM_GPIO_TOTAL_PINS (16) +#define SIM_GPIO_PINS_PER_BANK (8) +#define SIM_GPIO_TOTAL_BANKS (SIM_GPIO_TOTAL_PINS / SIM_GPIO_PINS_PER_BANK) + +#define SIM_GPIO_REGISTER_ADDRESS_SIZE (sizeof(USHORT)) + +// +// Pool tag for SimGpio allocations. +// + +#define SIM_GPIO_POOL_TAG 'GmiS' + +// +// Macro for pointer arithmetic. +// + +#define Add2Ptr(Ptr, Value) ((PVOID)((PUCHAR)(Ptr) + (Value))) + +// +// ---------------------------------------------------------------------- Types +// + +// +// Define the registers within the SimGPIO controller. There are 16 pins per +// controller. Note this is a logical device and thus may correspond to a +// physical bank or module if the GPIO controller in hardware has more than +// 16 pins. Below is the register set from a logical perspective. +// +// typedef struct _SIM_GPIO_REGISTERS { +// UCHAR ModeRegister1; +// UCHAR PolarityRegister1; +// UCHAR EnableRegister1; +// UCHAR StatusRegister1; +// UCHAR DirectionRegister1; +// UCHAR LevelRegister1; +// UCHAR ModeRegister2; +// UCHAR PolarityRegister2; +// UCHAR EnableRegister2; +// UCHAR StatusRegister2; +// UCHAR DirectionRegister2; +// UCHAR LevelRegister2; +// } SIM_GPIO_REGISTERS, *PSIM_GPIO_REGISTERS; +// +// + +typedef enum _SIM_GPIO_REGISTER_ADDRESS { + ModeRegister = 0x0, + PolarityRegister, + EnableRegister, + StatusRegister, + DirectionRegister, + LevelRegister, + MaximumSimGpioAddress +} SIM_GPIO_REGISTER_ADDRESS, *PSIM_GPIO_REGISTER_ADDRESS; + +struct _SIM_GPIO_CONTEXT; + +typedef struct _SIM_GPIO_BANK { + USHORT AddressBase; + struct _SIM_GPIO_CONTEXT *GpioContext; + UCHAR SavedRegisterContext[MaximumSimGpioAddress]; +} SIM_GPIO_BANK, *PSIM_GPIO_BANK; + +// +// The SimGPIO client driver device extension. +// + +struct _SIM_GPIO_CONTEXT { + USHORT TotalBanks; + USHORT TotalPins; + WDFDEVICE Device; + WDFIOTARGET SpbIoTarget; + LARGE_INTEGER SpbConnectionId; + WDFREQUEST SpbRequest; + // PSIM_GPIO_REGISTERS ControllerBase; + // ULONG Length; + SIM_GPIO_BANK Banks[SIM_GPIO_TOTAL_BANKS]; +}; + +typedef struct _SIM_GPIO_CONTEXT SIM_GPIO_CONTEXT; +typedef SIM_GPIO_CONTEXT *PSIM_GPIO_CONTEXT; + +// +// Request context +// +typedef struct _SIM_GPIO_REQUEST_CONTEXT { + WDFDEVICE Device; + + // + // Whether the request is a sequence or not. + // + + BOOLEAN SequenceRequest; +} SIM_GPIO_REQUEST_CONTEXT, *PSIM_GPIO_REQUEST_CONTEXT; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(SIM_GPIO_REQUEST_CONTEXT, GetRequestContext); + +// +// ----------------------------------------------------------------- Prototypes +// + +VOID +SimGpioDestroySpbConnection ( + _In_ PSIM_GPIO_CONTEXT SimGpioContext + ); + +NTSTATUS +SimGpioSpbReadByte ( + _In_ PSIM_GPIO_BANK GpioBank, + _In_ SIM_GPIO_REGISTER_ADDRESS RegisterAddress, + _Out_writes_(sizeof(UCHAR)) PUCHAR Data + ); + +NTSTATUS +SimGpioSetupSpbConnection ( + _In_ PSIM_GPIO_CONTEXT GpioContext + ); + +NTSTATUS +SimGpioSpbWriteByte ( + _In_ PSIM_GPIO_BANK GpioBank, + _In_ SIM_GPIO_REGISTER_ADDRESS RegisterAddress, + _In_ UCHAR Data + ); + +NTSTATUS +SimGpioRestoreBankHardwareContext ( + _In_ PVOID Context, + _In_ BANK_ID BankId + ); + +NTSTATUS +SimGpioSaveBankHardwareContext ( + _In_ PVOID Context, + _In_ BANK_ID BankId + ); + + diff --git a/gpio/samples/simgpio_i2c/simgpio_i2c.inx b/gpio/samples/simgpio_i2c/simgpio_i2c.inx Binary files differnew file mode 100644 index 00000000..ab8a130d --- /dev/null +++ b/gpio/samples/simgpio_i2c/simgpio_i2c.inx diff --git a/gpio/samples/simgpio_i2c/simgpio_i2c.rc b/gpio/samples/simgpio_i2c/simgpio_i2c.rc new file mode 100644 index 00000000..a2b60d9a --- /dev/null +++ b/gpio/samples/simgpio_i2c/simgpio_i2c.rc @@ -0,0 +1,12 @@ +#include <windows.h> + +#include <ntverp.h> + +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "Simulated GPIO I2C Client" +#define VER_INTERNALNAME_STR "simgpio_i2c.sys" +#define VER_ORIGINALFILENAME_STR "simgpio_i2c.sys" + +#include "common.ver" + diff --git a/gpio/samples/simgpio_i2c/simgpio_i2c.vcxproj b/gpio/samples/simgpio_i2c/simgpio_i2c.vcxproj new file mode 100644 index 00000000..1e2e182e --- /dev/null +++ b/gpio/samples/simgpio_i2c/simgpio_i2c.vcxproj @@ -0,0 +1,200 @@ +<?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|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{FBC7D66C-8D89-4ED5-B378-A4EBB8DED3B2}</ProjectGuid> + <RootNamespace>$(MSBuildProjectName)</RootNamespace> + <KMDF_VERSION_MAJOR>1</KMDF_VERSION_MAJOR> + <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> + <Platform Condition="'$(Platform)' == ''">Win32</Platform> + <SampleGuid>{ABDC1F8D-20A4-4A63-9D67-7B8374C9057D}</SampleGuid> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType>KMDF</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType>KMDF</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType>KMDF</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType>KMDF</DriverType> + <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> + <ConfigurationType>Driver</ConfigurationType> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <PropertyGroup> + <OutDir>$(IntDir)</OutDir> + </PropertyGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ItemGroup Label="WrappedTaskItems"> + <ClCompile Include="simgpio_i2c.c; spb_i2c.c"> + <WppEnabled>true</WppEnabled> + <WppKernelMode>true</WppKernelMode> + <WppTraceFunction>TraceEvents(LEVEL,FLAGS,MSG,...)</WppTraceFunction> + <WppGenerateUsingTemplateFile>{km-WdfDefault.tpl}*.tmh</WppGenerateUsingTemplateFile> + </ClCompile> + <Inf Include=".\simgpio_i2c.inx"> + <DateStamp>*</DateStamp> + <SpecifyDriverVerDirectiveDate>true</SpecifyDriverVerDirectiveDate> + <Architecture>$(InfArch)</Architecture> + <SpecifyArchitecture>true</SpecifyArchitecture> + <CopyOutput>.\$(IntDir)\simgpio_i2c.inf</CopyOutput> + </Inf> + <OtherWpp Include="simgpio_i2c.rc"> + <WppEnabled>true</WppEnabled> + <WppKernelMode>true</WppKernelMode> + <WppTraceFunction>TraceEvents(LEVEL,FLAGS,MSG,...)</WppTraceFunction> + <WppGenerateUsingTemplateFile>{km-WdfDefault.tpl}*.tmh</WppGenerateUsingTemplateFile> + </OtherWpp> + </ItemGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetName>simgpio_i2c</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetName>simgpio_i2c</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetName>simgpio_i2c</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetName>simgpio_i2c</TargetName> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\ksguid.lib;$(DDK_LIB_PATH)\ntstrsafe.lib;$(DDK_LIB_PATH)\msgpioclxstub.lib</AdditionalDependencies> + </Link> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)</AdditionalIncludeDirectories> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)</AdditionalIncludeDirectories> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)</AdditionalIncludeDirectories> + </Midl> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\ksguid.lib;$(DDK_LIB_PATH)\ntstrsafe.lib;$(DDK_LIB_PATH)\msgpioclxstub.lib</AdditionalDependencies> + </Link> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)</AdditionalIncludeDirectories> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)</AdditionalIncludeDirectories> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)</AdditionalIncludeDirectories> + </Midl> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\ksguid.lib;$(DDK_LIB_PATH)\ntstrsafe.lib;$(DDK_LIB_PATH)\msgpioclxstub.lib</AdditionalDependencies> + </Link> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)</AdditionalIncludeDirectories> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)</AdditionalIncludeDirectories> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)</AdditionalIncludeDirectories> + </Midl> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\ksguid.lib;$(DDK_LIB_PATH)\ntstrsafe.lib;$(DDK_LIB_PATH)\msgpioclxstub.lib</AdditionalDependencies> + </Link> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)</AdditionalIncludeDirectories> + </ResourceCompile> + <ClCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)</AdditionalIncludeDirectories> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <ExceptionHandling> + </ExceptionHandling> + </ClCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SPB_INC_PATH)\$(SPB_VERSION_MAJOR).$(SPB_VERSION_MINOR)</AdditionalIncludeDirectories> + </Midl> + </ItemDefinitionGroup> + <ItemGroup> + <ResourceCompile Include="simgpio_i2c.rc" /> + </ItemGroup> + <ItemGroup> + <Inf Exclude="@(Inf)" Include="*.inf" /> + <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" /> + <FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" /> + </ItemGroup> + <ItemGroup> + <None Exclude="@(None)" Include="*.txt;*.htm;*.html" /> + <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" /> + <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" /> + </ItemGroup> + <ItemGroup> + <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> +</Project>
\ No newline at end of file diff --git a/gpio/samples/simgpio_i2c/simgpio_i2c.vcxproj.Filters b/gpio/samples/simgpio_i2c/simgpio_i2c.vcxproj.Filters new file mode 100644 index 00000000..e5d09d4d --- /dev/null +++ b/gpio/samples/simgpio_i2c/simgpio_i2c.vcxproj.Filters @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> + <UniqueIdentifier>{522EE08F-03EE-45E0-875A-A47E2E9F025C}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files"> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + <UniqueIdentifier>{22B4A1F1-B05A-4383-A9D2-BDBF45C56975}</UniqueIdentifier> + </Filter> + <Filter Include="Resource Files"> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> + <UniqueIdentifier>{E394D95C-DF2A-4F28-9A22-A3C121FC5394}</UniqueIdentifier> + </Filter> + <Filter Include="Driver Files"> + <Extensions>inf;inv;inx;mof;mc;</Extensions> + <UniqueIdentifier>{5D13D9B5-61BC-45B1-AF0D-4EDE9C7D2F80}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="simgpio_i2c.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="spb_i2c.c"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <FilesToPackage Include=".\Debug\\simgpio_i2c.inf"> + <Filter>Driver Files</Filter> + </FilesToPackage> + <Inf Include=".\simgpio_i2c.inx"> + <Filter>Driver Files</Filter> + </Inf> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="simgpio_i2c.rc"> + <Filter>Resource Files</Filter> + </ResourceCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/gpio/samples/simgpio_i2c/spb_i2c.c b/gpio/samples/simgpio_i2c/spb_i2c.c new file mode 100644 index 00000000..a5ac3fc3 --- /dev/null +++ b/gpio/samples/simgpio_i2c/spb_i2c.c @@ -0,0 +1,666 @@ + +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + spb_i2c.c + +Abstract: + + This sample implements a GPIO client driver for simulated GPIO (SimGpio) + controller. + + Note: DIRQL in the comments below refers to device IRQL, which is any + IRQL > DISPATCH_LEVEL (and less than some IRQL reserved for OS use). + + +Environment: + + Kernel mode + +--*/ + +// +// ------------------------------------------------------------------- Includes +// + +#include <ntddk.h> +#include <wdf.h> +#include <gpioclx.h> +#define RESHUB_USE_HELPER_ROUTINES +#include "reshub.h" // Resource and descriptor definitions +#include "spb.h" // SPB definitions +#include "simgpio_i2c.h" +#include "trace.h" +#include "spb_i2c.tmh" + +// +// -------------------------------------------------------------------- Defines +// + +NTSTATUS +SimGpioSpbRead ( + _In_ PSIM_GPIO_CONTEXT GpioContext, + _In_ SIM_GPIO_REGISTER_ADDRESS RegisterAddress, + _Out_writes_(DataLength) PUCHAR Data, + _In_ USHORT DataLength + ); + +NTSTATUS +SimGpioSpbSequence ( + _In_ PSIM_GPIO_CONTEXT GpioContext, + _In_reads_(SequenceLength) PVOID Sequence, + _In_ SIZE_T SequenceLength + ); + +NTSTATUS +SimGpioSpbWrite ( + _In_ PSIM_GPIO_CONTEXT GpioContext, + _In_ USHORT RegisterAddress, + _In_reads_(DataLength) PUCHAR Data, + _In_ ULONG DataLength + ); + +// +// ------------------------------------------------------------------ Functions +// + +NTSTATUS +SimGpioSetupSpbConnection ( + _In_ PSIM_GPIO_CONTEXT GpioContext + ) + +/*++ + +Routine Description: + + This routine opens an I/O target to the controller driver using the + connection ID received during PrepareController callback. + + N.B. This function is not marked pageable because this function is in + the device power up path. + +Arguments: + + GpioContext - Supplies a pionter to the client driver's device context. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + WDF_OBJECT_ATTRIBUTES Attributes; + WDF_IO_TARGET_OPEN_PARAMS Parameters; + WDF_OBJECT_ATTRIBUTES RequestAttributes; + PSIM_GPIO_REQUEST_CONTEXT RequestContext; + NTSTATUS Status; + DECLARE_UNICODE_STRING_SIZE(SpbDevicePath, RESOURCE_HUB_PATH_SIZE); + + WDF_OBJECT_ATTRIBUTES_INIT(&Attributes); + Status = WdfIoTargetCreate(GpioContext->Device, + &Attributes, + &GpioContext->SpbIoTarget); + + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_INIT, + "%s: WdfIoTargetCreate failed to create SPB IoTarget! " + "Device = %p, Status:%#x\n", + __FUNCTION__, + GpioContext->Device, + Status); + + if (GpioContext->SpbIoTarget != NULL) { + WdfObjectDelete(GpioContext->SpbIoTarget); + } + + goto SetupSpbConnectionEnd; + } + + // + // Use the connection ID supplied to create the full device path. This + // device path (indirectly) represents the path for the I2C controller. + // + + Status = RESOURCE_HUB_CREATE_PATH_FROM_ID( + &SpbDevicePath, + GpioContext->SpbConnectionId.LowPart, + GpioContext->SpbConnectionId.HighPart); + + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_SPB, + "ResourceHub create device path (%wZ) failed Status:%#x", + &SpbDevicePath, + Status); + + goto SetupSpbConnectionEnd; + } + + // + // Initialize the parameters for the SPB IO target. + // + + WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME(&Parameters, + &SpbDevicePath, + (GENERIC_READ | GENERIC_WRITE)); + + Parameters.ShareAccess = 0; + Parameters.CreateDisposition = FILE_OPEN; + Parameters.FileAttributes = FILE_ATTRIBUTE_NORMAL; + + // + // Open the SPB IO target. This creates a handle to the I2C controller + // behind which SimGPO resides. + // + + Status = WdfIoTargetOpen(GpioContext->SpbIoTarget, &Parameters); + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_SPB, + "WdfIoTargetOpen failed to open SPB target Status:%#x", + Status); + + goto SetupSpbConnectionEnd; + } + + // + // Create a SPB request for writes and reads to the IO target. + // + + WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&RequestAttributes, \ + SIM_GPIO_REQUEST_CONTEXT); + + Status = WdfRequestCreate(&RequestAttributes, + NULL, + &GpioContext->SpbRequest); + + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_SPB, + "%s: WdfRequestCreate failed creating SpbRequest Status:%#x", + __FUNCTION__, + Status); + + goto SetupSpbConnectionEnd; + } + + // + // Initialize the request context with default values + // + + RequestContext = GetRequestContext(GpioContext->SpbRequest); + RequestContext->Device = GpioContext->Device; + RequestContext->SequenceRequest = FALSE; + +SetupSpbConnectionEnd: + return Status; +} + +VOID +SimGpioDestroySpbConnection ( + _In_ PSIM_GPIO_CONTEXT GpioContext + ) + +/*++ + +Routine Description: + + This routine closes the SPB I/O target and releases SPB-related resources. + + N.B. This function is not marked pageable because this function is in + the device power down path. + +Arguments: + + GpioContext - Supplies a pionter to the client driver's device context. + +Return Value: + + None. + +--*/ + +{ + + // + // Delete the SPB request. + // + + if (GpioContext->SpbRequest != NULL) { + WdfObjectDelete(GpioContext->SpbRequest); + } + + // + // Delete the IO target. Note this will also close if it is opened. + // + + if (GpioContext->SpbIoTarget != NULL) { + WdfObjectDelete(GpioContext->SpbIoTarget); + } + + return; +} + +NTSTATUS +SimGpioSpbReadByte ( + _In_ PSIM_GPIO_BANK GpioBank, + _In_ SIM_GPIO_REGISTER_ADDRESS RegisterAddress, + _Out_writes_(sizeof(UCHAR)) PUCHAR Data + ) + +/*++ + +Routine Description: + + This routine performs a single-byte read from the SPB I/O target. + + N.B. This routine is called at PASSIVE_LEVEL for off-SoC GPIOs but is not + marked as PAGED_CODE as it could be executed late in the hibernate or + early in resume sequence (or the deep-idle sequence). + +Arguments: + + GpioBank - Supplies a pionter to the GPIO bank to be read from. + + RegisterAddress - Supplies the bank-relative register address to be read. + + Data - Supplies the byte buffer to read the data into. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + USHORT ActualAddress; + PSIM_GPIO_CONTEXT GpioContext; + NTSTATUS Status; + + if (RegisterAddress >= MaximumSimGpioAddress) { + Status = STATUS_NOT_SUPPORTED; + goto SpbReadByteEnd; + } + + ActualAddress = GpioBank->AddressBase + (USHORT)RegisterAddress; + GpioContext = GpioBank->GpioContext; + Status = SimGpioSpbRead(GpioContext, RegisterAddress, Data, sizeof(UCHAR)); + +SpbReadByteEnd: + return Status; +} + +NTSTATUS +SimGpioSpbRead ( + _In_ PSIM_GPIO_CONTEXT GpioContext, + _In_ SIM_GPIO_REGISTER_ADDRESS RegisterAddress, + _Out_writes_(DataLength) PUCHAR Data, + _In_ USHORT DataLength + ) + +/*++ + +Routine Description: + + This routine performs a read from the SPB I/O target. + + N.B. This routine is called at PASSIVE_LEVEL for off-SoC GPIOs but is not + marked as PAGED_CODE as it could be executed late in the hibernate or + early in resume sequence (or the deep-idle sequence). + +Arguments: + + GpioContext - Supplies a pionter to the client driver's device context. + + RegisterAddress - Supplies the absolute register address to read from. + + Data - Supplies the output byte buffer containing the data read. + + DataLength - Supplies the length of the byte buffer data. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + USHORT Address; + UCHAR Index; + SPB_TRANSFER_LIST_AND_ENTRIES(2) Sequence; + NTSTATUS Status; + + // + // Build the SPB sequence (send address, read data). + // + + Address = RegisterAddress; + SPB_TRANSFER_LIST_INIT(&(Sequence.List), 2); + Index = 0; // silence prefast + Sequence.List.Transfers[Index] = SPB_TRANSFER_LIST_ENTRY_INIT_SIMPLE( + SpbTransferDirectionToDevice, + 0, + &Address, + SIM_GPIO_REGISTER_ADDRESS_SIZE); + + Index += 1; // silence prefast + Sequence.List.Transfers[Index] = SPB_TRANSFER_LIST_ENTRY_INIT_SIMPLE( + SpbTransferDirectionFromDevice, + 0x0, + Data, + DataLength); + + // + // Send the read as a sequence request to the SPB target. + // + + Status = SimGpioSpbSequence(GpioContext, &Sequence, sizeof(Sequence)); + if (!NT_SUCCESS(Status)) { + TraceEvents(TRACE_LEVEL_ERROR, + TRACE_FLAG_SPB, + "%s: SpbSequence failed sending a read sequence! " + "Status:%#x\n", + __FUNCTION__, + Status); + } + + return Status; +} + +NTSTATUS +SimGpioSpbWriteByte ( + _In_ PSIM_GPIO_BANK GpioBank, + _In_ SIM_GPIO_REGISTER_ADDRESS RegisterAddress, + _In_ UCHAR Data + ) + +/*++ + +Routine Description: + + This routine performs a single-byte write to the SPB I/O target. + + N.B. This routine is called at PASSIVE_LEVEL for off-SoC GPIOs but is not + marked as PAGED_CODE as it could be executed late in the hibernate or + early in resume sequence (or the deep-idle sequence). + +Arguments: + + GpioBank - Supplies a pionter to the GPIO bank to be written to. + + RegisterAddress - Supplies the bank-relative register address to write to. + + Data - Supplies the data to be written. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + USHORT ActualAddress; + PSIM_GPIO_CONTEXT GpioContext; + NTSTATUS Status; + + if (RegisterAddress >= MaximumSimGpioAddress) { + Status = STATUS_NOT_SUPPORTED; + goto SpbWriteByteEnd; + } + + ActualAddress = GpioBank->AddressBase + (USHORT)RegisterAddress; + GpioContext = GpioBank->GpioContext; + Status = SimGpioSpbWrite(GpioContext, RegisterAddress, &Data, sizeof(UCHAR)); + +SpbWriteByteEnd: + return Status; +} + +NTSTATUS +SimGpioSpbWrite ( + _In_ PSIM_GPIO_CONTEXT GpioContext, + _In_ USHORT RegisterAddress, + _In_reads_(DataLength) PUCHAR Data, + _In_ ULONG DataLength + ) + +/*++ + +Routine Description: + + This routine performs a write to the SPB I/O target. + + N.B. This routine is called at PASSIVE_LEVEL for off-SoC GPIOs but is not + marked as PAGED_CODE as it could be executed late in the hibernate or + early in resume sequence (or the deep-idle sequence). + +Arguments: + + GpioContext - Supplies a pionter to the client driver's device context. + + RegisterAddress - Supplies the absolute register address to write to. + + Data - Supplies the byte buffer containing the data to be written. + + DataLength - Supplies the length of the write buffer. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + PUCHAR Buffer; + ULONG BufferLength; + ULONG_PTR BytesWritten; + WDF_MEMORY_DESCRIPTOR MemoryDescriptor; + WDFMEMORY MemoryWrite; + NTSTATUS Status; + + MemoryWrite = NULL; + if ((Data == NULL) || (DataLength <= 0)) { + Status = STATUS_INVALID_PARAMETER; + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_SPB, + "%s: Invalid write request! Data:%p Length:%lu Status:%#x\n", + __FUNCTION__, + Data, + DataLength, + Status); + + goto SpbWriteEnd; + } + + // + // A SPB write-write is a single write request with the register + // and data combined in one buffer. So we need to allocate memory + // for the size of a register + data length + + BufferLength = DataLength + SIM_GPIO_REGISTER_ADDRESS_SIZE; + Status = WdfMemoryCreate(WDF_NO_OBJECT_ATTRIBUTES, + NonPagedPoolNx, + SIM_GPIO_POOL_TAG, + BufferLength, + &MemoryWrite, + (PVOID*)&Buffer); + + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_SPB, + "%s: WdfMemoryCreate failed allocating memory buffer for write!" + "Status:%#x\n", + __FUNCTION__, + Status); + + goto SpbWriteEnd; + } + + // + // Setup the write buffer. The buffer should contain address followed by + // data. + // + + RtlCopyMemory(Buffer, &RegisterAddress, SIM_GPIO_REGISTER_ADDRESS_SIZE); + RtlCopyMemory((Buffer + SIM_GPIO_REGISTER_ADDRESS_SIZE), Data, DataLength); + + // + // Send the request synchronously. + // + + WDF_MEMORY_DESCRIPTOR_INIT_HANDLE(&MemoryDescriptor, MemoryWrite, NULL); + Status = WdfIoTargetSendWriteSynchronously( + GpioContext->SpbIoTarget, + NULL, + &MemoryDescriptor, + NULL, + NULL, + &BytesWritten); + + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_SPB, + "%s: WdfIoTargetSendWriteSynchronously failed! Status = %#x\n", + __FUNCTION__, + Status); + + goto SpbWriteEnd; + } + +SpbWriteEnd: + if (MemoryWrite != NULL) { + WdfObjectDelete(MemoryWrite); + } + + return Status; +} + +NTSTATUS +SimGpioSpbSequence ( + _In_ PSIM_GPIO_CONTEXT GpioContext, + _In_reads_(SequenceLength) PVOID Sequence, + _In_ SIZE_T SequenceLength + ) + +/*++ + +Routine Description: + + This routine issues a sequence read-write request to the SPB I/O target. + + N.B. This routine is called at PASSIVE_LEVEL for off-SoC GPIOs but is not + marked as PAGED_CODE as it could be executed late in the hibernate or + early in resume sequence (or the deep-idle sequence). + +Arguments: + + GpioContext - Supplies a pionter to the client driver's device context. + + Sequence - Supplies a pointer to a list of sequence transfers. + + SequenceLength - Supplies the length of sequence transfers. + +Return Value: + + NTSTATUS code. + +--*/ + +{ + + WDF_OBJECT_ATTRIBUTES Attributes; + ULONG_PTR BytesReturned; + WDF_MEMORY_DESCRIPTOR MemoryDescriptor; + WDFMEMORY MemorySequence; + NTSTATUS Status; + + if ((Sequence == NULL) || (SequenceLength == 0)) { + Status = STATUS_INVALID_PARAMETER; + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_SPB, + "%s: Invalid sequence request! Sequence:%p Length:%lu Status:%#x\n", + __FUNCTION__, + Sequence, + (ULONG)SequenceLength, + Status); + + goto SpbSequenceEnd; + } + + // + // Create preallocated WDFMEMORY. + // + + WDF_OBJECT_ATTRIBUTES_INIT(&Attributes); + Status = WdfMemoryCreatePreallocated( + &Attributes, + Sequence, + SequenceLength, + &MemorySequence); + + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_SPB, + "%s: WdfMemoryCreatePreallocated failed! Status:%#x\n", + __FUNCTION__, + Status); + + goto SpbSequenceEnd; + } + + WDF_MEMORY_DESCRIPTOR_INIT_HANDLE(&MemoryDescriptor, MemorySequence, NULL); + + // + // Send the SPB sequence IOCTL. + // + + Status = WdfIoTargetSendIoctlSynchronously( + GpioContext->SpbIoTarget, + NULL, + IOCTL_SPB_EXECUTE_SEQUENCE, + &MemoryDescriptor, + NULL, + NULL, + &BytesReturned); + + if (!NT_SUCCESS(Status)) { + TraceEvents( + TRACE_LEVEL_ERROR, + TRACE_FLAG_SPB, + "%s: Failed sending SPB sequence request! Bytes:%lu Status:%#x", + __FUNCTION__, + (ULONG)BytesReturned, + Status); + + goto SpbSequenceEnd; + } + +SpbSequenceEnd: + return Status; +} + + diff --git a/gpio/samples/simgpio_i2c/trace.h b/gpio/samples/simgpio_i2c/trace.h new file mode 100644 index 00000000..22969f69 --- /dev/null +++ b/gpio/samples/simgpio_i2c/trace.h @@ -0,0 +1,75 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + + THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + PURPOSE. + +Module Name: + + trace.h + +Abstract: + + This sample implements a GPIO client driver for simulated GPIO (SimGpio) + controller. + + Note: DIRQL in the comments below refers to device IRQL, which is any + IRQL > DISPATCH_LEVEL (and less than some IRQL reserved for OS use). + +Environment: + + Kernel mode + +--*/ + +#ifndef _TRACE_H +#define _TRACE_H + +#include <evntrace.h> // For TRACE_LEVEL definitions + +// +// Define the tracing flags. +// +// Tracing GUID - e71e3b6e-aaf9-402c-97fe-7610cb3985db +// + +#define WPP_CONTROL_GUIDS \ + WPP_DEFINE_CONTROL_GUID( \ + SimGpioI2CTraceGuid, \ + (e71e3b6e,aaf9,402c,97fe,7610cb3985db), \ + WPP_DEFINE_BIT(TRACE_FLAG_INIT) \ + WPP_DEFINE_BIT(TRACE_FLAG_SPB) \ + WPP_DEFINE_BIT(TRACE_FLAG_REQUEST) \ + WPP_DEFINE_BIT(TRACE_FLAG_INTERRUPT) \ + WPP_DEFINE_BIT(TRACE_FLAG_IO) \ + ) + +#define WPP_FLAG_LEVEL_LOGGER(flag, level) \ + WPP_LEVEL_LOGGER(flag) + +#define WPP_FLAG_LEVEL_ENABLED(flag, level) \ + (WPP_LEVEL_ENABLED(flag) && \ + WPP_CONTROL(WPP_BIT_ ## flag).Level >= level) + +#define WPP_LEVEL_FLAGS_LOGGER(lvl,flags) \ + WPP_LEVEL_LOGGER(flags) + +#define WPP_LEVEL_FLAGS_ENABLED(lvl, flags) \ + (WPP_LEVEL_ENABLED(flags) && WPP_CONTROL(WPP_BIT_ ## flags).Level >= lvl) + +// +// This comment block is scanned by the trace preprocessor to define our +// Trace function. +// +// begin_wpp config +// FUNC FuncEntry{LEVEL=TRACE_LEVEL_VERBOSE}(FLAGS); +// FUNC FuncExit{LEVEL=TRACE_LEVEL_VERBOSE}(FLAGS); +// USEPREFIX(FuncEntry, "%!STDPREFIX! [%!FUNC!] --> entry"); +// USEPREFIX(FuncExit, "%!STDPREFIX! [%!FUNC!] <--"); +// end_wpp +// + +#endif _TRACE_H
\ No newline at end of file |
