OSRLogo
OSRLogoOSRLogoOSRLogo x Subscribe to The NT Insider
OSRLogo
x

Everything Windows Driver Development

x
x
x
GoToHomePage xLoginx
 
 

    Thu, 14 Mar 2019     118020 members

   Login
   Join


 
 
Contents
  Online Dump Analyzer
OSR Dev Blog
The NT Insider
The Basics
File Systems
Downloads
ListServer / Forum
  Express Links
  · The NT Insider Digital Edition - May-June 2016 Now Available!
  · Windows 8.1 Update: VS Express Now Supported
  · HCK Client install on Windows N versions
  · There's a WDFSTRING?
  · When CAN You Call WdfIoQueueP...ously

UMDF 101 - Understanding User Mode Driver Frameworks

I am sure you've heard a lot about the Kernel Mode Driver Framework (KMDF), the new paradigm for writing kernel mode drivers for Windows. But have you heard about the User Mode Driver Framework (UMDF)? This article introduces UMDF and gives a brief overview of its architecture.

 

Keep in mind throughout the course of our discussion that UMDF is still under active development.   Everything in this article is subject to change. In fact, it might already be wrong. So, check the documentation details before trying to write your UMDF driver, OK?

 

Introduction

UMDF is a new way of allowing drivers that support protocol bus based devices (initially just USB) to be written in user mode. The types of devices currently supported are:

  • Portable storage devices such as PDAs and cell phones
  • Portable media players
  • USB bulk transfer devices
  • Auxiliary display/video devices

Microsoft believes that by moving these types of drivers to user mode, it will simplify driver writing and improve the overall stability of the Windows operating system. I certainly agree with this statement since user mode is a much more forgiving environment. Let's face it, if you can develop a driver for a device without having to worry about the complexities of kernel mode programming or risking crashing a user's machine when it misbehaves, your customers and Microsoft will be much happier.

 

While UMDF drivers use an interface that is similar to the KMDF model in terms of the objects used, KMDF and UMDF are a little different. In fact, UMDF uses a Component Object Model (COM) based interface referred to as COM-Lite.

 

A COM based driver!?! When I first heard that UMDF was going to be a COM-based solution I was horrified! I had a few previous encounters with COM, and they were all bad, to say the least. I found the COM documentation hard to read and the tools to develop COM objects hard to use. Of course, I write kernel mode drivers for a living, and thus have no special expertise when it comes to COM. So, let's just say I started out a bit skeptical. I wanted to write a driver to control my device, not become a COM expert.

 

However, after examining UMDF and the associated samples, and playing with it for awhile, I have to admit it's not bad. It might take a little getting used to, but overall I think it's a model with which most developers of simple USB devices can get comfortable.

 

UMDF Architecture

As the name implies, a UMDF driver runs in user mode, specifically in a process running the executable image WUDFHost.exe. The general layout of UMDF modules is shown in Figure 1.

  

                             Figure 1 - UMDF Architecture

 

Figure 1 - Basic UMDF Architecture

UMDF drivers are implemented as (user mode) Dynamic Link Libraries (DLLs). The DLL uses a COM programming pattern and exposes a standard set of COM entry points. The driver can be written in C or C++, it uses the Win32 API, and it can even use the Active Template Library (ATL) for additional COM support. You should be aware that Microsoft advocates using C++ for UMDF drivers. While writing UMDF drivers in C is supported in theory, there currently are no C samples. I'll talk more about the driver and COM in the next section.

 

In the current UMDF architecture, the number of devices per WUDFHost process is currently limited to one. This means that if you have three identical devices that are supported by a UMDF driver plugged into the system, there will be three separate WUDFHost processes and three instances of your driver - 1 driver instance for each device. Since these drivers are in different processes, you have to implement your own mechanisms for sharing data between the processes.

 

In addition to the WUDFHost process hosting your driver, there is another user mode process that's part of the UMDF "system." The second process is the UMDF Driver Manager Service that communicates with the WUDFHost process running your driver and any other WUDFHost processes running on the system. This service, which is started as part of starting the device, is responsible for host process lifetimes and responding to messages from the UMDF Kernel Mode Reflector Driver.

 

Down in kernel mode, the UMDF Kernel Mode Reflector Driver (or simply Reflector) provides the necessary connections between user mode and a kernel device stack. There is one Reflector device instance installed in each kernel UMDF device stack. The Reflector is responsible for forwarding I/O, Power, and PnP Messages from Kernel Mode to the host process.

 

How does a user I/O sent to a UMDF driver get processed? As Figure 1 shows, a user mode application sends its requests to kernel mode as for any device. Once in kernel mode, the Reflector sends the requests to the proper WUDFHost process running in user mode for processing. The action of the requests is totally transparent to the application -- only the UMDF driver knows it is running in user mode.

 

UMDF Driver Architecture

As mentioned previously, a UMDF Driver is a COM based DLL. However, UMDF doesn't use the COM runtime library, which is a good thing because it gives most COM developers trouble. Therefore, UMDF does not use COM for loading, unloading, or controlling concurrency (the dreaded "apartments"). UMDF only uses COM as a programming pattern. The easiest way to think about this is that COM interfaces are C++ abstract base classes.

 

As for abstract base classes, UMDF has classes with which any KMDF developer will be familiar:

  • IWDFDriver
  • IWDFDevice
  • IWDFFile
  • IWDFIoQueue
  • IWDFIoRequest
  • IWDFIoTarget
  • IWDFMemory
  • IWDFObject

You'll notice that the names of these objects are pretty much the same as the KMDF model. This was done intentionally so that developers familiar with KMDF would find the UMDF model easy to learn. As with KMDF, UMDF driver writers select which callbacks (interfaces in COM terminology) to support for their objects. The UMDF framework calls these routines (methods in COM-speak) in the UMDF driver when the event associated with that interface occurs.

 

As previously mentioned, UMDF has classes that represent UMDF objects. These UDMF objects provide signatures for the callbacks they support in the form of interfaces. A UMDF driver implementation supports one or more of these interfaces. These interfaces are pure virtual functions, so a UDMF driver supporting an interface must implement all the functions in that callback's interface or the driver will not compile. To put this in COM terms, you create classes based on the UMDF classes and provide the interfaces/methods for the functionality you want to support.

 

Another COM related matter that you should be aware of is that COM objects all support an interface called IUnknown. This interface allows users of the COM object to obtain pointers to other interfaces supported by an object and to manage the interface pointers after obtaining them. This IUnknown interface supports three methods:

 

1. AddRef

2. Release

3. QueryInterface

 

The AddRef and Release methods are used to increment and decrement the reference count for the COM based object. This reference count allows COM to know whether or not the object is in use. If the reference count is 0, COM knows the object can be unloaded. For kernel mode driver writers this reference count is no different than the reference count in the Driver Object maintained by the I/O Manager.

 

It is important to understand the QueryInterface method since it is used by a caller to determine whether the queried object supports a particular interface. The caller performs this query by passing in the Interface Identifier (IID) of the interface for which it is looking. The callee responds with the address of the interface if it is supported. A successful call to QueryInterface always results in the object's reference count being incremented and thus must be released by the caller when the object's interface is no longer needed. So the way UDMF finds out what interfaces are supported by your driver's objects is by querying those objects via the QueryInterface routine.

 

Because UMDF uses interfaces to group the callbacks, there's a difference between how UMDF drivers and KMDF drivers handle not having a callback within a group. For KMDF, the driver can simply provide a NULL pointer for that method in the callback table. However, for UMDF the driver must provide an implementation for all the methods in a particular interface. So, for example, an IWDFQueue object supports the following interfaces:

  • IQueueCallbackCreateClose
  • IQueueCallbackDefaultIoHandler
  • IQueueCallbackDeviceIoControl
  • IQueueCallbackIoResume
  • IQueueCallbackIoStop
  • IQueueCallbackRead
  • IQueueCallbackStateChange
  • IQueueCallbackWrite 

If you want your CMyQueue IWDFQueue derived class to support the callbacks provided by the IQueueCallbackCreateClose interface, you would define your class as shown in Figure 2.

class CMyQueue :
            public IQueueCallbackCreateClose
            public CUnknown
{
 
    //
....// Pointer to IWDFIoQueue COM object instance that
    // represents my queue.
....//
 
IWDFIoQueue *m_IoQueue
 
    //
    // CUnknown Interface
    //

     STDMETHOD_(ULONG,AddRef) (VOID) {return CUnknown::AddRef();}

     STDMETHOD_(ULONG,Release) (VOID) {return CUnknown::Release();}

     STDMETHOD_(HRESULT, QueryInterface)(
        __in REFIID InterfaceId,
        __out PVOID *Object
        );

    //
    // IQueueCallbackCreateClose Interface
    //
    STDMETHOD_ (void, OnCreateFile)(
        __in IWDFIoQueue *pQueue,
        __in IWDFIoRequest *pRequest,
        __in IWDFFile *pFileObject
        );


    STDMETHOD_ (void, OnCleanupFile)(
        __in IWDFIoQueue *pWdfQueue,
        __in IWDFIoRequest *pWdfRequest,
        __in IWDFFile *pWdfFileObject
        );


    STDMETHOD_ (void, OnCloseFile)(
        __in IWDFIoQueue *pQueue,
        __in IWDFIoRequest* pRequest,
        __in IWDFFile* pFileObject
        );
            ....
}

Figure 2 - Supporting Callbacks from IQueueCallbackCreateClose in CMyQueue

As you can see, CMyQueue specified that the IWDFQueue derived class supports the IQueueCallbackCreateClose and CUnknown interfaces, which means the driver must support all the methods that these interfaces provide. Because it supports the IQueueCallbackCreateClose interface, the driver must provide callbacks for the OnCleanupFile, OnCloseFile, and OnCreateFile methods. These methods don't have to really do anything; they just have to be implemented. Similarly, because the driver supports CUnknown, which is derived from the COM base class IUnknown, the driver must support the AddRef, Release, and QueryInterface methods.

 

Just as all kernel mode drivers provide a routine named "DriverEntry" that is called by the I/O Manager when the driver is first loaded, a UMDF driver must support a well known IID for "IDriverEntry". A UMDF driver is queried (via its QueryInterface routine) for its IDriverEntry Interface when the DLL is loaded.

 

For example, if a driver writer is going to create an IWDFDriver derived class called CMyDriver, he/she would define it as shown in Figure 3.

class CMyDriver :
            public IDriverEntry,
            public CUnknown
{
    //
    // IDriverEntry methods
    //


    virtual
    HRESULT
    STDMETHODCALLTYPE
    OnInitialize(
        __in IWDFDriver *FxWdfDriver
        )
    {
        return S_OK;
    }


    virtual
    HRESULT
    STDMETHODCALLTYPE
    OnDeviceAdd(
        __in IWDFDriver *FxWdfDriver,
        __in IWDFDeviceInitialize *FxDeviceInit
        );


    virtual
    VOID
    STDMETHODCALLTYPE
    OnDeinitialize(
        __in IWDFDriver *FxWdfDriver
        )
    {
        return;
    }


    // IUnknown methods.
    //
    // We have to implement basic ones here
    // that redirect to the base class because
    // of the multiple inheritance.


    virtual
    ULONG
    STDMETHODCALLTYPE
    AddRef(
        VOID
        )
    {
        return __super::AddRef();
    }
 

    virtual
    ULONG
    STDMETHODCALLTYPE
    Release(
        VOID
       )
    {
        return __super::Release();
    }
 

    virtual
    HRESULT
    STDMETHODCALLTYPE
    QueryInterface(
        __in REFIID InterfaceId,
        __out PVOID *Object
        ); }

 

Figure 3 - IDriverEntry for CMyDriver

With this definition, you will then make sure the CMyDriver QueryInterface routine will successfully respond when queried for the IDriverEntry IID.

 

Once you have created an instance of your driver in your driver's CreateInstance method, your UMDF driver will start getting callbacks similar to those used by KMDF. For example, when the PnP Manager finds a device that your driver supports, your driver will be called at its OnDeviceAdd method. As in a KMDF driver, your driver will then create an instance of a device object, except in UMDF the device object is based on IWDFDevice. Eventually your driver will create one or more queues for your device based on IWDFIoQueue, as well as other IWDF based objects to manage your device.

 

While we're on the subject of IWDFDevice, let's talk about hardware. As you are aware, exporting access to hardware in user mode is considered a security risk. So this may lead you to ask, "How do I talk to my device if I can?t talk to the hardware?" As I mentioned earlier, UMDF currently supports protocol-based buses in general, and USB in particular. This bus architecture has an associated bus driver that makes it easy for drivers to interact with their device on the bus without having to directly access the hardware. This occurs because the USB bus driver is responsible for understanding how to deliver a packet of information to the selected device on its bus.

 

This leads to another question. Since these bus drivers are just responsible for enumerating their buses and delivering and receiving packets, who claims the device? Since you are writing the driver for the device, you might expect to claim the device, but this is incorrect. In UMDF, when working with a USB based device, a Microsoft driver called WinUSB claims the USB device on your behalf. Thus, when your IWDFDevice::OnPrepareHardware routine gets called (this method is the equivalent of the EvtDevicePrepareHardware callback in KMDF), your driver issues the Win32 function CreateFile for the device name created by WinUsb for your target device.

 

Once your driver has successfully completed the CreateFile call to connect to your device via WinUSB, the rest of the driver is almost trivial. Microsoft has conveniently provided an easy way to communicate with your device through the WinUSB driver using an API provided by "winusb.dll". This API provides all the routines necessary for your device to get descriptors, interfaces, etc.

 

Once you understand the WinUSB interface, you'll need to work with other UMDF objects. As I mentioned previously, UMDF has similar objects to KMDF. Your driver will create one or more IWDFIoQueue's in order to receive requests; it will probably use IWDFMemory objects etc.

  

Keep in mind that UMDF is as simple as KMDF once you get your hands around the COM-Lite aspects of the architecture. As with KMDF, the UMDF model does most of the work for you when it comes to PnP and Power.

 

Summary

I started out a skeptic when I was told that UDMF was based on COM. However, after a bit of playing-around with UMDF, I've seen the light! Writing a UDMF driver is relatively easy once you get your hands around using COM-Lite. Perhaps best of all, when your driver crashes while you're debugging it, the system stays running! Not bad at all, I'd say.

User Comments
Rate this article and give us feedback. Do you find anything missing? Share your opinion with the community!
Post Your Comment

"very nice!"
I am kind of NB so this one is great

Rating:
11-Sep-08, Nadav Hugim


"user mode drivr framework"
very clear and useful thanks

Rating:
04-Sep-07, valentino cristofoletto


"Usermode driver framework"
Exillent

Rating:
24-Mar-06, kalpesh gedia


Post Your Comments.
Print this article.
Email this article.
bottom nav links