My UMDF driver has some data - how do I get it to my app?

This came up as a question on NTDEV today. After I thought about it for a bit I realized it was actually a pretty good question. You've started writing your driver, or at least thinking about your driver. You know you'll get some data back from the device and you need to get it to the app. How do you do this?

For simplicity's sake, let's say there are two models for a driver to operate in. The "push" model and the "pull" model (of course there are more, but this captures the big ones.) When I say "push" and "pull" here I'm talking about both how the driver decides it should talk to the device, and how it returns the data to the driver.

But before I dive in, realize that I'm talking here about how the driver operates in "steady state" - the time between PNP and Power Management operations, between recovering from device errors/resets, the time when it's just servicing I/O requests.

Pull Model

We'll start with the Pull model because it's easier to understand. In steady state the typical pull-model driver does nothing on its own. When the app wants to talk to the device it sends a request to the driver, which triggers a driver callback. In the simplest case the driver formats a command to send to the device and uses the buffers from the application to hold any data the command returns. When the command returns the app's buffers contain the necessary data. The driver sets the information property on the request to indicate how many bytes of data the app is getting back then completes the request. When the app sees the request completion the data your driver provided will be in its buffers.

It's rarely this simple. Often you'll need to do apply some transformation to the data before you can return it. In that case your driver would allocate a buffer to use in the command you send to the device. When the command completes you use its result to write the correct data into the buffers on the app's requests. Then set information, complete the app's request, and free the intermediate buffer. Better yet, you can make the WDF memory object for the intermediate buffer a child of the request so that it's freed automatically when you complete the request.

Push Model

Say your device is going to bubble data up to your driver when something happens. It might be GPS coordinates, printer status, a finger print scan, etc… How do you get this data back to an application? Clearly you don't want the application to poll for data. All you want to do is push the data you're getting from the device up to the application. How do you do this?

The first thing to understand is that you can only give the application data that it asks for*. Unless the app makes a request and gives you a buffer you don't have a way to put data in its address space. So clearly the Push model is just a variant of the pull model.

In the pull model you let the app send you one or more requests asking for data. You queue those requests for later processing. In the background you have some code which is reading data from the device. When those read commands complete you pickup an app request off the queue, copy your command data into the app's request buffers then complete the app request.

You may choose to copy the data into multiple requests, you may choose not to complete the request if you don't have enough data to fill its buffer with just one command. And if you don't have any pending requests you need to decide whether to just throw the data away or whether to hold it in an internal buffer until the app comes down to ask for more.

The key difference between the push and pull models is whether the driver is sending commands to the device on its own, or whether every device command comes because the app sent your driver a request.

As I said before - it's never this simple. Looking at the UMDF Fx2 sample you see that it uses the pull model for read/write operations on the device and for most of the i/o controls that it supports. However it uses the push model to handle requests to read the state of the DIP switches.

   

-p

   

* You can use PNP device events if you need to push the fact that something has happened to any app which might be listening. But it's an unreliable mechanism and isn't suitable as a way to stream data.

Comments

  • Anonymous
    February 05, 2007
    A co-worker pointed out that i'd used the word pull twice in the last paragraph.  What i ment to say is that the driver uses the pull model for read/write and the push model for switch changes.  I've corrected the text.

  • Anonymous
    October 27, 2007
    The push model we have implemented in several places in our API for 1394 devices includes an event object that usually gets created by the application and passed down to the device driver. When the driver has something that is of interest to the application, the driver signals the event. Then it's up to the application to make the appropriate calls to the driver to retrieve the data. I think this model is much cleaner conceptually than making a bunch of a priori calls (how many?), marking them as pending, completing them as events occur, making new calls to replace those that got completed, etc. An interesting implementation point of this solution is how can you set things up so that multiple threads can wait and wake up when the driver notifies the application that something happened (wake multiple threads up for the same "something"). The solution we gave is too much to describe in a blog comment ;-) I guess I'll put it on my blog sometime. Cheers!

  • Anonymous
    November 26, 2008
    Hi, We have a number of legacy applications which rely on a kernel driver (WDM) that allocates a large non-paged buffer using an IOCTL call. The application then accesses this buffer directly to retrieve data. We are now re-designing our system but still need to support these legacy applications so we have decided to write a UMDF driver to provide this interface between the legacy apps and the new system. But we are having a problem allocating a large buffer in the UMDF application that the application can access directly. Is this possible, and if so, how? Thanks, EAK