Path: news.mitre.org!blanket.mitre.org!nntprelay.mathworks.com!peerfeed.ncal.verio.net!news.he.net!newshub.cts.com!newsfeed.cts.com!cmkrnl!jeh
From: jeh@cmkrnl.com (Jamie Hanrahan)
Newsgroups: comp.os.ms-windows.programmer.nt.kernel-mode
Subject: Re: Synchronize Device Driver
Message-ID: <1998Jan25.002454.8442@cmkrnl>
Date: 25 Jan 98 00:24:54 PST
References: <6ae2ku$a8a@bgtnsc02.worldnet.att.net>
Organization: Kernel Mode Systems, San Diego, CA
Lines: 78

In article <6ae2ku$a8a@bgtnsc02.worldnet.att.net>, Robert Scott Tracy 
<tallt@worldnet.att.net> writes:
> I currently take care of a device driver and have a need to synchronize
> requests to a particular device.  Through experience I have discovered
> that many devices cannot handle the transition of the asynchronous calls
> hitting the device without error.  My driver works in conjuction with a
> higer level driver that sends requests to me keeping all calls in my
> driver at passive level.  I currently translate the request to a
> particular SCSI or ATAPI command or series of commands, build an
> appropriate SRB and send a request down.  If the request fails in a
> particular manner, I have a completion routine that will retry my
> request.  My question is what type of method should I use to synchronize
> requests to a particular device, realizing there could be several
> devices on a system taking requests from my driver and I only wish to
> synchronize requests to a device, not requests through my driver.

By the way, the word I like to use for what you want to do is 
"serializing" the requests to the devices, not "synchronizing".  I 
find that use of the word "serializing" really helps people think of 
the right things to do, much better than the word "synchronizing".  

If you have a one-to-one correspondence between your device objects 
and the lower-level device objects to which you're sending requests, 
it is very simple.  Just set up your driver to use a StartIo routine.  
Call IoStartPacket in your Dispatch routine, and IoStartNextPacket in 
your completion routine when the driver you're layered on top of tells 
you a previous IRP is done.  Note that your driver dispatch routines 
will still see the asynchronous requests, in parallel if you want. 

This does not serialize requests through your driver - only 
through your device object.  ie if you have multiple device objects each 
can have its own "current IRP". 

If you have one device object and you're sending requests that come
through it to several different lower-level devices -- or, more
generally, if you have fewer device objects than lower-level devices
-- it is almost as easy:

In your device extension, allocate a KDEVICE_QUEUE for each
lower-level device.  When you're about to send an IRP to one of your
lower-level devices, do a KeInsertDeviceQueue on the IRP's
DeviceQueueentry field and the appropriate queue. 

A return status from this call of FALSE tells you that the busy bit in
the device queue structure (I hate to call them 'objects') was not
already set (but it is now), and the IRP was NOT put on the queue, and
you should send the IRP to your lower-level device.  (You will
probably want to allocate a "current IRP for each device" in your
device extension too.)  A return status of TRUE says that the busy bit
was already set, the IRP was put on the queue, and you shouldn't tell
your lower-level device about it yet. 

When the lower-level driver reports that the IRP is done, you do a
KeRemoveDeviceQueue on your device queue that corresponds to the
lower-level device.  If the queue is non-empty this call will remove
the IRP at the head and return the address of the DeviceQueueEntry
field from the IRP; you should start this IRP on the lower-level
device.  If this call returns a NULL the queue was empty, the busy bit
is now clear, and you can just return to caller; the next call to
KeInsertDeviceQueue on the same queue will return FALSE in that case. 

Note that there is an error in the doc re KeRemoveDeviceQueue. The doc 
claims that the return (if non-NULL) is the address of the IRP; in 
fact it is the address of whatever was on the queue.  Usually this is 
the DeviceQueueEntry within that huge union/struct/union at the end of 
the IRP.  

I say "usually" because there is no actual requirement that what you 
pass to KeInsertDeviceQueue has to be the DeviceQueueEntry field of an 
IRP - it has to be a pair of LIST_ENTRYs somewhere, but that's the 
only requirement.  

	--- Jamie Hanrahan, Kernel Mode Systems, San Diego CA
        Internet: jeh@cmkrnl.com (JH645)  CompuServe: 74140,2055  
drivers, internals, networks, applications, and training for VMS and Windows NT
NT driver FAQ, links, and other information:  http://www.cmkrnl.com/

If you post a reply in news, please don't e-mail it too.