UMDF Overview
UMDF Architecture
UMDF: User-Mode Driver Framework
- UMDF drivers abstract hardware functionality, run in the user-mode environment;
- UMDF drivers operate as part of a stack of drivers that manage a device;
Most of the requests that are sent to device drivers are packaged in I/O request packets (IRPs). Each device is represented by a device node, and each device node has a device stack. To send a read, write, or control request to a device, the I/O manager locates the device node for the device and then sends an IRP to the device stack of that node. - UMDF system-supplied components;
Driver host process
The driver host process loads vendor-supplied UMDF drivers and framework DLLs, provides an execution environment for user-mode drivers, and routes messages between drivers in a user-mode stack.
Driver manager
The driver manager is a Windows service that manages all instances of the Wudfhost driver host process. The driver manager launches and tracks information about each driver host process. Each host is a child process of the driver manager.
Wudfhost
Wudfhost.exe load one or more UMDF driver DLLs and framework DLLs. The driver hosting process provides a running environment to handle internal process communication, IO distribution, driver loading, driver layering, and thread pool management between the driver manager and the reflector.
Reflector
The reflector is a kernel-mode driver that permits an application and a driver host process (and user-mode device stacks) to communicate. The reflector creates a separate device object for each device instance and handles Plug and Play (PnP) and power I/O requests associated with each device instance. All communication between the application and the driver host process happens through the reflector.
- UMDF Component process summary
The construction and tear down of a user-mode stack is driven by Plug and Play (PnP) events.
The driver manager launches the driver host process and provides sufficient information to the launched process to build the user-mode stack.
The driver host process provides the execution environment for user-mode drivers and routes messages between drivers in the user-mode stack.
The reflector uses a message-based interprocess communication mechanism to communicate with the driver manager and host process.
To send an I/O request to a UMDF driver, an application calls a Win32 file I/O function, such as CreateFile, ReadFileEx, CancelIoEx, or DeviceIoControl. When the reflector receives a request from the client application, it sends the request to the appropriate driver host process. The driver host process then routes the request to the top of the correct user-mode device stack.
The request is either completed by one of the drivers in the user-mode stack or forwarded by one of the drivers back to the reflector. When the reflector receives a request from the user-mode driver stack, it sends the request down the kernel-mode stack for completion.
Development Process
- Driver workflow
Everything in the framework is represented by objects (Driver, Devices, Queues, etc.)
Objects have methods, events, and properties
Objects are accessed via handles from methods like WdfDeviceGetDriver
- Driver Initialization
DriverEntry – Creating the driver, First routine called
DriverEntry is the first driver-supplied routine called when driver is loaded
NTSTATUS
DriverEntry(
_In_ PDRIVER_OBJECT DriverObject,
_In_ PUNICODE_STRING RegistryPath
)
{
[…]
// Initialize the driver configuration structure.
//
WDF_DRIVER_CONFIG_INIT(&DriverConfig, OnDeviceAdd);
DriverConfig.EvtDriverUnload = OnDriverUnload;
//
// Create a framework driver object to represent our driver.
//
Status = WdfDriverCreate(DriverObject,
RegistryPath,
WDF_NO_OBJECT_ATTRIBUTES,
&DriverConfig,
WDF_NO_HANDLE);
NTSTATUS
OnDeviceAdd(
_In_ WDFDRIVER /*Driver*/,
_Inout_ PWDFDEVICE_INIT pDeviceInit
)
3. Hardware preparation
NTSTATUS
MultiDevice::OnD0Entry(
_In_ WDFDEVICE Device, // Supplies a handle to the framework device object
_In_ WDF_POWER_DEVICE_STATE /*PreviousState*/) // WDF_POWER_DEVICE_STATE-typed enumerator that identifies
for (unsigned int i = 0; i < SensorInstanceCount;i++)
{
pDevice = GetMultiDeviceContextFromSensorInstance(SensorInstancesBuffer[i]);
[…]
// configure sensor hardware
Status = pDevice->ConfigureSensor(); --------->Function implementation pointing to specific objects
if (!NT_SUCCESS(Status))
{
TraceError("ACC %!FUNC! Configure sensor failed: %!STATUS!", Status);
goto Exit;
}
InitPropVariantFromUInt32(SensorState_Idle,
&(pDevice->m_pProperties->List[SENSOR_PROPERTY_STATE].Value));
pDevice->m_PoweredOn = TRUE;
pDevice->m_D0Entry = TRUE;
}
- Device operational
// Register CLX callback function pointers
SENSOR_CONTROLLER_CONFIG SensorConfig;//contains pointers to callback functions that must be implemented by the driver, and passed on to the class extension to call
SENSOR_CONTROLLER_CONFIG_INIT(&SensorConfig);
SensorConfig.DriverIsPowerPolicyOwner = WdfUseDefault;//parents to take advantage of pep-based power management.
SensorConfig.EvtSensorStart = MultiDevice::OnStart;
SensorConfig.EvtSensorStop = MultiDevice::OnStop;
SensorConfig.EvtSensorGetSupportedDataFields = MultiDevice::OnGetSupportedDataFields;
SensorConfig.EvtSensorGetDataInterval = MultiDevice::OnGetDataInterval;
SensorConfig.EvtSensorSetDataInterval = MultiDevice::OnSetDataInterval;
SensorConfig.EvtSensorGetDataFieldProperties = MultiDevice::OnGetDataFieldProperties;
SensorConfig.EvtSensorGetDataThresholds = MultiDevice::OnGetDataThresholds;
SensorConfig.EvtSensorSetDataThresholds = MultiDevice::OnSetDataThresholds;
SensorConfig.EvtSensorGetProperties = MultiDevice::OnGetProperties;
SensorConfig.EvtSensorDeviceIoControl = MultiDevice::OnIoControl;
// Set up power capabilities and IO queues
Status = SensorsCxDeviceInitialize(Device, &SensorConfig);//initializes the sensor
UMDF Environment
Need to install Visual Studio and Windows Driver Kit (WDK) in advance.
- Install Visual Studio & WDK
- Pick a Windows Universal Driver template or sample
- Select driver architecture (ARM, x86, x64)
- Add your code, headers, libs, etc.
- Compile / debug / etc.
- Deploy to target machine (dev board)
- Run available tests (HLK)
According to base device class, create the device class corresponding to the sensor
// Base ---------------------------------------------------------------------
typedef class _ComboDevice
{}
// Linear Accelerometer --------------------------------------------------------------
typedef class _LinearAccelerometerDevice : public _ComboDevice
{}
Implement the function about the new device class
NTSTATUS
LinearAccelerometerDevice::Initialize(
_In_ WDFDEVICE Device,
_In_ SENSOROBJECT SensorInstance
)
NTSTATUS
LinearAccelerometerDevice::GetData(
)
{
Register the new device classes into the system
UMDF ACPI related
ROTM
The matrix in the method of right picture indicates that the X, Y, and Z axes of the sensor hardware are along the Y, -X, and -Z axes of the phone
PRIM
The method of right picture marks the sensor as primary sensor
Modify the ACPI Table
1: download the iasl compiler to modify the ACPI table, users should use the iasl.exe to do the ACPI table modification and configuration (https://www.acpica.org/downloads).
2: In cmd prompt w/ Admin privileges run command:
acpidump -b -n DSDT -o dsdt.dat.
iasl -d dsdt.dat
This dsdt.dsl file is the ACPI code of current system.
3: Add new device node in the dsl file of step2 and then recompile dsl by running this command:
iasl dsdt.dsl
An .aml file will be produce, rename the .aml output file as ACPITABL.DAT.
4: Copy the ACPITABL.DAT of step4 to C:\WINDOWS\SYSTEM32.
Run the following commands in cmd prompt w/ Admin privileges.
bcdedit /set testsigning on
bcdedit /set nointegritychecks on
5: Reboot and Device node has been added into the current ACPI.