Programming the Microsoft Windows Driver Model
The INF File
With the preceding background in how the PnP Manager and setup subsystem use the registry, we can begin to make sense of the INF file you supply. The purpose of an INF file is to instruct the setup subsystem how to install device-related files on an end user system and how to modify the registry. The DDK contains a very thorough discussion of INF-file syntax, which I won t repeat here. I would, however, like to provide a guide to the most commonly used portions of an INF.
An INF file contains a collection of sections introduced by a section name in brackets. Most sections contain a series of directives of the form keyword = value. The INF file begins with a Version section that identifies the type of device described by entries in the file and that specifies other global characteristics of a driver installation package. The following Version section contains the minimum of required information:
[Version] Signature=$CHICAGO$ Class=Sample ClassGuid={894A7460-A033-11d2-821E-444553540000} CatalogFile=whatever.cat DriverVer=mm/dd/yyyy ; Copyright 2002 by Proseware, Inc.
Signature can be one of several magic values. I use $Chicago$, which works on all WDM platforms. Class identifies the class of device. Table 15-4 lists the predefined classes that Windows XP already supports. ClassGuid uniquely identifies the device class. The DDK header file DEVGUID.H defines the GUIDs for standard device classes, and the DDK documentation entry for the Version section documents them as well. CatalogFile names the digital signature file that WHQL will send you after certifying your driver package; do your testing with an empty file. DriverVer specifies the date of the driver package and, optionally, a version number. The system uses the version information in ranking digitally signed drivers. You should also have a comment (that is, any line that starts with a semicolon) containing the word copyright. Note that you don t have to specify any actual copyright information, but you probably want to.
The setup subsystem will process an INF file whose Version section contains just the Signature and Class values. The other values shown in the preceding example are needed, however, if you want to have WHQL certify your driver.
INF Class Name | Description |
1394 | IEEE 1394 host bus controllers (but not peripherals) |
Battery | Battery devices |
CDROM | CD-ROM drives, including SCSI and IDE |
DiskDrive | Hard disk drives |
Display | Video adapters |
FDC | Floppy disk controllers |
FloppyDisk | Floppy disk drives |
HDC | Hard disk controllers |
HIDClass | Human input devices |
Image | Still-image capture devices, including cameras and scanners |
Infrared | Network Driver Interface Specification (NDIS) miniport drivers for Serial-IR and Fast-IR ports |
Keyboard | Keyboards |
MediumChanger | SCSI media changer devices |
Media | Multimedia devices, including audio, DVD, joysticks, and full-motion video capture devices |
Modem | Modems |
Monitor | Display monitors |
Mouse | Mouse and other pointing devices |
MTD | Memory technology driver for memory devices |
Multifunction | Combination devices |
MultiportSerial | Intelligent multiport serial cards |
Net | Network adapter cards |
NetClient | Network file system and print providers (client side) |
NetService | Server-side support for network file systems |
NetTrans | Network protocol drivers |
PCMCIA | Personal Computer Memory Card International Association (PCMCIA) and CardBus host controllers (but not peripherals) |
PNPPrinter | Bus-specific print class driver |
Ports | Serial and parallel ports |
Printer | Printers |
SCSIAdapter | SCSI and RAID controllers, host bus adapter miniports, and disk array controllers |
SmartCardReader | Smart card readers |
System | System devices |
TapeDrive | Tape drives |
USB | USB host controllers and hubs (but not peripherals) |
Volume | Logical storage volume drivers |
I find it useful to think of the bulk of an INF file as the linear description of a tree structure. Each section is a node in the tree, and each directive is a pointer to another section. Figure 15-8 illustrates the concept.
Figure 15-8. Tree structure of an INF file.
At the apex of the tree is a Manufacturer section that lists all the companies with hardware described in the file. For example:
[manufacturer] "Walter Oney Software"=DeviceList "Finest Organization On Earth Yet"=FOOEY [DeviceList]
Each individual manufacturer s model section (DeviceList and FOOEY in the example) describes one or more devices:
[DeviceList] Description=InstallSectionName,DeviceId,CompatibleIds
where Description is a human-readable description of the device and DeviceId identifies a hardware device. CompatibleIds, if present, is a list of other device identifiers with which the same driver will work. The InstallSectionName parameter identifies (or points to, in my tree metaphor) another section of the INF file that contains instructions for installing the software for a particular device. An example of an entry for a single type of device might be this (drawn from the PKTDMA sample in Chapter 7):
[DeviceList] "AMCC S5933 Development Board (DMA)"=DriverInstall,PCI\VEN_10E8&DEV_4750
The information in the Manufacturer section and in the model section (or sections) for individual manufacturers comes into play when the system needs to install a driver for a piece of hardware. A Plug and Play (PnP) device announces its presence and identity electronically. A bus driver detects it automatically and constructs a device identifier using on-board data. The system then attempts to locate preinstalled INF files that describe that particular device. INF files reside in the INF subdirectory of the Windows directory. If the system can t find a suitable INF file, it asks the end user to specify one.
A legacy device can t announce its own presence or identity. The end user therefore launches the add hardware wizard to install a legacy device and helps the wizard locate the right INF file. Key steps in this process include specifying the type of device being installed and the name of the manufacturer. See Figure 15-9.
The hardware wizard constructs dialog boxes such as the one shown in Figure 15-9 by enumerating all the INF files for a particular type of device, all of the statements in their Manufacturer sections, and all of the model statements for each of the manufacturers. You can guess that the manufacturer names that appear in the left pane of the dialog box come from the left sides of Manufacturer statements and that the model names that appear in the right pane come from the left sides of model statements.
Figure 15-9. Selecting a device during installation.
More About Hardware Wizard Dialog Boxes
Once the wizard is past the stage of looking for PnP devices, it builds a list of device classes and uses various SetupDiXxx routines from SETUPAPI.DLL to retrieve icons and descriptions. The information that SETUPAPI uses to implement these routines ultimately comes from the registry, where it was placed by entries in ClassInstall32 sections. Not every device class will be represented in the list the wizard will suppress information about classes that have the NoInstallClass attribute.After the end user selects a device class, the wizard calls SETUPAPI functions to construct lists of manufacturers and devices as described in the text. Devices mentioned in ExcludeFromSelect statements will be absent from these lists.
Install Sections
An install section contains the actual instructions that the installer needs to install software for a device. We ve been considering the PKTDMA sample. For that device, the DeviceList model section specifies the name DriverInstall. I find it useful to think of this name as identifying an array of sections, one for each Windows platform. The zero element in this array has the base name of the section (DriverInstall). You can have platform-specific array elements whose names start with the base name and contain one of the decorations listed in Table 15-5. The device installer looks for the install section having the most specialized decoration. Suppose, for example, that you have install sections with no decoration and with the .NTx86 decoration. If you re installing into Windows XP on an Intel x86 platform, the installer will use the .NTx86 section. If you re installing into Windows 98/Me, it will use the section without a decoration.
Platform | Install Section Decoration |
Any platform, including Windows 98/Me | [none] |
Any Windows XP platform | .NT |
Windows XP on Intel x86 | .NTx86 |
Windows XP on Intel 64-bit processor | .NTIA64 |
Because of the search rules I just outlined, all of the INF files for my sample drivers have the no-decoration and .NTx86-decoration install sections. That makes the INF files work fine on any Intel x86 platform.
Distinguishing Among Operating Systems
For operating systems after and including Windows XP, the INF syntax provides a rather tedious way to specify different drivers for different operating systems. You create multiple model sections with unique names, and you distinguish among them by appending TargetOsVersion strings to the model statements in the [Manufacturer] section. Refer to the DDK documentation for the full syntax of these strings. As an example, you can specify the following in an INF file to get different drivers installed for Windows 98, Windows 2000, and Windows XP:[Manufacturer] "Walter Oney Software"=DeviceList ; for 98/ME and 2K "Walter Oney Software"=DeviceList,NTx86.5.1 ; XP on x86 "Walter Oney Software"=DeviceList,Ntia64.5.1 ; XP on IA64
Following this section, you have three model sections listing all the same hardware models and specifying four uniquely named install sections for each model. There is an additional .NTx86 install section for Windows 2000, making a total of four install sections per hardware model. That is, you have model sections named [DeviceList], [DeviceList.NTx86.5.1], and so on, which point to install sections named (for example) [WidgetInstall], [WidgetInstall.NTx86], [WidgetInstall.NTx86.5.1], and so on. You ll want to comment this INF heavily so you can figure out later what you were doing!
The reason I called this mechanism tedious is that you can t simply append an operating system-specific decoration to an install section name. You have to create a separate tree of model statements that point to uniquely named install sections. The reason there are two different schemes for identifying platform-dependent and operating system-dependent sections is historical. Windows 98/Me doesn t see the decorated section names at all. Windows 2000 will append the decorations to install section (and a few other) names but has no notion of operating system dependence. To add the operating system dependence without breaking existing INF files, Microsoft pretty much needed a different scheme.
Further along in this chapter, I ll be discussing other INF sections whose names begin with the name of the install section. If you have multiple install sections in your array, these other sections have to include the platform-dependent decoration in their names too. For example, I ll be discussing a Services section that you use to install a description of the driver in the registry. You form the name of this section by taking the base name of the install section (for example, DriverInstall) plus the platform decoration (for example, NTx86) and adding the word Services, ending up with [DriverInstall.NTx86.Services].
A typical Windows XP install section will contain a CopyFiles directive and nothing else:
[DriverInstall.ntx86] CopyFiles=DriverCopyFiles
This CopyFiles directive indicates that we want the installer to use the information in another INF section for copying files onto the end user hard disk. For the PKTDMA sample, the other section is named DriverCopyFiles:
[DriverCopyFiles] pktdma.sys,,,2
This section directs the installer to copy PKTDMA.SYS to the end user s hard disk.
The statements in a CopyFiles section have this general form:
Destination,Source,Temporary,Flags
Destination is the name (without any directory name) of the file as it will eventually exist on the end user system. Source is the name of the file as it exists on the distribution media if that name is different from the Destination name; otherwise, it s just blank as in the example. In Windows 98/Me, if you might be installing a file that will be in use at the time of installation, you specify a temporary name in the Temporary parameter. Windows 98/Me will rename the temporary file to the Destination name on the next reboot. It s not necessary to use this parameter for Windows XP installs because the system automatically generates temporary names.
The Flags parameter contains a bit mask that governs whether the system will decompress a file and how the system deals with situations in which a file by the same name already exists. The interpretation of the flags depends in part on whether the INF and the driver are part of a package that Microsoft has digitally signed after certification. Refer to the DDK documentation for a full explanation of these flags. I ordinarily specify 2 for this parameter, which basically means that the setup will be considered to have failed if this file isn t successfully copied.
The filename by itself is not sufficient to tell the installer what it needs to know to copy a file. It also needs to know which directory you want the file copied to. In addition, if you have multiple diskettes in the installation set, it needs to know which diskette contains the source file. These pieces of information come from other sections of the INF file, as suggested by Figure 15-10. In the PKTDMA example, these sections are as follows:
[DestinationDirs] DefaultDestDir=10,System32\Drivers [SourceDisksFiles] pktdma.sys=1,objchk~1\i386, [SourceDisksNames] 1="WDM Book Companion Disc",disk1
The SourceDisksFiles section indicates that the installer can find PKTDMA.SYS on disk number 1 of the set, in a subdirectory whose 8.3 pathname is objchk~1\i386. The SourceDisksNames section indicates that disk number 1 has the human-readable label WDM Book Companion Disc and contains a file named disk1 that the installer can look for to verify that the correct diskette is in the drive. Note that these section names have an interior s that s very easy to miss.
Figure 15-10. Source and destination information for file copies.
The DestinationDirs section specifies the target directories for copy operations. DefaultDestDir is the target directory to use for any file whose target directory isn t otherwise specified. You use a numeric code to specify the target directory because the end user might choose to install Windows XP in a directory with a nonstandard name. Please refer to the DDK documentation entry for the DestinationDirs section for a complete list of the codes only a few of them are in common use, as follows:
-
Directory 10 is the Windows directory (for example, \Windows or \Winnt).
-
Directory 11 is the System directory (for example, \Windows\System or \Winnt\System32).
-
Directory 12 is the Drivers directory on a Windows XP system (for example, \Winnt\System32\Drivers). Unfortunately, this number has a different meaning on a Windows 98/Me system (for example, \Windows\System\Iosubsys).
WDM drivers reside in the Drivers directory. If your CopyFiles section applies only to a Windows XP installation, you can just specify directory number 12. If you want to share a CopyFiles section between Windows 98/Me and Windows XP installs, however, I recommend that you specify 10,System32\Drivers instead because it identifies the Drivers directory in both cases.
Defining the Driver Service
The INF syntax I ve described so far is sufficient for your driver file (or files) to be copied onto the end user s hard disk. You must also arrange for the PnP Manager to know which files to load. A .Services section accomplishes that goal, as in this example:
[DriverInstall.NTx86.Services] AddService=PKTDMA,2,DriverService [DriverService] ServiceType=1 StartType=3 ErrorControl=1 ServiceBinary=%10%\system32\drivers\pktdma.sys
The 2 in the AddService directive indicates that the PKTDMA service will be the function driver for the device. You form the name of this section by appending the word Services to the name of the install section to which it applies.
The end result of these directives will be a key in the HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services branch of the registry named PKTDMA (the first parameter in the AddService directive). It will define the service entry for the driver as a kernel-mode driver (ServiceType equal to 1) that should be demand-loaded by the PnP Manager (StartType equal to 3). Errors that occur during loading should be logged but should not by themselves prevent the system from starting (ErrorControl equal to 1). The executable image can be found in \Winnt\System32\Drivers\pktdma.sys (the value of ServiceBinary). By the way, when you look in the registry, you ll see that the name of the executable file is stored under the name ImagePath rather than ServiceBinary.
It s a good idea to make the name of the service (PKTDMA in this example) the same as the filename (PKTDMA.SYS in this example) of your driver binary file. Not only does this make it obvious which service name corresponds to which driver, but it also avoids a problem that can arise when two different service keys point to the same driver: any device that uses the same driver as a then-started device but under a different service name can t itself start.
Populating the Registry
Many INF sections can contain an AddReg verb that points to an add-registry section elsewhere in the INF file:
[SomeSection] AddReg=SomeAddReg
The add-registry section in turn contains statements that use a positional syntax to define registry values:
[SomeAddReg] key, subkey, value-name, flags, value
In this syntax template, key denotes either one of the standard root keys (HKCR, HKCU, HKLM, or HKU) or the context-dependent value HKR. HKR stands for relative root key and refers to a key that depends on where you put the AddReg verb that points to this add-registry section. Table 15-6 indicates what HKR means for various different AddReg sources.
Section Containing AddReg | Meaning of HKR |
Install section for example, [DriverInstall] | Driver key |
Hardware section for example, [Driver Install.ntx86.hw] | Hardware parameters key |
Service section for example, [Driver Install.ntx86.services] | Service key |
[ClassInstall32] or [ClassInstall] | Class key |
Event logging section (specified by optional fourth parameter in AddService directive) | Event logger s subkey for this driver |
Add interface section (specified by optional third parameter in AddInterface directive) | Interface key (not discussed in this book) |
Co-installer section for example, [Driver Install.CoInstallers] | Driver key |
The subkey parameter in the add-registry syntax specifies an optional subkey of key. I rarely specify a subkey when I use HKR for the main key, but it s essential to do so if you specify a different root key. For example, if I wanted to add an entry to the RunOnce key, I d have the following statement in an AddReg section:
HKLM,Software\Microsoft\Windows\CurrentVersion\RunOnce,<more stuff>
Incidentally, if you specify an absolute registry path like this, it doesn t matter which INF section contains the AddReg verb.
The value-name parameter in an add-registry statement specifies the value you want to set. Omit this parameter (that is, put nothing between the commas) if you want to set the default value in some registry key. Within a [ClassInstall32] AddReg section, the value names DeviceCharacteristics, DeviceType, Security, and Exclusive are treated specially they refer to values that are actually in the Properties subkey of the class key.
The flags parameter indicates the data format of the value you re setting and also specifies some optional behavior. Consult the DDK entry named INF AddReg Directive for complete details about these flags. I use only a few specific values in my own INF files, as follows:
-
0, which can be abbreviated just by omitting the parameter, indicates a REG_SZ value.
-
1 indicates a REG_BINARY value.
-
0x00010001 is something of a special case. It indicates a REG_DWORD value to Windowx XP but a REG_BINARY value to Windows 98/Me (which truncates the flags value to 16 bits and therefore never sees the 0x00010000 bit).
The value parameter is the value you re trying to set. For a REG_SZ value, you can supply a quoted string (within which text quotes are indicated by two consecutive double-quotation characters). Refer to the DDK entry entitled INF Strings Section for a full exposition of the syntax rules, and note that a string that contains no blanks or special characters can be coded without any quotation marks at all. Thus, the following are equivalent:
HKR,,NTMPDriver,,"devprop.sys" -or-HKR,,NTMPDriver,,devprop.sys
You indicate a binary value as a series of 8-bit values. This requirement is a bit of a pain in the neck when you re trying to define a REG_DWORD value in an INF section that you want to use both in Windows XP and in Windows 98/Me. This is how you specify a REG_DWORD parameter in a portable way:
[DriverInstall.ntx86.hw] AddReg=HwAddReg [DriverInstall.hw] AddReg=HwAddReg [HwAddReg] HKR,,ProgrammersShoeSize,0x00010001, 0x2A, 0, 0, 0
Both systems infer that the registry should end up with a REG_DWORD equaling 42 (decimal) from the fact that 32 bits worth of data are here.
Now that we ve covered this syntax information, let s consider a few examples of registry settings in INF files.
Initializing Hardware Configuration Settings
Configuration parameters that relate to the hardware belong in the hardware parameters key. I ve previously shown the whimsical example of a ProgrammersShoeSize parameter. A more realistic example would be a driver that services two different types of device that can t be distinguished at AddDevice time but that need to be treated somewhat differently. (There shouldn t be major differences between the devices because you should write two drivers if there are.)
I would code the AddDevice function for this driver to open the hardware parameters key and interrogate a value that I ll call BoardType. This will be a REG_DWORD value that can be 0 or 1. The code inside AddDevice would look like this (except that we d really have some error checks):
HANDLE hkey; status = IoOpenDeviceRegistryKey(pdo, PLUGPLAY_REGKEY_DEVICE, KEY_READ, &hkey); UNICODE_STRING valname; RtlInitUnicodeString(&valname, L"BoardType"); KEY_VALUE_PARTIAL_INFORMATION value; ULONG junk; status = ZwQueryValueKey(hkey, &valname, KeyValuePartialInformation, &value, sizeof(value), &junk); ULONG BoardType = *(PULONG) value.Data; ZwClose(hkey);
In this fragment, I rely on the fact that the C compiler will pad the value variable to the next address boundary consistent with the most stringent alignment required for one of its members. In this case, this fact means that value.Data will really be 4 bytes long, even though it s declared as being just 1 byte long.
In the INF file, I d probably have two different model statements and install sections, like this (note that I m leaving out quite a few of the other sections that would be in the INF file):
[DeviceList] "Widget Model A"=WidgetInstallA,... "Widget Model B"=WidgetInstallB,... [WidgetInstallA.ntx86.hw] AddReg=HwAddReg.A [WidgetInstallB.ntx86.hw] AddReg=HwAddReg.B [WidgetInstallA.hw] ; for Win98/Me AddReg=HwAddReg.A [WidgetInstallB.hw] ; ditto AddReg=HwAddReg.B [HwAddReg.A] HKR,,BoardType,0x00010001, 0,0,0,0 [HwAddReg.B] HKR,,BoardType,0x00010001, 1,0,0,0
Initializing the Driver Key
The only time I use the driver key is in the Windows 98/Me sections of my INF files, and then only because the Configuration Manager requires two values to be there to specify the WDM driver for the device. Nearly all of the samples in the companion content have the following entries in the INF file for this purpose:
[DriverInstall] AddReg=DriverAddReg [DriverAddReg] HKR,,DevLoader,,*ntkern HKR,,NTMPDriver,,whatever.sys
Initializing the Service Key
I rarely use the service key in a WDM driver. For the debug version of the drivers I build for my consulting clients, however, I ve built a general-purpose set of tracing functions that dovetail with a Device Manager property page for setting various options. One aspect of the tracing facility is that the DriverEntry function needs to initialize a flag word. The only pertinent registry key that s available to DriverEntry is the service key, the name to which the RegistryPath argument points.
Part of the DriverEntry initialization in one of these drivers looks like this:
ULONG DriverTraceFlags; KObjectAttributes oa(RegistryPath, OBJ_CASE_INSENSITIVE OBJ_KERNEL_HANDLE); HANDLE hkey; NTSTATUS status = ZwOpenKey(&hkey, KEY_READ, oa); if (NT_SUCCESS(status)) { GetRegistryValue(hkey, L"DriverTraceFlags", DriverTraceFlags); ZwClose(hkey); }
(This initialization is part of an elaborate class library that I use so I don t have to keep retyping or cutting and pasting standardized code. You can undoubtedly figure out what s going on.)
My property page provider presents a page containing some standard and some custom options. The custom options originate in some INF-file syntax like this:
[DriverService] ServiceType=1 StartType=3 ErrorControl=1 ServiceBinary=%10%\system32\drivers\whatever.sys AddReg=TraceFlags [TraceFlags] HKR,,CustomTraceName1,,"Board interrupts" HKR,,CustomTraceFlag1,0x00010001, 01,00,00,00 HKR,,CustomTraceName2,,"Reads && Writes from/to board registers" HKR,,CustomTraceFlag2,0x00010001, 02,00,00,00
The AddReg directive within the [DriverService] section is the crucial part of this example. Within the AddReg section it references, HKR refers to the service key.
Event Logging
You must prepare the way for your driver to log events by setting up a subkey in the event logger s own service key. You do this with syntax like the following (taken from the INF file for the EVENTLOG sample):
[DriverInstall.ntx86.Services] AddService=EventLogService,2,DriverService,EventLogging [DriverService] ServiceType=1 StartType=3 ErrorControl=1 ServiceBinary=%10%\system32\drivers\eventlog.sys [EventLogging] AddReg=EventLogAddReg [EventLogAddReg] HKR,,EventMessageFile,0x00020000, \ "%10%\System32\iologmsg.dll;%10%\system32\drivers\EventLog.sys" HKR,,TypesSupported,0x00010001,7
Incidentally, EventMessageFile has the type REG_EXPAND_SZ.
Security Settings
You can specify two kinds of security settings in an INF file. One setting applies to the registry entries you create in an add-registry section:
[SomeSection] AddReg=SomeAddReg [SomeAddReg]
The other setting supplies an override for device object security descriptors, either for an entire class of device or for just one instance:
[ClassInstall32] AddReg=ClassInstallAddReg [ClassInstallAddReg] HKR,,Security,,<security descriptor> -or- [DriverInstall.hw] AddReg=HwAddReg [HwAddReg] HKR,,Security,,<security descriptor>
In each of these cases, <security descriptor> is a string that succinctly encodes a standard security descriptor. The security description language is documented in the Platform SDK in connection with ConvertStringSecurity DescriptorToSecurityDescriptor and its complement. For example, the following security descriptor string grants all access to the system account, read/write/execute access to administrators, and read-only access to everyone else:
D:P(A;;GA;;;SY)(A;;GRGWGX;;;BA)(A;;GR;;;WD)
(This is the SDDL_DEVOBJ_SYS_ALL_ADM_RWX_WORLD_R string from WDMSEC.H, a header in the .NET DDK.)
This string has a visual appearance that only a parser could love. Here s a guided tour:
-
D:P introduces a discretionary access control list (ACL) with the protected attribute, which means it cannot be modified by access control entries inherited from parent objects.
-
Each of the three parentheses-delimited strings represents one access control entry (ACE).
-
(A;;GA;;;SY) specifies an access-allowed access control entry (A) with default flags (omitted second parameter). It gives GENERIC_ALL rights (GA) to the system account (SY). The objects for which you specify security descriptors in an INF file are implicit in the placement of the directive. Consequently, you would never specify an object GUID or an inherited object GUID in one of these entries (omitted fourth and fifth parameters).
-
(A;;GRGWGX;;;BA) grants GENERIC_READ (GR), GENERIC_WRITE (GW), and GENERIC_EXECUTE (GX) rights to members of the built-in administrators group (BA).
-
(A;;GR;;;WD) grants GENERIC_READ access to members of the Everyone (WD) group.
Strings and Localization
In all the examples I ve shown so far, I ve used literal string values in places where you might really want to present localized text or where you re going to be using the same string value over and over. You can create a table of named strings in an INF file and then use an escape convention to reference them as needed. Here s an example:
[DeviceList] %DESCRIPTION%=DriverInstall,*WCO1503 [Strings] DESCRIPTION="DEVPROP Sample"
You can provide a set of localized string sections too. For example:
[Manufacturer] %PROSEWARE%=DeviceList
When installing your driver, the system will pick the correct localized versions of the strings. It will fall back on the nonlocalized [Strings] section when necessary.
Device Identifiers
For true Plug and Play devices, the device identifier that appears in a manufacturer s model section of an INF is very important. Plug and Play devices are those that can electronically announce their presence and identity. A bus enumerator can find these devices automatically, and it can read some sort of on-board information to find out what kind each device is. Universal serial bus (USB) devices, for example, include vendor and product identification codes in their device descriptors, and the configuration space of Peripheral Component Interconnect (PCI) devices includes vendor and product codes.
NOTE
The lists of device identifier strings that appear here now also appear in the DDK, even including the examples I used in these lists for the first edition. I nevertheless thought it worthwhile to keep this material in the second edition for ease of reference.
When an enumerator detects a device, it constructs a list of device identification strings. One entry in the list is a complete identification of the device. This entry will end up naming the hardware key in the registry. Additional entries in the list are compatible identifiers. The PnP Manager uses all of the identifiers in the list when it tries to match a device with an INF file. Enumerators place more specific identifiers ahead of less specific identifiers so that vendors can supply specific drivers that will be found in preference to more general drivers. The algorithm for constructing the strings depends on the enumerator, as follows:
PCI Devices
The full device identifier has the form
PCI\VEN_vvvv&DEV_dddd&SUBSYS_ssssssss&REV_rr
where vvvv is the vendor identifier that the PCI Special Interest Group assigned to the manufacturer of the card, dddd is the device identifier that the manufacturer assigned to the card, ssssssss is the subsystem ID (often 0) reported by the card, and rr is the revision number.
For example, the display adapter on a now-discarded laptop computer (based on the Chips and Technologies 65550 chip) had this identifier:
PCI\VEN_102C&DEV_00E0&SUBSYS_00000000&REV_04
A device can also match an INF model with any of these identifiers:
PCI\VEN_vvvv&DEV_dddd&SUBSYS_ssssssss PCI\VEN_vvvv&DEV_dddd&REV_rr PCI\VEN_vvvv&DEV_dddd PCI\VEN_vvvv&DEV_dddd&REV_rr&CC_ccss PCI\VEN_vvvv&DEV_dddd&CC_ccsspp PCI\VEN_vvvv&DEV_dddd&CC_ccss PCI\VEN_vvvv&CC_ccsspp PCI\VEN_vvvv&CC_ccss PCI\VEN_vvvv PCI\CC_ccsspp PCI\CC_ccss
in which cc is the base class code from the configuration space, ss is the subclass code, and pp is the programming interface. For example, the following additional identifiers for the aforementioned discarded laptop s display adapter would have matched the information in an INF file:
PCI\VEN_102C&DEV_00E0&SUBSYS_00000000 PCI\VEN_102C&DEV_00E0&REV_04 PCI\VEN_102C&DEV_00E0 PCI\VEN_102C&DEV_00E0&REV_04&CC_0300 PCI\VEN_102C&DEV_00E0&CC_030000 PCI\VEN_102C&DEV_00E0&CC_0300 PCI\VEN_102C&CC_030000 PCI\VEN_102C&CC_0300 PCI\VEN_102C PCI\CC_030000 PCI\CC_0300
The INF that the system actually used for driver installation was the third one, which includes just the vendor and device identifiers.
PCMCIA Devices
The device identifier for a simple device has the form
PCMCIA\Manufacturer-Product-Crc
For example, the device identifier for the 3Com network card on the same dead laptop computer is
PCMCIA\MEGAHERTZ-CC10BT/2-BF05
For an individual function on a multifunction device, the identifier has the form
PCMCIA\Manufacturer-Product-DEVdddd-Crc
where Manufacturer is the name of the manufacturer and Product is the name of the product. The PCMCIA enumerator retrieves these strings directly from tuples on the card. Crc is the 4-digit hexadecimal cyclical redundancy check (CRC) checksum for the card. The child function number (dddd in the template) is a decimal number without leading zeros.
If the card doesn t have a manufacturer name, the identifier will have one of these three forms:
PCMCIA\UNKNOWN_MANUFACTURER-Crc PCMCIA\UNKNOWN_MANUFACTURER-DEVdddd-Crc PCMCIA\MTD-0000 or PCMCIA\MTD-0002
(The last of these three alternatives is for a flash memory card with no manufacturer identifier on the card. The identifier with 0000 is for an SRAM card, while the one with 0002 is for a ROM card.)
In addition to the device identifier just described, an INF file s model section can contain an identifier composed by replacing the four-digit hexadecimal CRC with a string containing the four-digit hexadecimal manufacturer code, a hyphen, and the four-digit hexadecimal manufacturer information code (both from on-board tuples). For example:
PCMCIA\MEGAHERTZ-CC10BT/2-0128-0103
SCSI Devices
The complete device identifier is
SCSI\ttttvvvvvvvvpppppppppppppppprrrr
where tttt is a device type code, vvvvvvvv is an 8-character vendor identifier, pppppppppppppppp is a 16-character product identifier, and rrrr is a 4-character revision-level value. The device type code is the only one of the identifier components that doesn t have a fixed length. The bus driver determines this portion of the device identifier by indexing an internal string table with the device type code from the device s inquiry data, as shown in Table 15-7. (This table includes only the SCSI standard type codes. The SCSI enumerator can return additional type names for other types of device see the DDK documentation for full information.) The remaining components are just the strings that appear in the device s inquiry data but with special characters (including space, comma, and any nonprinting graphic) replaced with an underscore.
SCSI Type Code | Device Type | Generic Type |
DIRECT_ACCESS_DEVICE (0) | Disk | GenDisk |
SEQUENTIAL_ACCESS_DEVICE (1) | Sequential |
|
PRINTER_DEVICE (2) | Printer | GenPrinter |
PROCESSOR_DEVICE (3) | Processor |
|
WRITE_ONCE_READ_MULTIPLE_DEVICE (4) | Worm | GenWorm |
READ_ONLY_DIRECT_ACCESS_DEVICE (5) | CdRom | GenCdRom |
SCANNER_DEVICE (6) | Scanner | GenScanner |
OPTICAL_DEVICE (7) | Optical | GenOptical |
MEDIUM_CHANGER (8) | Changer | ScsiChanger |
COMMUNICATION_DEVICE (9) | Net | ScsiNet |
For example, a disk drive on one of my workstations has this identifier:
SCSI\DiskSEAGATE_ST39102LW_______0004
The bus driver also creates these additional identifiers:
SCSI\ttttvvvvvvvvpppppppppppppppp SCSI\ttttvvvvvvvv SCSI\vvvvvvvvppppppppppppppppr vvvvvvvvppppppppppppppppr gggg
In the third and fourth of these additional identifiers, r represents just the first character of the revision identifier. In the last identifier, gggg is the generic type code from Table 15-7.
To carry forward the example of my disk drive, the bus driver generated these additional device identifiers:
SCSI\DiskSEAGATE_ST39102LW_______ SCSI\DiskSEAGATE_ SCSI\DiskSEAGATE_ST39102LW_______0 SEAGATE_ST39102LW_______0 GenDisk
The last of these (GenDisk) is the one that appeared as the device identifier in the INF file that the PnP Manager actually used to install a driver for this disk. In fact, the generic identifier is usually the one that s in the INF file because SCSI drivers tend to be generic.
IDE Devices
IDE devices receive device identifiers that are similar to SCSI identifiers:
IDE\ttttvpvprrrrrrrr IDE\vpvprrrrrrrr IDE\ttttvpvp vpvprrrrrrrr gggg
Here tttt is a device type name (same as SCSI); vpvp is a string containing the vendor name, an underscore, the vendor s product name, and enough underscores to bring the total to 40 characters; rrrrrrrr is an 8-character revision number; and gggg is a generic type name (almost the same as SCSI type names in Table 15-7). For IDE changer devices, the generic type name is GenChanger instead of ScsiChanger; other IDE generic names are the same as SCSI names.
For example, here are the device identifiers generated for an IDE hard drive on one of my desktop systems:
IDE\DiskMaxtor_91000D8__________________________SASX1B18 IDE\Maxtor_91000D8__________________________SASX1B18 IDE\DiskMaxtor_91000D8__________________________ Maxtor_91000D8__________________________SASX1B18 GenDisk
ISAPNP Devices
The ISA Plug-And-Play (ISAPNP) enumerator constructs two hardware identifiers:
ISAPNP\id*altid
where id and altid are EISA-style identifiers for the device three letters to identify the manufacturer and four hexadecimal digits to identify the particular device. If the device in question is one function of a multifunction card, the first identifier in the list takes this form:
ISAPNP\id_DEVnnnn
where nnnn is the decimal index (with leading zeros) of the function.
For example, the codec function of the Crystal Semiconductor audio card on one of my desktop machines has these two hardware identifiers:
ISAPNP\CSC6835_DEV0000 *CSC0000
The second of these identifiers is the one that matched the actual INF file.
USB Devices
The complete device identifier is
USB\VID_vvvv&PID_dddd&REV_rrrr
where vvvv is the four-digit hexadecimal vendor code assigned by the USB committee to the vendor, dddd is the four-digit hexadecimal product code assigned to the device by the vendor, and rrrr is the revision code. All three of these values appear in the device descriptor or interface descriptor for the device.
An INF model section can also specify these alternatives:
USB\VID_vvvv&PID_dddd USB\CLASS_cc&SUBCLASS_ss&PROT_pp USB\CLASS_cc&SUBCLASS_ss USB\CLASS_cc USB\COMPOSITE
where cc is the class code from the device or interface descriptor, ss is the subclass code, and pp is the protocol code. These values are in two-digit hexadecimal format.
A composite USB device is one that has more than one interface descriptor (not counting alternate settings) in its only configuration. If no more specific device identifier for a composite device matches an INF file, the system will match the USB\COMPOSITE compatible identifier with the generic parent driver. The generic parent driver in turn creates a PDO for each interface. The device identifier for the PDO is of the following form:
USB\VID_vvvv&PID_dddd&MI_nn
where vvvv and dddd are as before and nn is the bInterfaceNumber from the interface descriptor. The generic parent driver also creates compatible identifiers based on the class, subclass, and protocol codes in the interface descriptor:
USB\CLASS_cc&SUBCLASS_ss&PROT_pp USB\CLASS_cc&SUBCLASS_ss USB\CLASS_cc
1394 Devices
The 1394 bus driver constructs these identifiers for a device:
1394\VendorName&ModelName1394\UnitSpecId&UnitSwVersion
where VendorName is the name of the hardware vendor, ModelName identifies the device, UnitSpecId identifies the software specification authority, and UnitSwVersion identifies the software specification. The information used to construct these identifiers comes from the device s configuration ROM.
If a device has vendor and model name strings, the 1394 bus driver uses the first identifier as the hardware ID and the second identifier as the one and only compatible ID. If a device lacks a vendor or model name string, the bus driver uses the second identifier as the hardware ID.
Since I don t have a 1394 bus on any of my computers, I relied on fellow driver writer Jeff Kellam to provide me with two examples. The first example is for a Sony camera, for which the device identifier is
1394\SONY&CCM-DS250_1.08
The second example is for the 1394 bus itself operating in diagnostic mode; this device identifier is
1394\031887&040892
Please refer to the DDK for information about device identifiers in composite 1394 devices.
Identifiers for Generic Devices
The PnP Manager also works with device identifiers for generic devices that can appear on many different buses. These identifiers are of the form
*PNPdddd
where dddd is a four-digit hexadecimal type identifier. Microsoft keeps moving the list of these identifiers, so every link I publish breaks within a few months. Good luck finding it!
Driver Ranking
The DDK describes the complete algorithm that the setup program uses to rank drivers when it finds several INF files that all describe a new piece of hardware with greater or lesser specificity. (See the entry entitled, How Setup Selects Drivers. ) The entire algorithm is very complex and depends on several factors:
-
Whether a driver package is signed.
-
Whether a device identifier or a compatible identifier generated by the bus driver matches the device identifier or a compatible identifier in a model statement and, if so, how specific the match is. Look back a few pages, and notice how the bus drivers generate lists of identifiers that begin with a very specific identifier and then become progressively less specific and that they generate both device identifier lists and compatible identifier lists.
-
What the DriverVer statement in a signed INF file happens to say.
-
Which operating system platform you re installing the device for.
I won t repeat all the ranking rules because you and I are unlikely to be involved in situations in which very many of them matter. Three rough rules of thumb are these: First, signed drivers have absolute preference over unsigned drivers in Windows XP. Second, within a group of drivers that are all signed or all unsigned, a match involving more specific identifiers is preferred to a less specific match. Finally, all else being equal, setup will break a tie between signed drivers by comparing the DriverVer values. Thus, you ll want to get your driver signed and to specify a full device identifier in a model statement. When you distribute updates, you ll want to be sure you update the DriverVer value so that systems everywhere realize the new driver is really new.
Some Microsoft bus drivers generate compatible device identifiers that map to signed function drivers. This creates a horrible problem for a vendor who wants to provide an unsigned driver that matches a more specific device identifier. Here s an example of what can go wrong. Let s say you ve created a wonderful new USB mouse. (This is a real example, by the way, based on something that happened to one of my consulting clients who agreed to let his story be told.) The end user plugs this mouse in to a Windows XP computer. The USB hub driver creates a set of specific device identifiers based on the vendor and product ID in the device descriptor. So far, so good. If matters rested here, the system would go on to query for a device driver, and your driver would be found. In the interval between releasing the mouse and getting the mouse and the driver certified by WHQL, the user would also see an unsigned driver warning dialog box, which he or she could choose to ignore or not.
But matters don t rest there. The USB hub driver also creates a compatible device identifier based on the USB class code in the mouse s descriptors. The compatible ID matches a Microsoft INF that specifies HIDUSB.SYS as the driver for a HID-class device. Since the Microsoft driver is signed, the system always prefers it to an unsigned driver, even when the unsigned driver is for the specific product that has been detected and even though the end user would be willing to accept the unsigned driver.
At this point, the end user has to go through exactly the right steps in the Update Driver dialog boxes to get Microsoft s signed but generic driver replaced by your unsigned but specific driver. These steps are easy to get wrong and inevitably lead to support calls that wipe out the profit from the hardware sale. Not only do your users have to do this, but so do you when you want to test your driver package in-house. Your users have to do this not only when they first install your mouse but also every time they plug it in to a different port on their USB bus.
I m withholding editorial comment on the way the ranking algorithm works out in this kind of situation. You can do a couple of things to make sure something similar doesn t happen to you. You can try to avoid putting your device in a class for which Microsoft provides a generic driver based on a compatible ID. In the case of a HID device, this approach will render it unusable at boot time and might, for that reason, be unacceptable. Alternatively, you can make sure you have enough time and money to get WHQL certification for your device before you begin to sell it.
NOTE
Microsoft offers a test signature program that allows you to get what amounts to a dummy signature for a driver. This program is not practical for a solo consultant because it requires you to obtain a VeriSign certificate, which is assuredly not free and which in turn requires that you jump through various legal hoops that have nothing to do with your ability to write drivers. Microsoft designed the current test signature program to work well for large companies, from whom the great bulk of driver submissions ultimately come, and periodically reviews its plans to see whether smaller fry can be accommodated. I started down the path toward getting a test certificate just so I could try it out and tell you readers how it worked, but I gave up when it became apparent how inflexible the legal requirements were.
Tools for INF Files
If you look in the TOOLS subdirectory of the Windows XP DDK, you ll find two useful utilities for working with INF files. CHKINF will help you validate an INF file, and GENINF will help you build a new INF file. Tools for putting Version stamps in INF files that appeared in earlier DDKs appear to have been dropped in the .NET DDK.
CHKINF
CHKINF is actually a BAT file that runs a PERL script to examine and validate an INF file. You ll obviously need a PERL interpreter to use this tool. I got a copy once upon a time from http://www.perl.com. The Hardware Compatibility Tests also include a copy.
You can run CHKINF most easily from a command prompt. For example:
C:\winddk\3615\tools\chkinf>chkinf C:\newbook\chap15\devprop\sys\devprop.inf
CHKINF generates HTML output files in an HTM subdirectory. The output includes a summary of all the errors and warnings found by CHKINF, followed by an annotated version of the INF file itself. I decided to show the unvarnished truth about DEVPROP.INF so you can see for yourself how CHKINF will help you prepare your driver package. Parents be warned: Figure 15-11 contains graphic images that may be inappropriate for all viewers. Well, perhaps it s not as shocking in black and white.
Figure 15-11. Output from CHKINF utility.
CHKINF shows me that I omitted the Version and CatalogFile sections that would be needed if I ever wanted to get WHQL certification for this device. It also incorrectly, in my view tells me that the SAMPLE class GUID is undefined, even though the INF also contains a ClassInstall32 section to fully define the SAMPLE setup class. All of the warnings relate to things in the INF file that I might not have realized were going to happen. For example, telling me about unreferenced sections might alert me to a misspelling. As it happens, I m content with the INF as it stands and don t plan to take any action based on the warnings.
You should know that CHKINF verifies only the basic syntax and structure of an INF file. It doesn t verify that you copy files to the right directories, or even that you copy all the files that you use. (The WHQL signability test will, however, check these items.) It doesn t understand the [ClassInstall] section needed for a new device class in Windows 98/Me. It doesn t understand that an undecorated section name like [DriverInstall] would be used by Windows 98/Me and so warns that the other Windows 98/Me sections in the INF aren t referenced. In other words, CHKINF isn t a perfect tool, but it will give you useful information. Plus, your INF file has to pass CHKINF muster for you to pass WHQL.
GENINF
GENINF is a graphical-based wizard that can help you construct INF files. This tool has come a long way from its beginnings in the early betas of the Windows 2000 DDK, and I think that many readers can profit from at least trying it out. It wouldn t have helped me with the samples in this book, though, because it doesn t support custom setup classes or cross-platform INF files. It s also a bit limited in terms of the device classes it does support.
GENINF also doesn t embody some of the class-specific knowledge that you need to build a working INF file. For example, I used it to build an INF file for a HID minidriver, and it didn t generate the syntax that would be needed to make sure HIDCLASS and HIDPARSE were installed on the end user system. It didn t ask me whether my device was a joystick, which would have required some additional input with regard to axis and button configuration.
I m not carping here I think GENINF is a step in the right direction, but you need to realize that it won t write your INF file for you.
Debugging Your INF File
One of the biggest problems with debugging an INF file is that it s so much like the old game of Adventure. Do something wrong, and you learn, You can t do that. In other words, the system doesn t accept your INF file, and you have to try to figure out why, usually by removing more and more lines until you finally figure out what s causing the problem. I recall once spending several hours learning that Windows Me was unhappy with my INF file because one of the Windows 2000 sections had a name longer than 28 characters (a fact that was not then documented in the DDK). Once past that hurdle, I had similar pain learning that the disk-label field in a SourceDisksNames entry isn t optional in Windows Me.
In Windows XP, the device installer logs various information about the operations it performs in a disk file named SETUPAPI.LOG in the Windows directory. You can control the verbosity of the log and the name of the log file by manually changing entries in the registry key named HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Setup. Please consult the DDK documentation for detailed information about these settings. I just set the LogLevel parameter to 0xFFFFFFFF and get way more debugging output than I can usually use, but that s easier than trying to figure out the meanings of 32 bits in a mask.
Sometimes, a problem with your INF file or another aspect of setup shows up as a problem code in the Device Manager. (See Figure 15-12.) The DDK documents the problem codes under the heading Device Manager Error Messages, and there are about 50 of them. This particular example is what happens after you install the STUPID sample from Chapter 2. Problem code 31 is CM_PROB_FAILED_ADD, and it means that the driver s AddDevice function failed. This accurately describes the way STUPID works, of course.
Figure 15-12. A setup problem in the Device Manager.