How to Develop a UMDF Driver - Microsoft

How to Develop a UMDF Driver
Part 1
Outline
Architectural Goals
Architectural Description
Core components
Driver Manager, Reflector, Host Process, and Drivers
Driver startup and teardown
I/O flow
Layered drivers
Driver Programming Model
WDF object model
COM-lite
UMDF DDIs
Goals
An understanding of UMDF infrastructure
An understanding of the UMDF DDIs and how
they are structured
Architectural Goals
Stability
Driver failures should NOT bring down the system
Build on Windows NT I/O model
Applications are transparent to driver runtime
environment – User or Kernel mode
Asynchronous, layered drivers, and packet based
Integrate into PnP for device installs, driver load/unload
Security
Driver failures should NOT compromise the system
Driver runs in “LocalService” account
Shared model between KMDF and UMDF
Shared model != same DDIs or data structures
Architectural Block Diagram
Application(s)
Driver
Manager
Host Process
Host Process
UM
Driver
UM
Driver
WinSock
User
Kernel
Reflector
Windows Kernel
(I/O Mgr, PnP)
Reflector
Kernel Driver
(e.g., WinUSB)
Provided by:
Microsoft
ISV
IHV
Kernel Driver 2
Device Stack
Device Stack
UMDF Components
Driver manager
Global, system-wide Windows Service
Responsible for host process lifetime
Responds to messages from reflector
Always running
Started during installation of the first UMDF device
Host Process
Driver
Manager
UM
Driver
Reflector
UMDF Components
Reflector
Nucleus of UMDF
Installed with each device stack
Proxy for UM device stack in kernel
Forwards I/O, Power, and PnP messages from kernel to host
process
Ties the UM device stack to KM side
Ensures “good behavior” of UM drivers
Proper responses to messages
Timely completion of critical system messages
Tracks all outstanding kernel resources
Host Process
Driver
Manager
UM
Driver
Reflector
UMDF Components : Host Process
Child process of the UM Driver Manager
Unit of isolation for the device stack
Driver crash will not affect other device stacks
Container for Framework and UM driver(s)
Runtime environment for framework and driver
I/O dispatching, driver loading, driver layering, thread pool
Handles messages from reflector and driver manager
Driver
Manager
Runtime
Reflector
Framework
DDI
UM
Driver
Host Process - Framework
Implements all of the WDF model
Queues, Request, I/O target, etc
Implements default behavior
Reduces vendor written code
Exposes DDI
Implemented as DLL
Runtime
Framework
DDI
UM
Driver
IPC : Message Passing
Requirements
Packet-based and asynchronous
Cancelable
Fail-safe
Efficient data transfer
Secure
Several potential solution
Pending I/O, synchronous message passing
Windows codenamed “Longhorn” solution: ALPC
Standard Windows OS Component
Processing Messages
KM drivers handle I/O in caller’s thread
Obviously not a option for UMDF
Host has a thread pool processing messages
Dedicated thread for “critical” operations
To ensure I/O doesn’t block them
e.g., PnP/PM, Cancellation, Cleanup, and Close
Shared threads for non-critical operations like I/O
Pool could grow or shrink as load changes
Fixed at 2 threads for Beta 1
For long operations the driver should consider using
work-items
Driver Loading
5
Driver
Manager
3
Host
Runtime
2
Windows Kernel
(I/O Mgr, PnP)
Provided by:
Microsoft
IHV
1
Add Device
6
Framework
4
UM
Driver
IPC Channel
Reflector
Kernel Driver
Driver Loading (Layered UM Drivers)
7
Driver
Manager
3
Host
Runtime
8
5
6
2
Windows Kernel
(I/O Mgr, PnP)
Provided by:
Microsoft
IHV
1
Add Device
UM Filter
Driver
Framework
Framework
4
UM Driver
IPC Channel
Reflector
Kernel Driver
Driver Loading (Host Process)
Add Device
Host
Runtime
Add Device
Driver creates WDF Device
Framework
UM Driver
Driver creates WDF Queue
Driver creates WDF Queue
Add Device
Provided by:
Microsoft
IHV
I/O Data Flow
UM Filter
Driver
Framework
UM I/O Mgr
1
5
Host Process
Application
4
Device
Stack
6
7
9
8
Win32 I/O
API
Framework
10
11
3
UM Driver
12
IPC Message
Windows Kernel
2
IRP
“Up” Device
Object
“Down” Device
Object
Reflector
Provided by:
Microsoft
ISV
IHV
Kernel driver
I/O in Host Process
Invoke driver callback
passing in I/O request
Run-time
Host
File Object
UM
IRP
UM Irp
Framework
Device
Stack
IPC Message Host File Object returned
as context to the reflector.
Provided by:
Microsoft
ISV
IHV
Driver can return without
completing i/o (asynch)
Driver eventually
completes i/o.
UM Driver
Device Removal and Cleanup
Run-time
Framework
UM Driver
Device Remove
Message path similar to “add device” path
Driver gets several PnP notifications for remove
UM driver may unload when remove finishes
Host process may exit when all drivers are
unloaded
Host lifetime controlled by the driver manager
When Driver or Application Crashes
When the UM driver crashes:
Reflector gets notification from OS
Reflector tracks outstanding I/O in the host.
Completed with STATUS_DRIVER_PROCESS_TERMINATED
Win32 apps will see ERROR_DRIVER_PROCESS_TERMINATED
The kernel drivers for the device stack are unloaded
Device is disabled (yellow bang in device manager)
When the application crashes:
Reflectors gets IRP_MJ_CLEANUP
Sent to host on the “cancel” IPC channel
Host/UM driver complete pending I/O requests
Timeout Policy
UMDF enforces timeouts on “critical” operations
Operations that run under system wide locks
Operations that affect user experience
If operations do not complete on time
Host is forcibly terminated and error report is generated
Critical operations include
PnP and PM operations
These run under a system-wide PnP lock
Blocks other PnP operations
I/O Cancellation
Long term operations must be cancelable
Adversely affects user experience (application hangs)
In WinHEC release, time out = 1 minute
Will adjust based on feedback & failure report data
for RTM
Impersonation
Driver runs in LocalService security context
Drivers can impersonate the client process
Only for I/O requests
Not for PnP or other system messages
typedef enum _SECURITY_IMPERSONATION_LEVEL {
SecurityAnonymous,
SecurityIdentification,
SecurityImpersonation,
SecurityDelegation
} SECURITY_IMPERSONATION_LEVEL;
Using Impersonation
Application controls the allowed level
Specified in QOS settings in CreateFile API
See dwFlagsAndAttributes parameter in MSDN
INF sets the driver’s maximum desired level
Stored in registry during device installation
Set this as low as possible for your driver
Reduces chance of “elevation of privilege” attack
Driver requests impersonation for given request
Specifies desired level and a callback
Isolate impersonated code
Do minimal work to reduce the attack surface
UMDF Verifier
Built-in verification
Checks for problems in framework code
Checks for problems in device driver code
Always enabled & always fatal
Unless you have a debugger attached
DDI misusage result in verifier failure
Passing incorrect parameters, including NULL, in DDI
Incorrect DDI call sequence
More aggressive checks planned for Beta 2
Verifier failure causes a UMDF “Bugcheck”
Generates Memory Dump
Generates Error Report
Sends Error Report to Microsoft (opt-in)
UMDF Verifier Failures
Driver failures will “Bugcheck” the host
Bugcheck is NOT the “Blue Screen of Death”
We will pick a less scary name in Beta 2
Bugcheck will:
Save memory dump to log file directory
%windir%\System32\LogFiles\WUDF\xxx.dmp
Create an error report to report to Microsoft (opt-in)
Break into debugger if present
Prints out error message
Developer can continue…but that may lead to another crash
Terminate the host process and disable the device
UMDF Error Reporting
Current driver failure reports are hard to analyze
Too much information in the running kernel
Not enough fits into a minidump
User-Mode Drivers reports can be better
Compartmentalized – less information to collect
Specialized – not mixed with irrelevant state
Better collection – we’re not limited by crash dump
UMDF reports problems through Windows Error
Reporting (WER)
UMDF queues reports for later approval and upload
WER provides for a global “opt-in” to upload quicker
No modal crash dialogs
Bubble on the task bar indicating an incident has occurred
Please upload error reports for Beta 1
Even if you think it’s your driver
We need data to refine what we’re collecting
Types of Error Reports
UMDF will report the following problems :
UM Driver Verifier failure
Unhandled Exception in host process
Unexpected Host Termination
Failure or Timeout of “Critical” Operation
Error Report may contain:
Memory dump of the host process
Copy of UMDF’s internal trace log
Configuration information about the device
Device name, manufacturer, drivers installed
Driver binary versions
Internal analysis of the problem
Address of the last driver to framework call (or vice versa)
Problem code, exception info, etc...
Report contents depend on the problem detected
Device Driver Interfaces (DDI)
and Programming Patterns
Framework Object Model
Framework objects have a
hierarchical relationship
WDF Driver is the root object
Child lifetime scoped by parent
Every WDF object is defined by
Methods: actions on objects
Properties: are object attributes
Events: notifications from
Framework objects to the driver
WDF Driver
WDF Device
WDF
Queue
WDF
Queue
Key Framework Objects
Object
Driver
Device
I/O Request
I/O Queue
File
I/O Target
Description
Supports one or more devices
One per driver per host process
Tracks PnP/PM state changes and
notifies driver
Represents application’s I/O request
Controls I/O request flow into driver
Provides per-handle context for driver
Represents next lower device in stack
Encapsulates driver to driver
communication
Parent-Child Object Hierarchy
Applies to lifetime management
Lifetime of child is scoped within that of the parent
Child is cleaned up when the parent goes away
Driver
Device
I/O Queue
File
I/O Request
I/O Target
Framework Object Model
Framework
Driver
WDF Driver
MyDriver
MyDevice
WDF Device
WDF
Queue
WDF
Queue
WDF Objects
Provided by:
Microsoft
IHV
MyRead
Queue
MyWrite
Queue
Driver implemented
Callback Objects
Framework Object Model
Framework
Driver 1
MyDriver1
WDF Driver
MyDevice1
WDF Device
WDF Queue
WDF
Queue
WDF Driver
Device Stack
WDF Device
WDF Queue
WDF Queue
MyWrite
Queue1
MyDevice2
MyRead
Queue2
MyRead
Queue1
MyDriver2
MyWrite
Queue2
Driver 2
Each driver in the stack has its own set of
framework objects
DDI Design
Common object model with KMDF
Driver writers familiar with KMDF can quickly come up to speed
with UMDF
Interface based programming model
UM developers are familiar with C++ and OOP
Interfaces allow logical grouping of functions making it easy to
navigate through the DDI
Facilitates opt-in programming
Built-in extensibility
C++ is the supported language in the first version
Uses a small subset of COM (COM-Lite)
COM complexity is in COM RTL features
Threading Model, Automation, etc...
UMDF doesn’t depend on COM RTL
DDI : COM Interfaces
Problems solved by Interfaces
Allows us to evolve the DDI without changing exported functions
Older drivers do not need to be rebuilt
No C++ name mangling
No C++ fragile base class problem
COM facilitates this
Well understood
Existing tools like ATL
We don’t want to invent another model
C++ facilitates this
Interfaces are just abstract base classes
With only pure virtual functions
Compiler implements interface as function pointer table
(V-Table)
All You Need to Know about COM …
COM interfaces, by convention, start with “I”
e.g., IUnknown
All COM interfaces are derived from IUknown
QueryInterface, AddRef, Release
Lifetime management of COM objects
AddRef: takes a reference on Framework object
WDF object model simplifies ref-counting
Release: drops ref-count on Framework object
Driver should release interfaces retrieved from Fx when done
Query Interface (QI): allows discovery of
“interfaces” supported by the driver
Device Driver Interfaces (DDI)
UMDF DDI is in wudfddi.idl
interface IWDFObject : IUnknown
{
HRESULT
DeleteWdfObject();
...
VOID AcquireLock();
VOID ReleaseLock();
};
Simplified C++ equivalent generated by MIDL
struct IWDFObject : public IUnknown
{
virtual HRESULT DeleteWdfObject() = 0;
...
virtual VOID AcquireLock() = 0;
virtual VOID ReleaseLock() = 0;
};
IWDFObject is base interface for all WDF objects
Device Driver Interfaces (DDI)
DDIs are grouped into 2 types of interfaces
Functionality exported by Framework
By convention these begin with IWDF
E.g., IWDFDriver, IWDFDevice, IWDFIoQueue
Callbacks exported by Driver
These are of the form I<WdfObject><Function>
e.g., IQueueCallbackRead, IRequestCallbackCancel
Methods on callback interfaces begin with “On”
interface IQueueCallbackRead : IUnknown
{
void OnRead ( IWDFIoQueue*
pWdfQueue,
IWDFIoRequest* pWdfRequest,
SIZE_T
NumOfBytesToRead );
};
Device Driver Interfaces (DDI)
KMDF and UMDF DDI are similar
Tuned to language and runtime environment
KMDF
NTSTATUS WdfDeviceConfigureRequestDispatching(
WDFDEVICE
Device,
WDFQUEUE
Queue,
WDF_REQUEST_TYPE RequestType,
BOOLEAN
Forward
);
UMDF
HRESULT IWDFDevice::ConfigureRequestDispatching(
IWDFIoQueue *
pQueue,
WDF_REQUEST_TYPE RequestType,
BOOL
Forward
);
Device parameter is implicit in C++
Driver Entry
IDriverEntry is the top driver-exported interface
interface IDriverEntry::IUnknown {
HRESULT OnInitialize( IWDFDriver* pWdfDriver );
HRESULT OnDeviceAdd(
IWDFDriver*
pWdfDriver,
IWDFDeviceInitialize* pWdfDeviceInit
);
VOID
OnDeinitialize();
};
OnInitialize and OnDeinitialize
Do driver-wide initialization and cleanup
OnDeviceAdd
Invoked once for each new device detected by
Windows
Callback Objects
Callback objects = Callbacks + Context
Example: Creating Device Object
HRESULT CMyDriver::OnDeviceAdd(
IWDFDriver*
pDriver,
IWDFDeviceInitialize* pDeviceInit
) {
IUnknown *pDeviceCallback = NULL;
...
// Create callback object
hr = CMyDevice::CreateInstance( &pDeviceCallback,
1
pDeviceInit,
completionPort );
...
// Create WDF Device
hr = pDriver->CreateDevice( pDeviceInit,
pDeviceCallback,
2
&pIWDFDevice );
...
}
Callback Objects (con’t)
class CMyDevice :
public IDevicePnpHardware
{
private:
HANDLE
WINUSB_INTERFACE HANDLE
UCHAR
ULONG
...
public:
virtual HRESULT stdcall
// Callback interface exposed to
// framework.
m_CompletionPort;
m_UsbHandle;
m_BulkOutPipe;
m_BulkOutMaxPacket;
OnPrepareHardware(
IWDFDevice* pDevice
);
STDMETHOD( OnReleaseHardware )( IWDFDevice *pDevice );
// Factory method
static HRESULT CreateInstance(
IUnknown *pUnknown,
IWDFDeviceInitialize *pDeviceInit,
HANDLE CompletionPort
);
...
};
Context
Callback
Callback Objects (con’t)
static HRESULT CreateInstance(
IUnknown **ppUnknown,
IWDFDeviceInitialize *pDeviceInit,
HANDLE CompletionPort
) {
...
// Allocate our callback context
1 CMyDevice *pMyDevice = new CMyDevice();
...
// Get our callback interface
2 hr = pMyDevice->QueryInterface(
__uuidof(IUnknown),
(void **) ppUnknown
);
...
return hr;
}
Callback Objects (con’t)
1
OnDeviceAdd
IDriverEntry
Callback Object
WDF Driver
2
IWDFDriver::CreateDevice( … ) 3
WDF Device
5
Framework
Provided by:
Microsoft
IHV
IUnknown
MyDevice
4
QueryInterface for
• IDevicePnP
• IDevicePnPHardware
----
IDevicePnpHardware
Driver
Summary
Discussed driver loading/unloading, I/O data flow
Driver installation and setup are same as WDM drivers
Keep in the mind the “timeout” polices in reflector
Built-in verifier checks
Error reporting via WER
Discussed driver programming model
KMDF and UMDF share same model
UMDF DDI is based on C++ and COM-lite
Callback objects = Context + Callbacks
How to Develop a UMDF Driver
Part 2
Outline
Goals
Getting Started
Writing your Driver
Installing your Driver
Debugging the Driver
Plug and Play / Power Management
Design goals
Design overview
Device Driver Interface (DDI)
PnP/PM driver callback example
Goals
How to start writing and debugging a UMDF
driver
How to interact with UMDF’s Plug And Play and
Power Management support
The basics of creating and configuring a device
driver
Writing Your Driver
What is a User-Mode Driver?
User-Mode (UM) Drivers are DLLs
Provide driver’s “callback object” classes
Use COM programming pattern, not runtime
At least, not very much of the runtime
Expose standard COM entry points
DllGetClassObject, DllRegisterServer, DllUnregisterServer
Can write UM Drivers in C or C++
Implementing COM objects is easier in C++
Can use ATL with C++ for additional COM support
WDF Supplement CD contains several examples
This talk references the “Skeleton” driver
UMDF Driver DLL Exports
DllMain
Called when DLL loads (and unloads)
Construct global objects, initialize tracing
Dll[Un]RegisterServer
Called by CoInstaller during device installation
Skeleton sample uses ATL
Rather than write another implementation
DllGetClassObject
Called on device arrival by COM to get “class factory”
DllCanUnloadNow
Just return S_FALSE
Common Driver Classes
COM objects must implement IUnknown
Independently, or with help from CUnknown base class
UMDF driver implements these classes
CClassFactory
Instantiates your driver event handler
CMyDriver
Your Driver-Callback Class
Must implement IDriverEntry
Paired with an IWDFDriver object
CMyDevice
Your Device-Callback Class
May implement IDevicePnpHardware and/or IDevicePnp
Paired with an IWDFDevice object
UMDF Skeleton Driver
Example code based on UMDF Skeleton sample
Code in slides should not be used as-is
UMDF Skeleton does just enough to get loaded
Boilerplate code found in all UM Drivers
Minimal callback objects for driver and device
Description of files
ComSup.h & ComSup.cpp
COM Support code – provides classes for IUnknown & IClassFactory
DllSup.h & DllSup.cpp
DLL Support code – provides implementation of required exports
Depends on COM support code
Driver.h & Driver.cpp
Driver-Callback Class
Device.h & Device.cpp
Device-Callback Class
“TODO” comments mark where to add your driver code
COM and DLL support files require no changes
The driver and device files you’ll need to modify for your driver
Example: CMyDriver Definition (Driver.h)
class CMyDriver: public CUnknown, public IDriverEntry {
IDriverEntry *QueryIDriverEntry();
HRESULT Initialize();
public:
static HRESULT CreateInstance( PCMyDriver *Driver );
HRESULT OnInitialize( IWDFDriver *FxDriver ){
return S_OK;
}
HRESULT OnDeviceAdd(IWDFDriver *FxDriver,
IWDFDeviceInitialize *FxDeviceInit);
HRESULT OnDeinitialize( IWDFDriver *FxDriver ) {
return S_OK;
}
... // IUnknown methods, etc...
};
Example: CMyDriver Implementation (Driver.cpp)
HRESULT CMyDriver::CreateInstance( CMyDriver **Driver ) {
CMyDriver *driver = new CMyDriver();
if (driver == NULL) { return E_OUTOFMEMORY; }
HRESULT hr = driver->Initialize();
if (S_OK == hr) {
*Driver = driver;
} else {
driver->Release();
}
return hr;
}
HRESULT CMyDriver::Initialize() {
HRESULT hr = S_OK;
... // Do any initialization that could fail here
return hr;
}
Example: CMyDriver Implementation (con’t)
HRESULT CMyDriver::OnDeviceAdd(...) {
CMyDevice *deviceCallback; IWDFDevice *fxDevice;
HRESULT hr = CMyDevice::CreateInstance( FxDeviceInit,
&device );
if (S_OK == hr) {
... // Call SetLockingModel(), SetFilter(), etc...
hr = FxWdfDevice->CreateDevice(
FxDeviceInit,
deviceCallback->QueryIUnknown(),
&fxDevice
);
// Release Reference from QueryIUnknown()
deviceCallback->Release();
}
if (S_OK == hr)
{fxDevice->Release();}
if (NULL != deviceCallback) {deviceCallback->Release();}
return hr;
}
Example: CMyDevice Definition (Device.h)
class CMyDevice : public CUnknown {
HRESULT Initialize( IWDFDeviceInitialize *DeviceInit ) {
return S_OK;
}
static HRESULT CreateInstance(
IWdfDeviceInitialize *FxDeviceInit,
CMyDevice **Device
);
// Add IUnknown methods here
}
Writing Your Driver (Summary)
Create the common code
Required DLL Exports
COM support classes
Copy from skeleton, use existing ones (ATL), or write
your own
Implement CMyDriver class
Including the OnDeviceAdd() method
Allocate and initialize a CMyDevice object
Create IWDFDevice object & connect to CMyDevice callbacks
Implement CMyDevice class
Add Device-Callback interfaces later
Your driver should now load
But can’t talk to your device or do I/O
Next two talks will address that
Installing Your Driver
INF Based Installation
Just like WDM and kernel-mode WDF drivers
Device Matching, Driver Signing, etc... work normally
User-Mode Driver’s INF does extra work
Use UMDF CoInstaller
Register Driver DLLs
Configure the Driver List
Setup UM Driver Key
Add the Reflector driver
We plan to simplify the installation for Beta 2
Still INF based, but more support from CoInstaller
Registry Locations
UMDF keeps information in several registry keys
We’ll refer to them using these names as we go
Defined in the [Strings] section of the Skeleton INF
Key Name
Location
%UMDF_Software%
HKLM \
Software \
Microsoft \
Windows NT \
CurrentVersion \
WUDF
%UMDF_Services%
%UMDF_Software% \
Services
%UMDF_Host%
%UMDF_Services% \
{193a1820-d9ac-4997-8c55-be817523f6aa}
Use UMDF CoInstaller
System provided CoInstaller
Sets Driver Manager to start automatically
WUDFCoInstaller.dll is already installed
No need to copy the file
Driver’s INF must reference this CoInstaller
[Skeleton.CoInstallers]
AddReg = Skeleton.CoInstallers_AddReg
[Skeleton.CoInstallers_AddReg]
HKR,,CoInstallers32,0x00010000,“WUDFCoInstaller.dll”
Without the CoInstaller your driver may not start
Driver can’t start if the Driver Manager isn’t running
Problem code 37 in device manager
Register Driver DLLs
UMDF uses CoCreateInstance to load drivers
This requires the driver to be “registered”
INF must register any user-mode drivers it copies
Do this in the [DDInstall] section
[Skeleton]
CopyFiles=DriverCopy
RegisterDlls=Skeleton.RegisterDlls
[Skeleton.RegisterDlls]
11,,WUDFSkeleton.dll,1
Unregistered drivers will not load
Problem code 43 in the device manager
Configure the Driver List
UMDF loads drivers by “Driver Name”
We recommend the binary name (without extension)
UMDF Maintains its own driver list
Lists drivers in order of attachment to the stack
Drivers are listed by “Driver Name”
User-Mode drivers load above kernel-mode drivers
MultiString stored under the device node’s key
[Skeleton_AddReg]
HKR, “WUDF”, “DriverList”, 0x00010000, “WUDFSkeleton”
If this is missing
Problem 43 in device manager
Setup UM Driver Key
Create an entry under UMDF Services Key
One for each driver installed
Key name is the Device Name
Contains UMDF-Specific Driver Information:
The Driver’s CLSID
Configure this in the INF
Replace CLSID with your driver’s class ID.
[Skeleton_AddReg]
HKLM, %WUDF_Services%\WUDFSkeleton, “ComCLSID”, 0, “{CLSID}”
Without this:
Problem 43 in device manager
Adding the Reflector Driver
Reflector driver is WUDFRd.sys
Already installed on the system
Your INF must make this the top-most kernel driver
If you’re writing a UM function driver
Assign reflector as service with AddService directive
You’ll need a service install section too
If you’re writing a UM Upper-Level filter driver
Assign reflector as top-most Upper Device Filter
Without this UMDF is never loaded
Device may start, but no host process
Installing Your Driver (Summary)
Use the UMDF CoInstaller
Register your Driver DLLs with COM
Configure the Driver List
Setup UM Driver Key
Record your driver’s CLSID
Add Reflector driver to the kernel drivers
If it doesn’t work:
Is Driver Manager started?
Does device manager show a problem code?
Is WUDFHost.exe (host process) running ?
Debugging Your Driver
Debugging Overview
Similar to service debugging
UM Drivers cannot be started by the debugger
No Just-In-Time debugging
Host process starts when device is installed
Plug & Replug the device to restart
Or disable & re-enable in device manager
Each device stack runs in host process
WUDFHost.exe
Child of Driver Manager service
One stack per host-process for now
Debugger Options
WinDbg
GUI Debugger
Source and/or Assembly level debugging
UMDF debugger extension available
Can work as user-mode or kernel-mode debugger
See resources slide to find download location
Visual Studio Debugger
WDK and Visual Studio aren’t currently integrated
UMDF Debugger extension not available
Must copy mspdb71.dll from WDK to use it
We’re concentrating support on WinDbg
CDB & KD
If you prefer command line debugging, these work
User-Mode or Kernel-Mode Debugging
Can debug UMDF with UM or KM debugger
You can use WinDbg to debug either way
UM Debugger
Simpler and more familiar
Have to attach to each host process when it starts
KM Debugging
Requires a second computer to run debugger
More complex, but quite powerful
Stops entire computer – no UMDF timeouts
Can be always running
Catch UMDF Verifier problems like JIT debugger
Can debug and set breakpoints in host process
Use “.process /i <process object address>” to break into running host
See additional slides for KM Debugging tips
Enabling the Debugger
Host process started by Driver Manager service
You can’t attach debugger until after initialization
Host Process supports an “initial breakpoint” (IB)
Host waits N seconds for debugger to attach
Breaks into debugger once detected
Continues running once N seconds elapse
Timeout (in seconds) configured under UMDF_Host key
HostProcessDbgBreakOnStart = N
Enabling IB disables other UMDF timeouts
Like those on PnP operations
Initial Breakpoint is for UM Debugger (by default)
Can be configured to watch for both UM & KM debugger
Set high-bit in the timeout value
0x8000000 attempts once to break into either debugger
Attaching the UM Debugger
Cannot connect UM Debugger until host starts
Enable the Initial Breakpoint
Attach the device to the system
Or enable it in the device manager
If you have a single device attached
Run “windbg –pn WUDFHost.exe”
Repeat until it finds a host process to debug
If you have multiple devices attached
Find out process ID (PID) of the new host
Use tasklist.exe before & after to find new host process
Run “windbg –p PID”
UMDF Debugger Extension
WudfExt.dll
Copy into your path
“!load WudfExt.dll” debugger command loads the extension
Names are case sensitive
Command
!devstack
!dumpirps
!umirp
!wdfdriverinfo
!wdfdevicequeue
!wdfqueue
!wdfrequest
!wdfitf2obj
Description
Shows device stacks in the host process
Shows UM IRPs in the host process
Shows a host IRP
Shows info about a UM driver
Shows the I/O queues for a device
Shows an IoQueue
Shows an IoRequest
Converts an interface pointer to an object address
for above extensions.
Interesting UMDF Breakpoints (BP)
Hopefully, this will get you started
Event to debug
Host attempts to
load driver
Driver Load
Breakpoint (BP)
OLE32!CoCreateInstance
Watch for your CLSID
MyDriver!DllMain
Invoked first time when your DLL is loaded
Called after that too, so clear the BP once it hits
Creation of
CMyDriver
Driver attaches
device to stack
MyDriver!DllGetClassObject
Invoked to get your class factory
MyDriver!CMyDriver::CMyDriver
Invoked when the factory calls new
MyDriver!CMyDriver::OnDeviceAdd
Driver creates a device to attach to the stack
Tracing in Your UM Driver
Recommend using WPP tracing for debug output
Lightweight & always available, printf-style tracing
Easily captured to disk, screen or (kernel) debugger
Skeleton contains some basic tracing code
See MSDN documentation on Event Tracing
Collect output with tracelog.exe or traceview.exe
Tracelog comes with Windows
Traceview comes with WDK
For more information see:
“Collecting & Viewing Traces” in additional slides
MSDN & DDK documentation
What Do I Do When It Crashes? (Summary)
Check for configuration problems
Is there a PNP problem code?
Is there a host process running?
Is there a .dmp file WUDF log file directory?
%SystemRoot%\LogFiles\WUDF\*.dmp
Attach a debugger to the host as it starts
Or always enable the kernel debugger
Watch for UMDF Verifier failures
Walk through initialization
Use list of interesting breakpoints
Get debug output with WPP tracing
Plug and Play / Power Management
UMDF Design Goals for PnP/PM
Coordinate I/O delivery to driver with PnP/PM
events
The framework handles complexities of PnP/PM
Allow drivers to participate with optional callbacks
Support FDO and filter driver scenarios
No bus driver support for version 1
Same design as kernel mode WDF PnP/PM
UMDF PnP/PM Design
Implemented with a state machine
Consumes PnP/Power messages from the reflector
Suspends and resumes I/O queues as needed
Calls the driver if desired through a set of driver provided callback
functions
Driver callbacks are logically grouped into interfaces
IDevicePnpHardware
IDevicePnp
IDevicePnpSelfManagedIo
Driver “opts-in” by implementing one or more callback
interfaces
Software-only drivers generally don’t need any of these interfaces
Most drivers will only need IDevicePnpHardware
UMDF PnP/PM Interfaces
IDevicePnpHardware
First time hardware initialization / de-initialization
OnPrepareHardware()
OnReleaseHardware()
IDevicePnp
Device state change notifications
OnD0Entry()
OnD0Exit()
OnSurpriseRemoval()
OnQueryRemove()
OnQueryStop()
IDevicePnpSelfManagedIo
Custom I/O processing notifications
OnSelfManagedIoCleanup()
OnSelfManagedIoFlush()
OnSelfManagedIoInit()
OnSelfManagedIoSuspend()
OnSelfManagedIoRestart()
Device Driver Interface (DDI)
IDriverEntry::OnDeviceAdd
Driver must implement this interface
Called when framework receives “Add Device” message
Driver should perform initialization that does not require access to
hardware or lower level drivers
Driver also creates a device object and device callback object
(beyond the scope of this talk)
HRESULT CMyDriver::OnDeviceAdd(
IWDFDriver* pDriver,
IWDFDeviceInitialize* pDeviceInit
) {
// Read in driver properties
INamedPropertyStore * pPropStore = NULL;
hr = pDeviceInit->GetDevicePropertyStore( &pPropStore );
...
}
IDevicePnpHardware
OnPrepareHardware
Called when device is first started
Driver establishes connection to the device and performs one-time
initialization
HRESULT CMyDevice::OnPrepareHardware(
IWDFDevice* pDevice
) {
...
// Open the device
hr = pDevice->GetDeviceName( pDeviceName, … )
m_Handle = CreateFile( pDeviceName, … )
...
// Initialize the device and look for endpoints
if (WinUsb_Initialize( m_Handle, &m_UsbHandle )) {
return DiscoverBulkEndpoints();
}
IDevicePnpHardware
OnReleaseHardware
Called when device is removed or stopped
Driver should essentially undo anything it did in
OnPrepareHardware
HRESULT CDevice::OnReleaseHardware(
IWDFDevice* pDevice
) {
// Close the USB handle and the device’s file
// handle
if ( m_UsbHandle ) {
WinUsb_Free( m_UsbHandle );
}
if ( m_Handle ) {
CloseHandle( m_Handle );
}
return S_OK;
}
IDevicePnp
OnD0Exit
Called each time device should power down
Also called before the device is removed
Stop device’s I/O target with “leave pending I/O” flag
hr = m_pIoTarget->
Stop( WdfIoTargetLeaveSentIoPending );
Save device state
Set the device into low power state
OnD0Entry
Called each time device should power up
Also called when the device starts up
Set the device into working power state
Restore any previously saved state
Restart device’s I/O target
hr = m_pIoTarget->Start();
IDevicePnp
OnSurpriseRemoval
Called when the device has unexpectedly detached from the PC
Stop device’s I/O target with cancel flag
hr = m_pIoTarget->Stop( WdfIoTargetCancelSentIo )
OnQueryRemove
Allows driver to veto a device removal request
HRESULT CDevice::OnQueryRemove(
IWDFDevice* pDevice
) {
return S_OK;
// Allow device removal
}
OnQueryStop
Allows driver to veto a device stop request
HRESULT CDevice::OnQueryStop(
IWDFDevice* pDevice
) {
return E_FAIL;
// Disallow device stop
}
IDevicePnpSelfManagedIo
Notifications to drivers that perform I/O
dispatching unmanaged by the framework
OnSelfManagedIoInit()
Start I/O dispatching
OnSelfManagedIoSuspend()
Pause I/O dispatching
OnSelfManagedIoRestart()
Resume I/O dispatching
OnSelfManagedIoFlush()
Cancel or complete all pending I/O
OnSelfManagedIoCleanup()
Free I/O dispatching mechanisms
PnP/PM Driver Callback Example
Start Device
OnPrepareHardware()
Device Driver
Framework
Start Device
Off
On
Off
Start Device
OnD0Entry()
Device Driver
Framework
Start Device
Off
On
Off
Device Operational
Device Driver
Framework
On
On
Off
Suspend Device
OnD0Exit()
Device Driver
Framework
Power Down
On
On
Off
Resume Device
OnD0Entry()
Device Driver
Framework
Power Up
On
On
Off
Remove Device
OnD0Exit()
Device Driver
Framework
Remove
Device
On
On
Off
Remove Device
OnReleaseHardware()
Device Driver
Framework
Remove
Device
On
On
Off
WDM Message to UMDF Callback Mapping
Start -> Query Remove -> Remove
IRP_MN_START_DEVICE:
OnPrepareHardware()
OnD0Entry()
OnSelfManagedIoInit()
IRP_MN_QUERY_REMOVE_DEVICE:
OnQueryRemove()
IRP_MN_REMOVE_DEVICE:
OnSelfManagedIoSuspend()
OnD0Exit()
OnSelfManagedIoFlush()
OnReleaseHardware()
OnSelfManagedIoCleanup()
WDM Message to UMDF Callback Mapping
Start -> Surprise Removal -> Remove
IRP_MN_START_DEVICE:
OnPrepareHardware()
OnD0Entry()
OnSelfManagedIoInit()
IRP_MN_SURPRISE_REMOVAL:
OnSurpriseRemoval()
OnSelfManagedIoSuspend()
OnReleaseHardware()
OnSelfManagedIoFlush()
IRP_MN_REMOVE_DEVICE:
OnSelfManagedIoCleanup()
PnP/PM Timeouts
Each WDM PnP/PM message has a timeout
Currently, this timeout is set to 1 minute
The reflector will abort the host process if this timeout
is exceeded
Each WDM Pnp/PM message can produce
multiple driver callbacks, for example:
“AddDevice” results in:
OnInitialize
OnDeviceAdd
1 minute
IRP_MN_START results in:
OnPrepareHardware
OnD0Entry
OnSelfManagedIoInit
1 minute
Supplemental Slides
Example: DllMain
BOOL DllMain(
HINSTANCE ModuleHandle, DWORD Reason, PVOID Reserved
)
{
if (DLL_PROCESS_ATTACH == Reason) {
WPP_INIT_TRACING(“Microsoft\\UMDF\\UMDFSkeleton”);
g_ModuleHandle = ModuleHandle;
} else if (DLL_PROCESS_DETACH) == Reason) {
WPP_CLEANUP();
}
return TRUE;
}
See Skeleton\Host.cpp
Example: DllGetClassObject
HRESULT DllGetClassObject(
REFCLSID ClassId, REFIID InterfaceId, PVOID *Interface
)
{
PCClassFactory factory;
HRESULT hr;
*Interface = NULL;
if (!IsEqualCLSID( ClassId,
__uuidof(MyDriverCoClass) )) {
return CLASS_E_NOTAVAILABLE;
}
hr = CClassFactory::CreateInstance(&factory);
if (S_OK == hr) {
hr = factory->QueryInterface(InterfaceId, Interface);
factory->Release();
}
return hr;
}
Example: DllRegisterServer & DllCanUnloadNow
HRESULT DllRegisterServer() {
return UpdateCOMRegistration(
g_ModuleHandle,
IDR_MYDRIVER_CLASSINFO,
true,
__uuidof( MyDriverCoClass ),
L“UMDF Skeleton Sample Driver”
);
}
HRESULT DllUnregisterServer() {
return UpdateCOMRegistration(..., false, ...)
}
HRESULT DllCanUnloadNow() {
return S_FALSE;
}
Example: CUnknown Definition
class CUnknown : public IUnknown {
LONG m_RefCount;
public:
CUnknown() {
m_RefCount = 1;
}
virtual ~CUnknown() {
return;
}
IUnknown *QueryIUnknown(){
AddRef();
return (IUnknown *)this;
}
ULONG AddRef();
ULONG Release();
HRESULT QueryInterface( REFIID InterfaceId,
PVOID *Interface );
};
Example: CUnknown Implementation
ULONG CUnknown::AddRef(){
return InterlockedIncrement(&m_RefCount);
}
ULONG CUnknown::Release() {
ULONG count = InterlockedIncrement(&m_RefCount);
if (count == 0) {
delete this;
}
return count;
}
HRESULT CUnknown:QueryInterface(...) {
if (IsEqualIID(InterfaceId, __uuidof(IUnknown)) {
*Interface = QueryIUnknown();
return S_OK;
}
*Interface = NULL;
return E_NOINTERFACE;
}
Example: CClassFactory Definition
class CClassFactory : public CUnknown,
public IClassFactory {
public:
IClassFactory *QueryIClassFactory() {...}
// IUnknown
AddRef() {return __super::AddRef()}
Release() {return __super::Release()}
QueryInterface(...);
// IClassFactory
HRESULT CreateInstance(
IUnknown *Outer, REFIID InterfaceId, PVOID *Object
);
HRESULT LockServer(BOOL Lock) {
return S_OK;
}
};
Example: CClassFactory Implementation
HRESULT CClassFactory::QueryInterface(...) {
if(IsEqualIID(Interface, __uuidof(IClassFactory)) {
*Object = QueryIClassFactory();
return S_OK;
} else {
return CUnknown::QueryInterface(...);
}
}
HRESULT CClassFactory::CreateInstance(...) {
PCMyDriver driver;
HRESULT hr = CMyDriver::CreateInstance(&driver);
if (S_OK == hr) {
hr = driver->QueryInterface(InterfaceId, Object);
driver->Release();
}
return hr;
}
Debugging with WinDbg
Source Level GUI debugger
(Most) commands work on addresses or symbols
ModuleName!FunctionName
ModuleName!ClassName::MethodName
Best to always use fully resolved names
Setting Breakpoints
“bp <addr>” sets an excution breakpoint
Use bu if the function’s module isn’t loaded yet
�ba [w|r] [1|2|4|8] <addr>’ sets a watchpoint
ba r 4 MyDriver!g_MyArrayLength
Dumping Memory
d<type> [address] [L<length>]
type = byte, word, char, unicode, dword, quadword, string
dd MyMyDriver!g_MyArrayLength L1
dv dumps local variable names
Dumping Types
dt <variable name> dumps a specific local
dt <addr> <typename> dumps an address or symbol as a type
dt ??? MyDriver!CMyDevice
See the WinDbg help for more details
Debugging with a Kernel Debugger
WinDbg can be used as a kernel debugger
Must point Kernel debugger to host process
Automatic if the process requests a kernel break-in
Manually through the .process command
“!process 0 0” will list all process object addresses
Must reload user-mode symbols after attaching
“.reload /user”
Or the stack trace won’t have any symbols
Can set breakpoints on user-mode addresses
Must load user symbols first
Cannot break into a running host process
Must wait for a breakpoint
UMDF “Verifier” will break into KD if attached
Last chance to debug before process termination
Collecting & Viewing Traces
TraceLog.exe lets you control trace sessions
To start a session
Tracelog.exe -start MyLogger -guid MyDriver.ctl -level 255
-flag 65535 -f MyLogFile.etl
To end a session
Tracelog.exe -stop MyLogger
To print a session
TraceFmt –o MyLogFile.txt -p symbols.pri/TraceFormat MyLogFile.etl
Or use the GUI “TraceView” tool
Use in place of TraceLog or TraceFmt
Resources:
“WPP Software Tracing” page in DDK documentation
MSDN online page for these tools:
http://msdn.microsoft.com/library/default.asp?url=/library/enus/ddtools/hh/ddtools/tracetools_8eccb8f7-6f29-4683-87bdfa83618c32eb.xml.asp
How to Develop a UMDF Driver:
Part 3
Outline
I/O Processing Overview
I/O Queues
File Objects
Driver Layering
I/O Targets
Cancellation
Goals
A better understanding of:
How to incorporate I/O processing in a UMDF driver
Framework objects involved with I/O Request
processing
Knowledge of where to find resources for UMDF
I/O Request Processing Overview
I/O Request Processing in UMDF
You’ve seen WDF Driver and Device objects
These are used to configure a device stack
I/O processing involves many more objects for:
Flow control and internal request routing
Per request context and callbacks
Connections to other drivers in the stack and/or system
And some concepts that span across objects:
Driver Layering
Cancellation
Framework Objects
Driver
Device
Request
Memory (Input)
Memory (Output)
Queue
File
I/O Target
Framework Objects
These objects are associated with request processing
All derive from IWDFObject
Object
Description
Device
The information associated with a single layer in a device stack
Request
An I/O operation
Either sent to the driver or initiated by the driver to a lower
device
A flow control mechanism for Requests
An open handle to the device and any context the driver needs
to store with it
Another driver to which the Driver can send Requests
Either another user-mode layer in the device stack, the
kernel-mode portion of the device stack, or the top of
another device stack entirely.
Queue
File
I/O Target
Memory
A buffer associated with a request
I/O Request Processing – A Bird’s Eye view
Request
I/O Queue
Driver
I/O Target
Complete
I/O Request Object
Request
I/O Queue
Driver
I/O Target
Complete
I/O Request Object
Represents a request
Sent to the driver by a client
An application or another UM driver
Generated by a driver
Types of Requests presented to driver
Create
Read / Write / DeviceIoControl
Cleanup
Close
Request Parameters can be obtained from
IWDFIoRequest interface
I/O Queues
I/O Queues
Request
I/O Queue
Driver
I/O Target
Complete
I/O Queues
Queues provide flow control for I/O Requests
All requests dispatched to driver through I/O Queues
Driver can configure I/O routing for a device
Create one or more queues for a device
Configure routing for a type of I/O to a particular queue
“Default queue” handles any remaining I/O types
I/O Queue Dispatch Types
Dispatch Type controls how requests are
presented to driver
Queues allow following modes of dispatching:
Automatic: Queue presents requests via callbacks
(Push Model)
Manual: Requests are retrieved by driver
(Pull Model)
Dispatch type is set at Queue creation time
It is a per-Queue property
Automatic Dispatch
Queue presents request through driver
implemented callback interfaces
IQueueCallbackCreateClose
IQueueCallback[Read | Write | DeviceIoControl]
Automatic Dispatch Types
Sequential: allows at most one outstanding request at a
time
New Request not presented until current request is completed
Parallel: allows multiple outstanding requests
New Request can be presented even when previously
presented requests are not completed
The number of parallel requests presented is bounded
Driver should minimize blocking in presentation callbacks
Manual Dispatch
Driver pulls requests from the queue
Typically starts in response to Queue callback
IQueueCallbackStateChange
Empty to non-empty transition
Continues to pump I/O until “done”
No more I/O, bookmark request retrieved, error, etc...
Starts again on next Queue state change
HRESULT RetrieveNextRequest(
OUT IWDFIoRequest** ppRequest
);
It is possible for driver to pull requests from an
Automatic Queue
Callback Constraints
Callback constraints specify locking model for
Queue callbacks
Callback constraints options
Device Level: Callbacks invoked with device level
lock held
None: Callbacks invoked with no lock held
Specified at device creation time via
IWDFDeviceInitialize::SetLockingConstraint
Set before creating the WDF Device object
Per device property – applies to all queues
Callback Constraints (con’t)
Constraints apply only to Queue Callbacks
Queue callbacks include
Automatic Dispatch Callbacks
Queue state change callback
Request cancellation callback
If Device Level locking is used:
The above callbacks are synchronized
Request Completion is a not a Queue Callback –
not synchronized
Unsynchronized code can call
IWDFObject::AcquireLock
Call on object that matches your synchronization scope
Dispatch Type vs. Locking Model
Combining Dispatch Type and Locking Model
provides several modes of operation
For example:
Sequential Queue
Device Level Locking
Parallel Queue
Device Level Locking
Request1
Request1
Req1 Complete
Request2
Request2
Req2 Complete
Req1 Complete
Req2 Complete
Parallel Queue
No Locking
Request1
Request2
Req1 Complete
Req2 Complete
UMDF OSR USB Sample Walk-through
Requests and Queue Usage in the sample driver
(src\umdf\usb\driver)
Callback locking constraint used
(CMyDevice::Initialize in Device.cpp)
Dispatch type used
(CMyQueue::Initialize in Queue.cpp)
Queue callbacks used
(CMyQueue in Queue.h and Queue.cpp)
File Objects
File Object
File Object represents open connection to device
It is the session context for I/O
All requests associated with a File Object
in UMDF
KMDF and WDM requests sometimes don’t have one
File object for a request obtained using
IWDFIoRequest::GetFileObject
Trailing name used by client while opening
connection is available via
IWDFFile::GetFileName
File Object (con’t)
Device
File1
Request1
Request2
File2
Request1
Request2
File Object Lifetime
File Object’s lifetime spans
Create: File Object gets created in response to a
connection being opened
Cleanup: Notification of connection being torn down
and that Close is pending
Close: All I/O pertaining to this File Object has now
ceased. File Object is now disposed.
File Object is created and destroyed by
the Framework
All I/O operations happen in the context of
File object
i.e., they happen between Create and Close
These operations are: Read / Write / DeviceIoControl
File Object Lifetime Illustrated
Example of I/O sequence between App
and Driver
Application
hFile = CreateFile(…
…
Async/Sync Read/Write/IoCtl
…
CloseHandle(hFile);
Driver
Create Request
…
Read/Write/IoCtl Requests
…
Cleanup
…
Any remaining Requests drain
…
Close
Cleanup Handling in Driver
Cleanup received when client closes connection
Notification for driver to flush I/O for File Object
Cancel or complete outstanding I/O as soon as
possible
Close will not come until all I/O is finished
Cleanup is a “critical” operation in UMDF
Cannot fail, subject to timeout
Cleanup is not the end of I/O operations
That’s the Close Request
I/O received after a Cleanup should be
cancelled/completed immediately
UMDF OSR USB Sample Walk-through
File usage in the sample driver
(src\umdf\usb\driver)
(Queue.cpp)
CMyQueue::OnRead
CMyQueue::OnCreateFile
CMyQueue::OnCleanupFile
CMyQueue::OnCloseFile
Driver Layering
Driver Layering
UMDF supports driver layering
Multiple drivers build a device stack
“Function” driver provides core device behavior
“Filter” drivers modify aspects of device behavior
Filter is a property set at device creation time
All Framework Objects are per layer
Each layer works only with its own objects
e.g., each layer gets a Request object to complete
Driver sends request to next layer using
I/O Target
Represents the next driver in the stack
That driver may be in user mode or kernel mode
Driver Layering Diagram
Framework
Device
Queue
File
Request
Framework
Device
Queue
File
Request
Framework
Device
Device Stack
Queue
File
Request
Request Completion
When Driver receives a Request it can
Complete the Request synchronously
Forward it to an I/O Target or an I/O Queue
Hold on to it to complete later
Driver must complete a Request it receives
e.g., while forwarding request to I/O Target
Driver must register request-completion-callback object
Framework invokes callback when target completes request
Callback must complete request or arrange for completion
UM IRP not complete until all layers complete their
Request objects
Completion callback is not a device level event
Runs outside driver’s callback locking constraints
Request Completion Illustrated
Request
Completion Routine
Request
Completion Routine
Request
I/O Target
I/O Target
Request
I/O Queue
Driver
I/O Target
Complete
I/O Target Framework Object
Provides a way to send requests to
another device
Local I/O Target represents:
The next device in the stack
Another UM Driver, or the KM portion of the stack
Driver doesn’t need to care which it is
Remote I/O Target represents
Some other UM or KM device stack
Need feedback to prioritize this for Longhorn
I/O Target is responsible for
Sending I/O request to lower device
Detecting completion and invoking callback interface
Local I/O Target
Application
Device
I/O Target
Device
I/O Target
Device
I/O Target
User Mode
Kernel Mode
Up Device
Down Device
Device
Device
Device Stack
Reflector
I/O Target Functionality
State based I/O handling
Driver can start and stop the I/O target
In Response to a PnP Transition
For any driver specific reason (e.g., reconfigure after reset)
Supports two I/O modes:
Synchronous for short-term operations
Asynchronous with callback for long-term operations
Supports request timeout
Outstanding request is cancelled when timeout expires
Tracks sent requests for the driver
Allows automatic purge or flush on I/O target stop
Coordinates completion and cancellation
Bridging UM and KM stacks
I/O Target provides bridge between KM and UM drivers
Bottom-most I/O Target sends requests to kernel-mode stack
Driver could also “escape” to the Platform API
Send I/O using Win32 File I/O API directly
Advantages of using I/O Target
Handles complexity of coordinating completion, cancellation and
cleanup
Allows for filtering and layered stacks transparently
Allows routing of request to kernel mode in mode neutral way
Offers rich functionality
In Beta 1
Bottom-most I/O Target uses Win32 File I/O API internally
No way to use specific API (e.g., WinUSB)
Makes the escape necessary in such cases
UMDF OSR USB Sample Walk-through
I/O Target usage in the sample filter
(src\umdf\usb\filter)
Obtaining default I/O Target
(CMyQueue::Initialize in Queue.cpp)
Forwarding request down the stack
(CMyQueue::OnWrite in Queue.cpp)
(CMyQueue::ForwardRequest in Queue.cpp)
Request Cancellation
Request Cancellation
Application can cancel a request at any time by
Using CancelIo[Ex] API
Exiting application, etc.
Higher layer driver can cancel sent requests
Cancel tells driver to complete request soon
But not necessarily immediately
Driver should abort any long operations
Short synchronous requests can just be allowed to
complete normally
Cancel is a critical operation
Cannot be failed, subject to timeout, etc…
Cancellation is part of good user experience
Cancellation Overview
Request Owner can register a cancel callback
Cancel callback follows locking constraints
On Cancel:
Request’s internal state is marked as cancelled
Any callback registered after this will run immediately
Registered cancel callback is cleared then invoked
In the cancellation callback
Owner arranges to cancel and complete request
Remove callback before transferring ownership
e.g., when forwarding or completing the Request
Request Ownership
Request ownership starts with I/O Queue
Request ownership moves from:
Queue to Driver – Driver receives the Request
Driver to Queue – Driver forwards Request to Queue
Driver to I/O Target – Driver forwards Request to
I/O Target
Queue or I/O Target handle cancellation when
they own request
I/O Target notifies you through completion callback
Driver must handle cancel when it owns request
If it holds on to the request for a long time
Cancellation Handling in Driver
When driver receives a Request, it may:
Complete it soon
Shortly send it to I/O Target
Queue it back to Framework I/O Queue
Hold on to it (not using Framework I/O Queue)
Do (potentially) lengthy sync/asynch processing
Driver needs to handle cancellation only in the
last two cases
Cancellation Handling in Driver (con’t)
Driver may register a cancel notification callback
void IWDFIoRequest::MarkCancelable(
IRequestCallbackCancel* pCancelCallback
);
In callback it should cancel/complete the request
As soon as possible
Unregister callback when transferring ownership
Before sending request to I/O target or completing it
HRESULT IWdfIoRequest::UnmarkCancelable();
Failure return value indicates request has been canceled
Cancel routine has run or will run soon
Owner must ensure Request is completed
only once
Cancellation Illustrated
Let us take the example of OSR USB Driver
This driver uses device level locking
It submits I/O requests to WinUSB
The internal completion routine manually uses device level lock
Request state is stored in Request context
There are the following possibilities:
1. Request completes without cancellation
2. Request is cancelled
2a. Cancel callback runs before completion callback
2b. Cancel callback runs after completion callback
The driver makes sure that:
Request is completed only once
Request is not completed until both completion and cancellation
callback are done with it
Cancellation Illustrated (1)
OnRead*
…
Req->MarkCancelable
…
WinUSB_ReadPipe
…
OnCancel*
…
if (ctx->state == RequestCompleted) {
CompleteRequest
}
else {
CancelIoEx
ctx->state = CancelInvoked;
}
* Invoked under DeviceLock
OnUSBReadComplete
…
DeviceLock->AcquireLock();
if (Req->UnmarkCancelable()) {
DeviceLock->ReleaseLock();
CompleteRequest
}
else {
if (ctx->state == CancelInvoked) {
DeviceLock->ReleaseLock();
CompleteRequest
}
else {
ctx->state = RequestCompleted;
DeviceLock->ReleaseLock();
}
}
Cancellation Illustrated (2a)
OnRead*
…
Req->MarkCancelable
…
WinUSB_ReadPipe
…
OnCancel*
…
if (ctx->state == RequestCompleted) {
CompleteRequest
}
else {
CancelIoEx
ctx->state = CancelInvoked;
}
* Invoked under DeviceLock
OnUSBReadComplete
…
DeviceLock->AcquireLock();
if (Req->UnmarkCancelable()) {
DeviceLock->ReleaseLock();
CompleteRequest
}
else {
if (ctx->state == CancelInvoked) {
DeviceLock->ReleaseLock();
CompleteRequest
}
else {
ctx->state = RequestCompleted;
DeviceLock->ReleaseLock();
}
}
Cancellation Illustrated (2b)
OnRead*
…
Req->MarkCancelable
…
WinUSB_ReadPipe
…
OnCancel*
…
if (ctx->state == RequestCompleted) {
CompleteRequest
}
else {
CancelIoEx
ctx->state = CancelInvoked;
}
* Invoked under DeviceLock
OnUSBReadComplete
…
DeviceLock->AcquireLock();
if (Req->UnmarkCancelable()) {
DeviceLock->ReleaseLock();
CompleteRequest
}
else {
if (ctx->state == CancelInvoked) {
DeviceLock->ReleaseLock();
CompleteRequest
}
else {
ctx->state = RequestCompleted;
DeviceLock->ReleaseLock();
}
}
UMDF OSR USB Sample Walk-through
Cancellation walk-through in the sample driver
(src\umdf\usb\driver)
(Queue.cpp)
CMyQueue::OnRead
CMyQueue::OnCancel
CMyQueue::CompletionThread
Putting It Together
You’ve seen the following portions of the sample:
Driver (Driver.cpp)
CMyDriver::OnDeviceAdd
Device (Device.cpp)
CMyDevice::OnPrepareHardware
CMyDevice::OnReleaseHardware
Queue (Queue.cpp)
CMyQueue::OnCreateFile
CMyQueue::OnCleanupFile
CMyQueue::OnRead / CQueue::OnWrite
CMyQueue::OnCancel
CMyQueue::CompletionThread
Call to Action
Install the Windows Driver Kit
Join the WDF Beta Program
At http://beta.microsoft.com
Guest ID: Guest4WDF
Evaluate UMDF for your driver projects
Consider development time, customer support for
system crashes, etc.
Send Us Feedback – We want to know
If UMDF will meet your needs
What stops you from writing your drivers with UMDF
Additional Resources
Email
umdffdbk @ microsoft.com
Web Resources:
WDF Information:
http://www.microsoft.com/whdc/driver/wdf/default.mspx
Windows Debugger:
http://www.microsoft.com/whdc/devtools/debugging/default.mspx
External Resources
“Introduction to the Windows Driver Foundation: How To
Develop Device Drivers Using the Kernel Mode Driver
Framework” from OSR Press
Release date is September 2005
Focuses on KMDF but provides general WDF information as well
В© 2005 Microsoft Corporation. All rights reserved.
This presentation is for informational purposes only. Microsoft makes no warranties, express or implied, in this summary.