The sample files are not available from www.microsoft.com as stated here. You must install them from the CD. If your CD is damaged or missing, follow the support link from http://www.microsoft.com/mspress/books/6262.asp.
SP-2 to the sample
drivers incorporates a solution to the USBD.LIB compatibility problem based on
a special import library in the WIN98 directory. It is once again true that the
Windows 98 DDK is only needed for the PNPMON sample driver. Thanks to
Alexander Grigoriev.
The third sentence of the second paragraph in the
sidebar should read: “Most of the entries in this DLL are thin wrappers around
calls to kernel-mode functions that actually perform native API functionality.”
Thanks to Maxim Shatskih.
The third line of the code sample at the bottom of
the page should read:
printf(“Hello, world!\n”);
Don’t read too much into my use, in the fifth
paragraph on this page, of the phrase “file mapping” to describe how drivers
are mapped into virtual memory. The memory manager closes the .SYS file, which
has the important side effect of allowing you to replace the .SYS file while
the driver is running. Thanks to Maxim Shatskih.
In the third line of the paragraph following the
heading “Order of Driver Loading”, replace “device key” with “hardware
key”.
Box number 3 in Figure 2-7 should contain
“Function driver” (singular, not plural).
Table 2-1 could usefully mention the LONGLONG and
ULONGLONG types, which are typedef names for the native data types
__int64 and unsigned __int64, respectively. Thanks to Maxim Shatskih.
The DO_EXCLUSIVE flag means that only one
handle can be open at a time. The statement in the text implies that a single
thread might be able to open multiple handles to a DO_EXCLUSIVE device, which
is not the case. See also the discussion of the corresponding parameter to
IoCreateDevice on p. 62.
The text doesn’t emphasize strongly enough that
you must have a DriverUnload routine in a WDM driver because the system can’t
unload a driver that doesn’t have one.
In the fourth bullet, MajorFunction should
be in bold face.
A WDM driver must support IRP_MJ_SYSTEM_CONTROL
requests, at least to the extent of passing them down the PnP stack. The driver
verifier checks for this.
The DO_DEVICE_INITIALIZING flag is in the device
object, not the driver object (see the second line after the heading "Clear
DO_DEVICE_INITIALIZING"). Thanks to Chris Rhodes..
It’s expensive to exit from a __try block
with a return statement as shown in the example that spans these two
pages. Microsoft’s Pre-FAST tool will flag such usages.
Low virtual addresses are valid in an NTVDM
(Virtual DOS machine) process. Thanks to Maxim Shatskih.
When you allocate less than a page of memory with ExAllocatePoolWithTag,
and when pool tagging is enabled in the kernel, the tag appears in the four
bytes immediately preceding the memory block addressed by the returned pointer.
When you allocate a page or more, the pool manager keeps track of the tag
internally; it does not appear in memory. Thanks to Gennady Mayko.
The first line on this page should read:
#define DRIVERTAG 'KNUJ'
The code at note 3 should read:
PSINGLE_LIST_ENTRY psLink = PopEntryList(&SingleHead);
Thanks to Matthew Giedt.
The text should read as follows (missing
characters):![]()
In latin script—tlhonchaj chIljaj motlhmoHwI’pu’ —
“May the standard makers lose their nostrils.” Thanks to Marc Reinig. Font
available at http://www.kli.org.
The code fragment at the bottom of the page should read as follows:
if (bArriving)
RtlInitUnicodeString(&foo, L"Hello,
world!");
Thanks to Rusty Lai.
The first code fragment on the page should use ZwDeleteValueKey, the
function referred to in the text, instead of RtlDeleteValueKey, which
doesn’t even exist. Thanks to Maxim Shatskih.
The SystemRoot directory is not accessible to boot devices. Thanks to
Maxim Shatskih.
You can use the additional formatting code Z
in DbgPrint (or KdPrint) calls to display UNICODE_STRING values.
E.g.:
UNICODE_STRING foo;
RtlInitUnicodeString(&foo, L”Hello, world!”);
KdPrint((DRIVERNAME “ - %wZ\n”, &foo));
Don’t forget that you must be running at an
IRQL less than DISPATCH_LEVEL to display UNICODE strings.
All of the timestamps in the examples on these
pages and on p. 173 should be of the form “t” followed by a subscript number.
E.g., t1, t2, etc.
Sixth line of first paragraph: You use KeInitializeSpinLock, just
like with regular spin locks. Thanks to David Lavo.
The DDK uses the phrase “side effect” to describe
operations that the system performs coincident with placing a dispatcher object
into the signalled state. My editors substituted the wishy-washy phrase
“operations”, even though I pleaded with them to leave the DDK terminology in
place.
In the 5th prose paragraph, insert “the” to make phrase
read “the purpose of the extra parameters”.
In the last sentence on the page, insert the word
“arise” to make the line begin “can arise if you use”.
The sidebar should refer to ExAcquireFastMutex.
There is no such function as KeAcquireFastMutex. Thanks to Maxim
Shatskih.
In the last code sample, the “don’t do this”
comment should begin with a less-than: “<== don’t do this”.
The code snippet after the note should read:
ObDereferenceObject(FileObject);
Code flag 3 should be next to the line reading
“return STATUS_Xxx;”
In the same code fragment as the previous note, device
should instead read fdo. Thanks to Maxim Shatskih.
Driver programmers use the verbs “fail” and
“succeed” transitively to describe completing an IRP with a failure or a
success status, respectively. My editors nonetheless insisted on substituting
the awkward phrases “cause to fail” and “cause to succeed” throughout the book,
starting here.
In the fourth line of the second bullet point at
the top of this page, replace DO_BUFFERED_IO with DO_DIRECT_IO. Thanks to
Jonathan Taylor.
In Figure 5-6, the box labeled “Filter driver
completion routine” in the right-hand part of the drawing should connect to the
“CompetionRoutine” box in the second IO_STACK_LOCATION. The third stack
location is fallow, which is the whole point of using
IoSkipCurrentIrpStackLocation.
A reader who wanted (but failed)
to stay anonymous savaged the entire first edition of the book in an online
review because there was no explanation of what it means to “fake” an
interrupt. I mean calling the interrupt service routine (ISR) as a subroutine
in such a way that the ISR performs the same processing it would do for an
equivalent hardware interrupt. The PIOFAKE sample on the disc does this from
the TransferFirst subroutine.
In the fourth line of the last full paragraph on
the page, my editors insisted on using the ambiguous verb “post” instead of the
verb “pend”. “Post” is file-system-driver slang for what happens to an IRP when
you return STATUS_PENDING from a dispatch routine. I’ve never seen “post” used
in this sense in any device driver, and I don’t like it because it gives no
sense of what the IRP is “posted” on, or to, or whatever. I wrote “pend”, which
I think better describes what’s going on.
Pretend that the label “Queue IRPs” and the arrow
that connects it to the I/O thread was never there.
The fifth line of the note should read “modified while I was writing thsi buk….”
Chapter 9 discusses an IRP caching scheme that I find preferable to the parking technique described here. GenericCacheControlRequest and GenericUncacheControlRequest deal with the caveats mentioned here, and they are also closely (and automatically) tied to PNP, POWER, and CLEANUP code.
There should be a "return;" statement at the end of the page. Without it, the code attempts to release the spin lock twice. Thanks to Dave Probert.
Most programmers, including me, personify their
programs. In the fourth line of the fifth bullet, I’m pretty sure I wrote
“nobody” to describe the entity that hasn’t changed the cancel routine address
for the IRP. A gremlin changed it to “nothing”.
The first sentence of the first bullet point in
the list should refer to the “global cancel spin lock.” Thanks to
Maxim Shatskih.
The first statement in the ForwardAndForget
subroutine (pdx = etc.) should be deleted. Thanks to David Lavo.
The code sample should be calling IoBuildSynchronousFsdRequest, which is the whole point. Thanks to Maxim Shatskih.
The code sample should be calling KeInitializeEvent. Thanks to Maxim Shatskih and Brian Kenn.
In the first line, IRP_MJ_DISPATCH_CLEANUP should be IRP_MJ_CLEANUP. Thanks to Takin Nili-Esfahani.
I know you
realize what PCI and PCMCIA mean! My editors appear to have been concerned that
you might have forgotten, so they inserted definitions of these and many other
acronyms the first time they appear in a chapter. Next time, I’ll suggest we
put a glossary at the end of the book so as to avoid talking down to you
readers. And don’t get me started about the Microsoft® Windows® XP brand of
operating system for personal computers….
Figure 6-3 should show an extra state, “Stalled and Rejecting”, between the Stalled and Rejecting states. The point is that the stall and abort operations are independent and establish different conditions, either of which affects the flow of IRPs into and out of the queue.
In the fifth line from the bottom, replace pdx->LowerDeviceObject with fdo, so that the line reads:
DefaultPnpHandler(fdo, Irp);
Thanks to Niels Jensen.
In the third paragraph of the sidebar, replace the last sentence, which begins “The DDK doesn’t say so . . .” with this:
(While we’re on the
subject, note these additional details about IRP_MJ_SHUTDOWN. Like every other
IRP, this one will be sent first to the topmost FiDO in the PnP stack if any
driver in the stack has called IoRegisterShutdownNotification.
Furthermore, as many IRPs will be sent as there are drivers in the stack with
active notification requests. Thus, drivers should take care to do their
shutdown processing only once and should pass this IRP down the stack after
doing their own shutdown processing.)
On both these pages, DBT_QUERYREMOVEDEVICE should read DBT_DEVICEQUERYREMOVE instead. Thanks to Kyonho Park.
Some bus drivers attach private resource descriptors to the list. It would have more clear to put a default label into the switch on resource->type to emphasize that you shouldn’t do anything in particular with these private resources. You should detect unexpected port, memory, interrupt, or DMA resources, because their presence may indicate that you’ve been loaded for the wrong hardware somehow.
I’ve now been authorized to tell you that your I/O resources will appear in BAR order for a PCI device, on all platforms. Thus, if you have multiple resources of the same type and size, you can safely rely on their descriptors appearing in a predictable order.
Microsoft has changed the rules for handling CmResourceTypePort I/O resources on non-x86 platforms. You must now program your driver to work when you get either a CmResourceTypePort or a CmResourceTypeMemory resource for what you thought was a “port” type resource. The CM_RESOURCE_PORT_IO flag referred to on p. 376 is now obsolete. Refer to the article on this subject in the July 15, 2003, issue of WD-3 for more information and for suggestions about coding techniques.
You can avoid difficulty by always using memory-type registers in your hardware, in which case you will always get a CmResourceTypeMemory resource.
When the resources needed for a DMA operation are immediately available, AllocateAdapterChannel allocates them and calls your adapter control routine before returning. When the resources are not immediately available, AllocateAdapterChannel queues your device object and returns. The call to your adapter control routine happens later.
The fifth parameter to MapTransfer is a PULONG. Accordingly, the fragments on pages 407 and 409 should be calling the function with &pdx->xfer as the argument. Thanks to Phil Elwell.
When the resources needed for a DMA operation are immediately available, GetScatterGatherList allocates them and calls your execution routine before returning When the resources are not immediately available, GetScatterGatherList queues your device object and returns. The call to your execution routine happens later.
In the ninth line from the bottom of the page, delete the word “other” so the one complete sentence on this line reads, “There are only three event codes.”
Beginning in the Server 2003 version of Windows, you can call IoValidateDeviceIoControlAccess to help enforce a more stringent access policy than is defined by the FILE_XXX_ACCESS setting in an I/O control code.
The driver needs write access with METHOD_OUT_DIRECT. I suspect that most NT-capable computers also confer read access to any page that can be written, but such is not required for this buffering method.
The text (lines 7-8 of the 2d paragraph) should tell you to wait on the event when the target driver returns STATUS_PENDING. The Caution that appears further down the page elaborates when you should and should not wait.
The sixth-from-last line in the code sample on this page should read:
PIRP* pIrp = (PIRP*)
Irp->Tail.Overlay.DriverContext[0];
That is, the pIrp variable is the address of a variable in which we earlier saved a pointer to the IRP we’re canceling. (The code inside GENERIC.SYS reads this way, as it should.)
In Windows 2000, boot drivers should defer calling IoWMIRegistrationControl to register with the WMI subsystem until after they process IRP_MN_START_DEVICE for the first time. Running WMI initialization code before the I/O subsystem is completely initialized will lead to system crashes. Beginning with Windows XP releases, the operating system internally queues WMI registration calls until they can safely be performed.
Generally speaking, it does not hurt to move the registration call to the StartDevice routine in any driver for any platform. You mustn’t, however, register more than once because the checked build will ASSERT if you do. Thus, any registration call you place into StartDevice should be conditional so that you don’t mistakenly register a second time coming out of a PnP STOPPED state. Consult the chapter 10 samples, as modified by SP-3, for examples of how to do this.
The tenth line of the SetDataBlock routine should read:
if (instindex != 0)
That is, the expression involving the undefined variable instcount should
not be there. Thanks to Dave Matheny.
The 2d paragraph on this page was actually supposed to be a note on p. 516.
WDMSTUB.SYS is a WDM lower filter driver, rather than a VxD. In addition, versions beginning with 5.0.0.5 (which was part of SP-1 to this 2d edition) stub WmiSystemControl and WmiCompleteRequest in Windows 98 Gold only. This allows a driver to load which references these two WMILIB routines, since WMILIB is not available for that old platform. It remains true that WMI functionality would not work on Windows 98 Gold, of course.
In the last sentence before the subheading, replace “interface” with “device object”. That is, you should call ObReferenceObject to take an extra reference to your own device object in order to pin your driver in memory while your caller has an outstanding reference to the interface you’ve exported.
A controller or multifunction driver must also handle IRP_MJ_SYSTEM_CONTROL requests. Wearing the PDO hat, complete these requests with whatever status is already in the IRP. Wearing the FDO hat, at least pass them down the PnP stack. The Verifier makes sure you do these things by sending you an IRP with a bogus minor function code during your startup processing.
All the less-than signs in Table 12-2 should be less-than-or-equal signs. (In fact, they were in the manuscript.) That is, the upper bound for a high-speed bulk endpoint is 512 bytes and not 511 as implied by the printed table.
The second sentence in the second full paragraph
should say, “Strictly speaking, endpoints belong to interfaces….”
The largest polling frequency for a high-speed endpoint is 32 microframes,
or every 8 milliseconds. Thanks to Michael Luloh.
A better example of a multifunction device would
be a keyboard that has a physically integral SmartCard reader. (If I were
designing a keyboard/mouse device, I’d use a single HID interface with multiple
reports.)
The less-than signs in Table 12-7 should all be less-than-or-equal signs. (In fact, they were in the manuscript.)
In Table 12-8, note that we use UsbBuildVendorRequest for both vendor and class control requests.
Windows XP and later systems will ignore the MaximumTransferSize member of the USBD_PIPE_INFORMATION structure. Thus, if your driver never has to run on an earlier system, you can dispense with the multisegment logic discussed on pp. 605 ff. for LOOPBACK.
The eighth line of the first full paragraph should
read “releases the memory occupied by the URB . . ..” Thanks to Maxim Shatskih.
The text in the third line from the bottom of the page should refer to USAGE_MAXIMUM instead of LOGICAL_MAXIMUM.
There is a bug in all current versions of Windows that will cause a system lockup during startup of a HID minidriver that (a) has two or more input collections in its report descriptor, and (b) sets DevicesArePolled to TRUE.
If you have set the DevicesArePolled flag to FALSE (as is usually recommended) and complete an IOCTL_HID_READ_REPORT synchronously (i.e., in the dispatch routine), versions of HIDCLASS prior to XP will send you another READ_REPORT from the completion routine. If you keep completing IRPs, you’ll eventually overflow the stack. This can be a particular problem for a device of the second type listed in the bullet list here, in that you might have a large number of reports saved up from device interrupts. In a similar situation, I coded my READ_REPORT handler not to complete the IRP in a recursive-call situation.
The text doesn't correctly describe IOCTL_HID_WRITE_REPORT. First of all, HIDCLASS does not send this IOCTL when it gets an IOCTL_HID_SET_OUTPUT_REPORT--it sends an internal IOCTL with the code IOCTL_HID_SET_OUTPUT_REPORT instead. The buffering method for IOCTL_HID_WRITE_REPORT is METHOD_NEITHER, but the parameters are presented in a strange way. Namely, Irp->UserBuffer is the address of a HID_XFER_PACKET structure, and stack->Parameters.DeviceIoControl.InputBufferLength is the length of that structure. (Normally, a METHOD_NEITHER IOCTL uses stack->Parameters.DeviceIoControl.Type3InputBuffer to point to the input buffer).
The fourth line of the code fragment should read:
&stack->etc.
The last sentence on the page should begin “The
next two
arguments….”
The call to IoReleaseRemoveLock that appears in the third line of the
code sample on this page should not be there. Thanks to Martin Jansen.
The Callback routine shown at the top of the page should read:
VOID Callback(PDEVICE_OBJECT fdo, PRANDOM_JUNK
stuff)
{
PAGED_CODE();
. . .
IoFreeWorkItem(stuff->item);
ExFreePool(stuff);
}
The call to InterlockedIncrement in the last
code fragment on the page is missing a right-parenthesis. Thanks to Aviv
Blattner..
The last sentence of note 3 should begin “I
included the installer so I could….” (no “is”).
The caption of the note boxes on these pages
should be just “Note” instead of “On the CD”.
DefaultDestDir=10,\System32\Drivers ; ç don’t do this
Causes an error in 98/Me (but not in 2K or later systems) because the setup
program tries to use, e.g., C:\Windows\\System32\Drivers as the directory name.
So leave out the leading backslash. Thanks to Mark Giese.
SP-3 for the sample drivers contains a revised FILTER sample incorporating this idea.
The true reason for the DO_POWER_PAGABLE flag is this: the power manager needs to power-down devices with pageable power handlers first, before powering-down dvices with nonpageable power handlers. For this to be possible, all the drivers in a particular PnP stack need to have the same setting for the DO_POWER_PAGABLE flag.
The race condition alluded to in the sidebar should never happen because no other driver should be attaching to our device stack once the stack is built. Accordingly, the sidebar should just be stricken.
The CLASSKEY string should be defined as follows:
CLASSKEY=System\CurrentControlSet\Control\Class\{<class-GUID>}
Thanks to Werner Frischauf.
A line about two thirds of the way down in the AddDevice sample should read:
RtlInitUnicodeString(&edoname, namebuf);
The example illustrating how to create an Extra Device Object is a little too terse, leading people to generate code that doesn’t entirely work. Just before the ellipsis at the very end, I suggest the following additional code:
edo->Flags
&= ~DO_DEVICE_INITIALIZING;
In other words, you still have to clear DO_DEVICE_INITIALIZING, as with other device objects, to allow applications to open handles.
In the last line of the value for UpperFilters, replace the seventh parameter (63) with 73. Thanks to David Lavo.
In the third line of the third paragraph, SCIS.SYS
should read SCSIPORT.SYS. A service pack will correct the actual code sample, too. Thanks
to Maxim Shatskih.