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.
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.
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.
* 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.